lexxy 0.7.3.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.
@@ -6351,6 +6351,8 @@ const R$1=new Set(["http:","https:","mailto:","sms:","tel:"]);let y$1 = class y
6351
6351
 
6352
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))}
6353
6353
 
6354
+ const SILENT_UPDATE_TAGS = [ Dn, zn, Kn ];
6355
+
6354
6356
  function getNearestListItemNode(node) {
6355
6357
  let current = node;
6356
6358
  while (current !== null) {
@@ -6709,7 +6711,7 @@ class LexicalToolbarElement extends HTMLElement {
6709
6711
 
6710
6712
  this.editor.update(() => {
6711
6713
  this.editor.dispatchCommand(command, payload);
6712
- }, { tag: isKeyboard ? zn : undefined } );
6714
+ }, { tag: isKeyboard ? zn : undefined });
6713
6715
  }
6714
6716
 
6715
6717
  #bindHotkeys() {
@@ -6776,13 +6778,12 @@ class LexicalToolbarElement extends HTMLElement {
6776
6778
  }
6777
6779
 
6778
6780
  #monitorSelectionChanges() {
6779
- this.editor.registerCommand(
6780
- ie$1,
6781
- () => {
6782
- this.#closeDropdowns();
6781
+ this.editor.registerUpdateListener(() => {
6782
+ this.editor.getEditorState().read(() => {
6783
6783
  this.#updateButtonStates();
6784
- return false
6785
- }, Bi);
6784
+ this.#closeDropdowns();
6785
+ });
6786
+ });
6786
6787
  }
6787
6788
 
6788
6789
  #monitorHistoryChanges() {
@@ -7046,8 +7047,6 @@ class LexicalToolbarElement extends HTMLElement {
7046
7047
  }
7047
7048
  }
7048
7049
 
7049
- customElements.define("lexxy-toolbar", LexicalToolbarElement);
7050
-
7051
7050
  /**
7052
7051
  * Copyright (c) Meta Platforms, Inc. and affiliates.
7053
7052
  *
@@ -7243,6 +7242,8 @@ class ActionTextAttachmentNode extends ki {
7243
7242
  this.fileSize = fileSize;
7244
7243
  this.width = width;
7245
7244
  this.height = height;
7245
+
7246
+ this.editor = bs();
7246
7247
  }
7247
7248
 
7248
7249
  createDOM() {
@@ -7263,8 +7264,13 @@ class ActionTextAttachmentNode extends ki {
7263
7264
  return figure
7264
7265
  }
7265
7266
 
7266
- updateDOM() {
7267
- 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
7268
7274
  }
7269
7275
 
7270
7276
  getTextContent() {
@@ -7372,8 +7378,8 @@ class ActionTextAttachmentNode extends ki {
7372
7378
  });
7373
7379
 
7374
7380
  input.addEventListener("focusin", () => input.placeholder = "Add caption...");
7375
- input.addEventListener("blur", this.#handleCaptionInputBlurred.bind(this));
7376
- input.addEventListener("keydown", this.#handleCaptionInputKeydown.bind(this));
7381
+ input.addEventListener("blur", (event) => this.#handleCaptionInputBlurred(event));
7382
+ input.addEventListener("keydown", (event) => this.#handleCaptionInputKeydown(event));
7377
7383
 
7378
7384
  caption.appendChild(input);
7379
7385
 
@@ -7381,21 +7387,24 @@ class ActionTextAttachmentNode extends ki {
7381
7387
  }
7382
7388
 
7383
7389
  #handleCaptionInputBlurred(event) {
7384
- const input = event.target;
7385
-
7386
- input.placeholder = this.fileName;
7387
- this.#updateCaptionValueFromInput(input);
7390
+ this.#updateCaptionValueFromInput(event.target);
7388
7391
  }
7389
7392
 
7390
7393
  #updateCaptionValueFromInput(input) {
7391
- 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
+ });
7392
7398
  }
7393
7399
 
7394
7400
  #handleCaptionInputKeydown(event) {
7395
7401
  if (event.key === "Enter") {
7396
7402
  this.#updateCaptionValueFromInput(event.target);
7397
- dispatchCustomEvent(event.target, "lexxy:internal:move-to-next-line");
7398
7403
  event.preventDefault();
7404
+
7405
+ this.editor.update(() => {
7406
+ this.selectNext();
7407
+ }, { tag: Dn });
7399
7408
  }
7400
7409
  event.stopPropagation();
7401
7410
  }
@@ -7436,56 +7445,83 @@ class ActionTextAttachmentUploadNode extends ActionTextAttachmentNode {
7436
7445
  }
7437
7446
 
7438
7447
  constructor(node, key) {
7439
- const { file, uploadUrl, blobUrlTemplate, editor, progress } = node;
7448
+ const { file, uploadUrl, blobUrlTemplate, progress, width, height, uploadError } = node;
7440
7449
  super({ ...node, contentType: file.type }, key);
7441
7450
  this.file = file;
7442
7451
  this.uploadUrl = uploadUrl;
7443
7452
  this.blobUrlTemplate = blobUrlTemplate;
7444
- this.src = null;
7445
- this.editor = editor;
7446
- this.progress = progress || 0;
7453
+ this.progress = progress ?? null;
7454
+ this.width = width;
7455
+ this.height = height;
7456
+ this.uploadError = uploadError;
7447
7457
  }
7448
7458
 
7449
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
+
7450
7467
  const figure = this.createAttachmentFigure();
7451
7468
 
7452
7469
  if (this.isPreviewableAttachment) {
7453
- 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));
7454
7474
  } else {
7455
7475
  figure.appendChild(this.#createDOMForFile());
7456
7476
  }
7457
7477
 
7458
7478
  figure.appendChild(this.#createCaption());
7479
+ figure.appendChild(this.#createProgressBar());
7459
7480
 
7460
- const progressBar = createElement("progress", { value: this.progress, max: 100 });
7461
- figure.appendChild(progressBar);
7481
+ return figure
7482
+ }
7462
7483
 
7463
- // We wait for images to download so that we can pass the dimensions down to the attachment. We do this
7464
- // so that we can render images in edit mode with the dimensions set, which prevent vertical layout shifts.
7465
- this.#loadFigure(figure).then(() => this.#startUpload(progressBar, figure));
7484
+ updateDOM(prevNode, dom) {
7485
+ if (this.uploadError !== prevNode.uploadError) return true
7466
7486
 
7467
- return figure
7487
+ if (prevNode.progress !== this.progress) {
7488
+ const progress = dom.querySelector("progress");
7489
+ progress.value = this.progress ?? 0;
7490
+ }
7491
+
7492
+ return false
7468
7493
  }
7469
7494
 
7470
7495
  exportDOM() {
7471
7496
  const img = document.createElement("img");
7472
- if (this.src) {
7473
- img.src = this.src;
7474
- }
7475
7497
  return { element: img }
7476
7498
  }
7477
7499
 
7478
7500
  exportJSON() {
7479
7501
  return {
7502
+ ...super.exportJSON(),
7480
7503
  type: "action_text_attachment_upload",
7481
7504
  version: 1,
7482
- progress: this.progress,
7483
7505
  uploadUrl: this.uploadUrl,
7484
7506
  blobUrlTemplate: this.blobUrlTemplate,
7485
- ...super.exportJSON()
7507
+ progress: this.progress,
7508
+ width: this.width,
7509
+ height: this.height,
7510
+ uploadError: this.uploadError
7486
7511
  }
7487
7512
  }
7488
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
+
7489
7525
  #createDOMForImage() {
7490
7526
  return createElement("img")
7491
7527
  }
@@ -7511,94 +7547,126 @@ class ActionTextAttachmentUploadNode extends ActionTextAttachmentNode {
7511
7547
  return figcaption
7512
7548
  }
7513
7549
 
7514
- #loadFigure(figure) {
7515
- const image = figure.querySelector("img");
7516
- if (!image) {
7517
- return Promise.resolve()
7518
- } else {
7519
- return loadFileIntoImage(this.file, image)
7520
- }
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)
7521
7566
  }
7522
7567
 
7523
- async #startUpload(progressBar, figure) {
7568
+ async #startUploadIfNeeded() {
7569
+ if (this.#uploadStarted) return
7570
+
7571
+ this.#setUploadStarted();
7572
+
7524
7573
  const { DirectUpload } = await import('@rails/activestorage');
7525
- const shouldAuthenticateUploads = Lexxy.global.get("authenticatedUploads");
7526
7574
 
7527
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
+ }
7528
7585
 
7529
- upload.delegate = {
7586
+ #createUploadDelegate() {
7587
+ const shouldAuthenticateUploads = Lexxy.global.get("authenticatedUploads");
7588
+
7589
+ return {
7530
7590
  directUploadWillCreateBlobWithXHR: (request) => {
7531
7591
  if (shouldAuthenticateUploads) request.withCredentials = true;
7532
7592
  },
7533
7593
  directUploadWillStoreFileWithXHR: (request) => {
7534
7594
  if (shouldAuthenticateUploads) request.withCredentials = true;
7535
7595
 
7536
- request.upload.addEventListener("progress", (event) => {
7537
- this.editor.update(() => {
7538
- progressBar.value = Math.round(event.loaded / event.total * 100);
7539
- });
7540
- });
7596
+ const uploadProgressHandler = (event) => this.#handleUploadProgress(event);
7597
+ request.upload.addEventListener("progress", uploadProgressHandler);
7541
7598
  }
7542
- };
7599
+ }
7600
+ }
7543
7601
 
7544
- upload.create((error, blob) => {
7545
- if (error) {
7546
- this.#handleUploadError(figure);
7547
- } else {
7548
- this.#loadFigurePreviewFromBlob(blob, figure).then(() => {
7549
- this.#showUploadedAttachment(figure, blob);
7550
- });
7551
- }
7552
- });
7602
+ #setUploadStarted() {
7603
+ this.#setProgress(1);
7553
7604
  }
7554
7605
 
7555
- #handleUploadError(figure) {
7556
- figure.innerHTML = "";
7557
- figure.classList.add("attachment--error");
7558
- figure.appendChild(createElement("div", { innerText: `Error uploading ${this.file?.name ?? "image"}` }));
7606
+ #handleUploadProgress(event) {
7607
+ this.#setProgress(Math.round(event.loaded / event.total * 100));
7559
7608
  }
7560
7609
 
7561
- async #showUploadedAttachment(figure, blob) {
7610
+ #setProgress(progress) {
7562
7611
  this.editor.update(() => {
7563
- const image = figure.querySelector("img");
7564
-
7565
- const src = this.blobUrlTemplate
7566
- .replace(":signed_id", blob.signed_id)
7567
- .replace(":filename", encodeURIComponent(blob.filename));
7568
- const latest = xo(this.getKey());
7569
- if (latest) {
7570
- latest.replace(new ActionTextAttachmentNode({
7571
- tagName: this.tagName,
7572
- sgid: blob.attachable_sgid,
7573
- src: blob.previewable ? blob.url : src,
7574
- altText: blob.filename,
7575
- contentType: blob.content_type,
7576
- fileName: blob.filename,
7577
- fileSize: blob.byte_size,
7578
- width: image?.naturalWidth,
7579
- previewable: blob.previewable,
7580
- height: image?.naturalHeight
7581
- }));
7582
- }
7583
- }, { tag: Dn });
7612
+ this.getWritable().progress = progress;
7613
+ }, { tag: SILENT_UPDATE_TAGS });
7584
7614
  }
7585
7615
 
7586
- async #loadFigurePreviewFromBlob(blob, figure) {
7587
- if (blob.previewable) {
7588
- return new Promise((resolve) => {
7589
- this.editor.update(() => {
7590
- const image = this.#createDOMForImage();
7591
- image.addEventListener("load", () => {
7592
- resolve();
7593
- });
7594
- image.src = blob.url;
7595
- figure.insertBefore(image, figure.firstChild);
7596
- });
7597
- })
7598
- } else {
7599
- 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,
7600
7658
  }
7601
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
+ }
7602
7670
  }
7603
7671
 
7604
7672
  class HorizontalDividerNode extends ki {
@@ -9388,7 +9456,7 @@ class Contents {
9388
9456
  const blobUrlTemplate = this.editorElement.blobUrlTemplate;
9389
9457
 
9390
9458
  this.editor.update(() => {
9391
- const uploadedImageNode = new ActionTextAttachmentUploadNode({ file: file, uploadUrl: uploadUrl, blobUrlTemplate: blobUrlTemplate, editor: this.editor });
9459
+ const uploadedImageNode = new ActionTextAttachmentUploadNode({ file: file, uploadUrl: uploadUrl, blobUrlTemplate: blobUrlTemplate });
9392
9460
  this.insertAtCursor(uploadedImageNode);
9393
9461
  }, { tag: Dn });
9394
9462
  }
@@ -10077,7 +10145,7 @@ class Extensions {
10077
10145
 
10078
10146
  initializeToolbars() {
10079
10147
  if (this.#lexxyToolbar) {
10080
- this.enabledExtensions.forEach(ext => ext.initializeToobar(this.#lexxyToolbar));
10148
+ this.enabledExtensions.forEach(ext => ext.initializeToolbar(this.#lexxyToolbar));
10081
10149
  }
10082
10150
  }
10083
10151
 
@@ -10571,7 +10639,6 @@ class LexicalEditorElement extends HTMLElement {
10571
10639
  #initialize() {
10572
10640
  this.#synchronizeWithChanges();
10573
10641
  this.#registerComponents();
10574
- this.#listenForInvalidatedNodes();
10575
10642
  this.#handleEnter();
10576
10643
  this.#registerFocusEvents();
10577
10644
  this.#attachDebugHooks();
@@ -10584,11 +10651,11 @@ class LexicalEditorElement extends HTMLElement {
10584
10651
  this.editorContentElement ||= this.#createEditorContentElement();
10585
10652
 
10586
10653
  const editor = $t$2({
10587
- name: "lexxy/core",
10588
- namespace: "Lexxy",
10589
- theme: theme,
10590
- nodes: this.#lexicalNodes
10591
- },
10654
+ name: "lexxy/core",
10655
+ namespace: "Lexxy",
10656
+ theme: theme,
10657
+ nodes: this.#lexicalNodes
10658
+ },
10592
10659
  ...this.#lexicalExtensions
10593
10660
  );
10594
10661
 
@@ -10598,7 +10665,7 @@ class LexicalEditorElement extends HTMLElement {
10598
10665
  }
10599
10666
 
10600
10667
  get #lexicalExtensions() {
10601
- const extensions = [ ];
10668
+ const extensions = [];
10602
10669
  const richTextExtensions = [
10603
10670
  this.highlighter.lexicalExtension,
10604
10671
  TrixContentExtension,
@@ -10751,21 +10818,6 @@ class LexicalEditorElement extends HTMLElement {
10751
10818
  this.append(this.codeLanguagePicker);
10752
10819
  }
10753
10820
 
10754
- #listenForInvalidatedNodes() {
10755
- this.editor.getRootElement().addEventListener("lexxy:internal:invalidate-node", (event) => {
10756
- const { key, values } = event.detail;
10757
-
10758
- this.editor.update(() => {
10759
- const node = xo(key);
10760
-
10761
- if (node instanceof ActionTextAttachmentNode) {
10762
- const updatedNode = node.getWritable();
10763
- Object.assign(updatedNode, values);
10764
- }
10765
- });
10766
- });
10767
- }
10768
-
10769
10821
  #handleEnter() {
10770
10822
  // We can't prevent these externally using regular keydown because Lexical handles it first.
10771
10823
  this.editor.registerCommand(
@@ -10828,14 +10880,7 @@ class LexicalEditorElement extends HTMLElement {
10828
10880
 
10829
10881
 
10830
10882
  #attachDebugHooks() {
10831
- if (!LexicalEditorElement.debug) return
10832
-
10833
- this.#addUnregisterHandler(this.editor.registerUpdateListener(({ editorState }) => {
10834
- editorState.read(() => {
10835
- console.debug("HTML: ", this.value, "String:", this.toString());
10836
- console.debug("empty", this.isEmpty, "blank", this.isBlank);
10837
- });
10838
- }));
10883
+ return
10839
10884
  }
10840
10885
 
10841
10886
  #attachToolbar() {
@@ -10915,8 +10960,6 @@ class LexicalEditorElement extends HTMLElement {
10915
10960
  }
10916
10961
  }
10917
10962
 
10918
- customElements.define("lexxy-editor", LexicalEditorElement);
10919
-
10920
10963
  class ToolbarDropdown extends HTMLElement {
10921
10964
  connectedCallback() {
10922
10965
  this.container = this.closest("details");
@@ -11042,8 +11085,6 @@ class LinkDropdown extends ToolbarDropdown {
11042
11085
  }
11043
11086
  }
11044
11087
 
11045
- customElements.define("lexxy-link-dropdown", LinkDropdown);
11046
-
11047
11088
  const APPLY_HIGHLIGHT_SELECTOR = "button.lexxy-highlight-button";
11048
11089
  const REMOVE_HIGHLIGHT_SELECTOR = "[data-command='removeHighlight']";
11049
11090
 
@@ -11151,1425 +11192,1433 @@ class HighlightDropdown extends ToolbarDropdown {
11151
11192
  }
11152
11193
  }
11153
11194
 
11154
- customElements.define("lexxy-highlight-dropdown", HighlightDropdown);
11155
-
11156
- class TableController {
11157
- constructor(editorElement) {
11158
- this.editor = editorElement.editor;
11159
- this.contents = editorElement.contents;
11160
- this.selection = editorElement.selection;
11161
-
11162
- this.currentTableNodeKey = null;
11163
- this.currentCellKey = null;
11195
+ class BaseSource {
11196
+ // Template method to override
11197
+ async buildListItems(filter = "") {
11198
+ return Promise.resolve([])
11199
+ }
11164
11200
 
11165
- this.#registerKeyHandlers();
11201
+ // Template method to override
11202
+ promptItemFor(listItem) {
11203
+ return null
11166
11204
  }
11167
11205
 
11168
- destroy() {
11169
- this.currentTableNodeKey = null;
11170
- this.currentCellKey = null;
11206
+ // Protected
11171
11207
 
11172
- this.#unregisterKeyHandlers();
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
11173
11215
  }
11174
11216
 
11175
- get currentCell() {
11176
- if (!this.currentCellKey) return null
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
+ }
11226
+ }
11227
+ }
11177
11228
 
11178
- return this.editor.getEditorState().read(() => {
11179
- const cell = xo(this.currentCellKey);
11180
- return (cell instanceof xe) ? cell : null
11181
- })
11229
+ class LocalFilterSource extends BaseSource {
11230
+ async buildListItems(filter = "") {
11231
+ const promptItems = await this.fetchPromptItems();
11232
+ return this.#buildListItemsFromPromptItems(promptItems, filter)
11182
11233
  }
11183
11234
 
11184
- get currentTableNode() {
11185
- if (!this.currentTableNodeKey) return null
11235
+ // Template method to override
11236
+ async fetchPromptItems(filter) {
11237
+ return Promise.resolve([])
11238
+ }
11186
11239
 
11187
- return this.editor.getEditorState().read(() => {
11188
- const tableNode = xo(this.currentTableNodeKey);
11189
- return (tableNode instanceof hn) ? tableNode : null
11190
- })
11240
+ promptItemFor(listItem) {
11241
+ return this.promptItemByListItem.get(listItem)
11191
11242
  }
11192
11243
 
11193
- get currentRowCells() {
11194
- const currentRowIndex = this.currentRowIndex;
11244
+ #buildListItemsFromPromptItems(promptItems, filter) {
11245
+ const listItems = [];
11246
+ this.promptItemByListItem = new WeakMap();
11247
+ promptItems.forEach((promptItem) => {
11248
+ const searchableText = promptItem.getAttribute("search");
11195
11249
 
11196
- const rows = this.tableRows;
11197
- if (!rows) return null
11250
+ if (!filter || filterMatches(searchableText, filter)) {
11251
+ const listItem = this.buildListItemElementFor(promptItem);
11252
+ this.promptItemByListItem.set(listItem, promptItem);
11253
+ listItems.push(listItem);
11254
+ }
11255
+ });
11198
11256
 
11199
- return this.editor.getEditorState().read(() => {
11200
- return rows[currentRowIndex]?.getChildren() ?? null
11201
- }) ?? null
11257
+ return listItems
11202
11258
  }
11259
+ }
11203
11260
 
11204
- get currentRowIndex() {
11205
- const currentCell = this.currentCell;
11206
- if (!currentCell) return 0
11261
+ class InlinePromptSource extends LocalFilterSource {
11262
+ constructor(inlinePromptItems) {
11263
+ super();
11264
+ this.inlinePromptItemElements = Array.from(inlinePromptItems);
11265
+ }
11207
11266
 
11208
- return this.editor.getEditorState().read(() => {
11209
- return Ie$1(currentCell)
11210
- }) ?? 0
11267
+ async fetchPromptItems() {
11268
+ return Promise.resolve(this.inlinePromptItemElements)
11211
11269
  }
11270
+ }
11212
11271
 
11213
- get currentColumnCells() {
11214
- const columnIndex = this.currentColumnIndex;
11272
+ class DeferredPromptSource extends LocalFilterSource {
11273
+ constructor(url) {
11274
+ super();
11275
+ this.url = url;
11215
11276
 
11216
- const rows = this.tableRows;
11217
- if (!rows) return null
11277
+ this.fetchPromptItems();
11278
+ }
11218
11279
 
11219
- return this.editor.getEditorState().read(() => {
11220
- return rows.map(row => row.getChildAtIndex(columnIndex))
11221
- }) ?? null
11280
+ async fetchPromptItems() {
11281
+ this.promptItems ??= await this.loadPromptItemsFromUrl(this.url);
11282
+
11283
+ return Promise.resolve(this.promptItems)
11222
11284
  }
11285
+ }
11223
11286
 
11224
- get currentColumnIndex() {
11225
- const currentCell = this.currentCell;
11226
- if (!currentCell) return 0
11287
+ const DEBOUNCE_INTERVAL = 200;
11227
11288
 
11228
- return this.editor.getEditorState().read(() => {
11229
- return Ue$1(currentCell)
11230
- }) ?? 0
11289
+ class RemoteFilterSource extends BaseSource {
11290
+ constructor(url) {
11291
+ super();
11292
+
11293
+ this.baseURL = url;
11294
+ this.loadAndFilterListItems = debounceAsync(this.fetchFilteredListItems.bind(this), DEBOUNCE_INTERVAL);
11231
11295
  }
11232
11296
 
11233
- get tableRows() {
11234
- return this.editor.getEditorState().read(() => {
11235
- return this.currentTableNode?.getChildren()
11236
- }) ?? null
11297
+ async buildListItems(filter = "") {
11298
+ return await this.loadAndFilterListItems(filter)
11237
11299
  }
11238
11300
 
11239
- updateSelectedTable() {
11240
- let cellNode = null;
11241
- let tableNode = null;
11242
-
11243
- this.editor.getEditorState().read(() => {
11244
- const selection = Lr();
11245
- if (!selection || !this.selection.isTableCellSelected) return
11246
-
11247
- const node = selection.getNodes()[0];
11301
+ promptItemFor(listItem) {
11302
+ return this.promptItemByListItem.get(listItem)
11303
+ }
11248
11304
 
11249
- cellNode = Gt(node);
11250
- tableNode = Qt(node);
11251
- });
11305
+ async fetchFilteredListItems(filter) {
11306
+ const promptItems = await this.loadPromptItemsFromUrl(this.#urlFor(filter));
11307
+ return this.#buildListItemsFromPromptItems(promptItems)
11308
+ }
11252
11309
 
11253
- this.currentCellKey = cellNode?.getKey() ?? null;
11254
- this.currentTableNodeKey = tableNode?.getKey() ?? null;
11310
+ #urlFor(filter) {
11311
+ const url = new URL(this.baseURL, window.location.origin);
11312
+ url.searchParams.append("filter", filter);
11313
+ return url.toString()
11255
11314
  }
11256
11315
 
11257
- executeTableCommand(command, customIndex = null) {
11258
- if (command.action === "delete" && command.childType === "table") {
11259
- this.#deleteTable();
11260
- return
11261
- }
11316
+ #buildListItemsFromPromptItems(promptItems) {
11317
+ const listItems = [];
11318
+ this.promptItemByListItem = new WeakMap();
11262
11319
 
11263
- if (command.action === "toggle") {
11264
- this.#executeToggleStyle(command);
11265
- return
11320
+ for (const promptItem of promptItems) {
11321
+ const listItem = this.buildListItemElementFor(promptItem);
11322
+ this.promptItemByListItem.set(listItem, promptItem);
11323
+ listItems.push(listItem);
11266
11324
  }
11267
11325
 
11268
- this.#executeCommand(command, customIndex);
11326
+ return listItems
11269
11327
  }
11328
+ }
11270
11329
 
11271
- #executeCommand(command, customIndex = null) {
11272
- this.#selectCellAtSelection();
11273
- this.editor.dispatchCommand(this.#commandName(command));
11274
- this.#selectNextBestCell(command, customIndex);
11275
- }
11330
+ const NOTHING_FOUND_DEFAULT_MESSAGE = "Nothing found";
11276
11331
 
11277
- #executeToggleStyle(command) {
11278
- const childType = command.childType;
11332
+ class LexicalPromptElement extends HTMLElement {
11333
+ constructor() {
11334
+ super();
11335
+ this.keyListeners = [];
11336
+ }
11279
11337
 
11280
- let cells = null;
11281
- let headerState = null;
11338
+ static observedAttributes = [ "connected" ]
11282
11339
 
11283
- if (childType === "row") {
11284
- cells = this.currentRowCells;
11285
- headerState = ve$1.ROW;
11286
- } else if (childType === "column") {
11287
- cells = this.currentColumnCells;
11288
- headerState = ve$1.COLUMN;
11289
- }
11340
+ connectedCallback() {
11341
+ this.source = this.#createSource();
11290
11342
 
11291
- if (!cells || cells.length === 0) return
11343
+ this.#addTriggerListener();
11344
+ this.toggleAttribute("connected", true);
11345
+ }
11292
11346
 
11293
- this.editor.update(() => {
11294
- const firstCell = Be$1(cells[0]);
11295
- if (!firstCell) return
11347
+ disconnectedCallback() {
11348
+ this.source = null;
11349
+ this.popoverElement = null;
11350
+ }
11296
11351
 
11297
- const currentStyle = firstCell.getHeaderStyles();
11298
- const newStyle = currentStyle ^ headerState;
11299
11352
 
11300
- cells.forEach(cell => {
11301
- this.#setHeaderStyle(cell, newStyle, headerState);
11302
- });
11303
- });
11353
+ attributeChangedCallback(name, oldValue, newValue) {
11354
+ if (name === "connected" && this.isConnected && oldValue != null && oldValue !== newValue) {
11355
+ requestAnimationFrame(() => this.#reconnect());
11356
+ }
11304
11357
  }
11305
11358
 
11306
- #deleteTable() {
11307
- this.#selectCellAtSelection();
11308
- this.editor.dispatchCommand("deleteTable");
11359
+ get name() {
11360
+ return this.getAttribute("name")
11309
11361
  }
11310
11362
 
11311
- #selectCellAtSelection() {
11312
- this.editor.update(() => {
11313
- const selection = Lr();
11314
- if (!selection) return
11363
+ get trigger() {
11364
+ return this.getAttribute("trigger")
11365
+ }
11315
11366
 
11316
- const node = selection.getNodes()[0];
11367
+ get supportsSpaceInSearches() {
11368
+ return this.hasAttribute("supports-space-in-searches")
11369
+ }
11317
11370
 
11318
- Gt(node)?.selectEnd();
11319
- });
11371
+ get open() {
11372
+ return this.popoverElement?.classList?.contains("lexxy-prompt-menu--visible")
11320
11373
  }
11321
11374
 
11322
- #commandName(command) {
11323
- const { action, childType, direction } = command;
11375
+ get closed() {
11376
+ return !this.open
11377
+ }
11324
11378
 
11325
- const childTypeSuffix = upcaseFirst(childType);
11326
- const directionSuffix = action == "insert" ? upcaseFirst(direction) : "";
11327
- return `${action}Table${childTypeSuffix}${directionSuffix}`
11379
+ get #doesSpaceSelect() {
11380
+ return !this.supportsSpaceInSearches
11328
11381
  }
11329
11382
 
11330
- #setHeaderStyle(cell, newStyle, headerState) {
11331
- const tableCellNode = Be$1(cell);
11332
- tableCellNode?.setHeaderStyles(newStyle, headerState);
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
+ }
11333
11394
  }
11334
11395
 
11335
- async #selectCellAtIndex(rowIndex, columnIndex) {
11336
- // We wait for next frame, otherwise table operations might not have completed yet.
11337
- await nextFrame();
11396
+ #addTriggerListener() {
11397
+ const unregister = this.#editor.registerUpdateListener(({ editorState }) => {
11398
+ editorState.read(() => {
11399
+ const { node, offset } = this.#selection.selectedNodeWithOffset();
11400
+ if (!node) return
11338
11401
 
11339
- if (!this.currentTableNode) return
11402
+ if (lr(node)) {
11403
+ const fullText = node.getTextContent();
11404
+ const triggerLength = this.trigger.length;
11340
11405
 
11341
- const rows = this.tableRows;
11342
- if (!rows) return
11406
+ // Check if we have enough characters for the trigger
11407
+ if (offset >= triggerLength) {
11408
+ const textBeforeCursor = fullText.slice(offset - triggerLength, offset);
11343
11409
 
11344
- const row = rows[rowIndex];
11345
- if (!row) return
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;
11346
11413
 
11347
- this.editor.update(() => {
11348
- const cell = Be$1(row.getChildAtIndex(columnIndex));
11349
- cell?.selectEnd();
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
+ });
11350
11425
  });
11351
11426
  }
11352
11427
 
11353
- #selectNextBestCell(command, customIndex = null) {
11354
- const { childType, direction } = command;
11428
+ #addCursorPositionListener() {
11429
+ this.cursorPositionListener = this.#editor.registerUpdateListener(() => {
11430
+ if (this.closed) return
11355
11431
 
11356
- let rowIndex = this.currentRowIndex;
11357
- let columnIndex = customIndex !== null ? customIndex : this.currentColumnIndex;
11432
+ this.#editor.read(() => {
11433
+ const { node, offset } = this.#selection.selectedNodeWithOffset();
11434
+ if (!node) return
11358
11435
 
11359
- const deleteOffset = command.action === "delete" ? -1 : 0;
11360
- const offset = direction === "after" ? 1 : deleteOffset;
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;
11361
11441
 
11362
- if (childType === "row") {
11363
- rowIndex += offset;
11364
- } else if (childType === "column") {
11365
- columnIndex += offset;
11366
- }
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
+ });
11451
+ });
11452
+ }
11367
11453
 
11368
- this.#selectCellAtIndex(rowIndex, columnIndex);
11454
+ #removeCursorPositionListener() {
11455
+ if (this.cursorPositionListener) {
11456
+ this.cursorPositionListener();
11457
+ this.cursorPositionListener = null;
11458
+ }
11369
11459
  }
11370
11460
 
11371
- #selectNextRow() {
11372
- const rows = this.tableRows;
11373
- if (!rows) return
11461
+ get #editor() {
11462
+ return this.#editorElement.editor
11463
+ }
11374
11464
 
11375
- const nextRow = rows.at(this.currentRowIndex + 1);
11376
- if (!nextRow) return
11465
+ get #editorElement() {
11466
+ return this.closest("lexxy-editor")
11467
+ }
11377
11468
 
11378
- this.editor.update(() => {
11379
- nextRow.getChildAtIndex(this.currentColumnIndex)?.selectEnd();
11380
- });
11469
+ get #selection() {
11470
+ return this.#editorElement.selection
11381
11471
  }
11382
11472
 
11383
- #selectPreviousCell() {
11384
- const cell = this.currentCell;
11385
- if (!cell) return
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();
11386
11479
 
11387
- this.editor.update(() => {
11388
- cell.selectPrevious();
11389
- });
11390
- }
11480
+ this.#editorElement.addEventListener("keydown", this.#handleKeydownOnPopover);
11481
+ this.#editorElement.addEventListener("lexxy:change", this.#filterOptions);
11391
11482
 
11392
- #insertRowAndSelectFirstCell() {
11393
- this.executeTableCommand({ action: "insert", childType: "row", direction: "after" }, 0);
11483
+ this.#registerKeyListeners();
11484
+ this.#addCursorPositionListener();
11394
11485
  }
11395
11486
 
11396
- #deleteRowAndSelectLastCell() {
11397
- this.executeTableCommand({ action: "delete", childType: "row" }, -1);
11398
- }
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));
11399
11491
 
11400
- #deleteRowAndSelectNextNode() {
11401
- const tableNode = this.currentTableNode;
11402
- this.executeTableCommand({ action: "delete", childType: "row" });
11492
+ if (this.#doesSpaceSelect) {
11493
+ this.keyListeners.push(this.#editor.registerCommand(be$1, this.#handleSelectedOption.bind(this), Bi));
11494
+ }
11403
11495
 
11404
- this.editor.update(() => {
11405
- const next = tableNode?.getNextSibling();
11406
- if (Ii(next)) {
11407
- next.selectStart();
11408
- } else {
11409
- const newParagraph = Li();
11410
- this.currentTableNode.insertAfter(newParagraph);
11411
- newParagraph.selectStart();
11412
- }
11413
- });
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));
11414
11499
  }
11415
11500
 
11416
- #isCurrentCellEmpty() {
11417
- if (!this.currentTableNode) return false
11418
-
11419
- const cell = this.currentCell;
11420
- if (!cell) return false
11421
-
11422
- return cell.getTextContent().trim() === ""
11501
+ #handleArrowUp(event) {
11502
+ this.#moveSelectionUp();
11503
+ event.preventDefault();
11504
+ return true
11423
11505
  }
11424
11506
 
11425
- #isCurrentRowLast() {
11426
- if (!this.currentTableNode) return false
11427
-
11428
- const rows = this.tableRows;
11429
- if (!rows) return false
11430
-
11431
- return rows.length === this.currentRowIndex + 1
11507
+ #handleArrowDown(event) {
11508
+ this.#moveSelectionDown();
11509
+ event.preventDefault();
11510
+ return true
11432
11511
  }
11433
11512
 
11434
- #isCurrentRowEmpty() {
11435
- if (!this.currentTableNode) return false
11436
-
11437
- const cells = this.currentRowCells;
11438
- if (!cells) return false
11513
+ #selectFirstOption() {
11514
+ const firstOption = this.#listItemElements[0];
11439
11515
 
11440
- return cells.every(cell => cell.getTextContent().trim() === "")
11516
+ if (firstOption) {
11517
+ this.#selectOption(firstOption);
11518
+ }
11441
11519
  }
11442
11520
 
11443
- #isFirstCellInRow() {
11444
- if (!this.currentTableNode) return false
11445
-
11446
- const cells = this.currentRowCells;
11447
- if (!cells) return false
11448
-
11449
- return cells.indexOf(this.currentCell) === 0
11521
+ get #listItemElements() {
11522
+ return Array.from(this.popoverElement.querySelectorAll(".lexxy-prompt-menu__item"))
11450
11523
  }
11451
11524
 
11452
- #registerKeyHandlers() {
11453
- // We can't prevent these externally using regular keydown because Lexical handles it first.
11454
- this.unregisterBackspaceKeyHandler = this.editor.registerCommand(we$1, (event) => this.#handleBackspaceKey(event), Bi);
11455
- this.unregisterEnterKeyHandler = this.editor.registerCommand(Ne$2, (event) => this.#handleEnterKey(event), Bi);
11456
- }
11525
+ #selectOption(listItem) {
11526
+ this.#clearSelection();
11527
+ listItem.toggleAttribute("aria-selected", true);
11528
+ listItem.scrollIntoView({ block: "nearest", behavior: "smooth" });
11529
+ listItem.focus();
11457
11530
 
11458
- #unregisterKeyHandlers() {
11459
- this.unregisterBackspaceKeyHandler?.();
11460
- this.unregisterEnterKeyHandler?.();
11531
+ // Preserve selection to prevent cursor jump
11532
+ this.#selection.preservingSelection(() => {
11533
+ this.#editorElement.focus();
11534
+ });
11461
11535
 
11462
- this.unregisterBackspaceKeyHandler = null;
11463
- this.unregisterEnterKeyHandler = null;
11536
+ this.#editorContentElement.setAttribute("aria-controls", this.popoverElement.id);
11537
+ this.#editorContentElement.setAttribute("aria-activedescendant", listItem.id);
11538
+ this.#editorContentElement.setAttribute("aria-haspopup", "listbox");
11464
11539
  }
11465
11540
 
11466
- #handleBackspaceKey(event) {
11467
- if (!this.currentTableNode) return false
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");
11546
+ }
11468
11547
 
11469
- if (this.#isCurrentRowEmpty() && this.#isFirstCellInRow()) {
11470
- event.preventDefault();
11471
- this.#deleteRowAndSelectLastCell();
11472
- return true
11473
- }
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;
11474
11553
 
11475
- if (this.#isCurrentCellEmpty() && !this.#isFirstCellInRow()) {
11476
- event.preventDefault();
11477
- this.#selectPreviousCell();
11478
- return true
11554
+ if (!this.popoverElement.hasAttribute("data-anchored")) {
11555
+ this.popoverElement.style.left = `${x}px`;
11556
+ this.popoverElement.toggleAttribute("data-anchored", true);
11479
11557
  }
11480
11558
 
11481
- return false
11482
- }
11483
-
11484
- #handleEnterKey(event) {
11485
- if ((event.ctrlKey || event.metaKey) || event.shiftKey || !this.currentTableNode) return false
11486
-
11487
- if (this.selection.isInsideList || this.selection.isInsideCodeBlock) return false
11559
+ this.popoverElement.style.top = `${y + verticalOffset}px`;
11560
+ this.popoverElement.style.bottom = "auto";
11488
11561
 
11489
- event.preventDefault();
11562
+ const popoverRect = this.popoverElement.getBoundingClientRect();
11563
+ const isClippedAtBottom = popoverRect.bottom > window.innerHeight;
11490
11564
 
11491
- if (this.#isCurrentRowLast() && this.#isCurrentRowEmpty()) {
11492
- this.#deleteRowAndSelectNextNode();
11493
- } else if (this.#isCurrentRowLast()) {
11494
- this.#insertRowAndSelectFirstCell();
11495
- } else {
11496
- this.#selectNextRow();
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);
11497
11569
  }
11498
-
11499
- return true
11500
11570
  }
11501
- }
11502
11571
 
11503
- var TableIcons = {
11504
- "insert-row-before":
11505
- `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
11506
- <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"/>
11507
- <path d="M4.5 4.5H7.5V7.5H9V4.5H12L12 3L9 3V6.55671e-08L7.5 0V3L4.5 3V4.5Z"/>
11508
- </svg>`,
11509
-
11510
- "insert-row-after":
11511
- `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
11512
- <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"/>
11513
- <path d="M7.5 15V18H9V15H12V13.5H9V10.5H7.5V13.5H4.5V15H7.5Z"/>
11514
- </svg>`,
11515
-
11516
- "delete-row":
11517
- `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
11518
- <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"/>
11519
- <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"/>
11520
- </svg>`,
11521
-
11522
- "toggle-row":
11523
- `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
11524
- <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"/>
11525
- </svg>`,
11526
-
11527
- "insert-column-before":
11528
- `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
11529
- <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"/>
11530
- <path d="M3 7.5H0V9H3V12H4.5V9H7.5V7.5H4.5V4.5H3V7.5Z"/>
11531
- </svg>`,
11532
-
11533
- "insert-column-after":
11534
- `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
11535
- <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"/>
11536
- <path d="M15 7.5H18V9H15V12H13.5V9H10.5V7.5H13.5V4.5H15V7.5Z"/>
11537
- </svg>`,
11538
-
11539
- "delete-column":
11540
- `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
11541
- <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"/>
11542
- <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"/>
11543
- </svg>`,
11572
+ #resetPopoverPosition() {
11573
+ this.popoverElement.removeAttribute("data-clipped-at-bottom");
11574
+ this.popoverElement.removeAttribute("data-anchored");
11575
+ }
11544
11576
 
11545
- "toggle-column":
11546
- `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
11547
- <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" />
11548
- </svg>`,
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);
11549
11582
 
11550
- "delete-table":
11551
- `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
11552
- <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"/>
11553
- </svg>`
11554
- };
11583
+ this.#unregisterKeyListeners();
11584
+ this.#removeCursorPositionListener();
11555
11585
 
11556
- class TableTools extends HTMLElement {
11557
- connectedCallback() {
11558
- this.tableController = new TableController(this.#editorElement);
11586
+ await nextFrame();
11587
+ this.#addTriggerListener();
11588
+ }
11559
11589
 
11560
- this.#setUpButtons();
11561
- this.#monitorForTableSelection();
11562
- this.#registerKeyboardShortcuts();
11590
+ #unregisterKeyListeners() {
11591
+ this.keyListeners.forEach((unregister) => unregister());
11592
+ this.keyListeners = [];
11563
11593
  }
11564
11594
 
11565
- disconnectedCallback() {
11566
- this.#unregisterKeyboardShortcuts();
11595
+ #filterOptions = async () => {
11596
+ if (this.initialPrompt) {
11597
+ this.initialPrompt = false;
11598
+ return
11599
+ }
11567
11600
 
11568
- this.unregisterUpdateListener?.();
11569
- this.unregisterUpdateListener = null;
11601
+ if (this.#editorContents.containsTextBackUntil(this.trigger)) {
11602
+ await this.#showFilteredOptions();
11603
+ await nextFrame();
11604
+ this.#positionPopover();
11605
+ } else {
11606
+ this.#hidePopover();
11607
+ }
11608
+ }
11570
11609
 
11571
- this.removeEventListener("keydown", this.#handleToolsKeydown);
11610
+ async #showFilteredOptions() {
11611
+ const filter = this.#editorContents.textBackUntil(this.trigger);
11612
+ const filteredListItems = await this.source.buildListItems(filter);
11613
+ this.popoverElement.innerHTML = "";
11572
11614
 
11573
- this.tableController?.destroy();
11574
- this.tableController = null;
11615
+ if (filteredListItems.length > 0) {
11616
+ this.#showResults(filteredListItems);
11617
+ } else {
11618
+ this.#showEmptyResults();
11619
+ }
11620
+ this.#selectFirstOption();
11575
11621
  }
11576
11622
 
11577
- get #editor() {
11578
- return this.#editorElement.editor
11623
+ #showResults(filteredListItems) {
11624
+ this.popoverElement.classList.remove("lexxy-prompt-menu--empty");
11625
+ this.popoverElement.append(...filteredListItems);
11579
11626
  }
11580
11627
 
11581
- get #editorElement() {
11582
- return this.closest("lexxy-editor")
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);
11583
11633
  }
11584
11634
 
11585
- get #tableToolsButtons() {
11586
- return Array.from(this.querySelectorAll("button, details > summary"))
11635
+ get #emptyResultsMessage() {
11636
+ return this.getAttribute("empty-results") || NOTHING_FOUND_DEFAULT_MESSAGE
11587
11637
  }
11588
11638
 
11589
- #setUpButtons() {
11590
- this.appendChild(this.#createRowButtonsContainer());
11591
- this.appendChild(this.#createColumnButtonsContainer());
11592
-
11593
- this.appendChild(this.#createDeleteTableButton());
11594
- this.addEventListener("keydown", this.#handleToolsKeydown);
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
11595
11646
  }
11596
11647
 
11597
- #createButtonsContainer(childType, setCountProperty, moreMenu) {
11598
- const container = createElement("div", { className: `lexxy-table-control lexxy-table-control--${childType}` });
11599
-
11600
- const plusButton = this.#createButton(`Add ${childType}`, { action: "insert", childType, direction: "after" }, "+");
11601
- const minusButton = this.#createButton(`Remove ${childType}`, { action: "delete", childType }, "−");
11602
-
11603
- const dropdown = createElement("details", { className: "lexxy-table-control__more-menu" });
11604
- dropdown.setAttribute("name", "lexxy-dropdown");
11605
- dropdown.tabIndex = -1;
11606
-
11607
- const count = createElement("summary", {}, `_ ${childType}s`);
11608
- setCountProperty(count);
11609
- dropdown.appendChild(count);
11610
-
11611
- dropdown.appendChild(moreMenu);
11612
-
11613
- container.appendChild(minusButton);
11614
- container.appendChild(dropdown);
11615
- container.appendChild(plusButton);
11616
-
11617
- return container
11648
+ #moveSelectionDown() {
11649
+ const nextIndex = this.#selectedIndex + 1;
11650
+ if (nextIndex < this.#listItemElements.length) this.#selectOption(this.#listItemElements[nextIndex]);
11618
11651
  }
11619
11652
 
11620
- #createRowButtonsContainer() {
11621
- return this.#createButtonsContainer(
11622
- "row",
11623
- (count) => { this.rowCount = count; },
11624
- this.#createMoreMenuSection("row")
11625
- )
11653
+ #moveSelectionUp() {
11654
+ const previousIndex = this.#selectedIndex - 1;
11655
+ if (previousIndex >= 0) this.#selectOption(this.#listItemElements[previousIndex]);
11626
11656
  }
11627
11657
 
11628
- #createColumnButtonsContainer() {
11629
- return this.#createButtonsContainer(
11630
- "column",
11631
- (count) => { this.columnCount = count; },
11632
- this.#createMoreMenuSection("column")
11633
- )
11658
+ get #selectedIndex() {
11659
+ return this.#listItemElements.findIndex((item) => item.hasAttribute("aria-selected"))
11634
11660
  }
11635
11661
 
11636
- #createMoreMenuSection(childType) {
11637
- const section = createElement("div", { className: "lexxy-table-control__more-menu-details" });
11638
- const addBeforeButton = this.#createButton(`Add ${childType} before`, { action: "insert", childType, direction: "before" });
11639
- const addAfterButton = this.#createButton(`Add ${childType} after`, { action: "insert", childType, direction: "after" });
11640
- const toggleStyleButton = this.#createButton(`Toggle ${childType} style`, { action: "toggle", childType });
11641
- const deleteButton = this.#createButton(`Remove ${childType}`, { action: "delete", childType });
11662
+ get #selectedListItem() {
11663
+ return this.#listItemElements[this.#selectedIndex]
11664
+ }
11642
11665
 
11643
- section.appendChild(addBeforeButton);
11644
- section.appendChild(addAfterButton);
11645
- section.appendChild(toggleStyleButton);
11646
- section.appendChild(deleteButton);
11666
+ #handleSelectedOption(event) {
11667
+ event.preventDefault();
11668
+ event.stopPropagation();
11669
+ this.#optionWasSelected();
11670
+ return true
11671
+ }
11647
11672
 
11648
- return section
11673
+ #optionWasSelected() {
11674
+ this.#replaceTriggerWithSelectedItem();
11675
+ this.#hidePopover();
11676
+ this.#editorElement.focus();
11649
11677
  }
11650
11678
 
11651
- #createDeleteTableButton() {
11652
- const container = createElement("div", { className: "lexxy-table-control" });
11679
+ #replaceTriggerWithSelectedItem() {
11680
+ const promptItem = this.source.promptItemFor(this.#selectedListItem);
11653
11681
 
11654
- const deleteTableButton = this.#createButton("Delete this table?", { action: "delete", childType: "table" });
11655
- deleteTableButton.classList.add("lexxy-table-control__button--delete-table");
11682
+ if (!promptItem) { return }
11656
11683
 
11657
- container.appendChild(deleteTableButton);
11684
+ const templates = Array.from(promptItem.querySelectorAll("template[type='editor']"));
11685
+ const stringToReplace = `${this.trigger}${this.#editorContents.textBackUntil(this.trigger)}`;
11658
11686
 
11659
- this.deleteContainer = container;
11687
+ if (this.hasAttribute("insert-editable-text")) {
11688
+ this.#insertTemplatesAsEditableText(templates, stringToReplace);
11689
+ } else {
11690
+ this.#insertTemplatesAsAttachments(templates, stringToReplace, promptItem.getAttribute("sgid"));
11691
+ }
11692
+ }
11660
11693
 
11661
- return container
11694
+ #insertTemplatesAsEditableText(templates, stringToReplace) {
11695
+ this.#editor.update(() => {
11696
+ const nodes = templates.flatMap(template => this.#buildEditableTextNodes(template));
11697
+ this.#editorContents.replaceTextBackUntil(stringToReplace, nodes);
11698
+ });
11662
11699
  }
11663
11700
 
11664
- #createButton(label, command = {}, icon = this.#icon(command)) {
11665
- const button = createElement("button", {
11666
- className: "lexxy-table-control__button",
11667
- "aria-label": label,
11668
- type: "button"
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);
11669
11710
  });
11670
- button.tabIndex = -1;
11671
- button.innerHTML = `${icon} <span>${label}</span>`;
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
+ }
11672
11721
 
11673
- button.dataset.action = command.action;
11674
- button.dataset.childType = command.childType;
11675
- button.dataset.direction = command.direction;
11722
+ #getSpacerTextNode() {
11723
+ return sr(" ")
11724
+ }
11676
11725
 
11677
- button.addEventListener("click", () => this.#executeTableCommand(command));
11726
+ get #defaultPromptContentType() {
11727
+ const attachmentContentTypeNamespace = Lexxy.global.get("attachmentContentTypeNamespace");
11728
+ return `application/vnd.${attachmentContentTypeNamespace}.${this.name}`
11729
+ }
11678
11730
 
11679
- button.addEventListener("mouseover", () => this.#handleCommandButtonHover());
11680
- button.addEventListener("focus", () => this.#handleCommandButtonHover());
11681
- button.addEventListener("mouseout", () => this.#handleCommandButtonHover());
11731
+ #buildAttachmentNode(innerHtml, contentType, sgid) {
11732
+ return new CustomActionTextAttachmentNode({ sgid, contentType, innerHtml })
11733
+ }
11682
11734
 
11683
- return button
11735
+ get #editorContents() {
11736
+ return this.#editorElement.contents
11684
11737
  }
11685
11738
 
11686
- #registerKeyboardShortcuts() {
11687
- this.unregisterKeyboardShortcuts = this.#editor.registerCommand(me$1, this.#handleAccessibilityShortcutKey, Bi);
11739
+ get #editorContentElement() {
11740
+ return this.#editorElement.editorContentElement
11688
11741
  }
11689
11742
 
11690
- #unregisterKeyboardShortcuts() {
11691
- this.unregisterKeyboardShortcuts?.();
11692
- this.unregisterKeyboardShortcuts = null;
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
11693
11752
  }
11694
11753
 
11695
- #handleAccessibilityShortcutKey = (event) => {
11696
- if ((event.ctrlKey || event.metaKey) && event.shiftKey && event.key === "F10") {
11697
- const firstButton = this.querySelector("button, [tabindex]:not([tabindex='-1'])");
11698
- firstButton?.focus();
11754
+ #handlePopoverClick = (event) => {
11755
+ const listItem = event.target.closest(".lexxy-prompt-menu__item");
11756
+ if (listItem) {
11757
+ this.#selectOption(listItem);
11758
+ this.#optionWasSelected();
11699
11759
  }
11700
11760
  }
11701
11761
 
11702
- #handleToolsKeydown = (event) => {
11703
- if (event.key === "Escape") {
11704
- this.#handleEscapeKey();
11705
- } else {
11706
- handleRollingTabIndex(this.#tableToolsButtons, event);
11707
- }
11762
+ #reconnect() {
11763
+ this.disconnectedCallback();
11764
+ this.connectedCallback();
11708
11765
  }
11766
+ }
11709
11767
 
11710
- #handleEscapeKey() {
11711
- const cell = this.tableController.currentCell;
11712
- if (!cell) return
11768
+ class CodeLanguagePicker extends HTMLElement {
11769
+ connectedCallback() {
11770
+ this.editorElement = this.closest("lexxy-editor");
11771
+ this.editor = this.editorElement.editor;
11713
11772
 
11714
- this.#editor.update(() => {
11715
- cell.select();
11716
- this.#editor.focus();
11773
+ this.#attachLanguagePicker();
11774
+ this.#monitorForCodeBlockSelection();
11775
+ }
11776
+
11777
+ #attachLanguagePicker() {
11778
+ this.languagePickerElement = this.#createLanguagePicker();
11779
+
11780
+ this.languagePickerElement.addEventListener("change", () => {
11781
+ this.#updateCodeBlockLanguage(this.languagePickerElement.value);
11717
11782
  });
11718
11783
 
11719
- this.#update();
11784
+ this.languagePickerElement.setAttribute("nonce", getNonce());
11785
+ this.appendChild(this.languagePickerElement);
11720
11786
  }
11721
11787
 
11722
- async #handleCommandButtonHover() {
11723
- await nextFrame();
11788
+ #createLanguagePicker() {
11789
+ const selectElement = createElement("select", { className: "lexxy-code-language-picker", "aria-label": "Pick a language…", name: "lexxy-code-language" });
11724
11790
 
11725
- this.#clearCellStyles();
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);
11796
+ }
11726
11797
 
11727
- const activeElement = this.querySelector("button:hover, button:focus");
11728
- if (!activeElement) return
11798
+ return selectElement
11799
+ }
11729
11800
 
11730
- const command = {
11731
- action: activeElement.dataset.action,
11732
- childType: activeElement.dataset.childType,
11733
- direction: activeElement.dataset.direction
11734
- };
11801
+ get #languages() {
11802
+ const languages = { ...pt$1 };
11735
11803
 
11736
- let cellsToHighlight = null;
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";
11737
11810
 
11738
- switch (command.childType) {
11739
- case "row":
11740
- cellsToHighlight = this.tableController.currentRowCells;
11741
- break
11742
- case "column":
11743
- cellsToHighlight = this.tableController.currentColumnCells;
11744
- break
11745
- case "table":
11746
- cellsToHighlight = this.tableController.tableRows;
11747
- break
11748
- }
11811
+ const sortedEntries = Object.entries(languages)
11812
+ .sort(([ , a ], [ , b ]) => a.localeCompare(b));
11749
11813
 
11750
- if (!cellsToHighlight) return
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 ])
11818
+ }
11751
11819
 
11752
- cellsToHighlight.forEach(cell => {
11753
- const cellElement = this.#editor.getElementByKey(cell.getKey());
11754
- if (!cellElement) return
11820
+ #updateCodeBlockLanguage(language) {
11821
+ this.editor.update(() => {
11822
+ const codeNode = this.#getCurrentCodeNode();
11755
11823
 
11756
- cellElement.classList.toggle(theme.tableCellHighlight, true);
11757
- Object.assign(cellElement.dataset, command);
11824
+ if (codeNode) {
11825
+ codeNode.setLanguage(language);
11826
+ }
11758
11827
  });
11759
11828
  }
11760
11829
 
11761
- #monitorForTableSelection() {
11762
- this.unregisterUpdateListener = this.#editor.registerUpdateListener(() => {
11763
- this.tableController.updateSelectedTable();
11830
+ #monitorForCodeBlockSelection() {
11831
+ this.editor.registerUpdateListener(() => {
11832
+ this.editor.getEditorState().read(() => {
11833
+ const codeNode = this.#getCurrentCodeNode();
11764
11834
 
11765
- const tableNode = this.tableController.currentTableNode;
11766
- if (tableNode) {
11767
- this.#show();
11768
- } else {
11769
- this.#hide();
11770
- }
11835
+ if (codeNode) {
11836
+ this.#codeNodeWasSelected(codeNode);
11837
+ } else {
11838
+ this.#hideLanguagePicker();
11839
+ }
11840
+ });
11771
11841
  });
11772
11842
  }
11773
11843
 
11774
- #executeTableCommand(command) {
11775
- this.tableController.executeTableCommand(command);
11776
- this.#update();
11844
+ #getCurrentCodeNode() {
11845
+ const selection = Lr();
11846
+
11847
+ if (!yr(selection)) {
11848
+ return null
11849
+ }
11850
+
11851
+ const anchorNode = selection.anchor.getNode();
11852
+ const parentNode = anchorNode.getParent();
11853
+
11854
+ if (X$1(anchorNode)) {
11855
+ return anchorNode
11856
+ } else if (X$1(parentNode)) {
11857
+ return parentNode
11858
+ }
11859
+
11860
+ return null
11777
11861
  }
11778
11862
 
11779
- #show() {
11780
- this.style.display = "flex";
11781
- this.#update();
11863
+ #codeNodeWasSelected(codeNode) {
11864
+ const language = codeNode.getLanguage();
11865
+
11866
+ this.#updateLanguagePickerWith(language);
11867
+ this.#showLanguagePicker();
11868
+ this.#positionLanguagePicker(codeNode);
11782
11869
  }
11783
11870
 
11784
- #hide() {
11785
- this.style.display = "none";
11786
- this.#clearCellStyles();
11871
+ #updateLanguagePickerWith(language) {
11872
+ if (this.languagePickerElement && language) {
11873
+ const normalizedLanguage = dt$1(language);
11874
+ this.languagePickerElement.value = normalizedLanguage;
11875
+ }
11787
11876
  }
11788
11877
 
11789
- #update() {
11790
- this.#updateButtonsPosition();
11791
- this.#updateRowColumnCount();
11792
- this.#closeMoreMenu();
11793
- this.#handleCommandButtonHover();
11878
+ #positionLanguagePicker(codeNode) {
11879
+ const codeElement = this.editor.getElementByKey(codeNode.getKey());
11880
+ if (!codeElement) return
11881
+
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;
11886
+
11887
+ this.style.top = `${relativeTop}px`;
11888
+ this.style.right = `${relativeRight}px`;
11794
11889
  }
11795
11890
 
11796
- #closeMoreMenu() {
11797
- this.querySelector("details[open]")?.removeAttribute("open");
11891
+ #showLanguagePicker() {
11892
+ this.hidden = false;
11798
11893
  }
11799
11894
 
11800
- #updateButtonsPosition() {
11801
- const tableNode = this.tableController.currentTableNode;
11802
- if (!tableNode) return
11895
+ #hideLanguagePicker() {
11896
+ this.hidden = true;
11897
+ }
11898
+ }
11803
11899
 
11804
- const tableElement = this.#editor.getElementByKey(tableNode.getKey());
11805
- if (!tableElement) return
11900
+ class TableController {
11901
+ constructor(editorElement) {
11902
+ this.editor = editorElement.editor;
11903
+ this.contents = editorElement.contents;
11904
+ this.selection = editorElement.selection;
11806
11905
 
11807
- const tableRect = tableElement.getBoundingClientRect();
11808
- const editorRect = this.#editorElement.getBoundingClientRect();
11906
+ this.currentTableNodeKey = null;
11907
+ this.currentCellKey = null;
11809
11908
 
11810
- const relativeTop = tableRect.top - editorRect.top;
11811
- const relativeCenter = (tableRect.left + tableRect.right) / 2 - editorRect.left;
11812
- this.style.top = `${relativeTop}px`;
11813
- this.style.left = `${relativeCenter}px`;
11909
+ this.#registerKeyHandlers();
11814
11910
  }
11815
11911
 
11816
- #updateRowColumnCount() {
11817
- const tableNode = this.tableController.currentTableNode;
11818
- if (!tableNode) return
11912
+ destroy() {
11913
+ this.currentTableNodeKey = null;
11914
+ this.currentCellKey = null;
11819
11915
 
11820
- const tableElement = dn(this.#editor, tableNode);
11821
- if (!tableElement) return
11916
+ this.#unregisterKeyHandlers();
11917
+ }
11822
11918
 
11823
- const rowCount = tableElement.rows;
11824
- const columnCount = tableElement.columns;
11919
+ get currentCell() {
11920
+ if (!this.currentCellKey) return null
11825
11921
 
11826
- this.rowCount.textContent = `${rowCount} row${rowCount === 1 ? "" : "s"}`;
11827
- this.columnCount.textContent = `${columnCount} column${columnCount === 1 ? "" : "s"}`;
11922
+ return this.editor.getEditorState().read(() => {
11923
+ const cell = xo(this.currentCellKey);
11924
+ return (cell instanceof xe) ? cell : null
11925
+ })
11828
11926
  }
11829
11927
 
11830
- #setTableCellFocus() {
11831
- const cell = this.tableController.currentCell;
11832
- if (!cell) return
11833
-
11834
- const cellElement = this.#editor.getElementByKey(cell.getKey());
11835
- if (!cellElement) return
11928
+ get currentTableNode() {
11929
+ if (!this.currentTableNodeKey) return null
11836
11930
 
11837
- cellElement.classList.add(theme.tableCellFocus);
11931
+ return this.editor.getEditorState().read(() => {
11932
+ const tableNode = xo(this.currentTableNodeKey);
11933
+ return (tableNode instanceof hn) ? tableNode : null
11934
+ })
11838
11935
  }
11839
11936
 
11840
- #clearCellStyles() {
11841
- this.#editorElement.querySelectorAll(`.${theme.tableCellFocus}`)?.forEach(cell => {
11842
- cell.classList.remove(theme.tableCellFocus);
11843
- });
11844
-
11845
- this.#editorElement.querySelectorAll(`.${theme.tableCellHighlight}`)?.forEach(cell => {
11846
- cell.classList.remove(theme.tableCellHighlight);
11847
- cell.removeAttribute("data-action");
11848
- cell.removeAttribute("data-child-type");
11849
- cell.removeAttribute("data-direction");
11850
- });
11937
+ get currentRowCells() {
11938
+ const currentRowIndex = this.currentRowIndex;
11851
11939
 
11852
- this.#setTableCellFocus();
11853
- }
11940
+ const rows = this.tableRows;
11941
+ if (!rows) return null
11854
11942
 
11855
- #icon(command) {
11856
- const { action, childType } = command;
11857
- const direction = (action == "insert" ? command.direction : null);
11858
- const iconId = [ action, childType, direction ].filter(Boolean).join("-");
11859
- return TableIcons[iconId]
11943
+ return this.editor.getEditorState().read(() => {
11944
+ return rows[currentRowIndex]?.getChildren() ?? null
11945
+ }) ?? null
11860
11946
  }
11861
- }
11862
11947
 
11863
- customElements.define("lexxy-table-tools", TableTools);
11948
+ get currentRowIndex() {
11949
+ const currentCell = this.currentCell;
11950
+ if (!currentCell) return 0
11864
11951
 
11865
- class BaseSource {
11866
- // Template method to override
11867
- async buildListItems(filter = "") {
11868
- return Promise.resolve([])
11952
+ return this.editor.getEditorState().read(() => {
11953
+ return Ie$1(currentCell)
11954
+ }) ?? 0
11869
11955
  }
11870
11956
 
11871
- // Template method to override
11872
- promptItemFor(listItem) {
11873
- return null
11874
- }
11957
+ get currentColumnCells() {
11958
+ const columnIndex = this.currentColumnIndex;
11875
11959
 
11876
- // Protected
11960
+ const rows = this.tableRows;
11961
+ if (!rows) return null
11877
11962
 
11878
- buildListItemElementFor(promptItemElement) {
11879
- const template = promptItemElement.querySelector("template[type='menu']");
11880
- const fragment = template.content.cloneNode(true);
11881
- const listItemElement = createElement("li", { role: "option", id: generateDomId("prompt-item"), tabindex: "0" });
11882
- listItemElement.classList.add("lexxy-prompt-menu__item");
11883
- listItemElement.appendChild(fragment);
11884
- return listItemElement
11963
+ return this.editor.getEditorState().read(() => {
11964
+ return rows.map(row => row.getChildAtIndex(columnIndex))
11965
+ }) ?? null
11885
11966
  }
11886
11967
 
11887
- async loadPromptItemsFromUrl(url) {
11888
- try {
11889
- const response = await fetch(url);
11890
- const html = await response.text();
11891
- const promptItems = parseHtml(html).querySelectorAll("lexxy-prompt-item");
11892
- return Promise.resolve(Array.from(promptItems))
11893
- } catch (error) {
11894
- return Promise.reject(error)
11895
- }
11896
- }
11897
- }
11968
+ get currentColumnIndex() {
11969
+ const currentCell = this.currentCell;
11970
+ if (!currentCell) return 0
11898
11971
 
11899
- class LocalFilterSource extends BaseSource {
11900
- async buildListItems(filter = "") {
11901
- const promptItems = await this.fetchPromptItems();
11902
- return this.#buildListItemsFromPromptItems(promptItems, filter)
11972
+ return this.editor.getEditorState().read(() => {
11973
+ return Ue$1(currentCell)
11974
+ }) ?? 0
11903
11975
  }
11904
11976
 
11905
- // Template method to override
11906
- async fetchPromptItems(filter) {
11907
- return Promise.resolve([])
11977
+ get tableRows() {
11978
+ return this.editor.getEditorState().read(() => {
11979
+ return this.currentTableNode?.getChildren()
11980
+ }) ?? null
11908
11981
  }
11909
11982
 
11910
- promptItemFor(listItem) {
11911
- return this.promptItemByListItem.get(listItem)
11912
- }
11983
+ updateSelectedTable() {
11984
+ let cellNode = null;
11985
+ let tableNode = null;
11913
11986
 
11914
- #buildListItemsFromPromptItems(promptItems, filter) {
11915
- const listItems = [];
11916
- this.promptItemByListItem = new WeakMap();
11917
- promptItems.forEach((promptItem) => {
11918
- const searchableText = promptItem.getAttribute("search");
11987
+ this.editor.getEditorState().read(() => {
11988
+ const selection = Lr();
11989
+ if (!selection || !this.selection.isTableCellSelected) return
11919
11990
 
11920
- if (!filter || filterMatches(searchableText, filter)) {
11921
- const listItem = this.buildListItemElementFor(promptItem);
11922
- this.promptItemByListItem.set(listItem, promptItem);
11923
- listItems.push(listItem);
11924
- }
11991
+ const node = selection.getNodes()[0];
11992
+
11993
+ cellNode = Gt(node);
11994
+ tableNode = Qt(node);
11925
11995
  });
11926
11996
 
11927
- return listItems
11997
+ this.currentCellKey = cellNode?.getKey() ?? null;
11998
+ this.currentTableNodeKey = tableNode?.getKey() ?? null;
11928
11999
  }
11929
- }
11930
12000
 
11931
- class InlinePromptSource extends LocalFilterSource {
11932
- constructor(inlinePromptItems) {
11933
- super();
11934
- this.inlinePromptItemElements = Array.from(inlinePromptItems);
12001
+ executeTableCommand(command, customIndex = null) {
12002
+ if (command.action === "delete" && command.childType === "table") {
12003
+ this.#deleteTable();
12004
+ return
12005
+ }
12006
+
12007
+ if (command.action === "toggle") {
12008
+ this.#executeToggleStyle(command);
12009
+ return
12010
+ }
12011
+
12012
+ this.#executeCommand(command, customIndex);
11935
12013
  }
11936
12014
 
11937
- async fetchPromptItems() {
11938
- return Promise.resolve(this.inlinePromptItemElements)
12015
+ #executeCommand(command, customIndex = null) {
12016
+ this.#selectCellAtSelection();
12017
+ this.editor.dispatchCommand(this.#commandName(command));
12018
+ this.#selectNextBestCell(command, customIndex);
11939
12019
  }
11940
- }
11941
12020
 
11942
- class DeferredPromptSource extends LocalFilterSource {
11943
- constructor(url) {
11944
- super();
11945
- this.url = url;
12021
+ #executeToggleStyle(command) {
12022
+ const childType = command.childType;
11946
12023
 
11947
- this.fetchPromptItems();
11948
- }
12024
+ let cells = null;
12025
+ let headerState = null;
11949
12026
 
11950
- async fetchPromptItems() {
11951
- this.promptItems ??= await this.loadPromptItemsFromUrl(this.url);
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
+ }
11952
12034
 
11953
- return Promise.resolve(this.promptItems)
11954
- }
11955
- }
12035
+ if (!cells || cells.length === 0) return
11956
12036
 
11957
- const DEBOUNCE_INTERVAL = 200;
12037
+ this.editor.update(() => {
12038
+ const firstCell = Be$1(cells[0]);
12039
+ if (!firstCell) return
11958
12040
 
11959
- class RemoteFilterSource extends BaseSource {
11960
- constructor(url) {
11961
- super();
12041
+ const currentStyle = firstCell.getHeaderStyles();
12042
+ const newStyle = currentStyle ^ headerState;
11962
12043
 
11963
- this.baseURL = url;
11964
- this.loadAndFilterListItems = debounceAsync(this.fetchFilteredListItems.bind(this), DEBOUNCE_INTERVAL);
12044
+ cells.forEach(cell => {
12045
+ this.#setHeaderStyle(cell, newStyle, headerState);
12046
+ });
12047
+ });
11965
12048
  }
11966
12049
 
11967
- async buildListItems(filter = "") {
11968
- return await this.loadAndFilterListItems(filter)
12050
+ #deleteTable() {
12051
+ this.#selectCellAtSelection();
12052
+ this.editor.dispatchCommand("deleteTable");
11969
12053
  }
11970
12054
 
11971
- promptItemFor(listItem) {
11972
- return this.promptItemByListItem.get(listItem)
12055
+ #selectCellAtSelection() {
12056
+ this.editor.update(() => {
12057
+ const selection = Lr();
12058
+ if (!selection) return
12059
+
12060
+ const node = selection.getNodes()[0];
12061
+
12062
+ Gt(node)?.selectEnd();
12063
+ });
11973
12064
  }
11974
12065
 
11975
- async fetchFilteredListItems(filter) {
11976
- const promptItems = await this.loadPromptItemsFromUrl(this.#urlFor(filter));
11977
- return this.#buildListItemsFromPromptItems(promptItems)
12066
+ #commandName(command) {
12067
+ const { action, childType, direction } = command;
12068
+
12069
+ const childTypeSuffix = upcaseFirst(childType);
12070
+ const directionSuffix = action == "insert" ? upcaseFirst(direction) : "";
12071
+ return `${action}Table${childTypeSuffix}${directionSuffix}`
11978
12072
  }
11979
12073
 
11980
- #urlFor(filter) {
11981
- const url = new URL(this.baseURL, window.location.origin);
11982
- url.searchParams.append("filter", filter);
11983
- return url.toString()
12074
+ #setHeaderStyle(cell, newStyle, headerState) {
12075
+ const tableCellNode = Be$1(cell);
12076
+ tableCellNode?.setHeaderStyles(newStyle, headerState);
11984
12077
  }
11985
12078
 
11986
- #buildListItemsFromPromptItems(promptItems) {
11987
- const listItems = [];
11988
- this.promptItemByListItem = new WeakMap();
12079
+ async #selectCellAtIndex(rowIndex, columnIndex) {
12080
+ // We wait for next frame, otherwise table operations might not have completed yet.
12081
+ await nextFrame();
11989
12082
 
11990
- for (const promptItem of promptItems) {
11991
- const listItem = this.buildListItemElementFor(promptItem);
11992
- this.promptItemByListItem.set(listItem, promptItem);
11993
- listItems.push(listItem);
11994
- }
12083
+ if (!this.currentTableNode) return
11995
12084
 
11996
- return listItems
11997
- }
11998
- }
12085
+ const rows = this.tableRows;
12086
+ if (!rows) return
11999
12087
 
12000
- const NOTHING_FOUND_DEFAULT_MESSAGE = "Nothing found";
12088
+ const row = rows[rowIndex];
12089
+ if (!row) return
12001
12090
 
12002
- class LexicalPromptElement extends HTMLElement {
12003
- constructor() {
12004
- super();
12005
- this.keyListeners = [];
12091
+ this.editor.update(() => {
12092
+ const cell = Be$1(row.getChildAtIndex(columnIndex));
12093
+ cell?.selectEnd();
12094
+ });
12006
12095
  }
12007
12096
 
12008
- static observedAttributes = [ "connected" ]
12097
+ #selectNextBestCell(command, customIndex = null) {
12098
+ const { childType, direction } = command;
12099
+
12100
+ let rowIndex = this.currentRowIndex;
12101
+ let columnIndex = customIndex !== null ? customIndex : this.currentColumnIndex;
12009
12102
 
12010
- connectedCallback() {
12011
- this.source = this.#createSource();
12103
+ const deleteOffset = command.action === "delete" ? -1 : 0;
12104
+ const offset = direction === "after" ? 1 : deleteOffset;
12012
12105
 
12013
- this.#addTriggerListener();
12014
- this.toggleAttribute("connected", true);
12015
- }
12106
+ if (childType === "row") {
12107
+ rowIndex += offset;
12108
+ } else if (childType === "column") {
12109
+ columnIndex += offset;
12110
+ }
12016
12111
 
12017
- disconnectedCallback() {
12018
- this.source = null;
12019
- this.popoverElement = null;
12112
+ this.#selectCellAtIndex(rowIndex, columnIndex);
12020
12113
  }
12021
12114
 
12115
+ #selectNextRow() {
12116
+ const rows = this.tableRows;
12117
+ if (!rows) return
12022
12118
 
12023
- attributeChangedCallback(name, oldValue, newValue) {
12024
- if (name === "connected" && this.isConnected && oldValue != null && oldValue !== newValue) {
12025
- requestAnimationFrame(() => this.#reconnect());
12026
- }
12027
- }
12119
+ const nextRow = rows.at(this.currentRowIndex + 1);
12120
+ if (!nextRow) return
12028
12121
 
12029
- get name() {
12030
- return this.getAttribute("name")
12122
+ this.editor.update(() => {
12123
+ nextRow.getChildAtIndex(this.currentColumnIndex)?.selectEnd();
12124
+ });
12031
12125
  }
12032
12126
 
12033
- get trigger() {
12034
- return this.getAttribute("trigger")
12035
- }
12127
+ #selectPreviousCell() {
12128
+ const cell = this.currentCell;
12129
+ if (!cell) return
12036
12130
 
12037
- get supportsSpaceInSearches() {
12038
- return this.hasAttribute("supports-space-in-searches")
12131
+ this.editor.update(() => {
12132
+ cell.selectPrevious();
12133
+ });
12039
12134
  }
12040
12135
 
12041
- get open() {
12042
- return this.popoverElement?.classList?.contains("lexxy-prompt-menu--visible")
12136
+ #insertRowAndSelectFirstCell() {
12137
+ this.executeTableCommand({ action: "insert", childType: "row", direction: "after" }, 0);
12043
12138
  }
12044
12139
 
12045
- get closed() {
12046
- return !this.open
12140
+ #deleteRowAndSelectLastCell() {
12141
+ this.executeTableCommand({ action: "delete", childType: "row" }, -1);
12047
12142
  }
12048
12143
 
12049
- get #doesSpaceSelect() {
12050
- return !this.supportsSpaceInSearches
12051
- }
12144
+ #deleteRowAndSelectNextNode() {
12145
+ const tableNode = this.currentTableNode;
12146
+ this.executeTableCommand({ action: "delete", childType: "row" });
12052
12147
 
12053
- #createSource() {
12054
- const src = this.getAttribute("src");
12055
- if (src) {
12056
- if (this.hasAttribute("remote-filtering")) {
12057
- return new RemoteFilterSource(src)
12148
+ this.editor.update(() => {
12149
+ const next = tableNode?.getNextSibling();
12150
+ if (Ii(next)) {
12151
+ next.selectStart();
12058
12152
  } else {
12059
- return new DeferredPromptSource(src)
12153
+ const newParagraph = Li();
12154
+ this.currentTableNode.insertAfter(newParagraph);
12155
+ newParagraph.selectStart();
12060
12156
  }
12061
- } else {
12062
- return new InlinePromptSource(this.querySelectorAll("lexxy-prompt-item"))
12063
- }
12157
+ });
12064
12158
  }
12065
12159
 
12066
- #addTriggerListener() {
12067
- const unregister = this.#editor.registerUpdateListener(({ editorState }) => {
12068
- editorState.read(() => {
12069
- const { node, offset } = this.#selection.selectedNodeWithOffset();
12070
- if (!node) return
12160
+ #isCurrentCellEmpty() {
12161
+ if (!this.currentTableNode) return false
12071
12162
 
12072
- if (lr(node)) {
12073
- const fullText = node.getTextContent();
12074
- const triggerLength = this.trigger.length;
12163
+ const cell = this.currentCell;
12164
+ if (!cell) return false
12075
12165
 
12076
- // Check if we have enough characters for the trigger
12077
- if (offset >= triggerLength) {
12078
- const textBeforeCursor = fullText.slice(offset - triggerLength, offset);
12166
+ return cell.getTextContent().trim() === ""
12167
+ }
12079
12168
 
12080
- // Check if trigger is at the start of the text node (new line case) or preceded by space or newline
12081
- if (textBeforeCursor === this.trigger) {
12082
- const isAtStart = offset === triggerLength;
12169
+ #isCurrentRowLast() {
12170
+ if (!this.currentTableNode) return false
12083
12171
 
12084
- const charBeforeTrigger = offset > triggerLength ? fullText[offset - triggerLength - 1] : null;
12085
- const isPrecededBySpaceOrNewline = charBeforeTrigger === " " || charBeforeTrigger === "\n";
12172
+ const rows = this.tableRows;
12173
+ if (!rows) return false
12086
12174
 
12087
- if (isAtStart || isPrecededBySpaceOrNewline) {
12088
- unregister();
12089
- this.#showPopover();
12090
- }
12091
- }
12092
- }
12093
- }
12094
- });
12095
- });
12175
+ return rows.length === this.currentRowIndex + 1
12096
12176
  }
12097
12177
 
12098
- #addCursorPositionListener() {
12099
- this.cursorPositionListener = this.#editor.registerUpdateListener(() => {
12100
- if (this.closed) return
12101
-
12102
- this.#editor.read(() => {
12103
- const { node, offset } = this.#selection.selectedNodeWithOffset();
12104
- if (!node) return
12178
+ #isCurrentRowEmpty() {
12179
+ if (!this.currentTableNode) return false
12105
12180
 
12106
- if (lr(node) && offset > 0) {
12107
- const fullText = node.getTextContent();
12108
- const textBeforeCursor = fullText.slice(0, offset);
12109
- const lastTriggerIndex = textBeforeCursor.lastIndexOf(this.trigger);
12110
- const triggerEndIndex = lastTriggerIndex + this.trigger.length - 1;
12181
+ const cells = this.currentRowCells;
12182
+ if (!cells) return false
12111
12183
 
12112
- // If trigger is not found, or cursor is at or before the trigger end position, hide popover
12113
- if (lastTriggerIndex === -1 || offset <= triggerEndIndex) {
12114
- this.#hidePopover();
12115
- }
12116
- } else {
12117
- // Cursor is not in a text node or at offset 0, hide popover
12118
- this.#hidePopover();
12119
- }
12120
- });
12121
- });
12184
+ return cells.every(cell => cell.getTextContent().trim() === "")
12122
12185
  }
12123
12186
 
12124
- #removeCursorPositionListener() {
12125
- if (this.cursorPositionListener) {
12126
- this.cursorPositionListener();
12127
- this.cursorPositionListener = null;
12128
- }
12129
- }
12187
+ #isFirstCellInRow() {
12188
+ if (!this.currentTableNode) return false
12130
12189
 
12131
- get #editor() {
12132
- return this.#editorElement.editor
12133
- }
12190
+ const cells = this.currentRowCells;
12191
+ if (!cells) return false
12134
12192
 
12135
- get #editorElement() {
12136
- return this.closest("lexxy-editor")
12193
+ return cells.indexOf(this.currentCell) === 0
12137
12194
  }
12138
12195
 
12139
- get #selection() {
12140
- return this.#editorElement.selection
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);
12141
12200
  }
12142
12201
 
12143
- async #showPopover() {
12144
- this.popoverElement ??= await this.#buildPopover();
12145
- this.#resetPopoverPosition();
12146
- await this.#filterOptions();
12147
- this.popoverElement.classList.toggle("lexxy-prompt-menu--visible", true);
12148
- this.#selectFirstOption();
12149
-
12150
- this.#editorElement.addEventListener("keydown", this.#handleKeydownOnPopover);
12151
- this.#editorElement.addEventListener("lexxy:change", this.#filterOptions);
12202
+ #unregisterKeyHandlers() {
12203
+ this.unregisterBackspaceKeyHandler?.();
12204
+ this.unregisterEnterKeyHandler?.();
12152
12205
 
12153
- this.#registerKeyListeners();
12154
- this.#addCursorPositionListener();
12206
+ this.unregisterBackspaceKeyHandler = null;
12207
+ this.unregisterEnterKeyHandler = null;
12155
12208
  }
12156
12209
 
12157
- #registerKeyListeners() {
12158
- // We can't use a regular keydown for Enter as Lexical handles it first
12159
- this.keyListeners.push(this.#editor.registerCommand(Ne$2, this.#handleSelectedOption.bind(this), Bi));
12160
- this.keyListeners.push(this.#editor.registerCommand(Me$2, this.#handleSelectedOption.bind(this), Bi));
12210
+ #handleBackspaceKey(event) {
12211
+ if (!this.currentTableNode) return false
12161
12212
 
12162
- if (this.#doesSpaceSelect) {
12163
- this.keyListeners.push(this.#editor.registerCommand(be$1, this.#handleSelectedOption.bind(this), Bi));
12213
+ if (this.#isCurrentRowEmpty() && this.#isFirstCellInRow()) {
12214
+ event.preventDefault();
12215
+ this.#deleteRowAndSelectLastCell();
12216
+ return true
12164
12217
  }
12165
12218
 
12166
- // Register arrow keys with HIGH priority to prevent Lexical's selection handlers from running
12167
- this.keyListeners.push(this.#editor.registerCommand(ke$2, this.#handleArrowUp.bind(this), Bi));
12168
- this.keyListeners.push(this.#editor.registerCommand(Te$2, this.#handleArrowDown.bind(this), Bi));
12169
- }
12219
+ if (this.#isCurrentCellEmpty() && !this.#isFirstCellInRow()) {
12220
+ event.preventDefault();
12221
+ this.#selectPreviousCell();
12222
+ return true
12223
+ }
12170
12224
 
12171
- #handleArrowUp(event) {
12172
- this.#moveSelectionUp();
12173
- event.preventDefault();
12174
- return true
12225
+ return false
12175
12226
  }
12176
12227
 
12177
- #handleArrowDown(event) {
12178
- this.#moveSelectionDown();
12179
- event.preventDefault();
12180
- return true
12181
- }
12228
+ #handleEnterKey(event) {
12229
+ if ((event.ctrlKey || event.metaKey) || event.shiftKey || !this.currentTableNode) return false
12182
12230
 
12183
- #selectFirstOption() {
12184
- const firstOption = this.#listItemElements[0];
12231
+ if (this.selection.isInsideList || this.selection.isInsideCodeBlock) return false
12185
12232
 
12186
- if (firstOption) {
12187
- this.#selectOption(firstOption);
12233
+ event.preventDefault();
12234
+
12235
+ if (this.#isCurrentRowLast() && this.#isCurrentRowEmpty()) {
12236
+ this.#deleteRowAndSelectNextNode();
12237
+ } else if (this.#isCurrentRowLast()) {
12238
+ this.#insertRowAndSelectFirstCell();
12239
+ } else {
12240
+ this.#selectNextRow();
12188
12241
  }
12189
- }
12190
12242
 
12191
- get #listItemElements() {
12192
- return Array.from(this.popoverElement.querySelectorAll(".lexxy-prompt-menu__item"))
12243
+ return true
12193
12244
  }
12245
+ }
12194
12246
 
12195
- #selectOption(listItem) {
12196
- this.#clearSelection();
12197
- listItem.toggleAttribute("aria-selected", true);
12198
- listItem.scrollIntoView({ block: "nearest", behavior: "smooth" });
12199
- listItem.focus();
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>`,
12200
12253
 
12201
- // Preserve selection to prevent cursor jump
12202
- this.#selection.preservingSelection(() => {
12203
- this.#editorElement.focus();
12204
- });
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>`,
12205
12270
 
12206
- this.#editorContentElement.setAttribute("aria-controls", this.popoverElement.id);
12207
- this.#editorContentElement.setAttribute("aria-activedescendant", listItem.id);
12208
- this.#editorContentElement.setAttribute("aria-haspopup", "listbox");
12209
- }
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>`,
12210
12276
 
12211
- #clearSelection() {
12212
- this.#listItemElements.forEach((item) => { item.toggleAttribute("aria-selected", false); });
12213
- this.#editorContentElement.removeAttribute("aria-controls");
12214
- this.#editorContentElement.removeAttribute("aria-activedescendant");
12215
- this.#editorContentElement.removeAttribute("aria-haspopup");
12216
- }
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>`,
12217
12282
 
12218
- #positionPopover() {
12219
- const { x, y, fontSize } = this.#selection.cursorPosition;
12220
- const editorRect = this.#editorElement.getBoundingClientRect();
12221
- const contentRect = this.#editorContentElement.getBoundingClientRect();
12222
- const verticalOffset = contentRect.top - editorRect.top;
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>`,
12223
12288
 
12224
- if (!this.popoverElement.hasAttribute("data-anchored")) {
12225
- this.popoverElement.style.left = `${x}px`;
12226
- this.popoverElement.toggleAttribute("data-anchored", true);
12227
- }
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>`,
12228
12293
 
12229
- this.popoverElement.style.top = `${y + verticalOffset}px`;
12230
- this.popoverElement.style.bottom = "auto";
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
+ };
12231
12299
 
12232
- const popoverRect = this.popoverElement.getBoundingClientRect();
12233
- const isClippedAtBottom = popoverRect.bottom > window.innerHeight;
12300
+ class TableTools extends HTMLElement {
12301
+ connectedCallback() {
12302
+ this.tableController = new TableController(this.#editorElement);
12234
12303
 
12235
- if (isClippedAtBottom || this.popoverElement.hasAttribute("data-clipped-at-bottom")) {
12236
- this.popoverElement.style.top = `${y + verticalOffset - popoverRect.height - fontSize}px`;
12237
- this.popoverElement.style.bottom = "auto";
12238
- this.popoverElement.toggleAttribute("data-clipped-at-bottom", true);
12239
- }
12304
+ this.#setUpButtons();
12305
+ this.#monitorForTableSelection();
12306
+ this.#registerKeyboardShortcuts();
12240
12307
  }
12241
12308
 
12242
- #resetPopoverPosition() {
12243
- this.popoverElement.removeAttribute("data-clipped-at-bottom");
12244
- this.popoverElement.removeAttribute("data-anchored");
12245
- }
12309
+ disconnectedCallback() {
12310
+ this.#unregisterKeyboardShortcuts();
12246
12311
 
12247
- async #hidePopover() {
12248
- this.#clearSelection();
12249
- this.popoverElement.classList.toggle("lexxy-prompt-menu--visible", false);
12250
- this.#editorElement.removeEventListener("lexxy:change", this.#filterOptions);
12251
- this.#editorElement.removeEventListener("keydown", this.#handleKeydownOnPopover);
12312
+ this.unregisterUpdateListener?.();
12313
+ this.unregisterUpdateListener = null;
12252
12314
 
12253
- this.#unregisterKeyListeners();
12254
- this.#removeCursorPositionListener();
12315
+ this.removeEventListener("keydown", this.#handleToolsKeydown);
12255
12316
 
12256
- await nextFrame();
12257
- this.#addTriggerListener();
12317
+ this.tableController?.destroy();
12318
+ this.tableController = null;
12258
12319
  }
12259
12320
 
12260
- #unregisterKeyListeners() {
12261
- this.keyListeners.forEach((unregister) => unregister());
12262
- this.keyListeners = [];
12321
+ get #editor() {
12322
+ return this.#editorElement.editor
12263
12323
  }
12264
12324
 
12265
- #filterOptions = async () => {
12266
- if (this.initialPrompt) {
12267
- this.initialPrompt = false;
12268
- return
12269
- }
12270
-
12271
- if (this.#editorContents.containsTextBackUntil(this.trigger)) {
12272
- await this.#showFilteredOptions();
12273
- await nextFrame();
12274
- this.#positionPopover();
12275
- } else {
12276
- this.#hidePopover();
12277
- }
12325
+ get #editorElement() {
12326
+ return this.closest("lexxy-editor")
12278
12327
  }
12279
12328
 
12280
- async #showFilteredOptions() {
12281
- const filter = this.#editorContents.textBackUntil(this.trigger);
12282
- const filteredListItems = await this.source.buildListItems(filter);
12283
- this.popoverElement.innerHTML = "";
12284
-
12285
- if (filteredListItems.length > 0) {
12286
- this.#showResults(filteredListItems);
12287
- } else {
12288
- this.#showEmptyResults();
12289
- }
12290
- this.#selectFirstOption();
12329
+ get #tableToolsButtons() {
12330
+ return Array.from(this.querySelectorAll("button, details > summary"))
12291
12331
  }
12292
12332
 
12293
- #showResults(filteredListItems) {
12294
- this.popoverElement.classList.remove("lexxy-prompt-menu--empty");
12295
- this.popoverElement.append(...filteredListItems);
12296
- }
12333
+ #setUpButtons() {
12334
+ this.appendChild(this.#createRowButtonsContainer());
12335
+ this.appendChild(this.#createColumnButtonsContainer());
12297
12336
 
12298
- #showEmptyResults() {
12299
- this.popoverElement.classList.add("lexxy-prompt-menu--empty");
12300
- const el = createElement("li", { innerHTML: this.#emptyResultsMessage });
12301
- el.classList.add("lexxy-prompt-menu__item--empty");
12302
- this.popoverElement.append(el);
12337
+ this.appendChild(this.#createDeleteTableButton());
12338
+ this.addEventListener("keydown", this.#handleToolsKeydown);
12303
12339
  }
12304
12340
 
12305
- get #emptyResultsMessage() {
12306
- return this.getAttribute("empty-results") || NOTHING_FOUND_DEFAULT_MESSAGE
12307
- }
12341
+ #createButtonsContainer(childType, setCountProperty, moreMenu) {
12342
+ const container = createElement("div", { className: `lexxy-table-control lexxy-table-control--${childType}` });
12308
12343
 
12309
- #handleKeydownOnPopover = (event) => {
12310
- if (event.key === "Escape") {
12311
- this.#hidePopover();
12312
- this.#editorElement.focus();
12313
- event.stopPropagation();
12314
- }
12315
- // Arrow keys are now handled via Lexical commands with HIGH priority
12316
- }
12344
+ const plusButton = this.#createButton(`Add ${childType}`, { action: "insert", childType, direction: "after" }, "+");
12345
+ const minusButton = this.#createButton(`Remove ${childType}`, { action: "delete", childType }, "−");
12317
12346
 
12318
- #moveSelectionDown() {
12319
- const nextIndex = this.#selectedIndex + 1;
12320
- if (nextIndex < this.#listItemElements.length) this.#selectOption(this.#listItemElements[nextIndex]);
12321
- }
12347
+ const dropdown = createElement("details", { className: "lexxy-table-control__more-menu" });
12348
+ dropdown.setAttribute("name", "lexxy-dropdown");
12349
+ dropdown.tabIndex = -1;
12322
12350
 
12323
- #moveSelectionUp() {
12324
- const previousIndex = this.#selectedIndex - 1;
12325
- if (previousIndex >= 0) this.#selectOption(this.#listItemElements[previousIndex]);
12326
- }
12351
+ const count = createElement("summary", {}, `_ ${childType}s`);
12352
+ setCountProperty(count);
12353
+ dropdown.appendChild(count);
12327
12354
 
12328
- get #selectedIndex() {
12329
- return this.#listItemElements.findIndex((item) => item.hasAttribute("aria-selected"))
12330
- }
12355
+ dropdown.appendChild(moreMenu);
12331
12356
 
12332
- get #selectedListItem() {
12333
- return this.#listItemElements[this.#selectedIndex]
12334
- }
12357
+ container.appendChild(minusButton);
12358
+ container.appendChild(dropdown);
12359
+ container.appendChild(plusButton);
12335
12360
 
12336
- #handleSelectedOption(event) {
12337
- event.preventDefault();
12338
- event.stopPropagation();
12339
- this.#optionWasSelected();
12340
- return true
12361
+ return container
12341
12362
  }
12342
12363
 
12343
- #optionWasSelected() {
12344
- this.#replaceTriggerWithSelectedItem();
12345
- this.#hidePopover();
12346
- this.#editorElement.focus();
12364
+ #createRowButtonsContainer() {
12365
+ return this.#createButtonsContainer(
12366
+ "row",
12367
+ (count) => { this.rowCount = count; },
12368
+ this.#createMoreMenuSection("row")
12369
+ )
12347
12370
  }
12348
12371
 
12349
- #replaceTriggerWithSelectedItem() {
12350
- const promptItem = this.source.promptItemFor(this.#selectedListItem);
12372
+ #createColumnButtonsContainer() {
12373
+ return this.#createButtonsContainer(
12374
+ "column",
12375
+ (count) => { this.columnCount = count; },
12376
+ this.#createMoreMenuSection("column")
12377
+ )
12378
+ }
12351
12379
 
12352
- if (!promptItem) { return }
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 });
12353
12386
 
12354
- const templates = Array.from(promptItem.querySelectorAll("template[type='editor']"));
12355
- const stringToReplace = `${this.trigger}${this.#editorContents.textBackUntil(this.trigger)}`;
12387
+ section.appendChild(addBeforeButton);
12388
+ section.appendChild(addAfterButton);
12389
+ section.appendChild(toggleStyleButton);
12390
+ section.appendChild(deleteButton);
12356
12391
 
12357
- if (this.hasAttribute("insert-editable-text")) {
12358
- this.#insertTemplatesAsEditableText(templates, stringToReplace);
12359
- } else {
12360
- this.#insertTemplatesAsAttachments(templates, stringToReplace, promptItem.getAttribute("sgid"));
12361
- }
12392
+ return section
12362
12393
  }
12363
12394
 
12364
- #insertTemplatesAsEditableText(templates, stringToReplace) {
12365
- this.#editor.update(() => {
12366
- const nodes = templates.flatMap(template => this.#buildEditableTextNodes(template));
12367
- this.#editorContents.replaceTextBackUntil(stringToReplace, nodes);
12368
- });
12369
- }
12395
+ #createDeleteTableButton() {
12396
+ const container = createElement("div", { className: "lexxy-table-control" });
12370
12397
 
12371
- #buildEditableTextNodes(template) {
12372
- return m$1(this.#editor, parseHtml(`${template.innerHTML}`))
12373
- }
12398
+ const deleteTableButton = this.#createButton("Delete this table?", { action: "delete", childType: "table" });
12399
+ deleteTableButton.classList.add("lexxy-table-control__button--delete-table");
12374
12400
 
12375
- #insertTemplatesAsAttachments(templates, stringToReplace, fallbackSgid = null) {
12376
- this.#editor.update(() => {
12377
- const attachmentNodes = this.#buildAttachmentNodes(templates, fallbackSgid);
12378
- const spacedAttachmentNodes = attachmentNodes.flatMap(node => [ node, this.#getSpacerTextNode() ]).slice(0, -1);
12379
- this.#editorContents.replaceTextBackUntil(stringToReplace, spacedAttachmentNodes);
12380
- });
12381
- }
12401
+ container.appendChild(deleteTableButton);
12382
12402
 
12383
- #buildAttachmentNodes(templates, fallbackSgid = null) {
12384
- return templates.map(
12385
- template => this.#buildAttachmentNode(
12386
- template.innerHTML,
12387
- template.getAttribute("content-type") || this.#defaultPromptContentType,
12388
- template.getAttribute("sgid") || fallbackSgid
12389
- ))
12390
- }
12403
+ this.deleteContainer = container;
12391
12404
 
12392
- #getSpacerTextNode() {
12393
- return sr(" ")
12405
+ return container
12394
12406
  }
12395
12407
 
12396
- get #defaultPromptContentType() {
12397
- const attachmentContentTypeNamespace = Lexxy.global.get("attachmentContentTypeNamespace");
12398
- return `application/vnd.${attachmentContentTypeNamespace}.${this.name}`
12399
- }
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;
12400
12420
 
12401
- #buildAttachmentNode(innerHtml, contentType, sgid) {
12402
- return new CustomActionTextAttachmentNode({ sgid, contentType, innerHtml })
12403
- }
12421
+ button.addEventListener("click", () => this.#executeTableCommand(command));
12404
12422
 
12405
- get #editorContents() {
12406
- return this.#editorElement.contents
12423
+ button.addEventListener("mouseover", () => this.#handleCommandButtonHover());
12424
+ button.addEventListener("focus", () => this.#handleCommandButtonHover());
12425
+ button.addEventListener("mouseout", () => this.#handleCommandButtonHover());
12426
+
12427
+ return button
12407
12428
  }
12408
12429
 
12409
- get #editorContentElement() {
12410
- return this.#editorElement.editorContentElement
12430
+ #registerKeyboardShortcuts() {
12431
+ this.unregisterKeyboardShortcuts = this.#editor.registerCommand(me$1, this.#handleAccessibilityShortcutKey, Bi);
12411
12432
  }
12412
12433
 
12413
- async #buildPopover() {
12414
- 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.
12415
- popoverContainer.classList.add("lexxy-prompt-menu");
12416
- popoverContainer.style.position = "absolute";
12417
- popoverContainer.setAttribute("nonce", getNonce());
12418
- popoverContainer.append(...await this.source.buildListItems());
12419
- popoverContainer.addEventListener("click", this.#handlePopoverClick);
12420
- this.#editorElement.appendChild(popoverContainer);
12421
- return popoverContainer
12434
+ #unregisterKeyboardShortcuts() {
12435
+ this.unregisterKeyboardShortcuts?.();
12436
+ this.unregisterKeyboardShortcuts = null;
12422
12437
  }
12423
12438
 
12424
- #handlePopoverClick = (event) => {
12425
- const listItem = event.target.closest(".lexxy-prompt-menu__item");
12426
- if (listItem) {
12427
- this.#selectOption(listItem);
12428
- 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();
12429
12443
  }
12430
12444
  }
12431
12445
 
12432
- #reconnect() {
12433
- this.disconnectedCallback();
12434
- this.connectedCallback();
12446
+ #handleToolsKeydown = (event) => {
12447
+ if (event.key === "Escape") {
12448
+ this.#handleEscapeKey();
12449
+ } else {
12450
+ handleRollingTabIndex(this.#tableToolsButtons, event);
12451
+ }
12435
12452
  }
12436
- }
12437
12453
 
12438
- customElements.define("lexxy-prompt", LexicalPromptElement);
12454
+ #handleEscapeKey() {
12455
+ const cell = this.tableController.currentCell;
12456
+ if (!cell) return
12439
12457
 
12440
- class CodeLanguagePicker extends HTMLElement {
12441
- connectedCallback() {
12442
- this.editorElement = this.closest("lexxy-editor");
12443
- this.editor = this.editorElement.editor;
12458
+ this.#editor.update(() => {
12459
+ cell.select();
12460
+ this.#editor.focus();
12461
+ });
12444
12462
 
12445
- this.#attachLanguagePicker();
12446
- this.#monitorForCodeBlockSelection();
12463
+ this.#update();
12447
12464
  }
12448
12465
 
12449
- #attachLanguagePicker() {
12450
- this.languagePickerElement = this.#createLanguagePicker();
12451
-
12452
- this.languagePickerElement.addEventListener("change", () => {
12453
- this.#updateCodeBlockLanguage(this.languagePickerElement.value);
12454
- });
12466
+ async #handleCommandButtonHover() {
12467
+ await nextFrame();
12455
12468
 
12456
- this.languagePickerElement.setAttribute("nonce", getNonce());
12457
- this.appendChild(this.languagePickerElement);
12458
- }
12469
+ this.#clearCellStyles();
12459
12470
 
12460
- #createLanguagePicker() {
12461
- 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
12462
12473
 
12463
- for (const [ value, label ] of Object.entries(this.#languages)) {
12464
- const option = document.createElement("option");
12465
- option.value = value;
12466
- option.textContent = label;
12467
- selectElement.appendChild(option);
12468
- }
12474
+ const command = {
12475
+ action: activeElement.dataset.action,
12476
+ childType: activeElement.dataset.childType,
12477
+ direction: activeElement.dataset.direction
12478
+ };
12469
12479
 
12470
- return selectElement
12471
- }
12480
+ let cellsToHighlight = null;
12472
12481
 
12473
- get #languages() {
12474
- 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
+ }
12475
12493
 
12476
- if (!languages.ruby) languages.ruby = "Ruby";
12477
- if (!languages.php) languages.php = "PHP";
12478
- if (!languages.go) languages.go = "Go";
12479
- if (!languages.bash) languages.bash = "Bash";
12480
- if (!languages.json) languages.json = "JSON";
12481
- if (!languages.diff) languages.diff = "Diff";
12494
+ if (!cellsToHighlight) return
12482
12495
 
12483
- const sortedEntries = Object.entries(languages)
12484
- .sort(([ , a ], [ , b ]) => a.localeCompare(b));
12496
+ cellsToHighlight.forEach(cell => {
12497
+ const cellElement = this.#editor.getElementByKey(cell.getKey());
12498
+ if (!cellElement) return
12485
12499
 
12486
- // Place the "plain" entry first, then the rest of language sorted alphabetically
12487
- const plainIndex = sortedEntries.findIndex(([ key ]) => key === "plain");
12488
- const plainEntry = sortedEntries.splice(plainIndex, 1)[0];
12489
- return Object.fromEntries([ plainEntry, ...sortedEntries ])
12500
+ cellElement.classList.toggle(theme.tableCellHighlight, true);
12501
+ Object.assign(cellElement.dataset, command);
12502
+ });
12490
12503
  }
12491
12504
 
12492
- #updateCodeBlockLanguage(language) {
12493
- this.editor.update(() => {
12494
- const codeNode = this.#getCurrentCodeNode();
12505
+ #monitorForTableSelection() {
12506
+ this.unregisterUpdateListener = this.#editor.registerUpdateListener(() => {
12507
+ this.tableController.updateSelectedTable();
12495
12508
 
12496
- if (codeNode) {
12497
- codeNode.setLanguage(language);
12509
+ const tableNode = this.tableController.currentTableNode;
12510
+ if (tableNode) {
12511
+ this.#show();
12512
+ } else {
12513
+ this.#hide();
12498
12514
  }
12499
12515
  });
12500
12516
  }
12501
12517
 
12502
- #monitorForCodeBlockSelection() {
12503
- this.editor.registerUpdateListener(() => {
12504
- this.editor.getEditorState().read(() => {
12505
- const codeNode = this.#getCurrentCodeNode();
12518
+ #executeTableCommand(command) {
12519
+ this.tableController.executeTableCommand(command);
12520
+ this.#update();
12521
+ }
12506
12522
 
12507
- if (codeNode) {
12508
- this.#codeNodeWasSelected(codeNode);
12509
- } else {
12510
- this.#hideLanguagePicker();
12511
- }
12512
- });
12513
- });
12523
+ #show() {
12524
+ this.style.display = "flex";
12525
+ this.#update();
12514
12526
  }
12515
12527
 
12516
- #getCurrentCodeNode() {
12517
- const selection = Lr();
12528
+ #hide() {
12529
+ this.style.display = "none";
12530
+ this.#clearCellStyles();
12531
+ }
12518
12532
 
12519
- if (!yr(selection)) {
12520
- return null
12521
- }
12533
+ #update() {
12534
+ this.#updateButtonsPosition();
12535
+ this.#updateRowColumnCount();
12536
+ this.#closeMoreMenu();
12537
+ this.#handleCommandButtonHover();
12538
+ }
12522
12539
 
12523
- const anchorNode = selection.anchor.getNode();
12524
- const parentNode = anchorNode.getParent();
12540
+ #closeMoreMenu() {
12541
+ this.querySelector("details[open]")?.removeAttribute("open");
12542
+ }
12525
12543
 
12526
- if (X$1(anchorNode)) {
12527
- return anchorNode
12528
- } else if (X$1(parentNode)) {
12529
- return parentNode
12530
- }
12544
+ #updateButtonsPosition() {
12545
+ const tableNode = this.tableController.currentTableNode;
12546
+ if (!tableNode) return
12531
12547
 
12532
- return null
12533
- }
12548
+ const tableElement = this.#editor.getElementByKey(tableNode.getKey());
12549
+ if (!tableElement) return
12534
12550
 
12535
- #codeNodeWasSelected(codeNode) {
12536
- const language = codeNode.getLanguage();
12551
+ const tableRect = tableElement.getBoundingClientRect();
12552
+ const editorRect = this.#editorElement.getBoundingClientRect();
12537
12553
 
12538
- this.#updateLanguagePickerWith(language);
12539
- this.#showLanguagePicker();
12540
- 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`;
12541
12558
  }
12542
12559
 
12543
- #updateLanguagePickerWith(language) {
12544
- if (this.languagePickerElement && language) {
12545
- const normalizedLanguage = dt$1(language);
12546
- this.languagePickerElement.value = normalizedLanguage;
12547
- }
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"}`;
12548
12572
  }
12549
12573
 
12550
- #positionLanguagePicker(codeNode) {
12551
- const codeElement = this.editor.getElementByKey(codeNode.getKey());
12552
- if (!codeElement) return
12574
+ #setTableCellFocus() {
12575
+ const cell = this.tableController.currentCell;
12576
+ if (!cell) return
12553
12577
 
12554
- const codeRect = codeElement.getBoundingClientRect();
12555
- const editorRect = this.editorElement.getBoundingClientRect();
12556
- const relativeTop = codeRect.top - editorRect.top;
12557
- const relativeRight = editorRect.right - codeRect.right;
12578
+ const cellElement = this.#editor.getElementByKey(cell.getKey());
12579
+ if (!cellElement) return
12558
12580
 
12559
- this.style.top = `${relativeTop}px`;
12560
- this.style.right = `${relativeRight}px`;
12581
+ cellElement.classList.add(theme.tableCellFocus);
12561
12582
  }
12562
12583
 
12563
- #showLanguagePicker() {
12564
- 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();
12565
12597
  }
12566
12598
 
12567
- #hideLanguagePicker() {
12568
- 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]
12569
12604
  }
12570
12605
  }
12571
12606
 
12572
- 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
+ }
12573
12622
 
12574
12623
  function highlightCode() {
12575
12624
  const elements = document.querySelectorAll("pre[data-language]");
@@ -12625,5 +12674,8 @@ class LexxyExtension {
12625
12674
 
12626
12675
  const configure = Lexxy.configure;
12627
12676
 
12677
+ // Pushing elements definition to after the current call stack to allow global configuration to take place first
12678
+ setTimeout(defineElements, 0);
12679
+
12628
12680
  export { ActionTextAttachmentNode, ActionTextAttachmentUploadNode, CustomActionTextAttachmentNode, LexxyExtension as Extension, HorizontalDividerNode, configure, highlightCode as highlightAll, highlightCode };
12629
12681
  //# sourceMappingURL=lexxy.js.map