lexxy 0.1.18.beta → 0.1.20.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d349d41ecbbcda9310a94ca35f54b0c888dd484a6edb7728a34c02a1576f0a36
4
- data.tar.gz: b806d34fb4fc44969d94f1d625fbb0edf2410ab4eb91c3d71abe66170bc3483b
3
+ metadata.gz: 28ddded3fff65c55a5e194a1ba8246411fad9193b32ab319cc5a6330ab999420
4
+ data.tar.gz: 98a6bf19a11687400a21baed5117d65de05f02e595f3d7684f1fb70f8c8ffeea
5
5
  SHA512:
6
- metadata.gz: 7062ff796823d7218ad93683b81bc215813a9c7844da2d57626da33021fb6c25a4049d8b7f534a28df2f210a7f8ae2f9f19324f542417743556e9955871ab606
7
- data.tar.gz: 38049c719a2bce471c97281f034040aaf26fd2c673b0839d9a36f44568405f5200bb2250be665ffba747db2f637f6711390881558ac199fcb0a566bf46d4a640
6
+ metadata.gz: 00025443273a087c88de7463ab211f85e1e887ee3e33958f4813101c370f6f5949d79e88ba1c3e4347f77d4a5aaebf0231215f2af86e3fdaab35ce230572927f
7
+ data.tar.gz: ebb4db2b79be6937eca5af5e77aa62c8efd41a8e5bb6af0576865dd2f415f6d8de26f2e8c476e60c5f648ef0ce37cd1333e2490efd5c8c4fc5b4f28367d0f870
@@ -5869,6 +5869,10 @@ class ActionTextAttachmentUploadNode extends ActionTextAttachmentNode {
5869
5869
  return new ActionTextAttachmentUploadNode({ ...node }, node.__key)
5870
5870
  }
5871
5871
 
5872
+ static importJSON(serializedNode) {
5873
+ return new ActionTextAttachmentUploadNode({ ...serializedNode })
5874
+ }
5875
+
5872
5876
  constructor({ file, uploadUrl, blobUrlTemplate, editor, progress }, key) {
5873
5877
  super({ contentType: file.type }, key);
5874
5878
  this.file = file;
@@ -5908,6 +5912,17 @@ class ActionTextAttachmentUploadNode extends ActionTextAttachmentNode {
5908
5912
  return { element: img }
5909
5913
  }
5910
5914
 
5915
+ exportJSON() {
5916
+ return {
5917
+ type: "action_text_attachment_upload",
5918
+ version: 1,
5919
+ progress: this.progress,
5920
+ uploadUrl: this.uploadUrl,
5921
+ blobUrlTemplate: this.blobUrlTemplate,
5922
+ ...super.exportJSON()
5923
+ }
5924
+ }
5925
+
5911
5926
  #createDOMForImage() {
5912
5927
  return createElement("img")
5913
5928
  }
@@ -6193,8 +6208,10 @@ class CommandDispatcher {
6193
6208
 
6194
6209
  dispatchInsertHorizontalDivider() {
6195
6210
  this.editor.update(() => {
6196
- this.contents.insertAtCursor(new HorizontalDividerNode());
6211
+ this.contents.insertAtCursorEnsuringLineBelow(new HorizontalDividerNode());
6197
6212
  });
6213
+
6214
+ this.editor.focus();
6198
6215
  }
6199
6216
 
6200
6217
  dispatchRotateHeadingFormat() {
@@ -6406,6 +6423,52 @@ class Selection {
6406
6423
  });
6407
6424
  }
6408
6425
 
6426
+ selectedNodeWithOffset() {
6427
+ const selection = Lr();
6428
+ if (!selection) return { node: null, offset: 0 }
6429
+
6430
+ if (yr(selection)) {
6431
+ return {
6432
+ node: selection.anchor.getNode(),
6433
+ offset: selection.anchor.offset
6434
+ }
6435
+ } else if (xr(selection)) {
6436
+ const [ node ] = selection.getNodes();
6437
+ return {
6438
+ node,
6439
+ offset: 0
6440
+ }
6441
+ }
6442
+
6443
+ return { node: null, offset: 0 }
6444
+ }
6445
+
6446
+ preservingSelection(fn) {
6447
+ let selectionState = null;
6448
+
6449
+ this.editor.getEditorState().read(() => {
6450
+ const selection = Lr();
6451
+ if (selection && yr(selection)) {
6452
+ selectionState = {
6453
+ anchor: { key: selection.anchor.key, offset: selection.anchor.offset },
6454
+ focus: { key: selection.focus.key, offset: selection.focus.offset }
6455
+ };
6456
+ }
6457
+ });
6458
+
6459
+ fn();
6460
+
6461
+ if (selectionState) {
6462
+ this.editor.update(() => {
6463
+ const selection = Lr();
6464
+ if (selection && yr(selection)) {
6465
+ selection.anchor.set(selectionState.anchor.key, selectionState.anchor.offset, "text");
6466
+ selection.focus.set(selectionState.focus.key, selectionState.focus.offset, "text");
6467
+ }
6468
+ });
6469
+ }
6470
+ }
6471
+
6409
6472
  get hasSelectedWordsInSingleLine() {
6410
6473
  const selection = Lr();
6411
6474
  if (!yr(selection)) return false
@@ -6774,22 +6837,24 @@ class Selection {
6774
6837
  const node = this.nodeAfterCursor;
6775
6838
  if (node instanceof ki) {
6776
6839
  this.#selectInLexical(node);
6840
+ return true
6777
6841
  } else {
6778
6842
  this.#contents.deleteSelectedNodes();
6779
6843
  }
6780
6844
 
6781
- return true
6845
+ return false
6782
6846
  }
6783
6847
 
6784
6848
  #deletePreviousOrNext() {
6785
6849
  const node = this.nodeBeforeCursor;
6786
6850
  if (node instanceof ki) {
6787
6851
  this.#selectInLexical(node);
6852
+ return true
6788
6853
  } else {
6789
6854
  this.#contents.deleteSelectedNodes();
6790
6855
  }
6791
6856
 
6792
- return true
6857
+ return false
6793
6858
  }
6794
6859
 
6795
6860
  #getValidSelectionRange() {
@@ -7361,6 +7426,11 @@ class Contents {
7361
7426
  });
7362
7427
  }
7363
7428
 
7429
+ insertAtCursorEnsuringLineBelow(node) {
7430
+ this.insertAtCursor(node);
7431
+ this.#insertLineBelowIfLastNode(node);
7432
+ }
7433
+
7364
7434
  insertNodeWrappingEachSelectedLine(newNodeFn) {
7365
7435
  this.editor.update(() => {
7366
7436
  const selection = Lr();
@@ -7645,6 +7715,17 @@ class Contents {
7645
7715
  return this.editorElement.selection
7646
7716
  }
7647
7717
 
7718
+ #insertLineBelowIfLastNode(node) {
7719
+ this.editor.update(() => {
7720
+ const nextSibling = node.getNextSibling();
7721
+ if (!nextSibling) {
7722
+ const newParagraph = Li();
7723
+ node.insertAfter(newParagraph);
7724
+ newParagraph.selectStart();
7725
+ }
7726
+ });
7727
+ }
7728
+
7648
7729
  #unwrap(node) {
7649
7730
  const children = node.getChildren();
7650
7731
 
@@ -7987,7 +8068,8 @@ class Contents {
7987
8068
  lastInsertedNode.insertAfter(textNodeAfter);
7988
8069
 
7989
8070
  this.#appendLineBreakIfNeeded(textNodeAfter.getParentOrThrow());
7990
- textNodeAfter.select(0, 0);
8071
+ const cursorOffset = textAfterCursor ? 0 : 1;
8072
+ textNodeAfter.select(cursorOffset, cursorOffset);
7991
8073
  }
7992
8074
 
7993
8075
  #insertReplacementNodes(startNode, replacementNodes) {
@@ -8849,6 +8931,14 @@ class LexicalPromptElement extends HTMLElement {
8849
8931
  return this.hasAttribute("supports-space-in-searches")
8850
8932
  }
8851
8933
 
8934
+ get open() {
8935
+ return this.popoverElement?.classList?.contains("lexxy-prompt-menu--visible")
8936
+ }
8937
+
8938
+ get closed() {
8939
+ return !this.open
8940
+ }
8941
+
8852
8942
  get #doesSpaceSelect() {
8853
8943
  return !this.supportsSpaceInSearches
8854
8944
  }
@@ -8869,20 +8959,14 @@ class LexicalPromptElement extends HTMLElement {
8869
8959
  #addTriggerListener() {
8870
8960
  const unregister = this.#editor.registerUpdateListener(() => {
8871
8961
  this.#editor.read(() => {
8872
- const selection = Lr();
8873
- if (!selection) return
8874
- let node;
8875
- if (yr(selection)) {
8876
- node = selection.anchor.getNode();
8877
- } else if (xr(selection)) {
8878
- [ node ] = selection.getNodes();
8879
- }
8962
+ const { node, offset } = this.#selection.selectedNodeWithOffset();
8963
+ if (!node) return
8880
8964
 
8881
- if (node && lr(node)) {
8882
- const text = node.getTextContent().trim();
8883
- const lastChar = [ ...text ].pop();
8965
+ if (lr(node) && offset > 0) {
8966
+ const fullText = node.getTextContent();
8967
+ const charBeforeCursor = fullText[offset - 1];
8884
8968
 
8885
- if (lastChar === this.trigger) {
8969
+ if (charBeforeCursor === this.trigger) {
8886
8970
  unregister();
8887
8971
  this.#showPopover();
8888
8972
  }
@@ -8891,6 +8975,38 @@ class LexicalPromptElement extends HTMLElement {
8891
8975
  });
8892
8976
  }
8893
8977
 
8978
+ #addCursorPositionListener() {
8979
+ this.cursorPositionListener = this.#editor.registerUpdateListener(() => {
8980
+ if (this.closed) return
8981
+
8982
+ this.#editor.read(() => {
8983
+ const { node, offset } = this.#selection.selectedNodeWithOffset();
8984
+ if (!node) return
8985
+
8986
+ if (lr(node) && offset > 0) {
8987
+ const fullText = node.getTextContent();
8988
+ const textBeforeCursor = fullText.slice(0, offset);
8989
+ const lastTriggerIndex = textBeforeCursor.lastIndexOf(this.trigger);
8990
+
8991
+ // If trigger is not found, or cursor is at or before the trigger position, hide popover
8992
+ if (lastTriggerIndex === -1 || offset <= lastTriggerIndex) {
8993
+ this.#hidePopover();
8994
+ }
8995
+ } else {
8996
+ // Cursor is not in a text node or at offset 0, hide popover
8997
+ this.#hidePopover();
8998
+ }
8999
+ });
9000
+ });
9001
+ }
9002
+
9003
+ #removeCursorPositionListener() {
9004
+ if (this.cursorPositionListener) {
9005
+ this.cursorPositionListener();
9006
+ this.cursorPositionListener = null;
9007
+ }
9008
+ }
9009
+
8894
9010
  get #editor() {
8895
9011
  return this.#editorElement.editor
8896
9012
  }
@@ -8914,6 +9030,7 @@ class LexicalPromptElement extends HTMLElement {
8914
9030
  this.#editorElement.addEventListener("lexxy:change", this.#filterOptions);
8915
9031
 
8916
9032
  this.#registerKeyListeners();
9033
+ this.#addCursorPositionListener();
8917
9034
  }
8918
9035
 
8919
9036
  #registerKeyListeners() {
@@ -8924,6 +9041,22 @@ class LexicalPromptElement extends HTMLElement {
8924
9041
  if (this.#doesSpaceSelect) {
8925
9042
  this.keyListeners.push(this.#editor.registerCommand(be$1, this.#handleSelectedOption.bind(this), Bi));
8926
9043
  }
9044
+
9045
+ // Register arrow keys with HIGH priority to prevent Lexical's selection handlers from running
9046
+ this.keyListeners.push(this.#editor.registerCommand(ke$1, this.#handleArrowUp.bind(this), Bi));
9047
+ this.keyListeners.push(this.#editor.registerCommand(Te$1, this.#handleArrowDown.bind(this), Bi));
9048
+ }
9049
+
9050
+ #handleArrowUp(event) {
9051
+ this.#moveSelectionUp();
9052
+ event.preventDefault();
9053
+ return true
9054
+ }
9055
+
9056
+ #handleArrowDown(event) {
9057
+ this.#moveSelectionDown();
9058
+ event.preventDefault();
9059
+ return true
8927
9060
  }
8928
9061
 
8929
9062
  #selectFirstOption() {
@@ -8941,8 +9074,14 @@ class LexicalPromptElement extends HTMLElement {
8941
9074
  #selectOption(listItem) {
8942
9075
  this.#clearSelection();
8943
9076
  listItem.toggleAttribute("aria-selected", true);
9077
+ listItem.scrollIntoView({ block: "nearest", behavior: "smooth" });
8944
9078
  listItem.focus();
8945
- this.#editorElement.focus();
9079
+
9080
+ // Preserve selection to prevent cursor jump
9081
+ this.#selection.preservingSelection(() => {
9082
+ this.#editorElement.focus();
9083
+ });
9084
+
8946
9085
  this.#editorContentElement.setAttribute("aria-controls", this.popoverElement.id);
8947
9086
  this.#editorContentElement.setAttribute("aria-activedescendant", listItem.id);
8948
9087
  this.#editorContentElement.setAttribute("aria-haspopup", "listbox");
@@ -8991,6 +9130,7 @@ class LexicalPromptElement extends HTMLElement {
8991
9130
  this.#editorElement.removeEventListener("keydown", this.#handleKeydownOnPopover);
8992
9131
 
8993
9132
  this.#unregisterKeyListeners();
9133
+ this.#removeCursorPositionListener();
8994
9134
 
8995
9135
  await nextFrame();
8996
9136
  this.#addTriggerListener();
@@ -9009,6 +9149,7 @@ class LexicalPromptElement extends HTMLElement {
9009
9149
 
9010
9150
  if (this.#editorContents.containsTextBackUntil(this.trigger)) {
9011
9151
  await this.#showFilteredOptions();
9152
+ await nextFrame();
9012
9153
  this.#positionPopover();
9013
9154
  } else {
9014
9155
  this.#hidePopover();
@@ -9049,15 +9190,8 @@ class LexicalPromptElement extends HTMLElement {
9049
9190
  this.#hidePopover();
9050
9191
  this.#editorElement.focus();
9051
9192
  event.stopPropagation();
9052
- } else if (event.key === "ArrowDown") {
9053
- this.#moveSelectionDown();
9054
- event.preventDefault();
9055
- event.stopPropagation();
9056
- } else if (event.key === "ArrowUp") {
9057
- this.#moveSelectionUp();
9058
- event.preventDefault();
9059
- event.stopPropagation();
9060
9193
  }
9194
+ // Arrow keys are now handled via Lexical commands with HIGH priority
9061
9195
  }
9062
9196
 
9063
9197
  #moveSelectionDown() {
Binary file
Binary file