lexxy 0.7.2.beta → 0.7.4.beta

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.
@@ -4716,6 +4716,7 @@ function buildConfig() {
4716
4716
  return {
4717
4717
  ALLOWED_TAGS: ALLOWED_HTML_TAGS.concat(Lexxy.global.get("attachmentTagName")),
4718
4718
  ALLOWED_ATTR: ALLOWED_HTML_ATTRIBUTES,
4719
+ ADD_URI_SAFE_ATTR: [ "caption", "filename" ],
4719
4720
  SAFE_FOR_XML: false // So that it does not strip attributes that contains serialized HTML (like content)
4720
4721
  }
4721
4722
  }
@@ -6350,6 +6351,8 @@ const R$1=new Set(["http:","https:","mailto:","sms:","tel:"]);let y$1 = class y
6350
6351
 
6351
6352
  const Ne$1=/^(\d+(?:\.\d+)?)px$/,ve$1={BOTH:3,COLUMN:2,NO_STATUS:0,ROW:1};class xe extends Ci{__colSpan;__rowSpan;__headerState;__width;__backgroundColor;__verticalAlign;static getType(){return "tablecell"}static clone(e){return new xe(e.__headerState,e.__colSpan,e.__width,e.__key)}afterCloneFrom(e){super.afterCloneFrom(e),this.__rowSpan=e.__rowSpan,this.__backgroundColor=e.__backgroundColor,this.__verticalAlign=e.__verticalAlign;}static importDOM(){return {td:e=>({conversion:Re$1,priority:0}),th:e=>({conversion:Re$1,priority:0})}}static importJSON(e){return Fe$1().updateFromJSON(e)}updateFromJSON(e){return super.updateFromJSON(e).setHeaderStyles(e.headerState).setColSpan(e.colSpan||1).setRowSpan(e.rowSpan||1).setWidth(e.width||void 0).setBackgroundColor(e.backgroundColor||null).setVerticalAlign(e.verticalAlign||void 0)}constructor(e=ve$1.NO_STATUS,t=1,n,o){super(o),this.__colSpan=t,this.__rowSpan=1,this.__headerState=e,this.__width=n,this.__backgroundColor=null,this.__verticalAlign=void 0;}createDOM(t){const n=document.createElement(this.getTag());return this.__width&&(n.style.width=`${this.__width}px`),this.__colSpan>1&&(n.colSpan=this.__colSpan),this.__rowSpan>1&&(n.rowSpan=this.__rowSpan),null!==this.__backgroundColor&&(n.style.backgroundColor=this.__backgroundColor),Te$1(this.__verticalAlign)&&(n.style.verticalAlign=this.__verticalAlign),lt$4(n,t.theme.tableCell,this.hasHeader()&&t.theme.tableCellHeader),n}exportDOM(e){const t=super.exportDOM(e);if(Cs(t.element)){const e=t.element;e.setAttribute("data-temporary-table-cell-lexical-key",this.getKey()),e.style.border="1px solid black",this.__colSpan>1&&(e.colSpan=this.__colSpan),this.__rowSpan>1&&(e.rowSpan=this.__rowSpan),e.style.width=`${this.getWidth()||75}px`,e.style.verticalAlign=this.getVerticalAlign()||"top",e.style.textAlign="start",null===this.__backgroundColor&&this.hasHeader()&&(e.style.backgroundColor="#f2f3f5");}return t}exportJSON(){return {...super.exportJSON(),...Te$1(this.__verticalAlign)&&{verticalAlign:this.__verticalAlign},backgroundColor:this.getBackgroundColor(),colSpan:this.__colSpan,headerState:this.__headerState,rowSpan:this.__rowSpan,width:this.getWidth()}}getColSpan(){return this.getLatest().__colSpan}setColSpan(e){const t=this.getWritable();return t.__colSpan=e,t}getRowSpan(){return this.getLatest().__rowSpan}setRowSpan(e){const t=this.getWritable();return t.__rowSpan=e,t}getTag(){return this.hasHeader()?"th":"td"}setHeaderStyles(e,t=ve$1.BOTH){const n=this.getWritable();return n.__headerState=e&t|n.__headerState&~t,n}getHeaderStyles(){return this.getLatest().__headerState}setWidth(e){const t=this.getWritable();return t.__width=e,t}getWidth(){return this.getLatest().__width}getBackgroundColor(){return this.getLatest().__backgroundColor}setBackgroundColor(e){const t=this.getWritable();return t.__backgroundColor=e,t}getVerticalAlign(){return this.getLatest().__verticalAlign}setVerticalAlign(e){const t=this.getWritable();return t.__verticalAlign=e||void 0,t}toggleHeaderStyle(e){const t=this.getWritable();return (t.__headerState&e)===e?t.__headerState-=e:t.__headerState+=e,t}hasHeaderState(e){return (this.getHeaderStyles()&e)===e}hasHeader(){return this.getLatest().__headerState!==ve$1.NO_STATUS}updateDOM(e){return e.__headerState!==this.__headerState||e.__width!==this.__width||e.__colSpan!==this.__colSpan||e.__rowSpan!==this.__rowSpan||e.__backgroundColor!==this.__backgroundColor||e.__verticalAlign!==this.__verticalAlign}isShadowRoot(){return true}collapseAtStart(){return true}canBeEmpty(){return false}canIndent(){return false}}function Te$1(e){return "middle"===e||"bottom"===e}function Re$1(e){const t=e,n=e.nodeName.toLowerCase();let o;Ne$1.test(t.style.width)&&(o=parseFloat(t.style.width));const r=Fe$1("th"===n?ve$1.ROW:ve$1.NO_STATUS,t.colSpan,o);r.__rowSpan=t.rowSpan;const l=t.style.backgroundColor;""!==l&&(r.__backgroundColor=l);const s=t.style.verticalAlign;Te$1(s)&&(r.__verticalAlign=s);const i=t.style,c=(i&&i.textDecoration||"").split(" "),a="700"===i.fontWeight||"bold"===i.fontWeight,u=c.includes("line-through"),h="italic"===i.fontStyle,p=c.includes("underline");return {after:e=>{const t=[];let n=null;const o=()=>{if(n){const e=n.getFirstChild();jn(e)&&1===n.getChildrenSize()&&e.remove();}};for(const r of e)ls(r)||lr(r)||jn(r)?(lr(r)&&(a&&r.toggleFormat("bold"),u&&r.toggleFormat("strikethrough"),h&&r.toggleFormat("italic"),p&&r.toggleFormat("underline")),n?n.append(r):(n=Li().append(r),t.push(n))):(t.push(r),o(),n=null);return o(),0===t.length&&t.push(Li()),t},node:r}}function Fe$1(e=ve$1.NO_STATUS,t=1,n){return fs(new xe(e,t,n))}function Oe$1(e){return e instanceof xe}const Ae$1=re$1("INSERT_TABLE_COMMAND");function Ke$1(e,...t){const n=new URL("https://lexical.dev/docs/error"),o=new URLSearchParams;o.append("code",e);for(const e of t)o.append("v",e);throw n.search=o.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.`)}let Ee$1 = class Ee extends Ci{__height;static getType(){return "tablerow"}static clone(e){return new Ee(e.__height,e.__key)}static importDOM(){return {tr:e=>({conversion:ke$1,priority:0})}}static importJSON(e){return Me$1().updateFromJSON(e)}updateFromJSON(e){return super.updateFromJSON(e).setHeight(e.height)}constructor(e,t){super(t),this.__height=e;}exportJSON(){const e=this.getHeight();return {...super.exportJSON(),...void 0===e?void 0:{height:e}}}createDOM(t){const n=document.createElement("tr");return this.__height&&(n.style.height=`${this.__height}px`),lt$4(n,t.theme.tableRow),n}extractWithChild(e,t,n){return "html"===n}isShadowRoot(){return true}setHeight(e){const t=this.getWritable();return t.__height=e,t}getHeight(){return this.getLatest().__height}updateDOM(e){return e.__height!==this.__height}canBeEmpty(){return false}canIndent(){return false}};function ke$1(e){const n=e;let o;return Ne$1.test(n.style.height)&&(o=parseFloat(n.style.height)),{after:e=>kt$6(e,Oe$1),node:Me$1(o)}}function Me$1(e){return fs(new Ee$1(e))}function $e$1(e){return e instanceof Ee$1}const ze$1="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement,We$1=ze$1&&"documentMode"in document?document.documentMode:null,He$1=ze$1&&/^(?!.*Seamonkey)(?=.*Firefox).*/i.test(navigator.userAgent);function Le$1(e,t,n=true){const o=gn();for(let r=0;r<e;r++){const e=Me$1();for(let o=0;o<t;o++){let t=ve$1.NO_STATUS;"object"==typeof n?(0===r&&n.rows&&(t|=ve$1.ROW),0===o&&n.columns&&(t|=ve$1.COLUMN)):n&&(0===r&&(t|=ve$1.ROW),0===o&&(t|=ve$1.COLUMN));const l=Fe$1(t),s=Li();s.append(sr()),l.append(s),e.append(l);}o.append(e);}return o}function Be$1(e){const t=zs(e,e=>Oe$1(e));return Oe$1(t)?t:null}function Pe$1(e){const t=zs(e,e=>$e$1(e));if($e$1(t))return t;throw new Error("Expected table cell to be inside of table row.")}function De$1(e){const t=zs(e,e=>mn(e));if(mn(t))return t;throw new Error("Expected table cell to be inside of table.")}function Ie$1(e){const t=Pe$1(e);return De$1(t).getChildren().findIndex(e=>e.is(t))}function Ue$1(e){return Pe$1(e).getChildren().findIndex(t=>t.is(e))}ze$1&&"InputEvent"in window&&!We$1&&new window.InputEvent("input");const qe$1=(e,t)=>e===ve$1.BOTH||e===t?t:ve$1.NO_STATUS;function je$1(e=true){const t=Lr();yr(t)||wt$1(t)||Ke$1(188);const n=t.anchor.getNode(),o=t.focus.getNode(),[r]=mt$1(n),[l,,s]=mt$1(o),[,i,c]=ft(s,l,r),{startRow:a}=c,{startRow:u}=i;return e?Ge$1(a+r.__rowSpan>u+l.__rowSpan?r:l,true):Ge$1(u<a?l:r,false)}function Ge$1(e,t=true){const[,,n]=mt$1(e),[o,r]=ft(n,e,e),l=o[0].length,{startRow:s}=r;let i=null;if(t){const t=s+e.__rowSpan-1,r=o[t],c=Me$1();for(let e=0;e<l;e++){const{cell:n,startRow:o}=r[e];if(o+n.__rowSpan-1<=t){const t=r[e].cell.__headerState,n=qe$1(t,ve$1.COLUMN);c.append(Fe$1(n).append(Li()));}else n.setRowSpan(n.__rowSpan+1);}const a=n.getChildAtIndex(t);$e$1(a)||Ke$1(256),a.insertAfter(c),i=c;}else {const e=s,t=o[e],r=Me$1();for(let n=0;n<l;n++){const{cell:o,startRow:l}=t[n];if(l===e){const e=t[n].cell.__headerState,o=qe$1(e,ve$1.COLUMN);r.append(Fe$1(o).append(Li()));}else o.setRowSpan(o.__rowSpan+1);}const c=n.getChildAtIndex(e);$e$1(c)||Ke$1(257),c.insertBefore(r),i=r;}return i}function Ze$1(e=true){const t=Lr();yr(t)||wt$1(t)||Ke$1(188);const n=t.anchor.getNode(),o=t.focus.getNode(),[r]=mt$1(n),[l,,s]=mt$1(o),[,i,c]=ft(s,l,r),{startColumn:a}=c,{startColumn:u}=i;return e?tt(a+r.__colSpan>u+l.__colSpan?r:l,true):tt(u<a?l:r,false)}function tt(e,t=true,n=true){const[,,o]=mt$1(e),[r,l]=ft(o,e,e),s=r.length,{startColumn:i}=l,c=t?i+e.__colSpan-1:i-1,a=o.getFirstChild();$e$1(a)||Ke$1(120);let u=null;function h(e=ve$1.NO_STATUS){const t=Fe$1(e).append(Li());return null===u&&(u=t),t}let d=a;e:for(let e=0;e<s;e++){if(0!==e){const e=d.getNextSibling();$e$1(e)||Ke$1(121),d=e;}const t=r[e],n=t[c<0?0:c].cell.__headerState,o=qe$1(n,ve$1.ROW);if(c<0){ct$1(d,h(o));continue}const{cell:l,startColumn:s,startRow:i}=t[c];if(s+l.__colSpan-1<=c){let n=l,r=i,s=c;for(;r!==e&&n.__rowSpan>1;){if(s-=l.__colSpan,!(s>=0)){d.append(h(o));continue e}{const{cell:e,startRow:o}=t[s];n=e,r=o;}}n.insertAfter(h(o));}else l.setColSpan(l.__colSpan+1);}null!==u&&n&&it$1(u);const f=o.getColWidths();if(f){const e=[...f],t=c<0?0:c,n=e[t];e.splice(t,0,n),o.setColWidths(e);}return u}function ot$1(){const e=Lr();yr(e)||wt$1(e)||Ke$1(188);const[t,n]=e.isBackward()?[e.focus.getNode(),e.anchor.getNode()]:[e.anchor.getNode(),e.focus.getNode()],[o,,r]=mt$1(t),[l]=mt$1(n),[s,i,c]=ft(r,o,l),{startRow:a}=i,{startRow:u}=c,h=u+l.__rowSpan-1;if(s.length===h-a+1)return void r.remove();const d=s[0].length,f=s[h+1],g=r.getChildAtIndex(h+1);for(let e=h;e>=a;e--){for(let t=d-1;t>=0;t--){const{cell:n,startRow:o,startColumn:r}=s[e][t];if(r===t){if(o<a||o+n.__rowSpan-1>h){const e=Math.max(o,a),t=Math.min(n.__rowSpan+o-1,h),r=e<=t?t-e+1:0;n.setRowSpan(n.__rowSpan-r);}if(o>=a&&o+n.__rowSpan-1>h&&e===h){null===g&&Ke$1(122);let o=null;for(let n=0;n<t;n++){const t=f[n],r=t.cell;t.startRow===e+1&&(o=r),r.__colSpan>1&&(n+=r.__colSpan-1);}null===o?ct$1(g,n):o.insertAfter(n);}}}const t=r.getChildAtIndex(e);$e$1(t)||Ke$1(206,String(e)),t.remove();}if(void 0!==f){const{cell:e}=f[0];it$1(e);}else {const e=s[a-1],{cell:t}=e[0];it$1(t);}}function lt$1(){const e=Lr();yr(e)||wt$1(e)||Ke$1(188);const t=e.anchor.getNode(),n=e.focus.getNode(),[o,,r]=mt$1(t),[l]=mt$1(n),[s,i,c]=ft(r,o,l),{startColumn:a}=i,{startRow:u,startColumn:h}=c,d=Math.min(a,h),f=Math.max(a+o.__colSpan-1,h+l.__colSpan-1),g=f-d+1;if(s[0].length===f-d+1)return r.selectPrevious(),void r.remove();const m=s.length;for(let e=0;e<m;e++)for(let t=d;t<=f;t++){const{cell:n,startColumn:o}=s[e][t];if(o<d){if(t===d){const e=d-o;n.setColSpan(n.__colSpan-Math.min(g,n.__colSpan-e));}}else if(o+n.__colSpan-1>f){if(t===f){const e=f-o+1;n.setColSpan(n.__colSpan-e);}}else n.remove();}const p=s[u],C=a>h?p[a+o.__colSpan]:p[h+l.__colSpan];if(void 0!==C){const{cell:e}=C;it$1(e);}else {const e=h<a?p[h-1]:p[a-1],{cell:t}=e;it$1(t);}const _=r.getColWidths();if(_){const e=[..._];e.splice(d,g),r.setColWidths(e);}}function it$1(e){const t=e.getFirstDescendant();null==t?e.selectStart():t.getParentOrThrow().selectStart();}function ct$1(e,t){const n=e.getFirstChild();null!==n?n.insertBefore(t):e.append(t);}function at(e){if(0===e.length)return null;const t=De$1(e[0]),[n]=gt(t,null,null);let o=1/0,r=-1/0,l=1/0,s=-1/0;const i=new Set;for(const t of n)for(const n of t){if(!n||!n.cell)continue;const t=n.cell.getKey();if(!i.has(t)&&e.some(e=>e.is(n.cell))){i.add(t);const e=n.startRow,c=n.startColumn,a=n.cell.__rowSpan||1,u=n.cell.__colSpan||1;o=Math.min(o,e),r=Math.max(r,e+a-1),l=Math.min(l,c),s=Math.max(s,c+u-1);}}if(o===1/0||l===1/0)return null;const c=r-o+1,a=s-l+1,u=n[o][l];if(!u.cell)return null;const h=u.cell;h.setColSpan(a),h.setRowSpan(c);const d=new Set([h.getKey()]);for(let e=o;e<=r;e++)for(let t=l;t<=s;t++){const o=n[e][t];if(!o.cell)continue;const r=o.cell,l=r.getKey();if(!d.has(l)){d.add(l);ut(r)||h.append(...r.getChildren()),r.remove();}}return 0===h.getChildrenSize()&&h.append(Li()),h}function ut(e){if(1!==e.getChildrenSize())return false;const t=e.getFirstChildOrThrow();return !(!Ii(t)||!t.isEmpty())}function dt(e){const[t,n,o]=mt$1(e),r=t.__colSpan,l=t.__rowSpan;if(1===r&&1===l)return;const[s,i]=ft(o,t,t),{startColumn:c,startRow:a}=i,u=t.__headerState&ve$1.COLUMN,h=Array.from({length:r},(e,t)=>{let n=u;for(let e=0;0!==n&&e<s.length;e++)n&=s[e][t+c].cell.__headerState;return n}),d=t.__headerState&ve$1.ROW,f=Array.from({length:l},(e,t)=>{let n=d;for(let e=0;0!==n&&e<s[0].length;e++)n&=s[t+a][e].cell.__headerState;return n});if(r>1){for(let e=1;e<r;e++)t.insertAfter(Fe$1(h[e]|f[0]).append(Li()));t.setColSpan(1);}if(l>1){let e;for(let t=1;t<l;t++){const o=a+t,l=s[o];e=(e||n).getNextSibling(),$e$1(e)||Ke$1(125);let i=null;for(let e=0;e<c;e++){const t=l[e],n=t.cell;t.startRow===o&&(i=n),n.__colSpan>1&&(e+=n.__colSpan-1);}if(null===i)for(let n=r-1;n>=0;n--)ct$1(e,Fe$1(h[n]|f[t]).append(Li()));else for(let e=r-1;e>=0;e--)i.insertAfter(Fe$1(h[e]|f[t]).append(Li()));}t.setRowSpan(1);}}function ft(e,t,n){const[o,r,l]=gt(e,t,n);return null===r&&Ke$1(207),null===l&&Ke$1(208),[o,r,l]}function gt(e,t,n){const o=[];let r=null,l=null;function s(e){let t=o[e];return void 0===t&&(o[e]=t=[]),t}const i=e.getChildren();for(let e=0;e<i.length;e++){const o=i[e];$e$1(o)||Ke$1(209);const c=s(e);for(let a=o.getFirstChild(),u=0;null!=a;a=a.getNextSibling()){for(Oe$1(a)||Ke$1(147);void 0!==c[u];)u++;const o={cell:a,startColumn:u,startRow:e},{__rowSpan:h,__colSpan:d}=a;for(let t=0;t<h&&!(e+t>=i.length);t++){const n=s(e+t);for(let e=0;e<d;e++)n[u+e]=o;}null!==t&&null===r&&t.is(a)&&(r=o),null!==n&&null===l&&n.is(a)&&(l=o);}}return [o,r,l]}function mt$1(e){let t;if(e instanceof xe)t=e;else if("__type"in e){const o=zs(e,Oe$1);Oe$1(o)||Ke$1(148),t=o;}else {const o=zs(e.getNode(),Oe$1);Oe$1(o)||Ke$1(148),t=o;}const o=t.getParent();$e$1(o)||Ke$1(149);const r=o.getParent();return mn(r)||Ke$1(210),[t,o,r]}function pt(e,t,n){let o,r=Math.min(t.startColumn,n.startColumn),l=Math.min(t.startRow,n.startRow),s=Math.max(t.startColumn+t.cell.__colSpan-1,n.startColumn+n.cell.__colSpan-1),i=Math.max(t.startRow+t.cell.__rowSpan-1,n.startRow+n.cell.__rowSpan-1);do{o=false;for(let t=0;t<e.length;t++)for(let n=0;n<e[0].length;n++){const c=e[t][n];if(!c)continue;const a=c.startColumn+c.cell.__colSpan-1,u=c.startRow+c.cell.__rowSpan-1,h=c.startColumn<=s&&a>=r,d=c.startRow<=i&&u>=l;if(h&&d){const e=Math.min(r,c.startColumn),t=Math.max(s,a),n=Math.min(l,c.startRow),h=Math.max(i,u);e===r&&t===s&&n===l&&h===i||(r=e,s=t,l=n,i=h,o=true);}}}while(o);return {maxColumn:s,maxRow:i,minColumn:r,minRow:l}}function Ct$1(e){const[t,,n]=mt$1(e),o=n.getChildren(),r=o.length,l=o[0].getChildren().length,s=new Array(r);for(let e=0;e<r;e++)s[e]=new Array(l);for(let e=0;e<r;e++){const n=o[e].getChildren();let r=0;for(let o=0;o<n.length;o++){for(;s[e][r];)r++;const l=n[o],i=l.__rowSpan||1,c=l.__colSpan||1;for(let t=0;t<i;t++)for(let n=0;n<c;n++)s[e+t][r+n]=l;if(t===l)return {colSpan:c,columnIndex:r,rowIndex:e,rowSpan:i};r+=c;}}return null}function _t$1(e){const[[t,o,r,l],[s,i,c,a]]=["anchor","focus"].map(t=>{const o=e[t].getNode(),r=zs(o,Oe$1);Oe$1(r)||Ke$1(238,t,o.getKey(),o.getType());const l=r.getParent();$e$1(l)||Ke$1(239,t);const s=l.getParent();return mn(s)||Ke$1(240,t),[o,r,l,s]});return l.is(a)||Ke$1(241),{anchorCell:o,anchorNode:t,anchorRow:r,anchorTable:l,focusCell:i,focusNode:s,focusRow:c,focusTable:a}}let St$1 = class St{tableKey;anchor;focus;_cachedNodes;dirty;constructor(e,t,n){this.anchor=t,this.focus=n,t._selection=this,n._selection=this,this._cachedNodes=null,this.dirty=false,this.tableKey=e;}getStartEndPoints(){return [this.anchor,this.focus]}isValid(){if("root"===this.tableKey||"root"===this.anchor.key||"element"!==this.anchor.type||"root"===this.focus.key||"element"!==this.focus.type)return false;const e=xo(this.tableKey),t=xo(this.anchor.key),n=xo(this.focus.key);return null!==e&&null!==t&&null!==n}isBackward(){return this.focus.isBefore(this.anchor)}getCachedNodes(){return this._cachedNodes}setCachedNodes(e){this._cachedNodes=e;}is(e){return wt$1(e)&&this.tableKey===e.tableKey&&this.anchor.is(e.anchor)&&this.focus.is(e.focus)}set(e,t,n){this.dirty=this.dirty||e!==this.tableKey||t!==this.anchor.key||n!==this.focus.key,this.tableKey=e,this.anchor.key=t,this.focus.key=n,this._cachedNodes=null;}clone(){return new St(this.tableKey,hr(this.anchor.key,this.anchor.offset,this.anchor.type),hr(this.focus.key,this.focus.offset,this.focus.type))}isCollapsed(){return false}extract(){return this.getNodes()}insertRawText(e){}insertText(){}hasFormat(e){let t=0;this.getNodes().filter(Oe$1).forEach(e=>{const n=e.getFirstChild();Ii(n)&&(t|=n.getTextFormat());});const n=R$6[e];return 0!==(t&n)}insertNodes(e){const t=this.focus.getNode();Si(t)||Ke$1(151);St$6(t.select(0,t.getChildrenSize())).insertNodes(e);}getShape(){const{anchorCell:e,focusCell:t}=_t$1(this),n=Ct$1(e);null===n&&Ke$1(153);const o=Ct$1(t);null===o&&Ke$1(155);const r=Math.min(n.columnIndex,o.columnIndex),l=Math.max(n.columnIndex+n.colSpan-1,o.columnIndex+o.colSpan-1),s=Math.min(n.rowIndex,o.rowIndex),i=Math.max(n.rowIndex+n.rowSpan-1,o.rowIndex+o.rowSpan-1);return {fromX:Math.min(r,l),fromY:Math.min(s,i),toX:Math.max(r,l),toY:Math.max(s,i)}}getNodes(){if(!this.isValid())return [];const e=this._cachedNodes;if(null!==e)return e;const{anchorTable:t,anchorCell:n,focusCell:o}=_t$1(this),r=o.getParents()[1];if(r!==t){if(t.isParentOf(o)){const e=r.getParent();null==e&&Ke$1(159),this.set(this.tableKey,o.getKey(),e.getKey());}else {const e=t.getParent();null==e&&Ke$1(158),this.set(this.tableKey,e.getKey(),o.getKey());}return this.getNodes()}const[l,s,i]=ft(t,n,o),{minColumn:c,maxColumn:a,minRow:u,maxRow:h}=pt(l,s,i),d=new Map([[t.getKey(),t]]);let f=null;for(let e=u;e<=h;e++)for(let t=c;t<=a;t++){const{cell:n}=l[e][t],o=n.getParent();$e$1(o)||Ke$1(160),o!==f&&(d.set(o.getKey(),o),f=o),d.has(n.getKey())||Nt$1(n,e=>{d.set(e.getKey(),e);});}const g=Array.from(d.values());return ti()||(this._cachedNodes=g),g}getTextContent(){const e=this.getNodes().filter(e=>Oe$1(e));let t="";for(let n=0;n<e.length;n++){const o=e[n],r=o.__parent,l=(e[n+1]||{}).__parent;t+=o.getTextContent()+(l!==r?"\n":"\t");}return t}};function wt$1(e){return e instanceof St$1}function bt$1(){const e=hr("root",0,"element"),t=hr("root",0,"element");return new St$1("root",e,t)}function yt$1(e,t,n){e.getKey(),t.getKey(),n.getKey();const o=Lr(),r=wt$1(o)?o.clone():bt$1();return r.set(e.getKey(),t.getKey(),n.getKey()),r}function Nt$1(e,t){const n=[[e]];for(let e=n.at(-1);void 0!==e&&n.length>0;e=n.at(-1)){const o=e.pop();void 0===o?n.pop():false!==t(o)&&Si(o)&&n.push(o.getChildren());}}function vt(e,t=bs()){const n=xo(e);mn(n)||Ke$1(231,e);const o=Ft$1(n,t.getElementByKey(e));return null===o&&Ke$1(232,e),{tableElement:o,tableNode:n}}let xt$1 = class xt{focusX;focusY;listenersToRemove;table;isHighlightingCells;anchorX;anchorY;tableNodeKey;anchorCell;focusCell;anchorCellNodeKey;focusCellNodeKey;editor;tableSelection;hasHijackedSelectionStyles;isSelecting;pointerType;shouldCheckSelection;abortController;listenerOptions;nextFocus;constructor(e,t){this.isHighlightingCells=false,this.anchorX=-1,this.anchorY=-1,this.focusX=-1,this.focusY=-1,this.listenersToRemove=new Set,this.tableNodeKey=t,this.editor=e,this.table={columns:0,domRows:[],rows:0},this.tableSelection=null,this.anchorCellNodeKey=null,this.focusCellNodeKey=null,this.anchorCell=null,this.focusCell=null,this.hasHijackedSelectionStyles=false,this.isSelecting=false,this.pointerType=null,this.shouldCheckSelection=false,this.abortController=new AbortController,this.listenerOptions={signal:this.abortController.signal},this.nextFocus=null,this.trackTable();}getTable(){return this.table}removeListeners(){this.abortController.abort("removeListeners"),Array.from(this.listenersToRemove).forEach(e=>e()),this.listenersToRemove.clear();}$lookup(){return vt(this.tableNodeKey,this.editor)}trackTable(){const e=new MutationObserver(e=>{this.editor.getEditorState().read(()=>{let t=false;for(let n=0;n<e.length;n++){const o=e[n].target.nodeName;if("TABLE"===o||"TBODY"===o||"THEAD"===o||"TR"===o){t=true;break}}if(!t)return;const{tableNode:n,tableElement:o}=this.$lookup();this.table=Ht(n,o);},{editor:this.editor});});this.editor.getEditorState().read(()=>{const{tableNode:t,tableElement:n}=this.$lookup();this.table=Ht(t,n),e.observe(n,{attributes:true,childList:true,subtree:true});},{editor:this.editor});}$clearHighlight(){const e=this.editor;this.isHighlightingCells=false,this.anchorX=-1,this.anchorY=-1,this.focusX=-1,this.focusY=-1,this.tableSelection=null,this.anchorCellNodeKey=null,this.focusCellNodeKey=null,this.anchorCell=null,this.focusCell=null,this.hasHijackedSelectionStyles=false,this.$enableHighlightStyle();const{tableNode:t,tableElement:n}=this.$lookup();Lt$1(e,Ht(t,n),null),null!==Lr()&&(wo(null),e.dispatchCommand(ie$1,void 0));}$enableHighlightStyle(){const e=this.editor,{tableElement:t}=this.$lookup();ut$3(t,e._config.theme.tableSelection),t.classList.remove("disable-selection"),this.hasHijackedSelectionStyles=false;}$disableHighlightStyle(){const{tableElement:t}=this.$lookup();lt$4(t,this.editor._config.theme.tableSelection),this.hasHijackedSelectionStyles=true;}$updateTableTableSelection(e){if(null!==e){e.tableKey!==this.tableNodeKey&&Ke$1(233,e.tableKey,this.tableNodeKey);const t=this.editor;this.tableSelection=e,this.isHighlightingCells=true,this.$disableHighlightStyle(),this.updateDOMSelection(),Lt$1(t,this.table,this.tableSelection);}else this.$clearHighlight();}setShouldCheckSelection(){this.shouldCheckSelection=true;}getAndClearShouldCheckSelection(){return !!this.shouldCheckSelection&&(this.shouldCheckSelection=false,true)}setNextFocus(e){this.nextFocus=e;}getAndClearNextFocus(){const{nextFocus:e}=this;return null!==e&&(this.nextFocus=null),e}updateDOMSelection(){if(null!==this.anchorCell&&null!==this.focusCell){const e=ps(this.editor._window);e&&e.rangeCount>0&&e.removeAllRanges();}}$setFocusCellForSelection(e,t=false){const n=this.editor,{tableNode:o}=this.$lookup(),r=e.x,l=e.y;if(this.focusCell=e,this.isHighlightingCells||this.anchorX===r&&this.anchorY===l&&!t){if(r===this.focusX&&l===this.focusY)return false}else this.isHighlightingCells=true,this.$disableHighlightStyle();if(this.focusX=r,this.focusY=l,this.isHighlightingCells){const t=ln(o,e.elem);if(null!=this.tableSelection&&null!=this.anchorCellNodeKey&&null!==t)return this.focusCellNodeKey=t.getKey(),this.tableSelection=yt$1(o,this.$getAnchorTableCellOrThrow(),t),wo(this.tableSelection),n.dispatchCommand(ie$1,void 0),Lt$1(n,this.table,this.tableSelection),true}return false}$getAnchorTableCell(){return this.anchorCellNodeKey?xo(this.anchorCellNodeKey):null}$getAnchorTableCellOrThrow(){const e=this.$getAnchorTableCell();return null===e&&Ke$1(234),e}$getFocusTableCell(){return this.focusCellNodeKey?xo(this.focusCellNodeKey):null}$getFocusTableCellOrThrow(){const e=this.$getFocusTableCell();return null===e&&Ke$1(235),e}$setAnchorCellForSelection(e){this.isHighlightingCells=false,this.anchorCell=e,this.anchorX=e.x,this.anchorY=e.y;const{tableNode:t}=this.$lookup(),n=ln(t,e.elem);if(null!==n){const e=n.getKey();this.tableSelection=null!=this.tableSelection?this.tableSelection.clone():bt$1(),this.anchorCellNodeKey=e;}}$formatCells(e){const t=Lr();wt$1(t)||Ke$1(236);const n=Ar(),o=n.anchor,r=n.focus,l=t.getNodes().filter(Oe$1);l.length>0||Ke$1(237);const s=l[0].getFirstChild(),i=Ii(s)?s.getFormatFlags(e,null):null;l.forEach(t=>{o.set(t.getKey(),0,"element"),r.set(t.getKey(),t.getChildrenSize(),"element"),n.formatText(e,i);}),wo(t),this.editor.dispatchCommand(ie$1,void 0);}$clearText(){const{editor:e}=this,t=xo(this.tableNodeKey);if(!mn(t))throw new Error("Expected TableNode.");const n=Lr();wt$1(n)||Ke$1(253);const o=n.getNodes().filter(Oe$1),r=t.getFirstChild(),l=t.getLastChild();if(o.length>0&&null!==r&&null!==l&&$e$1(r)&&$e$1(l)&&o[0]===r.getFirstChild()&&o[o.length-1]===l.getLastChild()){t.selectPrevious();const n=t.getParent();return t.remove(),void(bi(n)&&n.isEmpty()&&e.dispatchCommand(ae$1,void 0))}o.forEach(e=>{if(Si(e)){const t=Li(),n=sr();t.append(n),e.append(t),e.getChildren().forEach(e=>{e!==t&&e.remove();});}}),Lt$1(e,this.table,null),wo(null),e.dispatchCommand(ie$1,void 0);}};const Tt$1="__lexicalTableSelection";function Rt$1(e){return Cs(e)&&"TABLE"===e.nodeName}function Ft$1(e,t){if(!t)return t;const n=Rt$1(t)?t:e.getDOMSlot(t).element;return "TABLE"!==n.nodeName&&Ke$1(245,t.nodeName),n}function Ot(e){return e._window}function At$1(e,t){for(let n=t,o=null;null!==n;n=n.getParent()){if(e.is(n))return o;Oe$1(n)&&(o=n);}return null}const Kt=[[Te$2,"down"],[ke$2,"up"],[Se$1,"backward"],[xe$1,"forward"]],Et$1=[he$1,ge$1,le$2],kt$1=[we$1,Oe$2];function Mt$1(e,t,o,l){const s=o.getRootElement(),i=Ot(o);null!==s&&null!==i||Ke$1(246);const c=new xt$1(o,e.getKey()),a=Ft$1(e,t);!function(e,t){null!==$t$1(e)&&Ke$1(205);e[Tt$1]=t;}(a,c),c.listenersToRemove.add(()=>function(e,t){$t$1(e)===t&&delete e[Tt$1];}(a,c));const u=t=>{if(c.pointerType=t.pointerType,0!==t.button||!Ss(t.target)||!i)return;const n=zt$1(t.target);null!==n&&o.update(()=>{const o=Ir();if(He$1&&t.shiftKey&&Xt(o,e)&&(yr(o)||wt$1(o))){const r=o.anchor.getNode(),l=At$1(e,o.anchor.getNode());if(l)c.$setAnchorCellForSelection(rn(c,l)),c.$setFocusCellForSelection(n),tn(t);else {(e.isBefore(r)?e.selectStart():e.selectEnd()).anchor.set(o.anchor.key,o.anchor.offset,o.anchor.type);}}else "touch"!==t.pointerType&&c.$setAnchorCellForSelection(n);}),(()=>{if(c.isSelecting)return;const e=()=>{c.isSelecting=false,i.removeEventListener("pointerup",e),i.removeEventListener("pointermove",t);},t=n=>{if(1&~n.buttons&&c.isSelecting)return c.isSelecting=false,i.removeEventListener("pointerup",e),void i.removeEventListener("pointermove",t);if(!Ss(n.target))return;let r=null;const l=!(He$1||a.contains(n.target));if(l)r=Wt$1(a,n.target);else for(const e of document.elementsFromPoint(n.clientX,n.clientY))if(r=Wt$1(a,e),r)break;!r||null!==c.focusCell&&r.elem===c.focusCell.elem||(c.setNextFocus({focusCell:r,override:l}),o.dispatchCommand(ie$1,void 0));};c.isSelecting=true,i.addEventListener("pointerup",e,c.listenerOptions),i.addEventListener("pointermove",t,c.listenerOptions);})();};a.addEventListener("pointerdown",u,c.listenerOptions),c.listenersToRemove.add(()=>{a.removeEventListener("pointerdown",u);});const h=e=>{if(e.detail>=3&&Ss(e.target)){null!==zt$1(e.target)&&e.preventDefault();}};a.addEventListener("mousedown",h,c.listenerOptions),c.listenersToRemove.add(()=>{a.removeEventListener("mousedown",h);});const d=e=>{const t=e.target;0===e.button&&Ss(t)&&o.update(()=>{const e=Lr();wt$1(e)&&e.tableKey===c.tableNodeKey&&s.contains(t)&&c.$clearHighlight();});};i.addEventListener("pointerdown",d,c.listenerOptions),c.listenersToRemove.add(()=>{i.removeEventListener("pointerdown",d);});for(const[t,n]of Kt)c.listenersToRemove.add(o.registerCommand(t,t=>en(o,t,n,e,c),Bi));c.listenersToRemove.add(o.registerCommand(Ee$2,t=>{const n=Lr();if(wt$1(n)){const o=At$1(e,n.focus.getNode());if(null!==o)return tn(t),o.selectEnd(),true}return false},Bi));const g=t=>()=>{const o=Lr();if(!Xt(o,e))return false;if(wt$1(o))return c.$clearText(),true;if(yr(o)){if(!Oe$1(At$1(e,o.anchor.getNode())))return false;const r=o.anchor.getNode(),l=o.focus.getNode(),s=e.isParentOf(r),i=e.isParentOf(l);if(s&&!i||i&&!s)return c.$clearText(),true;const a=zs(o.anchor.getNode(),e=>Si(e)),u=a&&zs(a,e=>Si(e)&&Oe$1(e.getParent()));if(!Si(u)||!Si(a))return false;if(t===ge$1&&null===u.getPreviousSibling())return true}return false};for(const e of Et$1)c.listenersToRemove.add(o.registerCommand(e,g(e),Bi));const p=t=>{const n=Lr();if(!wt$1(n)&&!yr(n))return false;const o=e.isParentOf(n.anchor.getNode());if(o!==e.isParentOf(n.focus.getNode())){const t=o?"anchor":"focus",r=o?"focus":"anchor",{key:l,offset:s,type:i}=n[r];return e[n[t].isBefore(n[r])?"selectPrevious":"selectNext"]()[r].set(l,s,i),false}return !!Xt(n,e)&&(!!wt$1(n)&&(t&&(t.preventDefault(),t.stopPropagation()),c.$clearText(),true))};for(const e of kt$1)c.listenersToRemove.add(o.registerCommand(e,p,Bi));return c.listenersToRemove.add(o.registerCommand(Be$2,e=>{const t=Lr();if(t){if(!wt$1(t)&&!yr(t))return false;F$1(o,Lt$4(e,ClipboardEvent)?e:null,_$1(t));const n=p(e);return yr(t)?(t.removeText(),true):n}return false},Bi)),c.listenersToRemove.add(o.registerCommand(_e$1,t=>{const o=Lr();if(!Xt(o,e))return false;if(wt$1(o))return c.$formatCells(t),true;if(yr(o)){const e=zs(o.anchor.getNode(),e=>Oe$1(e));if(!Oe$1(e))return false}return false},Bi)),c.listenersToRemove.add(o.registerCommand(Le$2,t=>{const n=Lr();if(!wt$1(n)||!Xt(n,e))return false;const o=n.anchor.getNode(),r=n.focus.getNode();if(!Oe$1(o)||!Oe$1(r))return false;if(function(e,t){if(wt$1(e)){const n=e.anchor.getNode(),o=e.focus.getNode();if(t&&n&&o){const[e]=ft(t,n,o);return n.getKey()===e[0][0].cell.getKey()&&o.getKey()===e[e.length-1].at(-1).cell.getKey()}}return false}(n,e))return e.setFormat(t),true;const[l,s,i]=ft(e,o,r),c=Math.max(s.startRow+s.cell.__rowSpan-1,i.startRow+i.cell.__rowSpan-1),a=Math.max(s.startColumn+s.cell.__colSpan-1,i.startColumn+i.cell.__colSpan-1),u=Math.min(s.startRow,i.startRow),h=Math.min(s.startColumn,i.startColumn),d=new Set;for(let e=u;e<=c;e++)for(let n=h;n<=a;n++){const o=l[e][n].cell;if(d.has(o))continue;d.add(o),o.setFormat(t);const r=o.getChildren();for(let e=0;e<r.length;e++){const n=r[e];Si(n)&&!n.isInline()&&n.setFormat(t);}}return true},Bi)),c.listenersToRemove.add(o.registerCommand(ue$1,t=>{const r=Lr();if(!Xt(r,e))return false;if(wt$1(r))return c.$clearHighlight(),false;if(yr(r)){const l=zs(r.anchor.getNode(),e=>Oe$1(e));if(!Oe$1(l))return false;if("string"==typeof t){const n=on(o,r,e);if(n)return nn(n,e,[sr(t)]),true}}return false},Bi)),l&&c.listenersToRemove.add(o.registerCommand(Me$2,t=>{const o=Lr();if(!yr(o)||!o.isCollapsed()||!Xt(o,e))return false;const r=Gt(o.anchor.getNode());return !(null===r||!e.is(Qt(r)))&&(tn(t),function(e,t){const o="next"===t?"getNextSibling":"getPreviousSibling",r="next"===t?"getFirstChild":"getLastChild",l=e[o]();if(Si(l))return l.selectEnd();const s=zs(e,$e$1);null===s&&Ke$1(247);for(let e=s[o]();$e$1(e);e=e[o]()){const t=e[r]();if(Si(t))return t.selectEnd()}const i=zs(s,mn);null===i&&Ke$1(248);"next"===t?i.selectNext():i.selectPrevious();}(r,t.shiftKey?"previous":"next"),true)},Bi)),c.listenersToRemove.add(o.registerCommand(Ve$1,t=>e.isSelected(),Bi)),c.listenersToRemove.add(o.registerCommand(oe$2,(e,t)=>{if(o!==t)return false;const{nodes:r,selection:l}=e,s=l.getStartEndPoints(),i=wt$1(l),c=yr(l)&&null!==zs(l.anchor.getNode(),e=>Oe$1(e))&&null!==zs(l.focus.getNode(),e=>Oe$1(e))||i;if(1!==r.length||!mn(r[0])||!c||null===s)return false;const[a,u]=s,[h,d,g]=mt$1(a),p=zs(u.getNode(),e=>Oe$1(e));if(!(Oe$1(h)&&Oe$1(p)&&$e$1(d)&&mn(g)))return false;const C=r[0],[_,S,b]=ft(g,h,p),[y]=gt(C,null,null),N=_.length,v=N>0?_[0].length:0;let x=S.startRow,T=S.startColumn,R=y.length,F=R>0?y[0].length:0;if(i){const e=pt(_,S,b),t=e.maxRow-e.minRow+1,n=e.maxColumn-e.minColumn+1;x=e.minRow,T=e.minColumn,R=Math.min(R,t),F=Math.min(F,n);}let O=false;const A=Math.min(N,x+R)-1,K=Math.min(v,T+F)-1,E=new Set;for(let e=x;e<=A;e++)for(let t=T;t<=K;t++){const n=_[e][t];E.has(n.cell.getKey())||(1===n.cell.__rowSpan&&1===n.cell.__colSpan||(dt(n.cell),E.add(n.cell.getKey()),O=true));}let[k]=gt(g.getWritable(),null,null);const M=R-N+x;for(let e=0;e<M;e++){Ge$1(k[N-1][0].cell);}const $=F-v+T;for(let e=0;e<$;e++){tt(k[0][v-1].cell,true,false);}[k]=gt(g.getWritable(),null,null);for(let e=x;e<x+R;e++)for(let t=T;t<T+F;t++){const n=e-x,o=t-T,r=y[n][o];if(r.startRow!==n||r.startColumn!==o)continue;const l=r.cell;if(1!==l.__rowSpan||1!==l.__colSpan){const n=[],o=Math.min(e+l.__rowSpan,x+R)-1,r=Math.min(t+l.__colSpan,T+F)-1;for(let l=e;l<=o;l++)for(let e=t;e<=r;e++){const t=k[l][e];n.push(t.cell);}at(n),O=true;}const{cell:s}=k[e][t],i=s.getChildren();l.getChildren().forEach(e=>{if(lr(e)){Li().append(e),s.append(e);}else s.append(e);}),i.forEach(e=>e.remove());}if(i&&O){const[e]=gt(g.getWritable(),null,null);e[S.startRow][S.startColumn].cell.selectEnd();}return true},Bi)),c.listenersToRemove.add(o.registerCommand(ie$1,()=>{const t=Lr(),r=Ir(),l=c.getAndClearNextFocus();if(null!==l){const{focusCell:n}=l;if(wt$1(t)&&t.tableKey===c.tableNodeKey)return (n.x!==c.focusX||n.y!==c.focusY)&&(c.$setFocusCellForSelection(n),true);if(n!==c.anchorCell&&Xt(t,e))return c.$setFocusCellForSelection(n),true}if(c.getAndClearShouldCheckSelection()&&yr(r)&&yr(t)&&t.isCollapsed()){const o=t.anchor.getNode(),r=e.getFirstChild(),l=Gt(o);if(null!==l&&$e$1(r)){const t=r.getFirstChild();if(Oe$1(t)&&e.is(zs(l,n=>n.is(e)||n.is(t))))return t.selectStart(),true}}if(yr(t)){const{anchor:n,focus:l}=t,s=n.getNode(),i=l.getNode(),a=Gt(s),u=Gt(i),h=!(!a||!e.is(Qt(a))),d=!(!u||!e.is(Qt(u))),f=h!==d,g=h&&d,m=t.isBackward();if(f){const n=t.clone();if(d){const[t]=ft(e,u,u),o=t[0][0].cell,r=t[t.length-1].at(-1).cell;n.focus.set(m?o.getKey():r.getKey(),m?o.getChildrenSize():r.getChildrenSize(),"element");}else if(h){const[t]=ft(e,a,a),o=t[0][0].cell,r=t[t.length-1].at(-1).cell;n.anchor.set(m?r.getKey():o.getKey(),m?r.getChildrenSize():0,"element");}wo(n),Pt$1(o,c);}else if(g&&(a.is(u)||(c.$setAnchorCellForSelection(rn(c,a)),c.$setFocusCellForSelection(rn(c,u),true)),"touch"===c.pointerType&&c.isSelecting&&t.isCollapsed()&&yr(r)&&r.isCollapsed())){const e=Gt(r.anchor.getNode());e&&!e.is(u)&&(c.$setAnchorCellForSelection(rn(c,e)),c.$setFocusCellForSelection(rn(c,u),true),c.pointerType=null);}}else if(t&&wt$1(t)&&t.is(r)&&t.tableKey===e.getKey()){const n=ps(i);if(n&&n.anchorNode&&n.focusNode){const r=vo(n.focusNode),l=r&&!e.isParentOf(r),s=vo(n.anchorNode),i=s&&e.isParentOf(s);if(l&&i&&n.rangeCount>0){const r=Dr(n,o);r&&(r.anchor.set(e.getKey(),t.isBackward()?e.getChildrenSize():0,"element"),n.removeAllRanges(),wo(r));}}}return t&&!t.is(r)&&(wt$1(t)||wt$1(r))&&c.tableSelection&&!c.tableSelection.is(r)?(wt$1(t)&&t.tableKey===c.tableNodeKey?c.$updateTableTableSelection(t):!wt$1(t)&&wt$1(r)&&r.tableKey===c.tableNodeKey&&c.$updateTableTableSelection(null),false):(c.hasHijackedSelectionStyles&&!e.isSelected()?function(e,t){t.$enableHighlightStyle(),Bt$1(t.table,t=>{const n=t.elem;t.highlighted=false,Vt(e,t),n.getAttribute("style")||n.removeAttribute("style");});}(o,c):!c.hasHijackedSelectionStyles&&e.isSelected()&&Pt$1(o,c),false)},Bi)),c.listenersToRemove.add(o.registerCommand(ae$1,()=>{const t=Lr();if(!yr(t)||!t.isCollapsed()||!Xt(t,e))return false;const n=on(o,t,e);return !!n&&(nn(n,e),true)},Bi)),c}function $t$1(e){return e[Tt$1]||null}function zt$1(e){let t=e;for(;null!=t;){const e=t.nodeName;if("TD"===e||"TH"===e){const e=t._cell;return void 0===e?null:e}t=t.parentNode;}return null}function Wt$1(e,t){if(!e.contains(t))return null;let n=null;for(let o=t;null!=o;o=o.parentNode){if(o===e)return n;const t=o.nodeName;"TD"!==t&&"TH"!==t||(n=o._cell||null);}return null}function Ht(e,t){const n=[],o={columns:0,domRows:n,rows:0};let r=Ft$1(e,t).querySelector("tr"),l=0,s=0;for(n.length=0;null!=r;){const e=r.nodeName;if("TD"===e||"TH"===e){const e={elem:r,hasBackgroundColor:""!==r.style.backgroundColor,highlighted:false,x:l,y:s};r._cell=e;let t=n[s];void 0===t&&(t=n[s]=[]),t[l]=e;}else {const e=r.firstChild;if(null!=e){r=e;continue}}const t=r.nextSibling;if(null!=t){l++,r=t;continue}const o=r.parentNode;if(null!=o){const e=o.nextSibling;if(null==e)break;s++,l=0,r=e;}}return o.columns=l+1,o.rows=s+1,o}function Lt$1(e,t,n){const o=new Set(n?n.getNodes():[]);Bt$1(t,(t,n)=>{const r=t.elem;o.has(n)?(t.highlighted=true,jt$1(e,t)):(t.highlighted=false,Vt(e,t),r.getAttribute("style")||r.removeAttribute("style"));});}function Bt$1(e,t){const{domRows:n}=e;for(let e=0;e<n.length;e++){const o=n[e];if(o)for(let n=0;n<o.length;n++){const r=o[n];if(!r)continue;const l=vo(r.elem);null!==l&&t(r,l,{x:n,y:e});}}}function Pt$1(e,t){t.$disableHighlightStyle(),Bt$1(t.table,t=>{t.highlighted=true,jt$1(e,t);});}const Dt$1=(e,t,n,o,r)=>{const l="forward"===r;switch(r){case "backward":case "forward":return n!==(l?e.table.columns-1:0)?qt(t.getCellNodeFromCordsOrThrow(n+(l?1:-1),o,e.table),l):o!==(l?e.table.rows-1:0)?qt(t.getCellNodeFromCordsOrThrow(l?0:e.table.columns-1,o+(l?1:-1),e.table),l):l?t.selectNext():t.selectPrevious(),true;case "up":return 0!==o?qt(t.getCellNodeFromCordsOrThrow(n,o-1,e.table),false):t.selectPrevious(),true;case "down":return o!==e.table.rows-1?qt(t.getCellNodeFromCordsOrThrow(n,o+1,e.table),true):t.selectNext(),true;default:return false}};function It$1(e,t){let n,o;if(t.startColumn===e.minColumn)n="minColumn";else {if(t.startColumn+t.cell.__colSpan-1!==e.maxColumn)return null;n="maxColumn";}if(t.startRow===e.minRow)o="minRow";else {if(t.startRow+t.cell.__rowSpan-1!==e.maxRow)return null;o="maxRow";}return [n,o]}function Ut$1([e,t]){return ["minColumn"===e?"maxColumn":"minColumn","minRow"===t?"maxRow":"minRow"]}function Jt(e,t,[n,o]){const r=t[o],l=e[r];void 0===l&&Ke$1(250,o,String(r));const s=t[n],i=l[s];return void 0===i&&Ke$1(250,n,String(s)),i}function Yt(e,t,n,o,r){const l=pt(t,n,o),s=function(e,t){const{minColumn:n,maxColumn:o,minRow:r,maxRow:l}=t;let s=1,i=1,c=1,a=1;const u=e[r],h=e[l];for(let e=n;e<=o;e++)s=Math.max(s,u[e].cell.__rowSpan),a=Math.max(a,h[e].cell.__rowSpan);for(let t=r;t<=l;t++)i=Math.max(i,e[t][n].cell.__colSpan),c=Math.max(c,e[t][o].cell.__colSpan);return {bottomSpan:a,leftSpan:i,rightSpan:c,topSpan:s}}(t,l),{topSpan:i,leftSpan:c,bottomSpan:a,rightSpan:u}=s,h=function(e,t){const n=It$1(e,t);return null===n&&Ke$1(249,t.cell.getKey()),n}(l,n),[d,f]=Ut$1(h);let g=l[d],m=l[f];"forward"===r?g+="maxColumn"===d?1:c:"backward"===r?g-="minColumn"===d?1:u:"down"===r?m+="maxRow"===f?1:i:"up"===r&&(m-="minRow"===f?1:a);const p=t[m];if(void 0===p)return false;const C=p[g];if(void 0===C)return false;const[_,S]=function(e,t,n){const o=pt(e,t,n),r=It$1(o,t);if(r)return [Jt(e,o,r),Jt(e,o,Ut$1(r))];const l=It$1(o,n);if(l)return [Jt(e,o,Ut$1(l)),Jt(e,o,l)];const s=["minColumn","minRow"];return [Jt(e,o,s),Jt(e,o,Ut$1(s))]}(t,n,C),w=rn(e,_.cell),b=rn(e,S.cell);return e.$setAnchorCellForSelection(w),e.$setFocusCellForSelection(b,true),true}function Xt(e,t){if(yr(e)||wt$1(e)){const n=t.isParentOf(e.anchor.getNode()),o=t.isParentOf(e.focus.getNode());return n&&o}return false}function qt(e,t){t?e.selectStart():e.selectEnd();}function jt$1(t,n){const o=n.elem,r=t._config.theme;Oe$1(vo(o))||Ke$1(131),lt$4(o,r.tableCellSelected);}function Vt(e,t){const n=t.elem;Oe$1(vo(n))||Ke$1(131);const r=e._config.theme;ut$3(n,r.tableCellSelected);}function Gt(e){const t=zs(e,Oe$1);return Oe$1(t)?t:null}function Qt(e){const t=zs(e,mn);return mn(t)?t:null}function Zt(e,t,o,r,l,s,i){const c=xl(o.focus,l?"previous":"next");if(Ol(c))return false;let a=c;for(const e of ul(c).iterNodeCarets("shadowRoot")){if(!Hs(e)||!Si(e.origin))return false;a=e;}const u=a.getParentAtCaret();if(!Oe$1(u))return false;const h=u,d=function(e){for(const t of ul(e).iterNodeCarets("root")){const{origin:n}=t;if(Oe$1(n)){if(Gs(t))return il(n,e.direction)}else if(!$e$1(n))break}return null}(tl(h,a.direction)),f=zs(h,mn);if(!f||!f.is(s))return false;const g=e.getElementByKey(h.getKey()),m=zt$1(g);if(!g||!m)return false;const p=dn(e,f);if(i.table=p,d)if("extend"===r){const t=zt$1(e.getElementByKey(d.origin.getKey()));if(!t)return false;i.$setAnchorCellForSelection(m),i.$setFocusCellForSelection(t,true);}else {const e=El(d);Cl(o.anchor,e),Cl(o.focus,e);}else if("extend"===r)i.$setAnchorCellForSelection(m),i.$setFocusCellForSelection(m,true);else {const e=function(e){const t=sl(e);return Gs(t)?El(t):e}(tl(f,c.direction));Cl(o.anchor,e),Cl(o.focus,e);}return tn(t),true}function en(e,t,o,r,l){if(("up"===o||"down"===o)&&function(e){const t=e.getRootElement();if(!t)return false;return t.hasAttribute("aria-controls")&&"typeahead-menu"===t.getAttribute("aria-controls")}(e))return false;const s=Lr();if(!Xt(s,r)){if(yr(s)){if("backward"===o){if(s.focus.offset>0)return false;const e=function(e){for(let t=e,n=e;null!==n;t=n,n=n.getParent())if(Si(n)){if(n!==t&&n.getFirstChild()!==t)return null;if(!n.isInline())return n}return null}(s.focus.getNode());if(!e)return false;const n=e.getPreviousSibling();return !!mn(n)&&(tn(t),t.shiftKey?s.focus.set(n.getParentOrThrow().getKey(),n.getIndexWithinParent(),"element"):n.selectEnd(),true)}if(t.shiftKey&&("up"===o||"down"===o)){const e=s.focus.getNode();if(!s.isCollapsed()&&("up"===o&&!s.isBackward()||"down"===o&&s.isBackward())){let l=zs(e,e=>mn(e));if(Oe$1(l)&&(l=zs(l,mn)),l!==r)return false;if(!l)return false;const i="down"===o?l.getNextSibling():l.getPreviousSibling();if(!i)return false;let c=0;"up"===o&&Si(i)&&(c=i.getChildrenSize());let a=i;if("up"===o&&Si(i)){const e=i.getLastChild();a=e||i,c=lr(a)?a.getTextContentSize():0;}const u=s.clone();return u.focus.set(a.getKey(),c,lr(a)?"text":"element"),wo(u),tn(t),true}if(as(e)){const e="up"===o?s.getNodes()[s.getNodes().length-1]:s.getNodes()[0];if(e){if(null!==At$1(r,e)){const e=r.getFirstDescendant(),t=r.getLastDescendant();if(!e||!t)return false;const[n]=mt$1(e),[o]=mt$1(t),s=r.getCordsFromCellNode(n,l.table),i=r.getCordsFromCellNode(o,l.table),c=r.getDOMCellFromCordsOrThrow(s.x,s.y,l.table),a=r.getDOMCellFromCordsOrThrow(i.x,i.y,l.table);return l.$setAnchorCellForSelection(c),l.$setFocusCellForSelection(a,true),true}}return false}{let r=zs(e,e=>Si(e)&&!e.isInline());if(Oe$1(r)&&(r=zs(r,mn)),!r)return false;const i="down"===o?r.getNextSibling():r.getPreviousSibling();if(mn(i)&&l.tableNodeKey===i.getKey()){const e=i.getFirstDescendant(),n=i.getLastDescendant();if(!e||!n)return false;const[r]=mt$1(e),[l]=mt$1(n),c=s.clone();return c.focus.set(("up"===o?r:l).getKey(),"up"===o?0:l.getChildrenSize(),"element"),tn(t),wo(c),true}}}}return "down"===o&&an(e)&&l.setShouldCheckSelection(),false}if(yr(s)){if("backward"===o||"forward"===o){return Zt(e,t,s,t.shiftKey?"extend":"move","backward"===o,r,l)}if(s.isCollapsed()){const{anchor:i,focus:c}=s,a=zs(i.getNode(),Oe$1),u=zs(c.getNode(),Oe$1);if(!Oe$1(a)||!a.is(u))return false;const h=Qt(a);if(h!==r&&null!=h){const n=Ft$1(h,e.getElementByKey(h.getKey()));if(null!=n)return l.table=Ht(h,n),en(e,t,o,h,l)}const d=e.getElementByKey(a.__key),f=e.getElementByKey(i.key);if(null==f||null==d)return false;let g;if("element"===i.type)g=f.getBoundingClientRect();else {const t=ps(Ot(e));if(null===t||0===t.rangeCount)return false;g=t.getRangeAt(0).getBoundingClientRect();}const m="up"===o?a.getFirstChild():a.getLastChild();if(null==m)return false;const p=e.getElementByKey(m.__key);if(null==p)return false;const C=p.getBoundingClientRect();if("up"===o?C.top>g.top-g.height:g.bottom+g.height>C.bottom){tn(t);const e=r.getCordsFromCellNode(a,l.table);if(!t.shiftKey)return Dt$1(l,r,e.x,e.y,o);{const t=r.getDOMCellFromCordsOrThrow(e.x,e.y,l.table);l.$setAnchorCellForSelection(t),l.$setFocusCellForSelection(t,true);}return true}}}else if(wt$1(s)){const{anchor:i,focus:c}=s,a=zs(i.getNode(),Oe$1),u=zs(c.getNode(),Oe$1),[h]=s.getNodes();mn(h)||Ke$1(251);const d=Ft$1(h,e.getElementByKey(h.getKey()));if(!Oe$1(a)||!Oe$1(u)||!mn(h)||null==d)return false;l.$updateTableTableSelection(s);const f=Ht(h,d),g=r.getCordsFromCellNode(a,f),m=r.getDOMCellFromCordsOrThrow(g.x,g.y,f);if(l.$setAnchorCellForSelection(m),tn(t),t.shiftKey){const[e,t,n]=ft(r,a,u);return Yt(l,e,t,n,o)}return u.selectEnd(),true}return false}function tn(e){e.preventDefault(),e.stopImmediatePropagation(),e.stopPropagation();}function nn(e,t,n){const o=Li();"first"===e?t.insertBefore(o):t.insertAfter(o),o.append(...n||[]),o.selectEnd();}function on(e,t,o){const r=o.getParent();if(!r)return;const l=ps(Ot(e));if(!l)return;const s=l.anchorNode,i=e.getElementByKey(r.getKey()),c=Ft$1(o,e.getElementByKey(o.getKey()));if(!s||!i||!c||!i.contains(s)||c.contains(s))return;const a=zs(t.anchor.getNode(),e=>Oe$1(e));if(!a)return;const u=zs(a,e=>mn(e));if(!mn(u)||!u.is(o))return;const[h,d]=ft(o,a,a),f=h[0][0],g=h[h.length-1][h[0].length-1],{startRow:m,startColumn:p}=d,C=m===f.startRow&&p===f.startColumn,_=m===g.startRow&&p===g.startColumn;return C?"first":_?"last":void 0}function rn(e,t){const{tableNode:n}=e.$lookup(),o=n.getCordsFromCellNode(t,e.table);return n.getDOMCellFromCordsOrThrow(o.x,o.y,e.table)}function ln(e,t,n){return At$1(e,vo(t,n))}function sn(t,n,r){if(!n.theme.tableAlignment)return;const l=[],s=[];for(const e of ["center","right"]){const t=n.theme.tableAlignment[e];t&&(e===r?s:l).push(t);}ut$3(t,...l),lt$4(t,...s);}const cn=new WeakSet;function an(e=bs()){return cn.has(e)}function un(e,t){cn.add(e);}class hn extends Ci{__rowStriping;__frozenColumnCount;__frozenRowCount;__colWidths;static getType(){return "table"}getColWidths(){return this.getLatest().__colWidths}setColWidths(e){const t=this.getWritable();return t.__colWidths=e,t}static clone(e){return new hn(e.__key)}afterCloneFrom(e){super.afterCloneFrom(e),this.__colWidths=e.__colWidths,this.__rowStriping=e.__rowStriping,this.__frozenColumnCount=e.__frozenColumnCount,this.__frozenRowCount=e.__frozenRowCount;}static importDOM(){return {table:e=>({conversion:fn,priority:1})}}static importJSON(e){return gn().updateFromJSON(e)}updateFromJSON(e){return super.updateFromJSON(e).setRowStriping(e.rowStriping||false).setFrozenColumns(e.frozenColumnCount||0).setFrozenRows(e.frozenRowCount||0).setColWidths(e.colWidths)}constructor(e){super(e),this.__rowStriping=false,this.__frozenColumnCount=0,this.__frozenRowCount=0,this.__colWidths=void 0;}exportJSON(){return {...super.exportJSON(),colWidths:this.getColWidths(),frozenColumnCount:this.__frozenColumnCount?this.__frozenColumnCount:void 0,frozenRowCount:this.__frozenRowCount?this.__frozenRowCount:void 0,rowStriping:this.__rowStriping?this.__rowStriping:void 0}}extractWithChild(e,t,n){return "html"===n}getDOMSlot(e){const t=Rt$1(e)?e:e.querySelector("table");return Rt$1(t)||Ke$1(229),super.getDOMSlot(e).withElement(t).withAfter(t.querySelector("colgroup"))}createDOM(t,n){const o=document.createElement("table");this.__style&&(o.style.cssText=this.__style);const r=document.createElement("colgroup");if(o.appendChild(r),Ds(r),lt$4(o,t.theme.table),this.updateTableElement(null,o,t),an(n)){const n=document.createElement("div"),r=t.theme.tableScrollableWrapper;return r?lt$4(n,r):n.style.cssText="overflow-x: auto;",n.appendChild(o),this.updateTableWrapper(null,n,o,t),n}return o}updateTableWrapper(t,n,r,l){this.__frozenColumnCount!==(t?t.__frozenColumnCount:0)&&function(t,n,r,l){l>0?(lt$4(t,r.theme.tableFrozenColumn),n.setAttribute("data-lexical-frozen-column","true")):(ut$3(t,r.theme.tableFrozenColumn),n.removeAttribute("data-lexical-frozen-column"));}(n,r,l,this.__frozenColumnCount),this.__frozenRowCount!==(t?t.__frozenRowCount:0)&&function(t,n,r,l){l>0?(lt$4(t,r.theme.tableFrozenRow),n.setAttribute("data-lexical-frozen-row","true")):(ut$3(t,r.theme.tableFrozenRow),n.removeAttribute("data-lexical-frozen-row"));}(n,r,l,this.__frozenRowCount);}updateTableElement(t,n,r){this.__style!==(t?t.__style:"")&&(n.style.cssText=this.__style),this.__rowStriping!==(!!t&&t.__rowStriping)&&function(t,n,r){r?(lt$4(t,n.theme.tableRowStriping),t.setAttribute("data-lexical-row-striping","true")):(ut$3(t,n.theme.tableRowStriping),t.removeAttribute("data-lexical-row-striping"));}(n,r,this.__rowStriping),function(e,t,n,o){const r=e.querySelector("colgroup");if(!r)return;const l=[];for(let e=0;e<n;e++){const t=document.createElement("col"),n=o&&o[e];n&&(t.style.width=`${n}px`),l.push(t);}r.replaceChildren(...l);}(n,0,this.getColumnCount(),this.getColWidths()),sn(n,r,this.getFormatType());}updateDOM(e,t,n){const o=this.getDOMSlot(t).element;return t===o===an()||(Cs(r=t)&&"DIV"===r.nodeName&&this.updateTableWrapper(e,t,o,n),this.updateTableElement(e,o,n),false);var r;}exportDOM(e){const t=super.exportDOM(e),{element:n}=t;return {after:n=>{if(t.after&&(n=t.after(n)),!Rt$1(n)&&Cs(n)&&(n=n.querySelector("table")),!Rt$1(n))return null;sn(n,e._config,this.getFormatType());const[o]=gt(this,null,null),r=new Map;for(const e of o)for(const t of e){const e=t.cell.getKey();r.has(e)||r.set(e,{colSpan:t.cell.getColSpan(),startColumn:t.startColumn});}const s=new Set;for(const e of n.querySelectorAll(":scope > tr > [data-temporary-table-cell-lexical-key]")){const t=e.getAttribute("data-temporary-table-cell-lexical-key");if(t){const n=r.get(t);if(e.removeAttribute("data-temporary-table-cell-lexical-key"),n){r.delete(t);for(let e=0;e<n.colSpan;e++)s.add(e+n.startColumn);}}}const i=n.querySelector(":scope > colgroup");if(i){const e=Array.from(n.querySelectorAll(":scope > colgroup > col")).filter((e,t)=>s.has(t));i.replaceChildren(...e);}const c=n.querySelectorAll(":scope > tr");if(c.length>0){const e=document.createElement("tbody");for(const t of c)e.appendChild(t);n.append(e);}return n},element:!Rt$1(n)&&Cs(n)?n.querySelector("table"):n}}canBeEmpty(){return false}isShadowRoot(){return true}getCordsFromCellNode(e,t){const{rows:n,domRows:o}=t;for(let t=0;t<n;t++){const n=o[t];if(null!=n)for(let o=0;o<n.length;o++){const r=n[o];if(null==r)continue;const{elem:l}=r,s=ln(this,l);if(null!==s&&e.is(s))return {x:o,y:t}}}throw new Error("Cell not found in table.")}getDOMCellFromCords(e,t,n){const{domRows:o}=n,r=o[t];if(null==r)return null;const l=r[e<r.length?e:r.length-1];return null==l?null:l}getDOMCellFromCordsOrThrow(e,t,n){const o=this.getDOMCellFromCords(e,t,n);if(!o)throw new Error("Cell not found at cords.");return o}getCellNodeFromCords(e,t,n){const o=this.getDOMCellFromCords(e,t,n);if(null==o)return null;const r=vo(o.elem);return Oe$1(r)?r:null}getCellNodeFromCordsOrThrow(e,t,n){const o=this.getCellNodeFromCords(e,t,n);if(!o)throw new Error("Node at cords not TableCellNode.");return o}getRowStriping(){return Boolean(this.getLatest().__rowStriping)}setRowStriping(e){const t=this.getWritable();return t.__rowStriping=e,t}setFrozenColumns(e){const t=this.getWritable();return t.__frozenColumnCount=e,t}getFrozenColumns(){return this.getLatest().__frozenColumnCount}setFrozenRows(e){const t=this.getWritable();return t.__frozenRowCount=e,t}getFrozenRows(){return this.getLatest().__frozenRowCount}canSelectBefore(){return true}canIndent(){return false}getColumnCount(){const e=this.getFirstChild();if(!e)return 0;let t=0;return e.getChildren().forEach(e=>{Oe$1(e)&&(t+=e.getColSpan());}),t}}function dn(e,t){const n=e.getElementByKey(t.getKey());return null===n&&Ke$1(230),Ht(t,n)}function fn(e){const n=gn();e.hasAttribute("data-lexical-row-striping")&&n.setRowStriping(true),e.hasAttribute("data-lexical-frozen-column")&&n.setFrozenColumns(1),e.hasAttribute("data-lexical-frozen-row")&&n.setFrozenRows(1);const o=e.querySelector(":scope > colgroup");if(o){let e=[];for(const t of o.querySelectorAll(":scope > col")){let n=t.style.width||"";if(!Ne$1.test(n)&&(n=t.getAttribute("width")||"",!/^\d+$/.test(n))){e=void 0;break}e.push(parseFloat(n));}e&&n.setColWidths(e);}return {after:e=>kt$6(e,$e$1),node:n}}function gn(){return fs(new hn)}function mn(e){return e instanceof hn}function pn({rows:e,columns:t,includeHeaders:n}){const o=Lr()||Ir();if(!o||!yr(o))return false;if(Qt(o.anchor.getNode()))return false;const r=Le$1(Number(e),Number(t),n);At$5(r);const l=r.getFirstDescendant();return lr(l)&&l.select(),true}function Cn(e){$e$1(e.getParent())?e.isEmpty()&&e.append(Li()):e.remove();}function _n(e){mn(e.getParent())?Bt$3(e,Oe$1):e.remove();}function Sn(e){Bt$3(e,$e$1);const[t]=gt(e,null,null),n=t.reduce((e,t)=>Math.max(e,t.length),0),o=e.getChildren();for(let e=0;e<t.length;++e){const r=o[e];if(!r)continue;$e$1(r)||Ke$1(254,r.constructor.name,r.getType());const l=t[e].reduce((e,t)=>t?1+e:e,0);if(l!==n)for(let e=l;e<n;++e){const e=Fe$1();e.append(Li()),r.append(e);}}}function wn(e){if(e.detail<3||!Ss(e.target))return false;const t=vo(e.target);if(null===t)return false;const o=zs(t,e=>Si(e)&&!e.isInline());if(null===o)return false;return !!Oe$1(o.getParent())&&(o.select(0),true)}function yn(e,t=true){const n=new Map,o=(o,r,l)=>{const s=Ft$1(o,l),i=Mt$1(o,s,e,t);n.set(r,[i,s]);},r=e.registerMutationListener(hn,t=>{e.getEditorState().read(()=>{for(const[e,r]of t){const t=n.get(e);if("created"===r||"updated"===r){const{tableNode:r,tableElement:l}=vt(e);void 0===t?o(r,e,l):l!==t[1]&&(t[0].removeListeners(),n.delete(e),o(r,e,l));}else "destroyed"===r&&void 0!==t&&(t[0].removeListeners(),n.delete(e));}},{editor:e});},{skipInitialization:false});return ()=>{r();for(const[,[e]]of n)e.removeListeners();}}function Nn(e){return e.hasNodes([hn])||Ke$1(255),U$4(e.registerCommand(Ae$1,pn,Ki),e.registerCommand(oe$2,({nodes:t,selection:n},o)=>{if(e!==o||!yr(n))return false;return null!==Qt(n.anchor.getNode())&&t.some(mn)},Ki),e.registerCommand(se$1,wn,Ki),e.registerNodeTransform(hn,Sn),e.registerNodeTransform(Ee$1,_n),e.registerNodeTransform(xe,Cn))}
6352
6353
 
6354
+ const SILENT_UPDATE_TAGS = [ Dn, zn, Kn ];
6355
+
6353
6356
  function getNearestListItemNode(node) {
6354
6357
  let current = node;
6355
6358
  while (current !== null) {
@@ -6622,6 +6625,8 @@ class LexicalToolbarElement extends HTMLElement {
6622
6625
  super();
6623
6626
  this.internals = this.attachInternals();
6624
6627
  this.internals.role = "toolbar";
6628
+
6629
+ this.#createEditorPromise();
6625
6630
  }
6626
6631
 
6627
6632
  connectedCallback() {
@@ -6654,14 +6659,26 @@ class LexicalToolbarElement extends HTMLElement {
6654
6659
  this.#refreshToolbarOverflow();
6655
6660
  this.#bindFocusListeners();
6656
6661
 
6662
+ this.resolveEditorPromise(editorElement);
6663
+
6657
6664
  this.toggleAttribute("connected", true);
6658
6665
  }
6659
6666
 
6667
+ async getEditorElement() {
6668
+ return this.editorElement || await this.editorPromise
6669
+ }
6670
+
6660
6671
  #reconnect() {
6661
6672
  this.disconnectedCallback();
6662
6673
  this.connectedCallback();
6663
6674
  }
6664
6675
 
6676
+ #createEditorPromise() {
6677
+ this.editorPromise = new Promise((resolve) => {
6678
+ this.resolveEditorPromise = resolve;
6679
+ });
6680
+ }
6681
+
6665
6682
  #installResizeObserver() {
6666
6683
  this.resizeObserver = new ResizeObserver(() => this.#refreshToolbarOverflow());
6667
6684
  this.resizeObserver.observe(this);
@@ -6694,7 +6711,7 @@ class LexicalToolbarElement extends HTMLElement {
6694
6711
 
6695
6712
  this.editor.update(() => {
6696
6713
  this.editor.dispatchCommand(command, payload);
6697
- }, { tag: isKeyboard ? zn : undefined } );
6714
+ }, { tag: isKeyboard ? zn : undefined });
6698
6715
  }
6699
6716
 
6700
6717
  #bindHotkeys() {
@@ -6730,28 +6747,24 @@ class LexicalToolbarElement extends HTMLElement {
6730
6747
  }
6731
6748
 
6732
6749
  #bindFocusListeners() {
6733
- this.editorElement.addEventListener("lexxy:focus", this.#handleFocus);
6734
- this.editorElement.addEventListener("lexxy:blur", this.#handleFocusOut);
6735
- this.addEventListener("focusout", this.#handleFocusOut);
6750
+ this.editorElement.addEventListener("lexxy:focus", this.#handleEditorFocus);
6751
+ this.editorElement.addEventListener("lexxy:blur", this.#handleEditorBlur);
6736
6752
  this.addEventListener("keydown", this.#handleKeydown);
6737
6753
  }
6738
6754
 
6739
6755
  #unbindFocusListeners() {
6740
- this.editorElement.removeEventListener("lexxy:focus", this.#handleFocus);
6741
- this.editorElement.removeEventListener("lexxy:blur", this.#handleFocusOut);
6742
- this.removeEventListener("focusout", this.#handleFocusOut);
6756
+ this.editorElement.removeEventListener("lexxy:focus", this.#handleEditorFocus);
6757
+ this.editorElement.removeEventListener("lexxy:blur", this.#handleEditorBlur);
6743
6758
  this.removeEventListener("keydown", this.#handleKeydown);
6744
6759
  }
6745
6760
 
6746
- #handleFocus = () => {
6747
- this.#resetTabIndexValues();
6761
+ #handleEditorFocus = () => {
6748
6762
  this.#focusableItems[0].tabIndex = 0;
6749
6763
  }
6750
6764
 
6751
- #handleFocusOut = () => {
6752
- if (!this.contains(document.activeElement)) {
6753
- this.#resetTabIndexValues();
6754
- }
6765
+ #handleEditorBlur = () => {
6766
+ this.#resetTabIndexValues();
6767
+ this.#closeDropdowns();
6755
6768
  }
6756
6769
 
6757
6770
  #handleKeydown = (event) => {
@@ -6768,6 +6781,7 @@ class LexicalToolbarElement extends HTMLElement {
6768
6781
  this.editor.registerUpdateListener(() => {
6769
6782
  this.editor.getEditorState().read(() => {
6770
6783
  this.#updateButtonStates();
6784
+ this.#closeDropdowns();
6771
6785
  });
6772
6786
  });
6773
6787
  }
@@ -6858,11 +6872,13 @@ class LexicalToolbarElement extends HTMLElement {
6858
6872
  }
6859
6873
 
6860
6874
  #toolbarIsOverflowing() {
6861
- return this.scrollWidth > this.clientWidth
6875
+ // Safari can report inconsistent clientWidth values on more than 100% window zoom level,
6876
+ // that was affecting the toolbar overflow calculation. We're adding +1 to get around this issue.
6877
+ return (this.scrollWidth - this.#overflow.clientWidth) > this.clientWidth + 1
6862
6878
  }
6863
6879
 
6864
6880
  #refreshToolbarOverflow = () => {
6865
- this.#resetToolbar();
6881
+ this.#resetToolbarOverflow();
6866
6882
  this.#compactMenu();
6867
6883
 
6868
6884
  this.#overflow.style.display = this.#overflowMenu.children.length ? "block" : "none";
@@ -6888,7 +6904,7 @@ class LexicalToolbarElement extends HTMLElement {
6888
6904
  }
6889
6905
  }
6890
6906
 
6891
- #resetToolbar() {
6907
+ #resetToolbarOverflow() {
6892
6908
  const items = Array.from(this.#overflowMenu.children);
6893
6909
  items.sort((a, b) => this.#itemPosition(b) - this.#itemPosition(a));
6894
6910
 
@@ -6910,6 +6926,16 @@ class LexicalToolbarElement extends HTMLElement {
6910
6926
  });
6911
6927
  }
6912
6928
 
6929
+ #closeDropdowns() {
6930
+ this.#dropdowns.forEach((details) => {
6931
+ details.open = false;
6932
+ });
6933
+ }
6934
+
6935
+ get #dropdowns() {
6936
+ return this.querySelectorAll("details")
6937
+ }
6938
+
6913
6939
  get #overflow() {
6914
6940
  return this.querySelector(".lexxy-editor__toolbar-overflow")
6915
6941
  }
@@ -6951,9 +6977,8 @@ class LexicalToolbarElement extends HTMLElement {
6951
6977
  <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M7.65422 0.711575C7.1856 0.242951 6.42579 0.242951 5.95717 0.711575C5.48853 1.18021 5.48853 1.94 5.95717 2.40864L8.70864 5.16011L2.85422 11.0145C1.44834 12.4204 1.44833 14.6998 2.85422 16.1057L7.86011 21.1115C9.26599 22.5174 11.5454 22.5174 12.9513 21.1115L19.6542 14.4087C20.1228 13.94 20.1228 13.1802 19.6542 12.7115L11.8544 4.91171L11.2542 4.31158L7.65422 0.711575ZM4.55127 12.7115L10.4057 6.85716L17.1087 13.56H4.19981C4.19981 13.253 4.31696 12.9459 4.55127 12.7115ZM23.6057 20.76C23.6057 22.0856 22.5311 23.16 21.2057 23.16C19.8802 23.16 18.8057 22.0856 18.8057 20.76C18.8057 19.5408 19.8212 18.5339 20.918 17.4462C21.0135 17.3516 21.1096 17.2563 21.2057 17.16C21.3018 17.2563 21.398 17.3516 21.4935 17.4462C22.5903 18.5339 23.6057 19.5408 23.6057 20.76Z"/></svg>
6952
6978
  </summary>
6953
6979
  <lexxy-highlight-dropdown class="lexxy-editor__toolbar-dropdown-content">
6954
- <div data-button-group="color"></div>
6955
- <div data-button-group="background-color"></div>
6956
- <button data-command="removeHighlight" class="lexxy-editor__toolbar-dropdown-reset">Remove all coloring</button>
6980
+ <div class="lexxy-highlight-colors"></div>
6981
+ <button data-command="removeHighlight" class="lexxy-editor__toolbar-button lexxy-editor__toolbar-dropdown-reset">Remove all coloring</button>
6957
6982
  </lexxy-highlight-dropdown>
6958
6983
  </details>
6959
6984
 
@@ -6965,8 +6990,8 @@ class LexicalToolbarElement extends HTMLElement {
6965
6990
  <form method="dialog">
6966
6991
  <input type="url" placeholder="Enter a URL…" class="input">
6967
6992
  <div class="lexxy-editor__toolbar-dropdown-actions">
6968
- <button type="submit" class="btn" value="link">Link</button>
6969
- <button type="button" class="btn" value="unlink">Unlink</button>
6993
+ <button type="submit" class="lexxy-editor__toolbar-button" value="link">Link</button>
6994
+ <button type="button" class="lexxy-editor__toolbar-button" value="unlink">Unlink</button>
6970
6995
  </div>
6971
6996
  </form>
6972
6997
  </lexxy-link-dropdown>
@@ -7014,16 +7039,14 @@ class LexicalToolbarElement extends HTMLElement {
7014
7039
  <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M18.2599 8.26531C15.9672 6.56386 13.1237 5.77629 10.2823 6.05535C7.4408 6.33452 4.80455 7.66079 2.88681 9.77605C1.32245 11.5016 0.326407 13.6516 0.0127834 15.9352C-0.105117 16.7939 0.608975 17.4997 1.47567 17.4997C2.34228 17.4997 3.02969 16.7915 3.19149 15.9401C3.47682 14.4379 4.17156 13.0321 5.212 11.8844C6.60637 10.3464 8.52287 9.38139 10.589 9.17839C12.655 8.97546 14.7227 9.54856 16.3897 10.7858C17.5237 11.6275 18.4165 12.7361 18.9991 13.9997H15.4063C14.578 13.9997 13.9066 14.6714 13.9063 15.4997C13.9063 16.3281 14.5779 16.9997 15.4063 16.9997H22.4063C23.2348 16.9997 23.9063 16.3281 23.9063 15.4997V8.49968C23.9061 7.67144 23.2346 6.99968 22.4063 6.99968C21.578 6.99968 20.9066 7.67144 20.9063 8.49968V11.0212C20.1897 9.9704 19.2984 9.03613 18.2599 8.26531Z"/></svg>
7015
7040
  </button>
7016
7041
 
7017
- <details class="lexxy-editor__toolbar-overflow">
7042
+ <details class="lexxy-editor__toolbar-dropdown lexxy-editor__toolbar-overflow" name="lexxy-dropdown">
7018
7043
  <summary class="lexxy-editor__toolbar-button" aria-label="Show more toolbar buttons">•••</summary>
7019
- <div class="lexxy-editor__toolbar-overflow-menu" aria-label="More toolbar buttons"></div>
7044
+ <div class="lexxy-editor__toolbar-dropdown-content lexxy-editor__toolbar-overflow-menu" aria-label="More toolbar buttons"></div>
7020
7045
  </details>
7021
7046
  `
7022
7047
  }
7023
7048
  }
7024
7049
 
7025
- customElements.define("lexxy-toolbar", LexicalToolbarElement);
7026
-
7027
7050
  /**
7028
7051
  * Copyright (c) Meta Platforms, Inc. and affiliates.
7029
7052
  *
@@ -7066,6 +7089,8 @@ var theme = {
7066
7089
  tableCellSelected: "lexxy-content__table-cell--selected",
7067
7090
  tableSelection: "lexxy-content__table--selection",
7068
7091
  tableScrollableWrapper: "lexxy-content__table-wrapper",
7092
+ tableCellHighlight: "lexxy-content__table-cell--highlight",
7093
+ tableCellFocus: "lexxy-content__table-cell--focus",
7069
7094
  list: {
7070
7095
  nested: {
7071
7096
  listitem: "lexxy-nested-listitem",
@@ -7217,6 +7242,8 @@ class ActionTextAttachmentNode extends ki {
7217
7242
  this.fileSize = fileSize;
7218
7243
  this.width = width;
7219
7244
  this.height = height;
7245
+
7246
+ this.editor = bs();
7220
7247
  }
7221
7248
 
7222
7249
  createDOM() {
@@ -7237,8 +7264,13 @@ class ActionTextAttachmentNode extends ki {
7237
7264
  return figure
7238
7265
  }
7239
7266
 
7240
- updateDOM() {
7241
- return true
7267
+ updateDOM(_prevNode, dom) {
7268
+ const caption = dom.querySelector("figcaption textarea");
7269
+ if (caption && this.caption) {
7270
+ caption.value = this.caption;
7271
+ }
7272
+
7273
+ return false
7242
7274
  }
7243
7275
 
7244
7276
  getTextContent() {
@@ -7346,8 +7378,8 @@ class ActionTextAttachmentNode extends ki {
7346
7378
  });
7347
7379
 
7348
7380
  input.addEventListener("focusin", () => input.placeholder = "Add caption...");
7349
- input.addEventListener("blur", this.#handleCaptionInputBlurred.bind(this));
7350
- input.addEventListener("keydown", this.#handleCaptionInputKeydown.bind(this));
7381
+ input.addEventListener("blur", (event) => this.#handleCaptionInputBlurred(event));
7382
+ input.addEventListener("keydown", (event) => this.#handleCaptionInputKeydown(event));
7351
7383
 
7352
7384
  caption.appendChild(input);
7353
7385
 
@@ -7355,21 +7387,24 @@ class ActionTextAttachmentNode extends ki {
7355
7387
  }
7356
7388
 
7357
7389
  #handleCaptionInputBlurred(event) {
7358
- const input = event.target;
7359
-
7360
- input.placeholder = this.fileName;
7361
- this.#updateCaptionValueFromInput(input);
7390
+ this.#updateCaptionValueFromInput(event.target);
7362
7391
  }
7363
7392
 
7364
7393
  #updateCaptionValueFromInput(input) {
7365
- dispatchCustomEvent(input, "lexxy:internal:invalidate-node", { key: this.getKey(), values: { caption: input.value } });
7394
+ input.placeholder = this.fileName;
7395
+ this.editor.update(() => {
7396
+ this.getWritable().caption = input.value;
7397
+ });
7366
7398
  }
7367
7399
 
7368
7400
  #handleCaptionInputKeydown(event) {
7369
7401
  if (event.key === "Enter") {
7370
7402
  this.#updateCaptionValueFromInput(event.target);
7371
- dispatchCustomEvent(event.target, "lexxy:internal:move-to-next-line");
7372
7403
  event.preventDefault();
7404
+
7405
+ this.editor.update(() => {
7406
+ this.selectNext();
7407
+ }, { tag: Dn });
7373
7408
  }
7374
7409
  event.stopPropagation();
7375
7410
  }
@@ -7410,56 +7445,83 @@ class ActionTextAttachmentUploadNode extends ActionTextAttachmentNode {
7410
7445
  }
7411
7446
 
7412
7447
  constructor(node, key) {
7413
- const { file, uploadUrl, blobUrlTemplate, editor, progress } = node;
7448
+ const { file, uploadUrl, blobUrlTemplate, progress, width, height, uploadError } = node;
7414
7449
  super({ ...node, contentType: file.type }, key);
7415
7450
  this.file = file;
7416
7451
  this.uploadUrl = uploadUrl;
7417
7452
  this.blobUrlTemplate = blobUrlTemplate;
7418
- this.src = null;
7419
- this.editor = editor;
7420
- this.progress = progress || 0;
7453
+ this.progress = progress ?? null;
7454
+ this.width = width;
7455
+ this.height = height;
7456
+ this.uploadError = uploadError;
7421
7457
  }
7422
7458
 
7423
7459
  createDOM() {
7460
+ if (this.uploadError) return this.#createDOMForError()
7461
+
7462
+ // This side-effect is trigged on DOM load to fire only once and avoid multiple
7463
+ // uploads through cloning. The upload is guarded from restarting in case the
7464
+ // node is reloaded from saved state such as from history.
7465
+ this.#startUploadIfNeeded();
7466
+
7424
7467
  const figure = this.createAttachmentFigure();
7425
7468
 
7426
7469
  if (this.isPreviewableAttachment) {
7427
- figure.appendChild(this.#createDOMForImage());
7470
+ const img = figure.appendChild(this.#createDOMForImage());
7471
+
7472
+ // load file locally to set dimensions and prevent vertical shifting
7473
+ loadFileIntoImage(this.file, img).then(img => this.#setDimensionsFromImage(img));
7428
7474
  } else {
7429
7475
  figure.appendChild(this.#createDOMForFile());
7430
7476
  }
7431
7477
 
7432
7478
  figure.appendChild(this.#createCaption());
7479
+ figure.appendChild(this.#createProgressBar());
7480
+
7481
+ return figure
7482
+ }
7433
7483
 
7434
- const progressBar = createElement("progress", { value: this.progress, max: 100 });
7435
- figure.appendChild(progressBar);
7484
+ updateDOM(prevNode, dom) {
7485
+ if (this.uploadError !== prevNode.uploadError) return true
7436
7486
 
7437
- // We wait for images to download so that we can pass the dimensions down to the attachment. We do this
7438
- // so that we can render images in edit mode with the dimensions set, which prevent vertical layout shifts.
7439
- this.#loadFigure(figure).then(() => this.#startUpload(progressBar, figure));
7487
+ if (prevNode.progress !== this.progress) {
7488
+ const progress = dom.querySelector("progress");
7489
+ progress.value = this.progress ?? 0;
7490
+ }
7440
7491
 
7441
- return figure
7492
+ return false
7442
7493
  }
7443
7494
 
7444
7495
  exportDOM() {
7445
7496
  const img = document.createElement("img");
7446
- if (this.src) {
7447
- img.src = this.src;
7448
- }
7449
7497
  return { element: img }
7450
7498
  }
7451
7499
 
7452
7500
  exportJSON() {
7453
7501
  return {
7502
+ ...super.exportJSON(),
7454
7503
  type: "action_text_attachment_upload",
7455
7504
  version: 1,
7456
- progress: this.progress,
7457
7505
  uploadUrl: this.uploadUrl,
7458
7506
  blobUrlTemplate: this.blobUrlTemplate,
7459
- ...super.exportJSON()
7507
+ progress: this.progress,
7508
+ width: this.width,
7509
+ height: this.height,
7510
+ uploadError: this.uploadError
7460
7511
  }
7461
7512
  }
7462
7513
 
7514
+ get #uploadStarted() {
7515
+ return this.progress !== null
7516
+ }
7517
+
7518
+ #createDOMForError() {
7519
+ const figure = this.createAttachmentFigure();
7520
+ figure.classList.add("attachment--error");
7521
+ figure.appendChild(createElement("div", { innerText: `Error uploading ${this.file?.name ?? "file"}` }));
7522
+ return figure
7523
+ }
7524
+
7463
7525
  #createDOMForImage() {
7464
7526
  return createElement("img")
7465
7527
  }
@@ -7485,94 +7547,126 @@ class ActionTextAttachmentUploadNode extends ActionTextAttachmentNode {
7485
7547
  return figcaption
7486
7548
  }
7487
7549
 
7488
- #loadFigure(figure) {
7489
- const image = figure.querySelector("img");
7490
- if (!image) {
7491
- return Promise.resolve()
7492
- } else {
7493
- return loadFileIntoImage(this.file, image)
7494
- }
7550
+ #createProgressBar() {
7551
+ return createElement("progress", { value: this.progress ?? 0, max: 100 })
7552
+ }
7553
+
7554
+ #setDimensionsFromImage({ width, height }) {
7555
+ if (this.#hasDimensions) return
7556
+
7557
+ this.editor.update(() => {
7558
+ const writable = this.getWritable();
7559
+ writable.width = width;
7560
+ writable.height = height;
7561
+ }, { tag: SILENT_UPDATE_TAGS });
7562
+ }
7563
+
7564
+ get #hasDimensions() {
7565
+ return Boolean(this.width && this.height)
7495
7566
  }
7496
7567
 
7497
- async #startUpload(progressBar, figure) {
7568
+ async #startUploadIfNeeded() {
7569
+ if (this.#uploadStarted) return
7570
+
7571
+ this.#setUploadStarted();
7572
+
7498
7573
  const { DirectUpload } = await import('@rails/activestorage');
7499
- const shouldAuthenticateUploads = Lexxy.global.get("authenticatedUploads");
7500
7574
 
7501
7575
  const upload = new DirectUpload(this.file, this.uploadUrl, this);
7576
+ upload.delegate = this.#createUploadDelegate();
7577
+ upload.create((error, blob) => {
7578
+ if (error) {
7579
+ this.#handleUploadError(error);
7580
+ } else {
7581
+ this.#showUploadedAttachment(blob);
7582
+ }
7583
+ });
7584
+ }
7585
+
7586
+ #createUploadDelegate() {
7587
+ const shouldAuthenticateUploads = Lexxy.global.get("authenticatedUploads");
7502
7588
 
7503
- upload.delegate = {
7589
+ return {
7504
7590
  directUploadWillCreateBlobWithXHR: (request) => {
7505
7591
  if (shouldAuthenticateUploads) request.withCredentials = true;
7506
7592
  },
7507
7593
  directUploadWillStoreFileWithXHR: (request) => {
7508
7594
  if (shouldAuthenticateUploads) request.withCredentials = true;
7509
7595
 
7510
- request.upload.addEventListener("progress", (event) => {
7511
- this.editor.update(() => {
7512
- progressBar.value = Math.round(event.loaded / event.total * 100);
7513
- });
7514
- });
7596
+ const uploadProgressHandler = (event) => this.#handleUploadProgress(event);
7597
+ request.upload.addEventListener("progress", uploadProgressHandler);
7515
7598
  }
7516
- };
7599
+ }
7600
+ }
7517
7601
 
7518
- upload.create((error, blob) => {
7519
- if (error) {
7520
- this.#handleUploadError(figure);
7521
- } else {
7522
- this.#loadFigurePreviewFromBlob(blob, figure).then(() => {
7523
- this.#showUploadedAttachment(figure, blob);
7524
- });
7525
- }
7526
- });
7602
+ #setUploadStarted() {
7603
+ this.#setProgress(1);
7527
7604
  }
7528
7605
 
7529
- #handleUploadError(figure) {
7530
- figure.innerHTML = "";
7531
- figure.classList.add("attachment--error");
7532
- figure.appendChild(createElement("div", { innerText: `Error uploading ${this.file?.name ?? "image"}` }));
7606
+ #handleUploadProgress(event) {
7607
+ this.#setProgress(Math.round(event.loaded / event.total * 100));
7533
7608
  }
7534
7609
 
7535
- async #showUploadedAttachment(figure, blob) {
7610
+ #setProgress(progress) {
7536
7611
  this.editor.update(() => {
7537
- const image = figure.querySelector("img");
7538
-
7539
- const src = this.blobUrlTemplate
7540
- .replace(":signed_id", blob.signed_id)
7541
- .replace(":filename", encodeURIComponent(blob.filename));
7542
- const latest = xo(this.getKey());
7543
- if (latest) {
7544
- latest.replace(new ActionTextAttachmentNode({
7545
- tagName: this.tagName,
7546
- sgid: blob.attachable_sgid,
7547
- src: blob.previewable ? blob.url : src,
7548
- altText: blob.filename,
7549
- contentType: blob.content_type,
7550
- fileName: blob.filename,
7551
- fileSize: blob.byte_size,
7552
- width: image?.naturalWidth,
7553
- previewable: blob.previewable,
7554
- height: image?.naturalHeight
7555
- }));
7556
- }
7557
- }, { tag: Dn });
7612
+ this.getWritable().progress = progress;
7613
+ }, { tag: SILENT_UPDATE_TAGS });
7558
7614
  }
7559
7615
 
7560
- async #loadFigurePreviewFromBlob(blob, figure) {
7561
- if (blob.previewable) {
7562
- return new Promise((resolve) => {
7563
- this.editor.update(() => {
7564
- const image = this.#createDOMForImage();
7565
- image.addEventListener("load", () => {
7566
- resolve();
7567
- });
7568
- image.src = blob.url;
7569
- figure.insertBefore(image, figure.firstChild);
7570
- });
7571
- })
7572
- } else {
7573
- return Promise.resolve()
7616
+ #handleUploadError(error) {
7617
+ console.warn(`Upload error for ${this.file?.name ?? "file"}: ${error}`);
7618
+ this.editor.update(() => {
7619
+ this.getWritable().uploadError = true;
7620
+ }, { tag: SILENT_UPDATE_TAGS });
7621
+ }
7622
+
7623
+ async #showUploadedAttachment(blob) {
7624
+ this.editor.update(() => {
7625
+ this.replace(this.#toActionTextAttachmentNodeWith(blob));
7626
+ }, { tag: SILENT_UPDATE_TAGS });
7627
+ }
7628
+
7629
+ #toActionTextAttachmentNodeWith(blob) {
7630
+ const conversion = new AttachmentNodeConversion(this, blob);
7631
+ return conversion.toAttachmentNode()
7632
+ }
7633
+ }
7634
+
7635
+ class AttachmentNodeConversion {
7636
+ constructor(uploadNode, blob) {
7637
+ this.uploadNode = uploadNode;
7638
+ this.blob = blob;
7639
+ }
7640
+
7641
+ toAttachmentNode() {
7642
+ return new ActionTextAttachmentNode({
7643
+ ...this.uploadNode,
7644
+ ...this.#propertiesFromBlob,
7645
+ src: this.#src
7646
+ })
7647
+ }
7648
+
7649
+ get #propertiesFromBlob() {
7650
+ const { blob } = this;
7651
+ return {
7652
+ sgid: blob.attachable_sgid,
7653
+ altText: blob.filename,
7654
+ contentType: blob.content_type,
7655
+ fileName: blob.filename,
7656
+ fileSize: blob.byte_size,
7657
+ previewable: blob.previewable,
7574
7658
  }
7575
7659
  }
7660
+
7661
+ get #src() {
7662
+ return this.blob.previewable ? this.blob.url : this.#blobSrc
7663
+ }
7664
+
7665
+ get #blobSrc() {
7666
+ return this.uploadNode.blobUrlTemplate
7667
+ .replace(":signed_id", this.blob.signed_id)
7668
+ .replace(":filename", encodeURIComponent(this.blob.filename))
7669
+ }
7576
7670
  }
7577
7671
 
7578
7672
  class HorizontalDividerNode extends ki {
@@ -7647,30 +7741,6 @@ class HorizontalDividerNode extends ki {
7647
7741
  }
7648
7742
  }
7649
7743
 
7650
- class WrappedTableNode extends hn {
7651
- static clone(node) {
7652
- return new WrappedTableNode(node.__key)
7653
- }
7654
-
7655
- exportDOM(editor) {
7656
- const superExport = super.exportDOM(editor);
7657
-
7658
- return {
7659
- ...superExport,
7660
- after: (tableElement) => {
7661
- if (superExport.after) {
7662
- tableElement = superExport.after(tableElement);
7663
- const clonedTable = tableElement.cloneNode(true);
7664
- const wrappedTable = createElement("figure", { className: "lexxy-content__table-wrapper" }, clonedTable.outerHTML);
7665
- return wrappedTable
7666
- }
7667
-
7668
- return tableElement
7669
- }
7670
- }
7671
- }
7672
- }
7673
-
7674
7744
  const COMMANDS = [
7675
7745
  "bold",
7676
7746
  "italic",
@@ -7688,13 +7758,6 @@ const COMMANDS = [
7688
7758
  "uploadAttachments",
7689
7759
 
7690
7760
  "insertTable",
7691
- "insertTableRowAbove",
7692
- "insertTableRowBelow",
7693
- "insertTableColumnAfter",
7694
- "insertTableColumnBefore",
7695
- "deleteTableRow",
7696
- "deleteTableColumn",
7697
- "deleteTable",
7698
7761
 
7699
7762
  "undo",
7700
7763
  "redo"
@@ -7803,9 +7866,7 @@ class CommandDispatcher {
7803
7866
  }
7804
7867
 
7805
7868
  dispatchInsertHorizontalDivider() {
7806
- this.editor.update(() => {
7807
- this.contents.insertAtCursorEnsuringLineBelow(new HorizontalDividerNode());
7808
- });
7869
+ this.contents.insertAtCursorEnsuringLineBelow(new HorizontalDividerNode());
7809
7870
 
7810
7871
  this.editor.focus();
7811
7872
  }
@@ -7867,41 +7928,6 @@ class CommandDispatcher {
7867
7928
  this.editor.dispatchCommand(Ae$1, { "rows": 3, "columns": 3, "includeHeaders": true });
7868
7929
  }
7869
7930
 
7870
- dispatchInsertTableRowBelow() {
7871
- je$1(true);
7872
- }
7873
-
7874
- dispatchInsertTableRowAbove() {
7875
- je$1(false);
7876
- }
7877
-
7878
- dispatchInsertTableColumnAfter() {
7879
- Ze$1(true);
7880
- }
7881
-
7882
- dispatchInsertTableColumnBefore() {
7883
- Ze$1(false);
7884
- }
7885
-
7886
- dispatchDeleteTableRow() {
7887
- ot$1();
7888
- }
7889
-
7890
- dispatchDeleteTableColumn() {
7891
- lt$1();
7892
- }
7893
-
7894
- dispatchDeleteTable() {
7895
- this.editor.update(() => {
7896
- const selection = Lr();
7897
- if (!yr(selection)) return
7898
-
7899
- const anchorNode = selection.anchor.getNode();
7900
- const tableNode = Qt(anchorNode);
7901
- tableNode.remove();
7902
- });
7903
- }
7904
-
7905
7931
  dispatchUndo() {
7906
7932
  this.editor.dispatchCommand(pe$1, undefined);
7907
7933
  }
@@ -8195,6 +8221,14 @@ class Selection {
8195
8221
  return wt$5(anchorNode, q$2) !== null
8196
8222
  }
8197
8223
 
8224
+ get isTableCellSelected() {
8225
+ const selection = Lr();
8226
+ if (!yr(selection)) return false
8227
+
8228
+ const anchorNode = selection.anchor.getNode();
8229
+ return wt$5(anchorNode, xe) !== null
8230
+ }
8231
+
8198
8232
  get nodeAfterCursor() {
8199
8233
  const { anchorNode, offset } = this.#getCollapsedSelectionData();
8200
8234
  if (!anchorNode) return null
@@ -8707,17 +8741,6 @@ function sanitize(html) {
8707
8741
  return purify.sanitize(html, buildConfig())
8708
8742
  }
8709
8743
 
8710
- // Prevent the hardcoded background color
8711
- // A background color value is set by Lexical if background is null:
8712
- // https://github.com/facebook/lexical/blob/5bbbe849bd229e1db0e7b536e6a919520ada7bb2/packages/lexical-table/src/LexicalTableCellNode.ts#L187
8713
- function registerHeaderBackgroundTransform(editor) {
8714
- return editor.registerNodeTransform(xe, (node) => {
8715
- if (node.getBackgroundColor() === null) {
8716
- node.setBackgroundColor("");
8717
- }
8718
- })
8719
- }
8720
-
8721
8744
  function dasherize(value) {
8722
8745
  return value.replace(/([A-Z])/g, (_, char) => `-${char.toLowerCase()}`)
8723
8746
  }
@@ -8741,6 +8764,10 @@ function filterMatches(text, potentialMatch) {
8741
8764
  return normalizeFilteredText(text).includes(normalizeFilteredText(potentialMatch))
8742
8765
  }
8743
8766
 
8767
+ function upcaseFirst(string) {
8768
+ return string.charAt(0).toUpperCase() + string.slice(1)
8769
+ }
8770
+
8744
8771
  class EditorConfiguration {
8745
8772
  #editorElement
8746
8773
  #config
@@ -9182,15 +9209,14 @@ class Contents {
9182
9209
  new FormatEscaper(editorElement).monitor();
9183
9210
  }
9184
9211
 
9185
- insertHtml(html) {
9212
+ insertHtml(html, { tag } = {}) {
9186
9213
  this.editor.update(() => {
9187
9214
  const selection = Lr();
9188
-
9189
9215
  if (!yr(selection)) return
9190
9216
 
9191
9217
  const nodes = m$1(this.editor, parseHtml(html));
9192
9218
  selection.insertNodes(nodes);
9193
- });
9219
+ }, { tag });
9194
9220
  }
9195
9221
 
9196
9222
  insertAtCursor(node) {
@@ -9430,7 +9456,7 @@ class Contents {
9430
9456
  const blobUrlTemplate = this.editorElement.blobUrlTemplate;
9431
9457
 
9432
9458
  this.editor.update(() => {
9433
- const uploadedImageNode = new ActionTextAttachmentUploadNode({ file: file, uploadUrl: uploadUrl, blobUrlTemplate: blobUrlTemplate, editor: this.editor });
9459
+ const uploadedImageNode = new ActionTextAttachmentUploadNode({ file: file, uploadUrl: uploadUrl, blobUrlTemplate: blobUrlTemplate });
9434
9460
  this.insertAtCursor(uploadedImageNode);
9435
9461
  }, { tag: Dn });
9436
9462
  }
@@ -10065,14 +10091,14 @@ class Clipboard {
10065
10091
 
10066
10092
  #pasteMarkdown(text) {
10067
10093
  const html = k(text);
10068
- this.contents.insertHtml(html);
10094
+ this.contents.insertHtml(html, { tag: [ Fn ] });
10069
10095
  }
10070
10096
 
10071
10097
  #pasteRichText(clipboardData) {
10072
10098
  this.editor.update(() => {
10073
10099
  const selection = Lr();
10074
10100
  R$3(clipboardData, selection, this.editor);
10075
- });
10101
+ }, { tag: Fn });
10076
10102
  }
10077
10103
 
10078
10104
  #handlePastedFiles(clipboardData) {
@@ -10119,7 +10145,7 @@ class Extensions {
10119
10145
 
10120
10146
  initializeToolbars() {
10121
10147
  if (this.#lexxyToolbar) {
10122
- this.enabledExtensions.forEach(ext => ext.initializeToobar(this.#lexxyToolbar));
10148
+ this.enabledExtensions.forEach(ext => ext.initializeToolbar(this.#lexxyToolbar));
10123
10149
  }
10124
10150
  }
10125
10151
 
@@ -10306,6 +10332,124 @@ function $applyLanguage(conversionOutput, element) {
10306
10332
  conversionOutput.node.setLanguage(language);
10307
10333
  }
10308
10334
 
10335
+ class WrappedTableNode extends hn {
10336
+ static clone(node) {
10337
+ return new WrappedTableNode(node.__key)
10338
+ }
10339
+
10340
+ exportDOM(editor) {
10341
+ const superExport = super.exportDOM(editor);
10342
+
10343
+ return {
10344
+ ...superExport,
10345
+ after: (tableElement) => {
10346
+ if (superExport.after) {
10347
+ tableElement = superExport.after(tableElement);
10348
+ const clonedTable = tableElement.cloneNode(true);
10349
+ const wrappedTable = createElement("figure", { className: "lexxy-content__table-wrapper" }, clonedTable.outerHTML);
10350
+ return wrappedTable
10351
+ }
10352
+
10353
+ return tableElement
10354
+ }
10355
+ }
10356
+ }
10357
+ }
10358
+
10359
+ const TablesLexicalExtension = Kl({
10360
+ name: "lexxy/tables",
10361
+ nodes: [
10362
+ WrappedTableNode,
10363
+ {
10364
+ replace: hn,
10365
+ with: () => new WrappedTableNode()
10366
+ },
10367
+ xe,
10368
+ Ee$1
10369
+ ],
10370
+ register(editor) {
10371
+ // Register Lexical table plugins
10372
+ Nn(editor);
10373
+ yn(editor, true);
10374
+ un(editor);
10375
+
10376
+ // Bug fix: Prevent hardcoded background color (Lexical #8089)
10377
+ editor.registerNodeTransform(xe, (node) => {
10378
+ if (node.getBackgroundColor() === null) {
10379
+ node.setBackgroundColor("");
10380
+ }
10381
+ });
10382
+
10383
+ // Bug fix: Fix column header states (Lexical #8090)
10384
+ editor.registerNodeTransform(xe, (node) => {
10385
+ const headerState = node.getHeaderStyles();
10386
+
10387
+ if (headerState !== ve$1.ROW) return
10388
+
10389
+ const rowParent = node.getParent();
10390
+ const tableNode = rowParent?.getParent();
10391
+ if (!tableNode) return
10392
+
10393
+ const rows = tableNode.getChildren();
10394
+ const cellIndex = rowParent.getChildren().indexOf(node);
10395
+
10396
+ const cellsInRow = rowParent.getChildren();
10397
+ const isHeaderRow = cellsInRow.every(cell =>
10398
+ cell.getHeaderStyles() !== ve$1.NO_STATUS
10399
+ );
10400
+
10401
+ const isHeaderColumn = rows.every(row => {
10402
+ const cell = row.getChildren()[cellIndex];
10403
+ return cell && cell.getHeaderStyles() !== ve$1.NO_STATUS
10404
+ });
10405
+
10406
+ let newHeaderState = ve$1.NO_STATUS;
10407
+
10408
+ if (isHeaderRow) {
10409
+ newHeaderState |= ve$1.ROW;
10410
+ }
10411
+
10412
+ if (isHeaderColumn) {
10413
+ newHeaderState |= ve$1.COLUMN;
10414
+ }
10415
+
10416
+ if (newHeaderState !== headerState) {
10417
+ node.setHeaderStyles(newHeaderState, ve$1.BOTH);
10418
+ }
10419
+ });
10420
+
10421
+ editor.registerCommand("insertTableRowAfter", () => {
10422
+ je$1(true);
10423
+ }, Ri);
10424
+
10425
+ editor.registerCommand("insertTableRowBefore", () => {
10426
+ je$1(false);
10427
+ }, Ri);
10428
+
10429
+ editor.registerCommand("insertTableColumnAfter", () => {
10430
+ Ze$1(true);
10431
+ }, Ri);
10432
+
10433
+ editor.registerCommand("insertTableColumnBefore", () => {
10434
+ Ze$1(false);
10435
+ }, Ri);
10436
+
10437
+ editor.registerCommand("deleteTableRow", () => {
10438
+ ot$1();
10439
+ }, Ri);
10440
+
10441
+ editor.registerCommand("deleteTableColumn", () => {
10442
+ lt$1();
10443
+ }, Ri);
10444
+
10445
+ editor.registerCommand("deleteTable", () => {
10446
+ const selection = Lr();
10447
+ if (!yr(selection)) return false
10448
+ Qt(selection.anchor.getNode())?.remove();
10449
+ }, Ri);
10450
+ }
10451
+ });
10452
+
10309
10453
  class LexicalEditorElement extends HTMLElement {
10310
10454
  static formAssociated = true
10311
10455
  static debug = false
@@ -10495,10 +10639,8 @@ class LexicalEditorElement extends HTMLElement {
10495
10639
  #initialize() {
10496
10640
  this.#synchronizeWithChanges();
10497
10641
  this.#registerComponents();
10498
- this.#listenForInvalidatedNodes();
10499
10642
  this.#handleEnter();
10500
- this.#handleFocus();
10501
- this.#handleTables();
10643
+ this.#registerFocusEvents();
10502
10644
  this.#attachDebugHooks();
10503
10645
  this.#attachToolbar();
10504
10646
  this.#loadInitialValue();
@@ -10509,11 +10651,11 @@ class LexicalEditorElement extends HTMLElement {
10509
10651
  this.editorContentElement ||= this.#createEditorContentElement();
10510
10652
 
10511
10653
  const editor = $t$2({
10512
- name: "lexxy/core",
10513
- namespace: "Lexxy",
10514
- theme: theme,
10515
- nodes: this.#lexicalNodes
10516
- },
10654
+ name: "lexxy/core",
10655
+ namespace: "Lexxy",
10656
+ theme: theme,
10657
+ nodes: this.#lexicalNodes
10658
+ },
10517
10659
  ...this.#lexicalExtensions
10518
10660
  );
10519
10661
 
@@ -10523,10 +10665,11 @@ class LexicalEditorElement extends HTMLElement {
10523
10665
  }
10524
10666
 
10525
10667
  get #lexicalExtensions() {
10526
- const extensions = [ ];
10668
+ const extensions = [];
10527
10669
  const richTextExtensions = [
10528
10670
  this.highlighter.lexicalExtension,
10529
- TrixContentExtension
10671
+ TrixContentExtension,
10672
+ TablesLexicalExtension
10530
10673
  ];
10531
10674
 
10532
10675
  if (this.supportsRichText) {
@@ -10551,14 +10694,7 @@ class LexicalEditorElement extends HTMLElement {
10551
10694
  et$1,
10552
10695
  y$1,
10553
10696
  A,
10554
- HorizontalDividerNode,
10555
- WrappedTableNode,
10556
- {
10557
- replace: hn,
10558
- with: () => { return new WrappedTableNode() }
10559
- },
10560
- xe,
10561
- Ee$1,
10697
+ HorizontalDividerNode
10562
10698
  );
10563
10699
  }
10564
10700
 
@@ -10672,11 +10808,8 @@ class LexicalEditorElement extends HTMLElement {
10672
10808
  }
10673
10809
 
10674
10810
  #registerTableComponents() {
10675
- Nn(this.editor);
10676
- this.tableHandler = createElement("lexxy-table-handler");
10677
- this.append(this.tableHandler);
10678
-
10679
- this.#addUnregisterHandler(registerHeaderBackgroundTransform(this.editor));
10811
+ this.tableTools = createElement("lexxy-table-tools");
10812
+ this.append(this.tableTools);
10680
10813
  }
10681
10814
 
10682
10815
  #registerCodeHiglightingComponents() {
@@ -10685,21 +10818,6 @@ class LexicalEditorElement extends HTMLElement {
10685
10818
  this.append(this.codeLanguagePicker);
10686
10819
  }
10687
10820
 
10688
- #listenForInvalidatedNodes() {
10689
- this.editor.getRootElement().addEventListener("lexxy:internal:invalidate-node", (event) => {
10690
- const { key, values } = event.detail;
10691
-
10692
- this.editor.update(() => {
10693
- const node = xo(key);
10694
-
10695
- if (node instanceof ActionTextAttachmentNode) {
10696
- const updatedNode = node.getWritable();
10697
- Object.assign(updatedNode, values);
10698
- }
10699
- });
10700
- });
10701
- }
10702
-
10703
10821
  #handleEnter() {
10704
10822
  // We can't prevent these externally using regular keydown because Lexical handles it first.
10705
10823
  this.editor.registerCommand(
@@ -10723,12 +10841,27 @@ class LexicalEditorElement extends HTMLElement {
10723
10841
  );
10724
10842
  }
10725
10843
 
10726
- #handleFocus() {
10727
- // Lexxy handles focus and blur as commands
10728
- // see https://github.com/facebook/lexical/blob/d1a8e84fe9063a4f817655b346b6ff373aa107f0/packages/lexical/src/LexicalEvents.ts#L35
10729
- // and https://stackoverflow.com/a/72212077
10730
- this.editor.registerCommand(Ye, () => { dispatch(this, "lexxy:blur"); }, Ri);
10731
- this.editor.registerCommand(Ve$1, () => { dispatch(this, "lexxy:focus"); }, Ri);
10844
+ #registerFocusEvents() {
10845
+ this.addEventListener("focusin", this.#handleFocusIn);
10846
+ this.addEventListener("focusout", this.#handleFocusOut);
10847
+ }
10848
+
10849
+ #handleFocusIn(event) {
10850
+ if (this.#elementInEditorOrToolbar(event.target) && !this.currentlyFocused) {
10851
+ dispatch(this, "lexxy:focus");
10852
+ this.currentlyFocused = true;
10853
+ }
10854
+ }
10855
+
10856
+ #handleFocusOut(event) {
10857
+ if (!this.#elementInEditorOrToolbar(event.relatedTarget)) {
10858
+ dispatch(this, "lexxy:blur");
10859
+ this.currentlyFocused = false;
10860
+ }
10861
+ }
10862
+
10863
+ #elementInEditorOrToolbar(element) {
10864
+ return this.contains(element) || this.toolbarElement?.contains(element)
10732
10865
  }
10733
10866
 
10734
10867
  #onFocus() {
@@ -10745,22 +10878,9 @@ class LexicalEditorElement extends HTMLElement {
10745
10878
  }
10746
10879
  }
10747
10880
 
10748
- #handleTables() {
10749
- if (this.supportsRichText) {
10750
- this.removeTableSelectionObserver = yn(this.editor, true);
10751
- un(this.editor);
10752
- }
10753
- }
10754
10881
 
10755
10882
  #attachDebugHooks() {
10756
- if (!LexicalEditorElement.debug) return
10757
-
10758
- this.#addUnregisterHandler(this.editor.registerUpdateListener(({ editorState }) => {
10759
- editorState.read(() => {
10760
- console.debug("HTML: ", this.value, "String:", this.toString());
10761
- console.debug("empty", this.isEmpty, "blank", this.isBlank);
10762
- });
10763
- }));
10883
+ return
10764
10884
  }
10765
10885
 
10766
10886
  #attachToolbar() {
@@ -10840,18 +10960,17 @@ class LexicalEditorElement extends HTMLElement {
10840
10960
  }
10841
10961
  }
10842
10962
 
10843
- customElements.define("lexxy-editor", LexicalEditorElement);
10844
-
10845
10963
  class ToolbarDropdown extends HTMLElement {
10846
10964
  connectedCallback() {
10847
10965
  this.container = this.closest("details");
10848
10966
 
10849
10967
  this.container.addEventListener("toggle", this.#handleToggle.bind(this));
10850
10968
  this.container.addEventListener("keydown", this.#handleKeyDown.bind(this));
10969
+
10970
+ this.#onToolbarEditor(this.initialize.bind(this));
10851
10971
  }
10852
10972
 
10853
10973
  disconnectedCallback() {
10854
- this.#removeClickOutsideHandler();
10855
10974
  this.container.removeEventListener("keydown", this.#handleKeyDown.bind(this));
10856
10975
  }
10857
10976
 
@@ -10867,46 +10986,29 @@ class ToolbarDropdown extends HTMLElement {
10867
10986
  return this.toolbar.editor
10868
10987
  }
10869
10988
 
10870
- close() {
10871
- this.container.removeAttribute("open");
10989
+ initialize() {
10990
+ // Any post-editor initialization
10872
10991
  }
10873
10992
 
10874
- #handleToggle(event) {
10875
- if (this.container.open) {
10876
- this.#handleOpen(event.target);
10877
- } else {
10878
- this.#handleClose();
10879
- }
10880
- }
10881
-
10882
- #handleOpen() {
10883
- this.#interactiveElements[0].focus();
10884
- this.#setupClickOutsideHandler();
10885
-
10886
- this.#resetTabIndexValues();
10887
- }
10888
-
10889
- #handleClose() {
10890
- this.#removeClickOutsideHandler();
10993
+ close() {
10891
10994
  this.editor.focus();
10995
+ this.container.open = false;
10892
10996
  }
10893
10997
 
10894
- #setupClickOutsideHandler() {
10895
- if (this.clickOutsideHandler) return
10896
-
10897
- this.clickOutsideHandler = this.#handleClickOutside.bind(this);
10898
- document.addEventListener("click", this.clickOutsideHandler, true);
10998
+ async #onToolbarEditor(callback) {
10999
+ await this.toolbar.editorConnected;
11000
+ callback();
10899
11001
  }
10900
11002
 
10901
- #removeClickOutsideHandler() {
10902
- if (!this.clickOutsideHandler) return
10903
-
10904
- document.removeEventListener("click", this.clickOutsideHandler, true);
10905
- this.clickOutsideHandler = null;
11003
+ #handleToggle() {
11004
+ if (this.container.open) {
11005
+ this.#handleOpen();
11006
+ }
10906
11007
  }
10907
11008
 
10908
- #handleClickOutside({ target }) {
10909
- if (this.container.open && !this.container.contains(target)) this.close();
11009
+ async #handleOpen() {
11010
+ this.#interactiveElements[0].focus();
11011
+ this.#resetTabIndexValues();
10910
11012
  }
10911
11013
 
10912
11014
  #handleKeyDown(event) {
@@ -10983,8 +11085,6 @@ class LinkDropdown extends ToolbarDropdown {
10983
11085
  }
10984
11086
  }
10985
11087
 
10986
- customElements.define("lexxy-link-dropdown", LinkDropdown);
10987
-
10988
11088
  const APPLY_HIGHLIGHT_SELECTOR = "button.lexxy-highlight-button";
10989
11089
  const REMOVE_HIGHLIGHT_SELECTOR = "[data-command='removeHighlight']";
10990
11090
 
@@ -10994,19 +11094,14 @@ const REMOVE_HIGHLIGHT_SELECTOR = "[data-command='removeHighlight']";
10994
11094
  const NO_STYLE = Symbol("no_style");
10995
11095
 
10996
11096
  class HighlightDropdown extends ToolbarDropdown {
10997
- #initialized = false
10998
-
10999
11097
  connectedCallback() {
11000
11098
  super.connectedCallback();
11001
11099
  this.#registerToggleHandler();
11002
11100
  }
11003
11101
 
11004
- #ensureInitialized() {
11005
- if (this.#initialized) return
11006
-
11102
+ initialize() {
11007
11103
  this.#setUpButtons();
11008
11104
  this.#registerButtonHandlers();
11009
- this.#initialized = true;
11010
11105
  }
11011
11106
 
11012
11107
  #registerToggleHandler() {
@@ -11019,16 +11114,18 @@ class HighlightDropdown extends ToolbarDropdown {
11019
11114
  }
11020
11115
 
11021
11116
  #setUpButtons() {
11022
- this.#buttonGroups.forEach(buttonGroup => {
11023
- this.#populateButtonGroup(buttonGroup);
11024
- });
11117
+ const colorGroups = this.editorElement.config.get("highlight.buttons");
11118
+
11119
+ this.#populateButtonGroup("color", colorGroups.color);
11120
+ this.#populateButtonGroup("background-color", colorGroups["background-color"]);
11121
+
11122
+ const maxNumberOfColors = Math.max(colorGroups.color.length, colorGroups["background-color"].length);
11123
+ this.style.setProperty("--max-colors", maxNumberOfColors);
11025
11124
  }
11026
11125
 
11027
- #populateButtonGroup(buttonGroup) {
11028
- const attribute = buttonGroup.dataset.buttonGroup;
11029
- const values = this.editorElement.config.get(`highlight.buttons.${attribute}`) || [];
11126
+ #populateButtonGroup(attribute, values) {
11030
11127
  values.forEach((value, index) => {
11031
- buttonGroup.appendChild(this.#createButton(attribute, value, index));
11128
+ this.#buttonContainer.appendChild(this.#createButton(attribute, value, index));
11032
11129
  });
11033
11130
  }
11034
11131
 
@@ -11037,15 +11134,13 @@ class HighlightDropdown extends ToolbarDropdown {
11037
11134
  button.dataset.style = attribute;
11038
11135
  button.style.setProperty(attribute, value);
11039
11136
  button.dataset.value = value;
11040
- button.classList.add("lexxy-highlight-button");
11137
+ button.classList.add("lexxy-editor__toolbar-button", "lexxy-highlight-button");
11041
11138
  button.name = attribute + "-" + index;
11042
11139
  return button
11043
11140
  }
11044
11141
 
11045
11142
  #handleToggle({ newState }) {
11046
11143
  if (newState === "open") {
11047
- this.#ensureInitialized();
11048
-
11049
11144
  this.editor.getEditorState().read(() => {
11050
11145
  this.#updateColorButtonStates(Lr());
11051
11146
  });
@@ -11088,8 +11183,8 @@ class HighlightDropdown extends ToolbarDropdown {
11088
11183
  this.querySelector(REMOVE_HIGHLIGHT_SELECTOR).disabled = !hasHighlight;
11089
11184
  }
11090
11185
 
11091
- get #buttonGroups() {
11092
- return this.querySelectorAll("[data-button-group]")
11186
+ get #buttonContainer() {
11187
+ return this.querySelector(".lexxy-highlight-colors")
11093
11188
  }
11094
11189
 
11095
11190
  get #colorButtons() {
@@ -11097,1204 +11192,1433 @@ class HighlightDropdown extends ToolbarDropdown {
11097
11192
  }
11098
11193
  }
11099
11194
 
11100
- customElements.define("lexxy-highlight-dropdown", HighlightDropdown);
11101
-
11102
- class TableHandler extends HTMLElement {
11103
- connectedCallback() {
11104
- this.#setUpButtons();
11105
- this.#monitorForTableSelection();
11106
- this.#registerKeyboardShortcuts();
11107
- }
11108
-
11109
- disconnectedCallback() {
11110
- this.#unregisterKeyboardShortcuts();
11111
- }
11112
-
11113
- get #editor() {
11114
- return this.#editorElement.editor
11195
+ class BaseSource {
11196
+ // Template method to override
11197
+ async buildListItems(filter = "") {
11198
+ return Promise.resolve([])
11115
11199
  }
11116
11200
 
11117
- get #editorElement() {
11118
- return this.closest("lexxy-editor")
11201
+ // Template method to override
11202
+ promptItemFor(listItem) {
11203
+ return null
11119
11204
  }
11120
11205
 
11121
- get #currentCell() {
11122
- const selection = Lr();
11123
- if (!yr(selection)) return null
11206
+ // Protected
11124
11207
 
11125
- const anchorNode = selection.anchor.getNode();
11126
- return Be$1(anchorNode)
11208
+ buildListItemElementFor(promptItemElement) {
11209
+ const template = promptItemElement.querySelector("template[type='menu']");
11210
+ const fragment = template.content.cloneNode(true);
11211
+ const listItemElement = createElement("li", { role: "option", id: generateDomId("prompt-item"), tabindex: "0" });
11212
+ listItemElement.classList.add("lexxy-prompt-menu__item");
11213
+ listItemElement.appendChild(fragment);
11214
+ return listItemElement
11127
11215
  }
11128
11216
 
11129
- get #currentRow() {
11130
- const currentCell = this.#currentCell;
11131
- if (!currentCell) return 0
11132
- return Ie$1(currentCell)
11217
+ async loadPromptItemsFromUrl(url) {
11218
+ try {
11219
+ const response = await fetch(url);
11220
+ const html = await response.text();
11221
+ const promptItems = parseHtml(html).querySelectorAll("lexxy-prompt-item");
11222
+ return Promise.resolve(Array.from(promptItems))
11223
+ } catch (error) {
11224
+ return Promise.reject(error)
11225
+ }
11133
11226
  }
11227
+ }
11134
11228
 
11135
- get #currentColumn() {
11136
- const currentCell = this.#currentCell;
11137
- if (!currentCell) return 0
11138
- return Ue$1(currentCell)
11229
+ class LocalFilterSource extends BaseSource {
11230
+ async buildListItems(filter = "") {
11231
+ const promptItems = await this.fetchPromptItems();
11232
+ return this.#buildListItemsFromPromptItems(promptItems, filter)
11139
11233
  }
11140
11234
 
11141
- get #tableHandlerButtons() {
11142
- return Array.from(this.querySelectorAll("button, details > summary"))
11235
+ // Template method to override
11236
+ async fetchPromptItems(filter) {
11237
+ return Promise.resolve([])
11143
11238
  }
11144
11239
 
11145
- #registerKeyboardShortcuts() {
11146
- this.unregisterKeyboardShortcuts = this.#editor.registerCommand(me$1, this.#handleKeyDown, Bi);
11240
+ promptItemFor(listItem) {
11241
+ return this.promptItemByListItem.get(listItem)
11147
11242
  }
11148
11243
 
11149
- #unregisterKeyboardShortcuts() {
11150
- this.unregisterKeyboardShortcuts();
11151
- }
11244
+ #buildListItemsFromPromptItems(promptItems, filter) {
11245
+ const listItems = [];
11246
+ this.promptItemByListItem = new WeakMap();
11247
+ promptItems.forEach((promptItem) => {
11248
+ const searchableText = promptItem.getAttribute("search");
11152
11249
 
11153
- #handleKeyDown = (event) => {
11154
- if ((event.ctrlKey || event.metaKey) && event.shiftKey && event.key === "F10") {
11155
- const firstButton = this.querySelector("button, [tabindex]:not([tabindex='-1'])");
11156
- this.#setFocusStateOnSelectedCell();
11157
- firstButton?.focus();
11158
- } else if (event.key === "Escape") {
11159
- this.#editor.getEditorState().read(() => {
11160
- const cell = this.#currentCell;
11161
- if (!cell) return
11250
+ if (!filter || filterMatches(searchableText, filter)) {
11251
+ const listItem = this.buildListItemElementFor(promptItem);
11252
+ this.promptItemByListItem.set(listItem, promptItem);
11253
+ listItems.push(listItem);
11254
+ }
11255
+ });
11162
11256
 
11163
- this.#editor.update(() => {
11164
- cell.select();
11165
- });
11166
- });
11167
- this.#closeMoreMenu();
11168
- }
11257
+ return listItems
11169
11258
  }
11259
+ }
11170
11260
 
11171
- #handleTableHandlerKeydown = (event) => {
11172
- if (event.key === "Escape") {
11173
- this.#editor.focus();
11174
- } else {
11175
- handleRollingTabIndex(this.#tableHandlerButtons, event);
11176
- }
11261
+ class InlinePromptSource extends LocalFilterSource {
11262
+ constructor(inlinePromptItems) {
11263
+ super();
11264
+ this.inlinePromptItemElements = Array.from(inlinePromptItems);
11177
11265
  }
11178
11266
 
11179
- #setUpButtons() {
11180
- this.appendChild(this.#createRowButtonsContainer());
11181
- this.appendChild(this.#createColumnButtonsContainer());
11182
-
11183
- this.moreMenu = this.#createMoreMenu();
11184
- this.appendChild(this.moreMenu);
11185
- this.addEventListener("keydown", this.#handleTableHandlerKeydown);
11267
+ async fetchPromptItems() {
11268
+ return Promise.resolve(this.inlinePromptItemElements)
11186
11269
  }
11270
+ }
11187
11271
 
11188
- #showTableHandlerButtons() {
11189
- this.style.display = "flex";
11190
- this.#closeMoreMenu();
11272
+ class DeferredPromptSource extends LocalFilterSource {
11273
+ constructor(url) {
11274
+ super();
11275
+ this.url = url;
11191
11276
 
11192
- this.#updateRowColumnCount();
11193
- this.#setTableFocusState(true);
11277
+ this.fetchPromptItems();
11194
11278
  }
11195
11279
 
11196
- #hideTableHandlerButtons() {
11197
- this.style.display = "none";
11198
- this.#closeMoreMenu();
11280
+ async fetchPromptItems() {
11281
+ this.promptItems ??= await this.loadPromptItemsFromUrl(this.url);
11199
11282
 
11200
- this.#setTableFocusState(false);
11201
- this.currentTableNode = null;
11283
+ return Promise.resolve(this.promptItems)
11202
11284
  }
11285
+ }
11203
11286
 
11204
- #updateButtonsPosition(tableNode) {
11205
- const tableElement = this.#editor.getElementByKey(tableNode.getKey());
11206
- if (!tableElement) return
11287
+ const DEBOUNCE_INTERVAL = 200;
11207
11288
 
11208
- const tableRect = tableElement.getBoundingClientRect();
11209
- const editorRect = this.#editorElement.getBoundingClientRect();
11289
+ class RemoteFilterSource extends BaseSource {
11290
+ constructor(url) {
11291
+ super();
11210
11292
 
11211
- const relativeTop = tableRect.top - editorRect.top;
11212
- const relativeCenter = (tableRect.left + tableRect.right) / 2 - editorRect.left;
11213
- this.style.top = `${relativeTop}px`;
11214
- this.style.left = `${relativeCenter}px`;
11293
+ this.baseURL = url;
11294
+ this.loadAndFilterListItems = debounceAsync(this.fetchFilteredListItems.bind(this), DEBOUNCE_INTERVAL);
11215
11295
  }
11216
11296
 
11217
- #updateRowColumnCount() {
11218
- if (!this.currentTableNode) return
11219
-
11220
- const tableElement = dn(this.#editor, this.currentTableNode);
11221
- if (!tableElement) return
11222
-
11223
- const rowCount = tableElement.rows;
11224
- const columnCount = tableElement.columns;
11225
-
11226
- this.rowCount.textContent = `${rowCount} row${rowCount === 1 ? "" : "s"}`;
11227
- this.columnCount.textContent = `${columnCount} column${columnCount === 1 ? "" : "s"}`;
11297
+ async buildListItems(filter = "") {
11298
+ return await this.loadAndFilterListItems(filter)
11228
11299
  }
11229
11300
 
11230
- #createButton(icon, label, onClick) {
11231
- const button = createElement("button", {
11232
- className: "lexxy-table-control__button",
11233
- "aria-label": label,
11234
- type: "button"
11235
- });
11236
- button.tabIndex = -1;
11237
- button.innerHTML = `${icon} <span>${label}</span>`;
11238
- button.addEventListener("click", onClick.bind(this));
11239
-
11240
- return button
11301
+ promptItemFor(listItem) {
11302
+ return this.promptItemByListItem.get(listItem)
11241
11303
  }
11242
11304
 
11243
- #createRowButtonsContainer() {
11244
- const container = createElement("div", { className: "lexxy-table-control" });
11245
-
11246
- const plusButton = this.#createButton("+", "Add row", () => this.#insertTableRow("end"));
11247
- const minusButton = this.#createButton("−", "Remove row", () => this.#deleteTableRow("end"));
11248
-
11249
- this.rowCount = createElement("span");
11250
- this.rowCount.textContent = "_ rows";
11251
-
11252
- container.appendChild(minusButton);
11253
- container.appendChild(this.rowCount);
11254
- container.appendChild(plusButton);
11255
-
11256
- return container
11305
+ async fetchFilteredListItems(filter) {
11306
+ const promptItems = await this.loadPromptItemsFromUrl(this.#urlFor(filter));
11307
+ return this.#buildListItemsFromPromptItems(promptItems)
11257
11308
  }
11258
11309
 
11259
- #createColumnButtonsContainer() {
11260
- const container = createElement("div", { className: "lexxy-table-control" });
11261
-
11262
- const plusButton = this.#createButton("+", "Add column", () => this.#insertTableColumn("end"));
11263
- const minusButton = this.#createButton("−", "Remove column", () => this.#deleteTableColumn("end"));
11264
-
11265
- this.columnCount = createElement("span");
11266
- this.columnCount.textContent = "_ columns";
11267
-
11268
- container.appendChild(minusButton);
11269
- container.appendChild(this.columnCount);
11270
- container.appendChild(plusButton);
11271
-
11272
- return container
11310
+ #urlFor(filter) {
11311
+ const url = new URL(this.baseURL, window.location.origin);
11312
+ url.searchParams.append("filter", filter);
11313
+ return url.toString()
11273
11314
  }
11274
11315
 
11275
- #createMoreMenu() {
11276
- const container = createElement("details", {
11277
- className: "lexxy-table-control lexxy-table-control__more-menu"
11278
- });
11279
- container.setAttribute("name", "lexxy-dropdown");
11280
-
11281
- container.tabIndex = -1;
11282
-
11283
- const summary = createElement("summary", {}, "•••");
11284
- container.appendChild(summary);
11285
-
11286
- const details = createElement("div", { className: "lexxy-table-control__more-menu-details" });
11287
- container.appendChild(details);
11288
-
11289
- details.appendChild(this.#createRowSection());
11290
- details.appendChild(this.#createColumnSection());
11291
- details.appendChild(this.#createDeleteTableSection());
11316
+ #buildListItemsFromPromptItems(promptItems) {
11317
+ const listItems = [];
11318
+ this.promptItemByListItem = new WeakMap();
11292
11319
 
11293
- container.addEventListener("toggle", this.#handleMoreMenuToggle.bind(this));
11320
+ for (const promptItem of promptItems) {
11321
+ const listItem = this.buildListItemElementFor(promptItem);
11322
+ this.promptItemByListItem.set(listItem, promptItem);
11323
+ listItems.push(listItem);
11324
+ }
11294
11325
 
11295
- return container
11326
+ return listItems
11296
11327
  }
11328
+ }
11297
11329
 
11298
- #createColumnSection() {
11299
- const columnSection = createElement("section", { className: "lexxy-table-control__more-menu-section" });
11300
-
11301
- const columnButtons = [
11302
- { icon: this.#icon("add-column-before"), label: "Add column before", onClick: () => this.#insertTableColumn("left") },
11303
- { icon: this.#icon("add-column-after"), label: "Add column after", onClick: () => this.#insertTableColumn("right") },
11304
- { icon: this.#icon("remove-column"), label: "Remove column", onClick: this.#deleteTableColumn },
11305
- { icon: this.#icon("toggle-column-style"), label: "Toggle column style", onClick: this.#toggleColumnHeaderStyle },
11306
- ];
11307
-
11308
- columnButtons.forEach(button => {
11309
- const buttonElement = this.#createButton(button.icon, button.label, button.onClick);
11310
- columnSection.appendChild(buttonElement);
11311
- });
11330
+ const NOTHING_FOUND_DEFAULT_MESSAGE = "Nothing found";
11312
11331
 
11313
- return columnSection
11332
+ class LexicalPromptElement extends HTMLElement {
11333
+ constructor() {
11334
+ super();
11335
+ this.keyListeners = [];
11314
11336
  }
11315
11337
 
11316
- #createRowSection() {
11317
- const rowSection = createElement("section", { className: "lexxy-table-control__more-menu-section" });
11318
-
11319
- const rowButtons = [
11320
- { icon: this.#icon("add-row-above"), label: "Add row above", onClick: () => this.#insertTableRow("above") },
11321
- { icon: this.#icon("add-row-below"), label: "Add row below", onClick: () => this.#insertTableRow("below") },
11322
- { icon: this.#icon("remove-row"), label: "Remove row", onClick: this.#deleteTableRow },
11323
- { icon: this.#icon("toggle-row-style"), label: "Toggle row style", onClick: this.#toggleRowHeaderStyle }
11324
- ];
11338
+ static observedAttributes = [ "connected" ]
11325
11339
 
11326
- rowButtons.forEach(button => {
11327
- const buttonElement = this.#createButton(button.icon, button.label, button.onClick);
11328
- rowSection.appendChild(buttonElement);
11329
- });
11340
+ connectedCallback() {
11341
+ this.source = this.#createSource();
11330
11342
 
11331
- return rowSection
11343
+ this.#addTriggerListener();
11344
+ this.toggleAttribute("connected", true);
11332
11345
  }
11333
11346
 
11334
- #createDeleteTableSection() {
11335
- const deleteSection = createElement("section", { className: "lexxy-table-control__more-menu-section" });
11336
-
11337
- const deleteButton = { icon: this.#icon("delete-table"), label: "Delete table", onClick: this.#deleteTable };
11338
-
11339
- const buttonElement = this.#createButton(deleteButton.icon, deleteButton.label, deleteButton.onClick);
11340
- deleteSection.appendChild(buttonElement);
11341
-
11342
- return deleteSection
11347
+ disconnectedCallback() {
11348
+ this.source = null;
11349
+ this.popoverElement = null;
11343
11350
  }
11344
11351
 
11345
- #handleMoreMenuToggle() {
11346
- if (this.moreMenu.open) {
11347
- this.#setFocusStateOnSelectedCell();
11348
- } else {
11349
- this.#removeFocusStateFromSelectedCell();
11352
+
11353
+ attributeChangedCallback(name, oldValue, newValue) {
11354
+ if (name === "connected" && this.isConnected && oldValue != null && oldValue !== newValue) {
11355
+ requestAnimationFrame(() => this.#reconnect());
11350
11356
  }
11351
11357
  }
11352
11358
 
11353
- #closeMoreMenu() {
11354
- this.#removeFocusStateFromSelectedCell();
11355
- this.moreMenu.removeAttribute("open");
11359
+ get name() {
11360
+ return this.getAttribute("name")
11356
11361
  }
11357
11362
 
11358
- #monitorForTableSelection() {
11359
- this.#editor.registerUpdateListener(() => {
11360
- this.#editor.getEditorState().read(() => {
11361
- const selection = Lr();
11362
- if (!yr(selection)) return
11363
-
11364
- const anchorNode = selection.anchor.getNode();
11365
- const tableNode = Qt(anchorNode);
11366
-
11367
- if (tableNode) {
11368
- this.#tableCellWasSelected(tableNode);
11369
- } else {
11370
- this.#hideTableHandlerButtons();
11371
- }
11372
- });
11373
- });
11363
+ get trigger() {
11364
+ return this.getAttribute("trigger")
11374
11365
  }
11375
11366
 
11376
- #setTableFocusState(focused) {
11377
- this.#editorElement.querySelector("div.node--selected:has(table)")?.classList.remove("node--selected");
11367
+ get supportsSpaceInSearches() {
11368
+ return this.hasAttribute("supports-space-in-searches")
11369
+ }
11378
11370
 
11379
- if (focused && this.currentTableNode) {
11380
- const tableParent = this.#editor.getElementByKey(this.currentTableNode.getKey());
11381
- if (!tableParent) return
11382
- tableParent.classList.add("node--selected");
11383
- }
11371
+ get open() {
11372
+ return this.popoverElement?.classList?.contains("lexxy-prompt-menu--visible")
11384
11373
  }
11385
11374
 
11386
- #tableCellWasSelected(tableNode) {
11387
- this.currentTableNode = tableNode;
11388
- this.#updateButtonsPosition(tableNode);
11389
- this.#showTableHandlerButtons();
11375
+ get closed() {
11376
+ return !this.open
11390
11377
  }
11391
11378
 
11392
- #setFocusStateOnSelectedCell() {
11393
- this.#editor.getEditorState().read(() => {
11394
- const currentCell = this.#currentCell;
11395
- if (!currentCell) return
11379
+ get #doesSpaceSelect() {
11380
+ return !this.supportsSpaceInSearches
11381
+ }
11396
11382
 
11397
- const cellElement = this.#editor.getElementByKey(currentCell.getKey());
11398
- if (!cellElement) return
11383
+ #createSource() {
11384
+ const src = this.getAttribute("src");
11385
+ if (src) {
11386
+ if (this.hasAttribute("remote-filtering")) {
11387
+ return new RemoteFilterSource(src)
11388
+ } else {
11389
+ return new DeferredPromptSource(src)
11390
+ }
11391
+ } else {
11392
+ return new InlinePromptSource(this.querySelectorAll("lexxy-prompt-item"))
11393
+ }
11394
+ }
11395
+
11396
+ #addTriggerListener() {
11397
+ const unregister = this.#editor.registerUpdateListener(({ editorState }) => {
11398
+ editorState.read(() => {
11399
+ const { node, offset } = this.#selection.selectedNodeWithOffset();
11400
+ if (!node) return
11401
+
11402
+ if (lr(node)) {
11403
+ const fullText = node.getTextContent();
11404
+ const triggerLength = this.trigger.length;
11405
+
11406
+ // Check if we have enough characters for the trigger
11407
+ if (offset >= triggerLength) {
11408
+ const textBeforeCursor = fullText.slice(offset - triggerLength, offset);
11409
+
11410
+ // Check if trigger is at the start of the text node (new line case) or preceded by space or newline
11411
+ if (textBeforeCursor === this.trigger) {
11412
+ const isAtStart = offset === triggerLength;
11413
+
11414
+ const charBeforeTrigger = offset > triggerLength ? fullText[offset - triggerLength - 1] : null;
11415
+ const isPrecededBySpaceOrNewline = charBeforeTrigger === " " || charBeforeTrigger === "\n";
11416
+
11417
+ if (isAtStart || isPrecededBySpaceOrNewline) {
11418
+ unregister();
11419
+ this.#showPopover();
11420
+ }
11421
+ }
11422
+ }
11423
+ }
11424
+ });
11425
+ });
11426
+ }
11427
+
11428
+ #addCursorPositionListener() {
11429
+ this.cursorPositionListener = this.#editor.registerUpdateListener(() => {
11430
+ if (this.closed) return
11431
+
11432
+ this.#editor.read(() => {
11433
+ const { node, offset } = this.#selection.selectedNodeWithOffset();
11434
+ if (!node) return
11435
+
11436
+ if (lr(node) && offset > 0) {
11437
+ const fullText = node.getTextContent();
11438
+ const textBeforeCursor = fullText.slice(0, offset);
11439
+ const lastTriggerIndex = textBeforeCursor.lastIndexOf(this.trigger);
11440
+ const triggerEndIndex = lastTriggerIndex + this.trigger.length - 1;
11399
11441
 
11400
- cellElement.classList.add("table-cell--selected");
11442
+ // If trigger is not found, or cursor is at or before the trigger end position, hide popover
11443
+ if (lastTriggerIndex === -1 || offset <= triggerEndIndex) {
11444
+ this.#hidePopover();
11445
+ }
11446
+ } else {
11447
+ // Cursor is not in a text node or at offset 0, hide popover
11448
+ this.#hidePopover();
11449
+ }
11450
+ });
11401
11451
  });
11402
11452
  }
11403
11453
 
11404
- #removeFocusStateFromSelectedCell() {
11405
- this.#editorElement.querySelector(".table-cell--selected")?.classList.remove("table-cell--selected");
11454
+ #removeCursorPositionListener() {
11455
+ if (this.cursorPositionListener) {
11456
+ this.cursorPositionListener();
11457
+ this.cursorPositionListener = null;
11458
+ }
11406
11459
  }
11407
11460
 
11408
- #selectLastTableCell() {
11409
- if (!this.currentTableNode) return
11461
+ get #editor() {
11462
+ return this.#editorElement.editor
11463
+ }
11410
11464
 
11411
- const last = this.currentTableNode.getLastChild().getLastChild();
11412
- if (!Oe$1(last)) return
11465
+ get #editorElement() {
11466
+ return this.closest("lexxy-editor")
11467
+ }
11413
11468
 
11414
- last.selectEnd();
11469
+ get #selection() {
11470
+ return this.#editorElement.selection
11415
11471
  }
11416
11472
 
11417
- #deleteTable() {
11418
- this.#editor.dispatchCommand("deleteTable");
11473
+ async #showPopover() {
11474
+ this.popoverElement ??= await this.#buildPopover();
11475
+ this.#resetPopoverPosition();
11476
+ await this.#filterOptions();
11477
+ this.popoverElement.classList.toggle("lexxy-prompt-menu--visible", true);
11478
+ this.#selectFirstOption();
11419
11479
 
11420
- this.#closeMoreMenu();
11421
- this.#updateRowColumnCount();
11480
+ this.#editorElement.addEventListener("keydown", this.#handleKeydownOnPopover);
11481
+ this.#editorElement.addEventListener("lexxy:change", this.#filterOptions);
11482
+
11483
+ this.#registerKeyListeners();
11484
+ this.#addCursorPositionListener();
11485
+ }
11486
+
11487
+ #registerKeyListeners() {
11488
+ // We can't use a regular keydown for Enter as Lexical handles it first
11489
+ this.keyListeners.push(this.#editor.registerCommand(Ne$2, this.#handleSelectedOption.bind(this), Bi));
11490
+ this.keyListeners.push(this.#editor.registerCommand(Me$2, this.#handleSelectedOption.bind(this), Bi));
11491
+
11492
+ if (this.#doesSpaceSelect) {
11493
+ this.keyListeners.push(this.#editor.registerCommand(be$1, this.#handleSelectedOption.bind(this), Bi));
11494
+ }
11495
+
11496
+ // Register arrow keys with HIGH priority to prevent Lexical's selection handlers from running
11497
+ this.keyListeners.push(this.#editor.registerCommand(ke$2, this.#handleArrowUp.bind(this), Bi));
11498
+ this.keyListeners.push(this.#editor.registerCommand(Te$2, this.#handleArrowDown.bind(this), Bi));
11499
+ }
11500
+
11501
+ #handleArrowUp(event) {
11502
+ this.#moveSelectionUp();
11503
+ event.preventDefault();
11504
+ return true
11505
+ }
11506
+
11507
+ #handleArrowDown(event) {
11508
+ this.#moveSelectionDown();
11509
+ event.preventDefault();
11510
+ return true
11511
+ }
11512
+
11513
+ #selectFirstOption() {
11514
+ const firstOption = this.#listItemElements[0];
11515
+
11516
+ if (firstOption) {
11517
+ this.#selectOption(firstOption);
11518
+ }
11519
+ }
11520
+
11521
+ get #listItemElements() {
11522
+ return Array.from(this.popoverElement.querySelectorAll(".lexxy-prompt-menu__item"))
11523
+ }
11524
+
11525
+ #selectOption(listItem) {
11526
+ this.#clearSelection();
11527
+ listItem.toggleAttribute("aria-selected", true);
11528
+ listItem.scrollIntoView({ block: "nearest", behavior: "smooth" });
11529
+ listItem.focus();
11530
+
11531
+ // Preserve selection to prevent cursor jump
11532
+ this.#selection.preservingSelection(() => {
11533
+ this.#editorElement.focus();
11534
+ });
11535
+
11536
+ this.#editorContentElement.setAttribute("aria-controls", this.popoverElement.id);
11537
+ this.#editorContentElement.setAttribute("aria-activedescendant", listItem.id);
11538
+ this.#editorContentElement.setAttribute("aria-haspopup", "listbox");
11539
+ }
11540
+
11541
+ #clearSelection() {
11542
+ this.#listItemElements.forEach((item) => { item.toggleAttribute("aria-selected", false); });
11543
+ this.#editorContentElement.removeAttribute("aria-controls");
11544
+ this.#editorContentElement.removeAttribute("aria-activedescendant");
11545
+ this.#editorContentElement.removeAttribute("aria-haspopup");
11422
11546
  }
11423
11547
 
11424
- #insertTableRow(direction) {
11425
- this.#executeTableCommand("insert", "row", direction);
11548
+ #positionPopover() {
11549
+ const { x, y, fontSize } = this.#selection.cursorPosition;
11550
+ const editorRect = this.#editorElement.getBoundingClientRect();
11551
+ const contentRect = this.#editorContentElement.getBoundingClientRect();
11552
+ const verticalOffset = contentRect.top - editorRect.top;
11553
+
11554
+ if (!this.popoverElement.hasAttribute("data-anchored")) {
11555
+ this.popoverElement.style.left = `${x}px`;
11556
+ this.popoverElement.toggleAttribute("data-anchored", true);
11557
+ }
11558
+
11559
+ this.popoverElement.style.top = `${y + verticalOffset}px`;
11560
+ this.popoverElement.style.bottom = "auto";
11561
+
11562
+ const popoverRect = this.popoverElement.getBoundingClientRect();
11563
+ const isClippedAtBottom = popoverRect.bottom > window.innerHeight;
11564
+
11565
+ if (isClippedAtBottom || this.popoverElement.hasAttribute("data-clipped-at-bottom")) {
11566
+ this.popoverElement.style.top = `${y + verticalOffset - popoverRect.height - fontSize}px`;
11567
+ this.popoverElement.style.bottom = "auto";
11568
+ this.popoverElement.toggleAttribute("data-clipped-at-bottom", true);
11569
+ }
11426
11570
  }
11427
11571
 
11428
- #insertTableColumn(direction) {
11429
- this.#executeTableCommand("insert", "column", direction);
11572
+ #resetPopoverPosition() {
11573
+ this.popoverElement.removeAttribute("data-clipped-at-bottom");
11574
+ this.popoverElement.removeAttribute("data-anchored");
11430
11575
  }
11431
11576
 
11432
- #deleteTableRow(direction) {
11433
- this.#executeTableCommand("delete", "row", direction);
11577
+ async #hidePopover() {
11578
+ this.#clearSelection();
11579
+ this.popoverElement.classList.toggle("lexxy-prompt-menu--visible", false);
11580
+ this.#editorElement.removeEventListener("lexxy:change", this.#filterOptions);
11581
+ this.#editorElement.removeEventListener("keydown", this.#handleKeydownOnPopover);
11582
+
11583
+ this.#unregisterKeyListeners();
11584
+ this.#removeCursorPositionListener();
11585
+
11586
+ await nextFrame();
11587
+ this.#addTriggerListener();
11588
+ }
11589
+
11590
+ #unregisterKeyListeners() {
11591
+ this.keyListeners.forEach((unregister) => unregister());
11592
+ this.keyListeners = [];
11593
+ }
11594
+
11595
+ #filterOptions = async () => {
11596
+ if (this.initialPrompt) {
11597
+ this.initialPrompt = false;
11598
+ return
11599
+ }
11600
+
11601
+ if (this.#editorContents.containsTextBackUntil(this.trigger)) {
11602
+ await this.#showFilteredOptions();
11603
+ await nextFrame();
11604
+ this.#positionPopover();
11605
+ } else {
11606
+ this.#hidePopover();
11607
+ }
11608
+ }
11609
+
11610
+ async #showFilteredOptions() {
11611
+ const filter = this.#editorContents.textBackUntil(this.trigger);
11612
+ const filteredListItems = await this.source.buildListItems(filter);
11613
+ this.popoverElement.innerHTML = "";
11614
+
11615
+ if (filteredListItems.length > 0) {
11616
+ this.#showResults(filteredListItems);
11617
+ } else {
11618
+ this.#showEmptyResults();
11619
+ }
11620
+ this.#selectFirstOption();
11621
+ }
11622
+
11623
+ #showResults(filteredListItems) {
11624
+ this.popoverElement.classList.remove("lexxy-prompt-menu--empty");
11625
+ this.popoverElement.append(...filteredListItems);
11626
+ }
11627
+
11628
+ #showEmptyResults() {
11629
+ this.popoverElement.classList.add("lexxy-prompt-menu--empty");
11630
+ const el = createElement("li", { innerHTML: this.#emptyResultsMessage });
11631
+ el.classList.add("lexxy-prompt-menu__item--empty");
11632
+ this.popoverElement.append(el);
11633
+ }
11634
+
11635
+ get #emptyResultsMessage() {
11636
+ return this.getAttribute("empty-results") || NOTHING_FOUND_DEFAULT_MESSAGE
11637
+ }
11638
+
11639
+ #handleKeydownOnPopover = (event) => {
11640
+ if (event.key === "Escape") {
11641
+ this.#hidePopover();
11642
+ this.#editorElement.focus();
11643
+ event.stopPropagation();
11644
+ }
11645
+ // Arrow keys are now handled via Lexical commands with HIGH priority
11646
+ }
11647
+
11648
+ #moveSelectionDown() {
11649
+ const nextIndex = this.#selectedIndex + 1;
11650
+ if (nextIndex < this.#listItemElements.length) this.#selectOption(this.#listItemElements[nextIndex]);
11651
+ }
11652
+
11653
+ #moveSelectionUp() {
11654
+ const previousIndex = this.#selectedIndex - 1;
11655
+ if (previousIndex >= 0) this.#selectOption(this.#listItemElements[previousIndex]);
11656
+ }
11657
+
11658
+ get #selectedIndex() {
11659
+ return this.#listItemElements.findIndex((item) => item.hasAttribute("aria-selected"))
11660
+ }
11661
+
11662
+ get #selectedListItem() {
11663
+ return this.#listItemElements[this.#selectedIndex]
11664
+ }
11665
+
11666
+ #handleSelectedOption(event) {
11667
+ event.preventDefault();
11668
+ event.stopPropagation();
11669
+ this.#optionWasSelected();
11670
+ return true
11671
+ }
11672
+
11673
+ #optionWasSelected() {
11674
+ this.#replaceTriggerWithSelectedItem();
11675
+ this.#hidePopover();
11676
+ this.#editorElement.focus();
11677
+ }
11678
+
11679
+ #replaceTriggerWithSelectedItem() {
11680
+ const promptItem = this.source.promptItemFor(this.#selectedListItem);
11681
+
11682
+ if (!promptItem) { return }
11683
+
11684
+ const templates = Array.from(promptItem.querySelectorAll("template[type='editor']"));
11685
+ const stringToReplace = `${this.trigger}${this.#editorContents.textBackUntil(this.trigger)}`;
11686
+
11687
+ if (this.hasAttribute("insert-editable-text")) {
11688
+ this.#insertTemplatesAsEditableText(templates, stringToReplace);
11689
+ } else {
11690
+ this.#insertTemplatesAsAttachments(templates, stringToReplace, promptItem.getAttribute("sgid"));
11691
+ }
11692
+ }
11693
+
11694
+ #insertTemplatesAsEditableText(templates, stringToReplace) {
11695
+ this.#editor.update(() => {
11696
+ const nodes = templates.flatMap(template => this.#buildEditableTextNodes(template));
11697
+ this.#editorContents.replaceTextBackUntil(stringToReplace, nodes);
11698
+ });
11699
+ }
11700
+
11701
+ #buildEditableTextNodes(template) {
11702
+ return m$1(this.#editor, parseHtml(`${template.innerHTML}`))
11703
+ }
11704
+
11705
+ #insertTemplatesAsAttachments(templates, stringToReplace, fallbackSgid = null) {
11706
+ this.#editor.update(() => {
11707
+ const attachmentNodes = this.#buildAttachmentNodes(templates, fallbackSgid);
11708
+ const spacedAttachmentNodes = attachmentNodes.flatMap(node => [ node, this.#getSpacerTextNode() ]).slice(0, -1);
11709
+ this.#editorContents.replaceTextBackUntil(stringToReplace, spacedAttachmentNodes);
11710
+ });
11711
+ }
11712
+
11713
+ #buildAttachmentNodes(templates, fallbackSgid = null) {
11714
+ return templates.map(
11715
+ template => this.#buildAttachmentNode(
11716
+ template.innerHTML,
11717
+ template.getAttribute("content-type") || this.#defaultPromptContentType,
11718
+ template.getAttribute("sgid") || fallbackSgid
11719
+ ))
11720
+ }
11721
+
11722
+ #getSpacerTextNode() {
11723
+ return sr(" ")
11724
+ }
11725
+
11726
+ get #defaultPromptContentType() {
11727
+ const attachmentContentTypeNamespace = Lexxy.global.get("attachmentContentTypeNamespace");
11728
+ return `application/vnd.${attachmentContentTypeNamespace}.${this.name}`
11729
+ }
11730
+
11731
+ #buildAttachmentNode(innerHtml, contentType, sgid) {
11732
+ return new CustomActionTextAttachmentNode({ sgid, contentType, innerHtml })
11733
+ }
11734
+
11735
+ get #editorContents() {
11736
+ return this.#editorElement.contents
11737
+ }
11738
+
11739
+ get #editorContentElement() {
11740
+ return this.#editorElement.editorContentElement
11741
+ }
11742
+
11743
+ async #buildPopover() {
11744
+ const popoverContainer = createElement("ul", { role: "listbox", id: generateDomId("prompt-popover") }); // Avoiding [popover] due to not being able to position at an arbitrary X, Y position.
11745
+ popoverContainer.classList.add("lexxy-prompt-menu");
11746
+ popoverContainer.style.position = "absolute";
11747
+ popoverContainer.setAttribute("nonce", getNonce());
11748
+ popoverContainer.append(...await this.source.buildListItems());
11749
+ popoverContainer.addEventListener("click", this.#handlePopoverClick);
11750
+ this.#editorElement.appendChild(popoverContainer);
11751
+ return popoverContainer
11752
+ }
11753
+
11754
+ #handlePopoverClick = (event) => {
11755
+ const listItem = event.target.closest(".lexxy-prompt-menu__item");
11756
+ if (listItem) {
11757
+ this.#selectOption(listItem);
11758
+ this.#optionWasSelected();
11759
+ }
11434
11760
  }
11435
11761
 
11436
- #deleteTableColumn(direction) {
11437
- this.#executeTableCommand("delete", "column", direction);
11762
+ #reconnect() {
11763
+ this.disconnectedCallback();
11764
+ this.connectedCallback();
11438
11765
  }
11766
+ }
11439
11767
 
11440
- #executeTableCommand(action = "insert", childType = "row", direction) {
11441
- this.#editor.update(() => {
11442
- const currentCell = this.#currentCell;
11443
- if (!currentCell) return
11768
+ class CodeLanguagePicker extends HTMLElement {
11769
+ connectedCallback() {
11770
+ this.editorElement = this.closest("lexxy-editor");
11771
+ this.editor = this.editorElement.editor;
11444
11772
 
11445
- if (direction === "end") {
11446
- this.#selectLastTableCell();
11447
- }
11773
+ this.#attachLanguagePicker();
11774
+ this.#monitorForCodeBlockSelection();
11775
+ }
11448
11776
 
11449
- this.#dispatchTableCommand(action, childType, direction);
11777
+ #attachLanguagePicker() {
11778
+ this.languagePickerElement = this.#createLanguagePicker();
11450
11779
 
11451
- if (currentCell.isAttached()) {
11452
- currentCell.selectEnd();
11453
- }
11780
+ this.languagePickerElement.addEventListener("change", () => {
11781
+ this.#updateCodeBlockLanguage(this.languagePickerElement.value);
11454
11782
  });
11455
11783
 
11456
- this.#closeMoreMenu();
11457
- this.#updateRowColumnCount();
11784
+ this.languagePickerElement.setAttribute("nonce", getNonce());
11785
+ this.appendChild(this.languagePickerElement);
11458
11786
  }
11459
11787
 
11460
- #dispatchTableCommand(action, childType, direction) {
11461
- switch (action) {
11462
- case "insert":
11463
- switch (childType) {
11464
- case "row":
11465
- if (direction === "above") {
11466
- this.#editor.dispatchCommand("insertTableRowAbove");
11467
- } else {
11468
- this.#editor.dispatchCommand("insertTableRowBelow");
11469
- }
11470
- break
11471
- case "column":
11472
- if (direction === "left") {
11473
- this.#editor.dispatchCommand("insertTableColumnBefore");
11474
- } else {
11475
- this.#editor.dispatchCommand("insertTableColumnAfter");
11476
- }
11477
- break
11478
- }
11479
- break
11480
- case "delete":
11481
- switch (childType) {
11482
- case "row":
11483
- this.#editor.dispatchCommand("deleteTableRow");
11484
- break
11485
- case "column":
11486
- this.#editor.dispatchCommand("deleteTableColumn");
11487
- break
11488
- }
11489
- break
11788
+ #createLanguagePicker() {
11789
+ const selectElement = createElement("select", { className: "lexxy-code-language-picker", "aria-label": "Pick a language…", name: "lexxy-code-language" });
11790
+
11791
+ for (const [ value, label ] of Object.entries(this.#languages)) {
11792
+ const option = document.createElement("option");
11793
+ option.value = value;
11794
+ option.textContent = label;
11795
+ selectElement.appendChild(option);
11490
11796
  }
11491
- }
11492
11797
 
11493
- #toggleRowHeaderStyle() {
11494
- this.#editor.update(() => {
11495
- const rows = this.currentTableNode.getChildren();
11798
+ return selectElement
11799
+ }
11496
11800
 
11497
- const row = rows[this.#currentRow];
11498
- if (!row) return
11801
+ get #languages() {
11802
+ const languages = { ...pt$1 };
11499
11803
 
11500
- const cells = row.getChildren();
11501
- const firstCell = Be$1(cells[0]);
11502
- if (!firstCell) return
11804
+ if (!languages.ruby) languages.ruby = "Ruby";
11805
+ if (!languages.php) languages.php = "PHP";
11806
+ if (!languages.go) languages.go = "Go";
11807
+ if (!languages.bash) languages.bash = "Bash";
11808
+ if (!languages.json) languages.json = "JSON";
11809
+ if (!languages.diff) languages.diff = "Diff";
11503
11810
 
11504
- const currentStyle = firstCell.getHeaderStyles();
11505
- const newStyle = currentStyle ^ ve$1.ROW;
11811
+ const sortedEntries = Object.entries(languages)
11812
+ .sort(([ , a ], [ , b ]) => a.localeCompare(b));
11506
11813
 
11507
- cells.forEach(cell => {
11508
- this.#setHeaderStyle(cell, newStyle, ve$1.ROW);
11509
- });
11510
- });
11814
+ // Place the "plain" entry first, then the rest of language sorted alphabetically
11815
+ const plainIndex = sortedEntries.findIndex(([ key ]) => key === "plain");
11816
+ const plainEntry = sortedEntries.splice(plainIndex, 1)[0];
11817
+ return Object.fromEntries([ plainEntry, ...sortedEntries ])
11511
11818
  }
11512
11819
 
11513
- #toggleColumnHeaderStyle() {
11514
- this.#editor.update(() => {
11515
- const rows = this.currentTableNode.getChildren();
11516
-
11517
- const row = rows[this.#currentRow];
11518
- if (!row) return
11820
+ #updateCodeBlockLanguage(language) {
11821
+ this.editor.update(() => {
11822
+ const codeNode = this.#getCurrentCodeNode();
11519
11823
 
11520
- const cells = row.getChildren();
11521
- const selectedCell = Be$1(cells[this.#currentColumn]);
11522
- if (!selectedCell) return
11824
+ if (codeNode) {
11825
+ codeNode.setLanguage(language);
11826
+ }
11827
+ });
11828
+ }
11523
11829
 
11524
- const currentStyle = selectedCell.getHeaderStyles();
11525
- const newStyle = currentStyle ^ ve$1.COLUMN;
11830
+ #monitorForCodeBlockSelection() {
11831
+ this.editor.registerUpdateListener(() => {
11832
+ this.editor.getEditorState().read(() => {
11833
+ const codeNode = this.#getCurrentCodeNode();
11526
11834
 
11527
- rows.forEach(row => {
11528
- const cell = row.getChildren()[this.#currentColumn];
11529
- if (!cell) return
11530
- this.#setHeaderStyle(cell, newStyle, ve$1.COLUMN);
11835
+ if (codeNode) {
11836
+ this.#codeNodeWasSelected(codeNode);
11837
+ } else {
11838
+ this.#hideLanguagePicker();
11839
+ }
11531
11840
  });
11532
11841
  });
11533
11842
  }
11534
11843
 
11535
- #setHeaderStyle(cell, newStyle, headerState) {
11536
- const tableCellNode = Be$1(cell);
11537
-
11538
- if (tableCellNode) {
11539
- tableCellNode.setHeaderStyles(newStyle, headerState);
11540
- }
11541
- }
11542
-
11543
- #icon(name) {
11544
- const icons =
11545
- {
11546
- "add-row-above":
11547
- `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
11548
- <path d="M4 7L0 10V4L4 7ZM6.5 7.5H16.5V6.5H6.5V7.5ZM18 8C18 8.55228 17.5523 9 17 9H6C5.44772 9 5 8.55228 5 8V6C5 5.44772 5.44772 5 6 5H17C17.5523 5 18 5.44772 18 6V8Z"/><path d="M2 2C2 1.44772 2.44772 1 3 1H15C15.5523 1 16 1.44772 16 2C16 2.55228 15.5523 3 15 3H3C2.44772 3 2 2.55228 2 2Z"/><path d="M2 12C2 11.4477 2.44772 11 3 11H15C15.5523 11 16 11.4477 16 12C16 12.5523 15.5523 13 15 13H3C2.44772 13 2 12.5523 2 12Z"/><path d="M2 16C2 15.4477 2.44772 15 3 15H15C15.5523 15 16 15.4477 16 16C16 16.5523 15.5523 17 15 17H3C2.44772 17 2 16.5523 2 16Z"/>
11549
- </svg>`,
11550
-
11551
- "add-row-below":
11552
- `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
11553
- <path d="M4 11L0 8V14L4 11ZM6.5 10.5H16.5V11.5H6.5V10.5ZM18 10C18 9.44772 17.5523 9 17 9H6C5.44772 9 5 9.44772 5 10V12C5 12.5523 5.44772 13 6 13H17C17.5523 13 18 12.5523 18 12V10Z"/><path d="M2 16C2 16.5523 2.44772 17 3 17H15C15.5523 17 16 16.5523 16 16C16 15.4477 15.5523 15 15 15H3C2.44772 15 2 15.4477 2 16Z"/><path d="M2 6C2 6.55228 2.44772 7 3 7H15C15.5523 7 16 6.55228 16 6C16 5.44772 15.5523 5 15 5H3C2.44772 5 2 5.44772 2 6Z"/><path d="M2 2C2 2.55228 2.44772 3 3 3H15C15.5523 3 16 2.55228 16 2C16 1.44772 15.5523 1 15 1H3C2.44772 1 2 1.44772 2 2Z"/>
11554
- </svg>`,
11555
-
11556
- "remove-row":
11557
- `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
11558
- <path d="M17.9951 10.1025C17.9438 10.6067 17.5177 11 17 11H12.4922L13.9922 9.5H16.5V5.5L1.5 5.5L1.5 9.5H4.00586L5.50586 11H1L0.897461 10.9951C0.427034 10.9472 0.0527828 10.573 0.00488281 10.1025L0 10L1.78814e-07 5C2.61831e-07 4.48232 0.393332 4.05621 0.897461 4.00488L1 4L17 4C17.5523 4 18 4.44772 18 5V10L17.9951 10.1025Z"/><path d="M11.2969 15.0146L8.99902 12.7168L6.7002 15.0146L5.63965 13.9541L7.93848 11.6562L5.63965 9.3584L6.7002 8.29785L8.99902 10.5957L11.2969 8.29785L12.3574 9.3584L10.0596 11.6562L12.3574 13.9541L11.2969 15.0146Z"/>
11559
- </svg>`,
11560
-
11561
- "toggle-row-style":
11562
- `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
11563
- <path d="M1 2C1 1.44772 1.44772 1 2 1H7C7.55228 1 8 1.44772 8 2V7C8 7.55228 7.55228 8 7 8H2C1.44772 8 1 7.55228 1 7V2Z"/><path d="M2.5 15.5H6.5V11.5H2.5V15.5ZM8 16C8 16.5177 7.60667 16.9438 7.10254 16.9951L7 17H2L1.89746 16.9951C1.42703 16.9472 1.05278 16.573 1.00488 16.1025L1 16V11C1 10.4477 1.44772 10 2 10H7C7.55228 10 8 10.4477 8 11V16Z"/><path d="M10 2C10 1.44772 10.4477 1 11 1H16C16.5523 1 17 1.44772 17 2V7C17 7.55228 16.5523 8 16 8H11C10.4477 8 10 7.55228 10 7V2Z"/><path d="M11.5 15.5H15.5V11.5H11.5V15.5ZM17 16C17 16.5177 16.6067 16.9438 16.1025 16.9951L16 17H11L10.8975 16.9951C10.427 16.9472 10.0528 16.573 10.0049 16.1025L10 16V11C10 10.4477 10.4477 10 11 10H16C16.5523 10 17 10.4477 17 11V16Z"/>
11564
- </svg>`,
11565
-
11566
- "add-column-before":
11567
- `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
11568
- <path d="M7 4L10 2.62268e-07L4 0L7 4ZM7.5 6.5L7.5 16.5H6.5L6.5 6.5H7.5ZM8 18C8.55228 18 9 17.5523 9 17V6C9 5.44772 8.55229 5 8 5H6C5.44772 5 5 5.44772 5 6L5 17C5 17.5523 5.44772 18 6 18H8Z"/><path d="M2 2C1.44772 2 1 2.44772 1 3L1 15C1 15.5523 1.44772 16 2 16C2.55228 16 3 15.5523 3 15L3 3C3 2.44772 2.55229 2 2 2Z"/><path d="M12 2C11.4477 2 11 2.44772 11 3L11 15C11 15.5523 11.4477 16 12 16C12.5523 16 13 15.5523 13 15L13 3C13 2.44772 12.5523 2 12 2Z"/><path d="M16 2C15.4477 2 15 2.44772 15 3L15 15C15 15.5523 15.4477 16 16 16C16.5523 16 17 15.5523 17 15V3C17 2.44772 16.5523 2 16 2Z"/>
11569
- </svg>`,
11570
-
11571
- "add-column-after":
11572
- `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
11573
- <path d="M11 4L8 2.62268e-07L14 0L11 4ZM10.5 6.5V16.5H11.5V6.5H10.5ZM10 18C9.44772 18 9 17.5523 9 17V6C9 5.44772 9.44772 5 10 5H12C12.5523 5 13 5.44772 13 6V17C13 17.5523 12.5523 18 12 18H10Z"/><path d="M16 2C16.5523 2 17 2.44772 17 3L17 15C17 15.5523 16.5523 16 16 16C15.4477 16 15 15.5523 15 15V3C15 2.44772 15.4477 2 16 2Z"/><path d="M6 2C6.55228 2 7 2.44772 7 3L7 15C7 15.5523 6.55228 16 6 16C5.44772 16 5 15.5523 5 15L5 3C5 2.44772 5.44771 2 6 2Z"/><path d="M2 2C2.55228 2 3 2.44772 3 3L3 15C3 15.5523 2.55228 16 2 16C1.44772 16 1 15.5523 1 15V3C1 2.44772 1.44771 2 2 2Z"/>
11574
- </svg>`,
11575
-
11576
- "remove-column":
11577
- `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
11578
- <path d="M10.1025 0.00488281C10.6067 0.0562145 11 0.482323 11 1V5.50781L9.5 4.00781V1.5H5.5V16.5H9.5V13.9941L11 12.4941V17L10.9951 17.1025C10.9472 17.573 10.573 17.9472 10.1025 17.9951L10 18H5C4.48232 18 4.05621 17.6067 4.00488 17.1025L4 17V1C4 0.447715 4.44772 1.61064e-08 5 0H10L10.1025 0.00488281Z"/><path d="M12.7169 8.99999L15.015 11.2981L13.9543 12.3588L11.6562 10.0607L9.35815 12.3588L8.29749 11.2981L10.5956 8.99999L8.29749 6.7019L9.35815 5.64124L11.6562 7.93933L13.9543 5.64124L15.015 6.7019L12.7169 8.99999Z"/>
11579
- </svg>`,
11580
-
11581
- "toggle-column-style":
11582
- `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
11583
- <path d="M1 2C1 1.44772 1.44772 1 2 1H7C7.55228 1 8 1.44772 8 2V7C8 7.55228 7.55228 8 7 8H2C1.44772 8 1 7.55228 1 7V2Z"/><path d="M1 11C1 10.4477 1.44772 10 2 10H7C7.55228 10 8 10.4477 8 11V16C8 16.5523 7.55228 17 7 17H2C1.44772 17 1 16.5523 1 16V11Z"/><path d="M11.5 6.5H15.5V2.5H11.5V6.5ZM17 7C17 7.51768 16.6067 7.94379 16.1025 7.99512L16 8H11L10.8975 7.99512C10.427 7.94722 10.0528 7.57297 10.0049 7.10254L10 7V2C10 1.44772 10.4477 1 11 1H16C16.5523 1 17 1.44772 17 2V7Z"/><path d="M11.5 15.5H15.5V11.5H11.5V15.5ZM17 16C17 16.5177 16.6067 16.9438 16.1025 16.9951L16 17H11L10.8975 16.9951C10.427 16.9472 10.0528 16.573 10.0049 16.1025L10 16V11C10 10.4477 10.4477 10 11 10H16C16.5523 10 17 10.4477 17 11V16Z"/>
11584
- </svg>`,
11585
-
11586
- "delete-table":
11587
- `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
11588
- <path d="M18.2129 19.2305C18.0925 20.7933 16.7892 22 15.2217 22H7.77832C6.21084 22 4.90753 20.7933 4.78711 19.2305L4 9H19L18.2129 19.2305Z"/><path d="M13 2C14.1046 2 15 2.89543 15 4H19C19.5523 4 20 4.44772 20 5V6C20 6.55228 19.5523 7 19 7H4C3.44772 7 3 6.55228 3 6V5C3 4.44772 3.44772 4 4 4H8C8 2.89543 8.89543 2 10 2H13Z"/>
11589
- </svg>`
11590
- };
11844
+ #getCurrentCodeNode() {
11845
+ const selection = Lr();
11591
11846
 
11592
- return icons[name]
11593
- }
11594
- }
11847
+ if (!yr(selection)) {
11848
+ return null
11849
+ }
11595
11850
 
11596
- customElements.define("lexxy-table-handler", TableHandler);
11851
+ const anchorNode = selection.anchor.getNode();
11852
+ const parentNode = anchorNode.getParent();
11597
11853
 
11598
- class BaseSource {
11599
- // Template method to override
11600
- async buildListItems(filter = "") {
11601
- return Promise.resolve([])
11602
- }
11854
+ if (X$1(anchorNode)) {
11855
+ return anchorNode
11856
+ } else if (X$1(parentNode)) {
11857
+ return parentNode
11858
+ }
11603
11859
 
11604
- // Template method to override
11605
- promptItemFor(listItem) {
11606
11860
  return null
11607
11861
  }
11608
11862
 
11609
- // Protected
11863
+ #codeNodeWasSelected(codeNode) {
11864
+ const language = codeNode.getLanguage();
11610
11865
 
11611
- buildListItemElementFor(promptItemElement) {
11612
- const template = promptItemElement.querySelector("template[type='menu']");
11613
- const fragment = template.content.cloneNode(true);
11614
- const listItemElement = createElement("li", { role: "option", id: generateDomId("prompt-item"), tabindex: "0" });
11615
- listItemElement.classList.add("lexxy-prompt-menu__item");
11616
- listItemElement.appendChild(fragment);
11617
- return listItemElement
11866
+ this.#updateLanguagePickerWith(language);
11867
+ this.#showLanguagePicker();
11868
+ this.#positionLanguagePicker(codeNode);
11618
11869
  }
11619
11870
 
11620
- async loadPromptItemsFromUrl(url) {
11621
- try {
11622
- const response = await fetch(url);
11623
- const html = await response.text();
11624
- const promptItems = parseHtml(html).querySelectorAll("lexxy-prompt-item");
11625
- return Promise.resolve(Array.from(promptItems))
11626
- } catch (error) {
11627
- return Promise.reject(error)
11871
+ #updateLanguagePickerWith(language) {
11872
+ if (this.languagePickerElement && language) {
11873
+ const normalizedLanguage = dt$1(language);
11874
+ this.languagePickerElement.value = normalizedLanguage;
11628
11875
  }
11629
11876
  }
11630
- }
11631
-
11632
- class LocalFilterSource extends BaseSource {
11633
- async buildListItems(filter = "") {
11634
- const promptItems = await this.fetchPromptItems();
11635
- return this.#buildListItemsFromPromptItems(promptItems, filter)
11636
- }
11637
-
11638
- // Template method to override
11639
- async fetchPromptItems(filter) {
11640
- return Promise.resolve([])
11641
- }
11642
-
11643
- promptItemFor(listItem) {
11644
- return this.promptItemByListItem.get(listItem)
11645
- }
11646
11877
 
11647
- #buildListItemsFromPromptItems(promptItems, filter) {
11648
- const listItems = [];
11649
- this.promptItemByListItem = new WeakMap();
11650
- promptItems.forEach((promptItem) => {
11651
- const searchableText = promptItem.getAttribute("search");
11878
+ #positionLanguagePicker(codeNode) {
11879
+ const codeElement = this.editor.getElementByKey(codeNode.getKey());
11880
+ if (!codeElement) return
11652
11881
 
11653
- if (!filter || filterMatches(searchableText, filter)) {
11654
- const listItem = this.buildListItemElementFor(promptItem);
11655
- this.promptItemByListItem.set(listItem, promptItem);
11656
- listItems.push(listItem);
11657
- }
11658
- });
11882
+ const codeRect = codeElement.getBoundingClientRect();
11883
+ const editorRect = this.editorElement.getBoundingClientRect();
11884
+ const relativeTop = codeRect.top - editorRect.top;
11885
+ const relativeRight = editorRect.right - codeRect.right;
11659
11886
 
11660
- return listItems
11887
+ this.style.top = `${relativeTop}px`;
11888
+ this.style.right = `${relativeRight}px`;
11661
11889
  }
11662
- }
11663
11890
 
11664
- class InlinePromptSource extends LocalFilterSource {
11665
- constructor(inlinePromptItems) {
11666
- super();
11667
- this.inlinePromptItemElements = Array.from(inlinePromptItems);
11891
+ #showLanguagePicker() {
11892
+ this.hidden = false;
11668
11893
  }
11669
11894
 
11670
- async fetchPromptItems() {
11671
- return Promise.resolve(this.inlinePromptItemElements)
11895
+ #hideLanguagePicker() {
11896
+ this.hidden = true;
11672
11897
  }
11673
11898
  }
11674
11899
 
11675
- class DeferredPromptSource extends LocalFilterSource {
11676
- constructor(url) {
11677
- super();
11678
- this.url = url;
11679
-
11680
- this.fetchPromptItems();
11681
- }
11900
+ class TableController {
11901
+ constructor(editorElement) {
11902
+ this.editor = editorElement.editor;
11903
+ this.contents = editorElement.contents;
11904
+ this.selection = editorElement.selection;
11682
11905
 
11683
- async fetchPromptItems() {
11684
- this.promptItems ??= await this.loadPromptItemsFromUrl(this.url);
11906
+ this.currentTableNodeKey = null;
11907
+ this.currentCellKey = null;
11685
11908
 
11686
- return Promise.resolve(this.promptItems)
11909
+ this.#registerKeyHandlers();
11687
11910
  }
11688
- }
11689
-
11690
- const DEBOUNCE_INTERVAL = 200;
11691
11911
 
11692
- class RemoteFilterSource extends BaseSource {
11693
- constructor(url) {
11694
- super();
11912
+ destroy() {
11913
+ this.currentTableNodeKey = null;
11914
+ this.currentCellKey = null;
11695
11915
 
11696
- this.baseURL = url;
11697
- this.loadAndFilterListItems = debounceAsync(this.fetchFilteredListItems.bind(this), DEBOUNCE_INTERVAL);
11916
+ this.#unregisterKeyHandlers();
11698
11917
  }
11699
11918
 
11700
- async buildListItems(filter = "") {
11701
- return await this.loadAndFilterListItems(filter)
11702
- }
11919
+ get currentCell() {
11920
+ if (!this.currentCellKey) return null
11703
11921
 
11704
- promptItemFor(listItem) {
11705
- return this.promptItemByListItem.get(listItem)
11922
+ return this.editor.getEditorState().read(() => {
11923
+ const cell = xo(this.currentCellKey);
11924
+ return (cell instanceof xe) ? cell : null
11925
+ })
11706
11926
  }
11707
11927
 
11708
- async fetchFilteredListItems(filter) {
11709
- const promptItems = await this.loadPromptItemsFromUrl(this.#urlFor(filter));
11710
- return this.#buildListItemsFromPromptItems(promptItems)
11711
- }
11928
+ get currentTableNode() {
11929
+ if (!this.currentTableNodeKey) return null
11712
11930
 
11713
- #urlFor(filter) {
11714
- const url = new URL(this.baseURL, window.location.origin);
11715
- url.searchParams.append("filter", filter);
11716
- return url.toString()
11931
+ return this.editor.getEditorState().read(() => {
11932
+ const tableNode = xo(this.currentTableNodeKey);
11933
+ return (tableNode instanceof hn) ? tableNode : null
11934
+ })
11717
11935
  }
11718
11936
 
11719
- #buildListItemsFromPromptItems(promptItems) {
11720
- const listItems = [];
11721
- this.promptItemByListItem = new WeakMap();
11937
+ get currentRowCells() {
11938
+ const currentRowIndex = this.currentRowIndex;
11722
11939
 
11723
- for (const promptItem of promptItems) {
11724
- const listItem = this.buildListItemElementFor(promptItem);
11725
- this.promptItemByListItem.set(listItem, promptItem);
11726
- listItems.push(listItem);
11727
- }
11940
+ const rows = this.tableRows;
11941
+ if (!rows) return null
11728
11942
 
11729
- return listItems
11943
+ return this.editor.getEditorState().read(() => {
11944
+ return rows[currentRowIndex]?.getChildren() ?? null
11945
+ }) ?? null
11730
11946
  }
11731
- }
11732
11947
 
11733
- const NOTHING_FOUND_DEFAULT_MESSAGE = "Nothing found";
11948
+ get currentRowIndex() {
11949
+ const currentCell = this.currentCell;
11950
+ if (!currentCell) return 0
11734
11951
 
11735
- class LexicalPromptElement extends HTMLElement {
11736
- constructor() {
11737
- super();
11738
- this.keyListeners = [];
11952
+ return this.editor.getEditorState().read(() => {
11953
+ return Ie$1(currentCell)
11954
+ }) ?? 0
11739
11955
  }
11740
11956
 
11741
- static observedAttributes = [ "connected" ]
11742
-
11743
- connectedCallback() {
11744
- this.source = this.#createSource();
11957
+ get currentColumnCells() {
11958
+ const columnIndex = this.currentColumnIndex;
11745
11959
 
11746
- this.#addTriggerListener();
11747
- this.toggleAttribute("connected", true);
11748
- }
11960
+ const rows = this.tableRows;
11961
+ if (!rows) return null
11749
11962
 
11750
- disconnectedCallback() {
11751
- this.source = null;
11752
- this.popoverElement = null;
11963
+ return this.editor.getEditorState().read(() => {
11964
+ return rows.map(row => row.getChildAtIndex(columnIndex))
11965
+ }) ?? null
11753
11966
  }
11754
11967
 
11968
+ get currentColumnIndex() {
11969
+ const currentCell = this.currentCell;
11970
+ if (!currentCell) return 0
11755
11971
 
11756
- attributeChangedCallback(name, oldValue, newValue) {
11757
- if (name === "connected" && this.isConnected && oldValue != null && oldValue !== newValue) {
11758
- requestAnimationFrame(() => this.#reconnect());
11759
- }
11972
+ return this.editor.getEditorState().read(() => {
11973
+ return Ue$1(currentCell)
11974
+ }) ?? 0
11760
11975
  }
11761
11976
 
11762
- get name() {
11763
- return this.getAttribute("name")
11977
+ get tableRows() {
11978
+ return this.editor.getEditorState().read(() => {
11979
+ return this.currentTableNode?.getChildren()
11980
+ }) ?? null
11764
11981
  }
11765
11982
 
11766
- get trigger() {
11767
- return this.getAttribute("trigger")
11768
- }
11983
+ updateSelectedTable() {
11984
+ let cellNode = null;
11985
+ let tableNode = null;
11769
11986
 
11770
- get supportsSpaceInSearches() {
11771
- return this.hasAttribute("supports-space-in-searches")
11772
- }
11987
+ this.editor.getEditorState().read(() => {
11988
+ const selection = Lr();
11989
+ if (!selection || !this.selection.isTableCellSelected) return
11990
+
11991
+ const node = selection.getNodes()[0];
11992
+
11993
+ cellNode = Gt(node);
11994
+ tableNode = Qt(node);
11995
+ });
11773
11996
 
11774
- get open() {
11775
- return this.popoverElement?.classList?.contains("lexxy-prompt-menu--visible")
11997
+ this.currentCellKey = cellNode?.getKey() ?? null;
11998
+ this.currentTableNodeKey = tableNode?.getKey() ?? null;
11776
11999
  }
11777
12000
 
11778
- get closed() {
11779
- return !this.open
11780
- }
12001
+ executeTableCommand(command, customIndex = null) {
12002
+ if (command.action === "delete" && command.childType === "table") {
12003
+ this.#deleteTable();
12004
+ return
12005
+ }
11781
12006
 
11782
- get #doesSpaceSelect() {
11783
- return !this.supportsSpaceInSearches
12007
+ if (command.action === "toggle") {
12008
+ this.#executeToggleStyle(command);
12009
+ return
12010
+ }
12011
+
12012
+ this.#executeCommand(command, customIndex);
11784
12013
  }
11785
12014
 
11786
- #createSource() {
11787
- const src = this.getAttribute("src");
11788
- if (src) {
11789
- if (this.hasAttribute("remote-filtering")) {
11790
- return new RemoteFilterSource(src)
11791
- } else {
11792
- return new DeferredPromptSource(src)
11793
- }
11794
- } else {
11795
- return new InlinePromptSource(this.querySelectorAll("lexxy-prompt-item"))
11796
- }
12015
+ #executeCommand(command, customIndex = null) {
12016
+ this.#selectCellAtSelection();
12017
+ this.editor.dispatchCommand(this.#commandName(command));
12018
+ this.#selectNextBestCell(command, customIndex);
11797
12019
  }
11798
12020
 
11799
- #addTriggerListener() {
11800
- const unregister = this.#editor.registerUpdateListener(() => {
11801
- this.#editor.read(() => {
11802
- const { node, offset } = this.#selection.selectedNodeWithOffset();
11803
- if (!node) return
12021
+ #executeToggleStyle(command) {
12022
+ const childType = command.childType;
11804
12023
 
11805
- if (lr(node) && offset > 0) {
11806
- const fullText = node.getTextContent();
11807
- const charBeforeCursor = fullText[offset - 1];
12024
+ let cells = null;
12025
+ let headerState = null;
11808
12026
 
11809
- // Check if trigger is at the start of the text node (new line case) or preceded by space or newline
11810
- if (charBeforeCursor === this.trigger) {
11811
- const isAtStart = offset === 1;
12027
+ if (childType === "row") {
12028
+ cells = this.currentRowCells;
12029
+ headerState = ve$1.ROW;
12030
+ } else if (childType === "column") {
12031
+ cells = this.currentColumnCells;
12032
+ headerState = ve$1.COLUMN;
12033
+ }
11812
12034
 
11813
- const charBeforeTrigger = offset > 1 ? fullText[offset - 2] : null;
11814
- const isPrecededBySpaceOrNewline = charBeforeTrigger === " " || charBeforeTrigger === "\n";
12035
+ if (!cells || cells.length === 0) return
11815
12036
 
11816
- if (isAtStart || isPrecededBySpaceOrNewline) {
11817
- unregister();
11818
- this.#showPopover();
11819
- }
11820
- }
11821
- }
12037
+ this.editor.update(() => {
12038
+ const firstCell = Be$1(cells[0]);
12039
+ if (!firstCell) return
12040
+
12041
+ const currentStyle = firstCell.getHeaderStyles();
12042
+ const newStyle = currentStyle ^ headerState;
12043
+
12044
+ cells.forEach(cell => {
12045
+ this.#setHeaderStyle(cell, newStyle, headerState);
11822
12046
  });
11823
12047
  });
11824
12048
  }
11825
12049
 
11826
- #addCursorPositionListener() {
11827
- this.cursorPositionListener = this.#editor.registerUpdateListener(() => {
11828
- if (this.closed) return
12050
+ #deleteTable() {
12051
+ this.#selectCellAtSelection();
12052
+ this.editor.dispatchCommand("deleteTable");
12053
+ }
11829
12054
 
11830
- this.#editor.read(() => {
11831
- const { node, offset } = this.#selection.selectedNodeWithOffset();
11832
- if (!node) return
12055
+ #selectCellAtSelection() {
12056
+ this.editor.update(() => {
12057
+ const selection = Lr();
12058
+ if (!selection) return
11833
12059
 
11834
- if (lr(node) && offset > 0) {
11835
- const fullText = node.getTextContent();
11836
- const textBeforeCursor = fullText.slice(0, offset);
11837
- const lastTriggerIndex = textBeforeCursor.lastIndexOf(this.trigger);
12060
+ const node = selection.getNodes()[0];
11838
12061
 
11839
- // If trigger is not found, or cursor is at or before the trigger position, hide popover
11840
- if (lastTriggerIndex === -1 || offset <= lastTriggerIndex) {
11841
- this.#hidePopover();
11842
- }
11843
- } else {
11844
- // Cursor is not in a text node or at offset 0, hide popover
11845
- this.#hidePopover();
11846
- }
11847
- });
12062
+ Gt(node)?.selectEnd();
11848
12063
  });
11849
12064
  }
11850
12065
 
11851
- #removeCursorPositionListener() {
11852
- if (this.cursorPositionListener) {
11853
- this.cursorPositionListener();
11854
- this.cursorPositionListener = null;
11855
- }
11856
- }
12066
+ #commandName(command) {
12067
+ const { action, childType, direction } = command;
11857
12068
 
11858
- get #editor() {
11859
- return this.#editorElement.editor
12069
+ const childTypeSuffix = upcaseFirst(childType);
12070
+ const directionSuffix = action == "insert" ? upcaseFirst(direction) : "";
12071
+ return `${action}Table${childTypeSuffix}${directionSuffix}`
11860
12072
  }
11861
12073
 
11862
- get #editorElement() {
11863
- return this.closest("lexxy-editor")
12074
+ #setHeaderStyle(cell, newStyle, headerState) {
12075
+ const tableCellNode = Be$1(cell);
12076
+ tableCellNode?.setHeaderStyles(newStyle, headerState);
11864
12077
  }
11865
12078
 
11866
- get #selection() {
11867
- return this.#editorElement.selection
11868
- }
12079
+ async #selectCellAtIndex(rowIndex, columnIndex) {
12080
+ // We wait for next frame, otherwise table operations might not have completed yet.
12081
+ await nextFrame();
11869
12082
 
11870
- async #showPopover() {
11871
- this.popoverElement ??= await this.#buildPopover();
11872
- this.#resetPopoverPosition();
11873
- await this.#filterOptions();
11874
- this.popoverElement.classList.toggle("lexxy-prompt-menu--visible", true);
11875
- this.#selectFirstOption();
12083
+ if (!this.currentTableNode) return
11876
12084
 
11877
- this.#editorElement.addEventListener("keydown", this.#handleKeydownOnPopover);
11878
- this.#editorElement.addEventListener("lexxy:change", this.#filterOptions);
12085
+ const rows = this.tableRows;
12086
+ if (!rows) return
11879
12087
 
11880
- this.#registerKeyListeners();
11881
- this.#addCursorPositionListener();
12088
+ const row = rows[rowIndex];
12089
+ if (!row) return
12090
+
12091
+ this.editor.update(() => {
12092
+ const cell = Be$1(row.getChildAtIndex(columnIndex));
12093
+ cell?.selectEnd();
12094
+ });
11882
12095
  }
11883
12096
 
11884
- #registerKeyListeners() {
11885
- // We can't use a regular keydown for Enter as Lexical handles it first
11886
- this.keyListeners.push(this.#editor.registerCommand(Ne$2, this.#handleSelectedOption.bind(this), Bi));
11887
- this.keyListeners.push(this.#editor.registerCommand(Me$2, this.#handleSelectedOption.bind(this), Bi));
12097
+ #selectNextBestCell(command, customIndex = null) {
12098
+ const { childType, direction } = command;
11888
12099
 
11889
- if (this.#doesSpaceSelect) {
11890
- this.keyListeners.push(this.#editor.registerCommand(be$1, this.#handleSelectedOption.bind(this), Bi));
12100
+ let rowIndex = this.currentRowIndex;
12101
+ let columnIndex = customIndex !== null ? customIndex : this.currentColumnIndex;
12102
+
12103
+ const deleteOffset = command.action === "delete" ? -1 : 0;
12104
+ const offset = direction === "after" ? 1 : deleteOffset;
12105
+
12106
+ if (childType === "row") {
12107
+ rowIndex += offset;
12108
+ } else if (childType === "column") {
12109
+ columnIndex += offset;
11891
12110
  }
11892
12111
 
11893
- // Register arrow keys with HIGH priority to prevent Lexical's selection handlers from running
11894
- this.keyListeners.push(this.#editor.registerCommand(ke$2, this.#handleArrowUp.bind(this), Bi));
11895
- this.keyListeners.push(this.#editor.registerCommand(Te$2, this.#handleArrowDown.bind(this), Bi));
12112
+ this.#selectCellAtIndex(rowIndex, columnIndex);
11896
12113
  }
11897
12114
 
11898
- #handleArrowUp(event) {
11899
- this.#moveSelectionUp();
11900
- event.preventDefault();
11901
- return true
11902
- }
12115
+ #selectNextRow() {
12116
+ const rows = this.tableRows;
12117
+ if (!rows) return
11903
12118
 
11904
- #handleArrowDown(event) {
11905
- this.#moveSelectionDown();
11906
- event.preventDefault();
11907
- return true
12119
+ const nextRow = rows.at(this.currentRowIndex + 1);
12120
+ if (!nextRow) return
12121
+
12122
+ this.editor.update(() => {
12123
+ nextRow.getChildAtIndex(this.currentColumnIndex)?.selectEnd();
12124
+ });
11908
12125
  }
11909
12126
 
11910
- #selectFirstOption() {
11911
- const firstOption = this.#listItemElements[0];
12127
+ #selectPreviousCell() {
12128
+ const cell = this.currentCell;
12129
+ if (!cell) return
11912
12130
 
11913
- if (firstOption) {
11914
- this.#selectOption(firstOption);
11915
- }
12131
+ this.editor.update(() => {
12132
+ cell.selectPrevious();
12133
+ });
11916
12134
  }
11917
12135
 
11918
- get #listItemElements() {
11919
- return Array.from(this.popoverElement.querySelectorAll(".lexxy-prompt-menu__item"))
12136
+ #insertRowAndSelectFirstCell() {
12137
+ this.executeTableCommand({ action: "insert", childType: "row", direction: "after" }, 0);
11920
12138
  }
11921
12139
 
11922
- #selectOption(listItem) {
11923
- this.#clearSelection();
11924
- listItem.toggleAttribute("aria-selected", true);
11925
- listItem.scrollIntoView({ block: "nearest", behavior: "smooth" });
11926
- listItem.focus();
12140
+ #deleteRowAndSelectLastCell() {
12141
+ this.executeTableCommand({ action: "delete", childType: "row" }, -1);
12142
+ }
11927
12143
 
11928
- // Preserve selection to prevent cursor jump
11929
- this.#selection.preservingSelection(() => {
11930
- this.#editorElement.focus();
11931
- });
12144
+ #deleteRowAndSelectNextNode() {
12145
+ const tableNode = this.currentTableNode;
12146
+ this.executeTableCommand({ action: "delete", childType: "row" });
11932
12147
 
11933
- this.#editorContentElement.setAttribute("aria-controls", this.popoverElement.id);
11934
- this.#editorContentElement.setAttribute("aria-activedescendant", listItem.id);
11935
- this.#editorContentElement.setAttribute("aria-haspopup", "listbox");
12148
+ this.editor.update(() => {
12149
+ const next = tableNode?.getNextSibling();
12150
+ if (Ii(next)) {
12151
+ next.selectStart();
12152
+ } else {
12153
+ const newParagraph = Li();
12154
+ this.currentTableNode.insertAfter(newParagraph);
12155
+ newParagraph.selectStart();
12156
+ }
12157
+ });
11936
12158
  }
11937
12159
 
11938
- #clearSelection() {
11939
- this.#listItemElements.forEach((item) => { item.toggleAttribute("aria-selected", false); });
11940
- this.#editorContentElement.removeAttribute("aria-controls");
11941
- this.#editorContentElement.removeAttribute("aria-activedescendant");
11942
- this.#editorContentElement.removeAttribute("aria-haspopup");
11943
- }
12160
+ #isCurrentCellEmpty() {
12161
+ if (!this.currentTableNode) return false
11944
12162
 
11945
- #positionPopover() {
11946
- const { x, y, fontSize } = this.#selection.cursorPosition;
11947
- const editorRect = this.#editorElement.getBoundingClientRect();
11948
- const contentRect = this.#editorContentElement.getBoundingClientRect();
11949
- const verticalOffset = contentRect.top - editorRect.top;
12163
+ const cell = this.currentCell;
12164
+ if (!cell) return false
11950
12165
 
11951
- if (!this.popoverElement.hasAttribute("data-anchored")) {
11952
- this.popoverElement.style.left = `${x}px`;
11953
- this.popoverElement.toggleAttribute("data-anchored", true);
11954
- }
12166
+ return cell.getTextContent().trim() === ""
12167
+ }
11955
12168
 
11956
- this.popoverElement.style.top = `${y + verticalOffset}px`;
11957
- this.popoverElement.style.bottom = "auto";
12169
+ #isCurrentRowLast() {
12170
+ if (!this.currentTableNode) return false
11958
12171
 
11959
- const popoverRect = this.popoverElement.getBoundingClientRect();
11960
- const isClippedAtBottom = popoverRect.bottom > window.innerHeight;
12172
+ const rows = this.tableRows;
12173
+ if (!rows) return false
11961
12174
 
11962
- if (isClippedAtBottom || this.popoverElement.hasAttribute("data-clipped-at-bottom")) {
11963
- this.popoverElement.style.top = `${y + verticalOffset - popoverRect.height - fontSize}px`;
11964
- this.popoverElement.style.bottom = "auto";
11965
- this.popoverElement.toggleAttribute("data-clipped-at-bottom", true);
11966
- }
12175
+ return rows.length === this.currentRowIndex + 1
11967
12176
  }
11968
12177
 
11969
- #resetPopoverPosition() {
11970
- this.popoverElement.removeAttribute("data-clipped-at-bottom");
11971
- this.popoverElement.removeAttribute("data-anchored");
12178
+ #isCurrentRowEmpty() {
12179
+ if (!this.currentTableNode) return false
12180
+
12181
+ const cells = this.currentRowCells;
12182
+ if (!cells) return false
12183
+
12184
+ return cells.every(cell => cell.getTextContent().trim() === "")
11972
12185
  }
11973
12186
 
11974
- async #hidePopover() {
11975
- this.#clearSelection();
11976
- this.popoverElement.classList.toggle("lexxy-prompt-menu--visible", false);
11977
- this.#editorElement.removeEventListener("lexxy:change", this.#filterOptions);
11978
- this.#editorElement.removeEventListener("keydown", this.#handleKeydownOnPopover);
12187
+ #isFirstCellInRow() {
12188
+ if (!this.currentTableNode) return false
11979
12189
 
11980
- this.#unregisterKeyListeners();
11981
- this.#removeCursorPositionListener();
12190
+ const cells = this.currentRowCells;
12191
+ if (!cells) return false
11982
12192
 
11983
- await nextFrame();
11984
- this.#addTriggerListener();
12193
+ return cells.indexOf(this.currentCell) === 0
11985
12194
  }
11986
12195
 
11987
- #unregisterKeyListeners() {
11988
- this.keyListeners.forEach((unregister) => unregister());
11989
- this.keyListeners = [];
12196
+ #registerKeyHandlers() {
12197
+ // We can't prevent these externally using regular keydown because Lexical handles it first.
12198
+ this.unregisterBackspaceKeyHandler = this.editor.registerCommand(we$1, (event) => this.#handleBackspaceKey(event), Bi);
12199
+ this.unregisterEnterKeyHandler = this.editor.registerCommand(Ne$2, (event) => this.#handleEnterKey(event), Bi);
11990
12200
  }
11991
12201
 
11992
- #filterOptions = async () => {
11993
- if (this.initialPrompt) {
11994
- this.initialPrompt = false;
11995
- return
12202
+ #unregisterKeyHandlers() {
12203
+ this.unregisterBackspaceKeyHandler?.();
12204
+ this.unregisterEnterKeyHandler?.();
12205
+
12206
+ this.unregisterBackspaceKeyHandler = null;
12207
+ this.unregisterEnterKeyHandler = null;
12208
+ }
12209
+
12210
+ #handleBackspaceKey(event) {
12211
+ if (!this.currentTableNode) return false
12212
+
12213
+ if (this.#isCurrentRowEmpty() && this.#isFirstCellInRow()) {
12214
+ event.preventDefault();
12215
+ this.#deleteRowAndSelectLastCell();
12216
+ return true
11996
12217
  }
11997
12218
 
11998
- if (this.#editorContents.containsTextBackUntil(this.trigger)) {
11999
- await this.#showFilteredOptions();
12000
- await nextFrame();
12001
- this.#positionPopover();
12002
- } else {
12003
- this.#hidePopover();
12219
+ if (this.#isCurrentCellEmpty() && !this.#isFirstCellInRow()) {
12220
+ event.preventDefault();
12221
+ this.#selectPreviousCell();
12222
+ return true
12004
12223
  }
12224
+
12225
+ return false
12005
12226
  }
12006
12227
 
12007
- async #showFilteredOptions() {
12008
- const filter = this.#editorContents.textBackUntil(this.trigger);
12009
- const filteredListItems = await this.source.buildListItems(filter);
12010
- this.popoverElement.innerHTML = "";
12228
+ #handleEnterKey(event) {
12229
+ if ((event.ctrlKey || event.metaKey) || event.shiftKey || !this.currentTableNode) return false
12011
12230
 
12012
- if (filteredListItems.length > 0) {
12013
- this.#showResults(filteredListItems);
12231
+ if (this.selection.isInsideList || this.selection.isInsideCodeBlock) return false
12232
+
12233
+ event.preventDefault();
12234
+
12235
+ if (this.#isCurrentRowLast() && this.#isCurrentRowEmpty()) {
12236
+ this.#deleteRowAndSelectNextNode();
12237
+ } else if (this.#isCurrentRowLast()) {
12238
+ this.#insertRowAndSelectFirstCell();
12014
12239
  } else {
12015
- this.#showEmptyResults();
12240
+ this.#selectNextRow();
12016
12241
  }
12017
- this.#selectFirstOption();
12018
- }
12019
12242
 
12020
- #showResults(filteredListItems) {
12021
- this.popoverElement.classList.remove("lexxy-prompt-menu--empty");
12022
- this.popoverElement.append(...filteredListItems);
12243
+ return true
12023
12244
  }
12245
+ }
12024
12246
 
12025
- #showEmptyResults() {
12026
- this.popoverElement.classList.add("lexxy-prompt-menu--empty");
12027
- const el = createElement("li", { innerHTML: this.#emptyResultsMessage });
12028
- el.classList.add("lexxy-prompt-menu__item--empty");
12029
- this.popoverElement.append(el);
12030
- }
12247
+ var TableIcons = {
12248
+ "insert-row-before":
12249
+ `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
12250
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M7.86804e-07 15C8.29055e-07 15.8284 0.671574 16.5 1.5 16.5H15L15.1533 16.4922C15.8593 16.4205 16.4205 15.8593 16.4922 15.1533L16.5 15V4.5L16.4922 4.34668C16.4154 3.59028 15.7767 3 15 3H13.5L13.5 4.5H15V9H1.5L1.5 4.5L3 4.5V3H1.5C0.671574 3 1.20956e-06 3.67157 1.24577e-06 4.5L7.86804e-07 15ZM15 10.5V15H1.5L1.5 10.5H15Z"/>
12251
+ <path d="M4.5 4.5H7.5V7.5H9V4.5H12L12 3L9 3V6.55671e-08L7.5 0V3L4.5 3V4.5Z"/>
12252
+ </svg>`,
12253
+
12254
+ "insert-row-after":
12255
+ `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
12256
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M7.86804e-07 13.5C7.50592e-07 14.3284 0.671574 15 1.5 15H3V13.5H1.5L1.5 9L15 9V13.5H13.5V15H15C15.7767 15 16.4154 14.4097 16.4922 13.6533L16.5 13.5V3L16.4922 2.84668C16.4205 2.14069 15.8593 1.57949 15.1533 1.50781L15 1.5L1.5 1.5C0.671574 1.5 1.28803e-06 2.17157 1.24577e-06 3L7.86804e-07 13.5ZM15 3V7.5L1.5 7.5L1.5 3L15 3Z"/>
12257
+ <path d="M7.5 15V18H9V15H12V13.5H9V10.5H7.5V13.5H4.5V15H7.5Z"/>
12258
+ </svg>`,
12259
+
12260
+ "delete-row":
12261
+ `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
12262
+ <path d="M16.4922 12.1533C16.4154 12.9097 15.7767 13.5 15 13.5L12 13.5V12H15V6L1.5 6L1.5 12H4.5V13.5H1.5C0.723337 13.5 0.0846104 12.9097 0.00781328 12.1533L7.86804e-07 12L1.04907e-06 6C1.17362e-06 5.22334 0.590278 4.58461 1.34668 4.50781L1.5 4.5L15 4.5C15.8284 4.5 16.5 5.17157 16.5 6V12L16.4922 12.1533Z"/>
12263
+ <path d="M10.3711 15.9316L8.25 13.8096L6.12793 15.9316L5.06738 14.8711L7.18945 12.75L5.06738 10.6289L6.12793 9.56836L8.25 11.6895L10.3711 9.56836L11.4316 10.6289L9.31055 12.75L11.4316 14.8711L10.3711 15.9316Z"/>
12264
+ </svg>`,
12265
+
12266
+ "toggle-row":
12267
+ `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
12268
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M0.00781328 13.6533C0.0846108 14.4097 0.723337 15 1.5 15L15 15L15.1533 14.9922C15.8593 14.9205 16.4205 14.3593 16.4922 13.6533L16.5 13.5V4.5L16.4922 4.34668C16.4205 3.64069 15.8593 3.07949 15.1533 3.00781L15 3L1.5 3C0.671574 3 1.24863e-06 3.67157 1.18021e-06 4.5L7.86804e-07 13.5L0.00781328 13.6533ZM15 9V13.5L1.5 13.5L1.5 9L15 9Z"/>
12269
+ </svg>`,
12270
+
12271
+ "insert-column-before":
12272
+ `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
12273
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M4.5 0C3.67157 0 3 0.671573 3 1.5V3H4.5V1.5H9V15H4.5V13.5H3V15C3 15.7767 3.59028 16.4154 4.34668 16.4922L4.5 16.5H15L15.1533 16.4922C15.8593 16.4205 16.4205 15.8593 16.4922 15.1533L16.5 15V1.5C16.5 0.671573 15.8284 6.03989e-09 15 0H4.5ZM15 15H10.5V1.5H15V15Z"/>
12274
+ <path d="M3 7.5H0V9H3V12H4.5V9H7.5V7.5H4.5V4.5H3V7.5Z"/>
12275
+ </svg>`,
12276
+
12277
+ "insert-column-after":
12278
+ `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
12279
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M13.5 0C14.3284 0 15 0.671573 15 1.5V3H13.5V1.5H9V15H13.5V13.5H15V15C15 15.7767 14.4097 16.4154 13.6533 16.4922L13.5 16.5H3L2.84668 16.4922C2.14069 16.4205 1.57949 15.8593 1.50781 15.1533L1.5 15V1.5C1.5 0.671573 2.17157 6.03989e-09 3 0H13.5ZM3 15H7.5V1.5H3V15Z"/>
12280
+ <path d="M15 7.5H18V9H15V12H13.5V9H10.5V7.5H13.5V4.5H15V7.5Z"/>
12281
+ </svg>`,
12282
+
12283
+ "delete-column":
12284
+ `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
12285
+ <path d="M12.1533 0.0078125C12.9097 0.0846097 13.5 0.723336 13.5 1.5V4.5H12V1.5H6V15H12V12H13.5V15C13.5 15.7767 12.9097 16.4154 12.1533 16.4922L12 16.5H6C5.22334 16.5 4.58461 15.9097 4.50781 15.1533L4.5 15V1.5C4.5 0.671573 5.17157 2.41596e-08 6 0H12L12.1533 0.0078125Z"/>
12286
+ <path d="M15.9316 6.12891L13.8105 8.24902L15.9326 10.3711L14.8711 11.4316L12.75 9.31055L10.6289 11.4316L9.56738 10.3711L11.6885 8.24902L9.56836 6.12891L10.6289 5.06836L12.75 7.18848L14.8711 5.06836L15.9316 6.12891Z"/>
12287
+ </svg>`,
12288
+
12289
+ "toggle-column":
12290
+ `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
12291
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M13.6533 17.9922C14.4097 17.9154 15 17.2767 15 16.5L15 3L14.9922 2.84668C14.9205 2.14069 14.3593 1.57949 13.6533 1.50781L13.5 1.5L4.5 1.5L4.34668 1.50781C3.59028 1.58461 3 2.22334 3 3L3 16.5C3 17.2767 3.59028 17.9154 4.34668 17.9922L4.5 18L13.5 18L13.6533 17.9922ZM9 3L13.5 3L13.5 16.5L9 16.5L9 3Z" />
12292
+ </svg>`,
12293
+
12294
+ "delete-table":
12295
+ `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
12296
+ <path d="M18.2129 19.2305C18.0925 20.7933 16.7892 22 15.2217 22H7.77832C6.21084 22 4.90753 20.7933 4.78711 19.2305L4 9H19L18.2129 19.2305Z"/><path d="M13 2C14.1046 2 15 2.89543 15 4H19C19.5523 4 20 4.44772 20 5V6C20 6.55228 19.5523 7 19 7H4C3.44772 7 3 6.55228 3 6V5C3 4.44772 3.44772 4 4 4H8C8 2.89543 8.89543 2 10 2H13Z"/>
12297
+ </svg>`
12298
+ };
12031
12299
 
12032
- get #emptyResultsMessage() {
12033
- return this.getAttribute("empty-results") || NOTHING_FOUND_DEFAULT_MESSAGE
12034
- }
12300
+ class TableTools extends HTMLElement {
12301
+ connectedCallback() {
12302
+ this.tableController = new TableController(this.#editorElement);
12035
12303
 
12036
- #handleKeydownOnPopover = (event) => {
12037
- if (event.key === "Escape") {
12038
- this.#hidePopover();
12039
- this.#editorElement.focus();
12040
- event.stopPropagation();
12041
- }
12042
- // Arrow keys are now handled via Lexical commands with HIGH priority
12304
+ this.#setUpButtons();
12305
+ this.#monitorForTableSelection();
12306
+ this.#registerKeyboardShortcuts();
12043
12307
  }
12044
12308
 
12045
- #moveSelectionDown() {
12046
- const nextIndex = this.#selectedIndex + 1;
12047
- if (nextIndex < this.#listItemElements.length) this.#selectOption(this.#listItemElements[nextIndex]);
12309
+ disconnectedCallback() {
12310
+ this.#unregisterKeyboardShortcuts();
12311
+
12312
+ this.unregisterUpdateListener?.();
12313
+ this.unregisterUpdateListener = null;
12314
+
12315
+ this.removeEventListener("keydown", this.#handleToolsKeydown);
12316
+
12317
+ this.tableController?.destroy();
12318
+ this.tableController = null;
12048
12319
  }
12049
12320
 
12050
- #moveSelectionUp() {
12051
- const previousIndex = this.#selectedIndex - 1;
12052
- if (previousIndex >= 0) this.#selectOption(this.#listItemElements[previousIndex]);
12321
+ get #editor() {
12322
+ return this.#editorElement.editor
12053
12323
  }
12054
12324
 
12055
- get #selectedIndex() {
12056
- return this.#listItemElements.findIndex((item) => item.hasAttribute("aria-selected"))
12325
+ get #editorElement() {
12326
+ return this.closest("lexxy-editor")
12057
12327
  }
12058
12328
 
12059
- get #selectedListItem() {
12060
- return this.#listItemElements[this.#selectedIndex]
12329
+ get #tableToolsButtons() {
12330
+ return Array.from(this.querySelectorAll("button, details > summary"))
12061
12331
  }
12062
12332
 
12063
- #handleSelectedOption(event) {
12064
- event.preventDefault();
12065
- event.stopPropagation();
12066
- this.#optionWasSelected();
12067
- return true
12068
- }
12333
+ #setUpButtons() {
12334
+ this.appendChild(this.#createRowButtonsContainer());
12335
+ this.appendChild(this.#createColumnButtonsContainer());
12069
12336
 
12070
- #optionWasSelected() {
12071
- this.#replaceTriggerWithSelectedItem();
12072
- this.#hidePopover();
12073
- this.#editorElement.focus();
12337
+ this.appendChild(this.#createDeleteTableButton());
12338
+ this.addEventListener("keydown", this.#handleToolsKeydown);
12074
12339
  }
12075
12340
 
12076
- #replaceTriggerWithSelectedItem() {
12077
- const promptItem = this.source.promptItemFor(this.#selectedListItem);
12341
+ #createButtonsContainer(childType, setCountProperty, moreMenu) {
12342
+ const container = createElement("div", { className: `lexxy-table-control lexxy-table-control--${childType}` });
12078
12343
 
12079
- if (!promptItem) { return }
12344
+ const plusButton = this.#createButton(`Add ${childType}`, { action: "insert", childType, direction: "after" }, "+");
12345
+ const minusButton = this.#createButton(`Remove ${childType}`, { action: "delete", childType }, "−");
12080
12346
 
12081
- const templates = Array.from(promptItem.querySelectorAll("template[type='editor']"));
12082
- const stringToReplace = `${this.trigger}${this.#editorContents.textBackUntil(this.trigger)}`;
12347
+ const dropdown = createElement("details", { className: "lexxy-table-control__more-menu" });
12348
+ dropdown.setAttribute("name", "lexxy-dropdown");
12349
+ dropdown.tabIndex = -1;
12083
12350
 
12084
- if (this.hasAttribute("insert-editable-text")) {
12085
- this.#insertTemplatesAsEditableText(templates, stringToReplace);
12086
- } else {
12087
- this.#insertTemplatesAsAttachments(templates, stringToReplace, promptItem.getAttribute("sgid"));
12088
- }
12089
- }
12351
+ const count = createElement("summary", {}, `_ ${childType}s`);
12352
+ setCountProperty(count);
12353
+ dropdown.appendChild(count);
12090
12354
 
12091
- #insertTemplatesAsEditableText(templates, stringToReplace) {
12092
- this.#editor.update(() => {
12093
- const nodes = templates.flatMap(template => this.#buildEditableTextNodes(template));
12094
- this.#editorContents.replaceTextBackUntil(stringToReplace, nodes);
12095
- });
12096
- }
12355
+ dropdown.appendChild(moreMenu);
12097
12356
 
12098
- #buildEditableTextNodes(template) {
12099
- return m$1(this.#editor, parseHtml(`${template.innerHTML}`))
12100
- }
12357
+ container.appendChild(minusButton);
12358
+ container.appendChild(dropdown);
12359
+ container.appendChild(plusButton);
12101
12360
 
12102
- #insertTemplatesAsAttachments(templates, stringToReplace, fallbackSgid = null) {
12103
- this.#editor.update(() => {
12104
- const attachmentNodes = this.#buildAttachmentNodes(templates, fallbackSgid);
12105
- const spacedAttachmentNodes = attachmentNodes.flatMap(node => [ node, this.#getSpacerTextNode() ]).slice(0, -1);
12106
- this.#editorContents.replaceTextBackUntil(stringToReplace, spacedAttachmentNodes);
12107
- });
12361
+ return container
12108
12362
  }
12109
12363
 
12110
- #buildAttachmentNodes(templates, fallbackSgid = null) {
12111
- return templates.map(
12112
- template => this.#buildAttachmentNode(
12113
- template.innerHTML,
12114
- template.getAttribute("content-type") || this.#defaultPromptContentType,
12115
- template.getAttribute("sgid") || fallbackSgid
12116
- ))
12364
+ #createRowButtonsContainer() {
12365
+ return this.#createButtonsContainer(
12366
+ "row",
12367
+ (count) => { this.rowCount = count; },
12368
+ this.#createMoreMenuSection("row")
12369
+ )
12117
12370
  }
12118
12371
 
12119
- #getSpacerTextNode() {
12120
- return sr(" ")
12372
+ #createColumnButtonsContainer() {
12373
+ return this.#createButtonsContainer(
12374
+ "column",
12375
+ (count) => { this.columnCount = count; },
12376
+ this.#createMoreMenuSection("column")
12377
+ )
12121
12378
  }
12122
12379
 
12123
- get #defaultPromptContentType() {
12124
- const attachmentContentTypeNamespace = Lexxy.global.get("attachmentContentTypeNamespace");
12125
- return `application/vnd.${attachmentContentTypeNamespace}.${this.name}`
12380
+ #createMoreMenuSection(childType) {
12381
+ const section = createElement("div", { className: "lexxy-table-control__more-menu-details" });
12382
+ const addBeforeButton = this.#createButton(`Add ${childType} before`, { action: "insert", childType, direction: "before" });
12383
+ const addAfterButton = this.#createButton(`Add ${childType} after`, { action: "insert", childType, direction: "after" });
12384
+ const toggleStyleButton = this.#createButton(`Toggle ${childType} style`, { action: "toggle", childType });
12385
+ const deleteButton = this.#createButton(`Remove ${childType}`, { action: "delete", childType });
12386
+
12387
+ section.appendChild(addBeforeButton);
12388
+ section.appendChild(addAfterButton);
12389
+ section.appendChild(toggleStyleButton);
12390
+ section.appendChild(deleteButton);
12391
+
12392
+ return section
12126
12393
  }
12127
12394
 
12128
- #buildAttachmentNode(innerHtml, contentType, sgid) {
12129
- return new CustomActionTextAttachmentNode({ sgid, contentType, innerHtml })
12395
+ #createDeleteTableButton() {
12396
+ const container = createElement("div", { className: "lexxy-table-control" });
12397
+
12398
+ const deleteTableButton = this.#createButton("Delete this table?", { action: "delete", childType: "table" });
12399
+ deleteTableButton.classList.add("lexxy-table-control__button--delete-table");
12400
+
12401
+ container.appendChild(deleteTableButton);
12402
+
12403
+ this.deleteContainer = container;
12404
+
12405
+ return container
12130
12406
  }
12131
12407
 
12132
- get #editorContents() {
12133
- return this.#editorElement.contents
12408
+ #createButton(label, command = {}, icon = this.#icon(command)) {
12409
+ const button = createElement("button", {
12410
+ className: "lexxy-table-control__button",
12411
+ "aria-label": label,
12412
+ type: "button"
12413
+ });
12414
+ button.tabIndex = -1;
12415
+ button.innerHTML = `${icon} <span>${label}</span>`;
12416
+
12417
+ button.dataset.action = command.action;
12418
+ button.dataset.childType = command.childType;
12419
+ button.dataset.direction = command.direction;
12420
+
12421
+ button.addEventListener("click", () => this.#executeTableCommand(command));
12422
+
12423
+ button.addEventListener("mouseover", () => this.#handleCommandButtonHover());
12424
+ button.addEventListener("focus", () => this.#handleCommandButtonHover());
12425
+ button.addEventListener("mouseout", () => this.#handleCommandButtonHover());
12426
+
12427
+ return button
12134
12428
  }
12135
12429
 
12136
- get #editorContentElement() {
12137
- return this.#editorElement.editorContentElement
12430
+ #registerKeyboardShortcuts() {
12431
+ this.unregisterKeyboardShortcuts = this.#editor.registerCommand(me$1, this.#handleAccessibilityShortcutKey, Bi);
12138
12432
  }
12139
12433
 
12140
- async #buildPopover() {
12141
- const popoverContainer = createElement("ul", { role: "listbox", id: generateDomId("prompt-popover") }); // Avoiding [popover] due to not being able to position at an arbitrary X, Y position.
12142
- popoverContainer.classList.add("lexxy-prompt-menu");
12143
- popoverContainer.style.position = "absolute";
12144
- popoverContainer.setAttribute("nonce", getNonce());
12145
- popoverContainer.append(...await this.source.buildListItems());
12146
- popoverContainer.addEventListener("click", this.#handlePopoverClick);
12147
- this.#editorElement.appendChild(popoverContainer);
12148
- return popoverContainer
12434
+ #unregisterKeyboardShortcuts() {
12435
+ this.unregisterKeyboardShortcuts?.();
12436
+ this.unregisterKeyboardShortcuts = null;
12149
12437
  }
12150
12438
 
12151
- #handlePopoverClick = (event) => {
12152
- const listItem = event.target.closest(".lexxy-prompt-menu__item");
12153
- if (listItem) {
12154
- this.#selectOption(listItem);
12155
- this.#optionWasSelected();
12439
+ #handleAccessibilityShortcutKey = (event) => {
12440
+ if ((event.ctrlKey || event.metaKey) && event.shiftKey && event.key === "F10") {
12441
+ const firstButton = this.querySelector("button, [tabindex]:not([tabindex='-1'])");
12442
+ firstButton?.focus();
12156
12443
  }
12157
12444
  }
12158
12445
 
12159
- #reconnect() {
12160
- this.disconnectedCallback();
12161
- this.connectedCallback();
12446
+ #handleToolsKeydown = (event) => {
12447
+ if (event.key === "Escape") {
12448
+ this.#handleEscapeKey();
12449
+ } else {
12450
+ handleRollingTabIndex(this.#tableToolsButtons, event);
12451
+ }
12162
12452
  }
12163
- }
12164
12453
 
12165
- customElements.define("lexxy-prompt", LexicalPromptElement);
12454
+ #handleEscapeKey() {
12455
+ const cell = this.tableController.currentCell;
12456
+ if (!cell) return
12166
12457
 
12167
- class CodeLanguagePicker extends HTMLElement {
12168
- connectedCallback() {
12169
- this.editorElement = this.closest("lexxy-editor");
12170
- this.editor = this.editorElement.editor;
12458
+ this.#editor.update(() => {
12459
+ cell.select();
12460
+ this.#editor.focus();
12461
+ });
12171
12462
 
12172
- this.#attachLanguagePicker();
12173
- this.#monitorForCodeBlockSelection();
12463
+ this.#update();
12174
12464
  }
12175
12465
 
12176
- #attachLanguagePicker() {
12177
- this.languagePickerElement = this.#createLanguagePicker();
12178
-
12179
- this.languagePickerElement.addEventListener("change", () => {
12180
- this.#updateCodeBlockLanguage(this.languagePickerElement.value);
12181
- });
12466
+ async #handleCommandButtonHover() {
12467
+ await nextFrame();
12182
12468
 
12183
- this.languagePickerElement.setAttribute("nonce", getNonce());
12184
- this.appendChild(this.languagePickerElement);
12185
- }
12469
+ this.#clearCellStyles();
12186
12470
 
12187
- #createLanguagePicker() {
12188
- const selectElement = createElement("select", { className: "lexxy-code-language-picker", "aria-label": "Pick a language…", name: "lexxy-code-language" });
12471
+ const activeElement = this.querySelector("button:hover, button:focus");
12472
+ if (!activeElement) return
12189
12473
 
12190
- for (const [ value, label ] of Object.entries(this.#languages)) {
12191
- const option = document.createElement("option");
12192
- option.value = value;
12193
- option.textContent = label;
12194
- selectElement.appendChild(option);
12195
- }
12474
+ const command = {
12475
+ action: activeElement.dataset.action,
12476
+ childType: activeElement.dataset.childType,
12477
+ direction: activeElement.dataset.direction
12478
+ };
12196
12479
 
12197
- return selectElement
12198
- }
12480
+ let cellsToHighlight = null;
12199
12481
 
12200
- get #languages() {
12201
- const languages = { ...pt$1 };
12482
+ switch (command.childType) {
12483
+ case "row":
12484
+ cellsToHighlight = this.tableController.currentRowCells;
12485
+ break
12486
+ case "column":
12487
+ cellsToHighlight = this.tableController.currentColumnCells;
12488
+ break
12489
+ case "table":
12490
+ cellsToHighlight = this.tableController.tableRows;
12491
+ break
12492
+ }
12202
12493
 
12203
- if (!languages.ruby) languages.ruby = "Ruby";
12204
- if (!languages.php) languages.php = "PHP";
12205
- if (!languages.go) languages.go = "Go";
12206
- if (!languages.bash) languages.bash = "Bash";
12207
- if (!languages.json) languages.json = "JSON";
12208
- if (!languages.diff) languages.diff = "Diff";
12494
+ if (!cellsToHighlight) return
12209
12495
 
12210
- const sortedEntries = Object.entries(languages)
12211
- .sort(([ , a ], [ , b ]) => a.localeCompare(b));
12496
+ cellsToHighlight.forEach(cell => {
12497
+ const cellElement = this.#editor.getElementByKey(cell.getKey());
12498
+ if (!cellElement) return
12212
12499
 
12213
- // Place the "plain" entry first, then the rest of language sorted alphabetically
12214
- const plainIndex = sortedEntries.findIndex(([ key ]) => key === "plain");
12215
- const plainEntry = sortedEntries.splice(plainIndex, 1)[0];
12216
- return Object.fromEntries([ plainEntry, ...sortedEntries ])
12500
+ cellElement.classList.toggle(theme.tableCellHighlight, true);
12501
+ Object.assign(cellElement.dataset, command);
12502
+ });
12217
12503
  }
12218
12504
 
12219
- #updateCodeBlockLanguage(language) {
12220
- this.editor.update(() => {
12221
- const codeNode = this.#getCurrentCodeNode();
12505
+ #monitorForTableSelection() {
12506
+ this.unregisterUpdateListener = this.#editor.registerUpdateListener(() => {
12507
+ this.tableController.updateSelectedTable();
12222
12508
 
12223
- if (codeNode) {
12224
- codeNode.setLanguage(language);
12509
+ const tableNode = this.tableController.currentTableNode;
12510
+ if (tableNode) {
12511
+ this.#show();
12512
+ } else {
12513
+ this.#hide();
12225
12514
  }
12226
12515
  });
12227
12516
  }
12228
12517
 
12229
- #monitorForCodeBlockSelection() {
12230
- this.editor.registerUpdateListener(() => {
12231
- this.editor.getEditorState().read(() => {
12232
- const codeNode = this.#getCurrentCodeNode();
12518
+ #executeTableCommand(command) {
12519
+ this.tableController.executeTableCommand(command);
12520
+ this.#update();
12521
+ }
12233
12522
 
12234
- if (codeNode) {
12235
- this.#codeNodeWasSelected(codeNode);
12236
- } else {
12237
- this.#hideLanguagePicker();
12238
- }
12239
- });
12240
- });
12523
+ #show() {
12524
+ this.style.display = "flex";
12525
+ this.#update();
12241
12526
  }
12242
12527
 
12243
- #getCurrentCodeNode() {
12244
- const selection = Lr();
12528
+ #hide() {
12529
+ this.style.display = "none";
12530
+ this.#clearCellStyles();
12531
+ }
12245
12532
 
12246
- if (!yr(selection)) {
12247
- return null
12248
- }
12533
+ #update() {
12534
+ this.#updateButtonsPosition();
12535
+ this.#updateRowColumnCount();
12536
+ this.#closeMoreMenu();
12537
+ this.#handleCommandButtonHover();
12538
+ }
12249
12539
 
12250
- const anchorNode = selection.anchor.getNode();
12251
- const parentNode = anchorNode.getParent();
12540
+ #closeMoreMenu() {
12541
+ this.querySelector("details[open]")?.removeAttribute("open");
12542
+ }
12252
12543
 
12253
- if (X$1(anchorNode)) {
12254
- return anchorNode
12255
- } else if (X$1(parentNode)) {
12256
- return parentNode
12257
- }
12544
+ #updateButtonsPosition() {
12545
+ const tableNode = this.tableController.currentTableNode;
12546
+ if (!tableNode) return
12258
12547
 
12259
- return null
12260
- }
12548
+ const tableElement = this.#editor.getElementByKey(tableNode.getKey());
12549
+ if (!tableElement) return
12261
12550
 
12262
- #codeNodeWasSelected(codeNode) {
12263
- const language = codeNode.getLanguage();
12551
+ const tableRect = tableElement.getBoundingClientRect();
12552
+ const editorRect = this.#editorElement.getBoundingClientRect();
12264
12553
 
12265
- this.#updateLanguagePickerWith(language);
12266
- this.#showLanguagePicker();
12267
- this.#positionLanguagePicker(codeNode);
12554
+ const relativeTop = tableRect.top - editorRect.top;
12555
+ const relativeCenter = (tableRect.left + tableRect.right) / 2 - editorRect.left;
12556
+ this.style.top = `${relativeTop}px`;
12557
+ this.style.left = `${relativeCenter}px`;
12268
12558
  }
12269
12559
 
12270
- #updateLanguagePickerWith(language) {
12271
- if (this.languagePickerElement && language) {
12272
- const normalizedLanguage = dt$1(language);
12273
- this.languagePickerElement.value = normalizedLanguage;
12274
- }
12560
+ #updateRowColumnCount() {
12561
+ const tableNode = this.tableController.currentTableNode;
12562
+ if (!tableNode) return
12563
+
12564
+ const tableElement = dn(this.#editor, tableNode);
12565
+ if (!tableElement) return
12566
+
12567
+ const rowCount = tableElement.rows;
12568
+ const columnCount = tableElement.columns;
12569
+
12570
+ this.rowCount.textContent = `${rowCount} row${rowCount === 1 ? "" : "s"}`;
12571
+ this.columnCount.textContent = `${columnCount} column${columnCount === 1 ? "" : "s"}`;
12275
12572
  }
12276
12573
 
12277
- #positionLanguagePicker(codeNode) {
12278
- const codeElement = this.editor.getElementByKey(codeNode.getKey());
12279
- if (!codeElement) return
12574
+ #setTableCellFocus() {
12575
+ const cell = this.tableController.currentCell;
12576
+ if (!cell) return
12280
12577
 
12281
- const codeRect = codeElement.getBoundingClientRect();
12282
- const editorRect = this.editorElement.getBoundingClientRect();
12283
- const relativeTop = codeRect.top - editorRect.top;
12578
+ const cellElement = this.#editor.getElementByKey(cell.getKey());
12579
+ if (!cellElement) return
12284
12580
 
12285
- this.style.top = `${relativeTop}px`;
12581
+ cellElement.classList.add(theme.tableCellFocus);
12286
12582
  }
12287
12583
 
12288
- #showLanguagePicker() {
12289
- this.hidden = false;
12584
+ #clearCellStyles() {
12585
+ this.#editorElement.querySelectorAll(`.${theme.tableCellFocus}`)?.forEach(cell => {
12586
+ cell.classList.remove(theme.tableCellFocus);
12587
+ });
12588
+
12589
+ this.#editorElement.querySelectorAll(`.${theme.tableCellHighlight}`)?.forEach(cell => {
12590
+ cell.classList.remove(theme.tableCellHighlight);
12591
+ cell.removeAttribute("data-action");
12592
+ cell.removeAttribute("data-child-type");
12593
+ cell.removeAttribute("data-direction");
12594
+ });
12595
+
12596
+ this.#setTableCellFocus();
12290
12597
  }
12291
12598
 
12292
- #hideLanguagePicker() {
12293
- this.hidden = true;
12599
+ #icon(command) {
12600
+ const { action, childType } = command;
12601
+ const direction = (action == "insert" ? command.direction : null);
12602
+ const iconId = [ action, childType, direction ].filter(Boolean).join("-");
12603
+ return TableIcons[iconId]
12294
12604
  }
12295
12605
  }
12296
12606
 
12297
- customElements.define("lexxy-code-language-picker", CodeLanguagePicker);
12607
+ function defineElements() {
12608
+ const elements = {
12609
+ "lexxy-toolbar": LexicalToolbarElement,
12610
+ "lexxy-editor": LexicalEditorElement,
12611
+ "lexxy-link-dropdown": LinkDropdown,
12612
+ "lexxy-highlight-dropdown": HighlightDropdown,
12613
+ "lexxy-prompt": LexicalPromptElement,
12614
+ "lexxy-code-language-picker": CodeLanguagePicker,
12615
+ "lexxy-table-tools": TableTools,
12616
+ };
12617
+
12618
+ Object.entries(elements).forEach(([ name, element ]) => {
12619
+ customElements.define(name, element);
12620
+ });
12621
+ }
12298
12622
 
12299
12623
  function highlightCode() {
12300
12624
  const elements = document.querySelectorAll("pre[data-language]");
@@ -12350,5 +12674,8 @@ class LexxyExtension {
12350
12674
 
12351
12675
  const configure = Lexxy.configure;
12352
12676
 
12677
+ // Pushing elements definition to after the current call stack to allow global configuration to take place first
12678
+ setTimeout(defineElements, 0);
12679
+
12353
12680
  export { ActionTextAttachmentNode, ActionTextAttachmentUploadNode, CustomActionTextAttachmentNode, LexxyExtension as Extension, HorizontalDividerNode, configure, highlightCode as highlightAll, highlightCode };
12354
12681
  //# sourceMappingURL=lexxy.js.map