lexxy 0.9.6.beta → 0.9.8.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: 0b3e32d6616564bae023ac462dadcbc6a9b3ca526e152d1f0166233e64483548
4
- data.tar.gz: c8f19bbac5ae8129e4b70e769233005598005b04f9820e4f025dccaca67ced73
3
+ metadata.gz: 35be558d191256d60a92fd727768d719247399dfa0f017d54560a4a7eb775666
4
+ data.tar.gz: 3f568f51b50c2cf790aa0ae1803dcd6350e22c9a09a354f0048473df1b41e831
5
5
  SHA512:
6
- metadata.gz: 5f4862084d7dd764d555ab65995f005249071ab747314ee499150f2708b2ec09af785f0347fd3dc5a7039d61a9b64889f5300a47ea463171633bd865c521ce6b
7
- data.tar.gz: a1eb5afc29e8ef304c534bb75c9375d0f3dbc80aafbb55ab7981ff8de9043acbf9f07ac88569d46fe0824c6da41759d42330e300acf9a8083c1fdf2bed0f8cac
6
+ metadata.gz: bf76840177aee96461993510ebce333a632b8c8781a81ed973ee9231d29b63817e3a22e0f7be73c1175f1868e4d7d5bf1a23dd506f2fc1c3c2e0df0a7f360634
7
+ data.tar.gz: b87a831bc8e1d4ec5a558ea3df22a19ea0c9fda29d3e7dde772aaffb31202f1746810ad8b029f59310e4a6722ff4a1d017394a47accf208e247535ebd314d59e
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  A modern rich text editor for Rails.
4
4
 
5
5
  > [!IMPORTANT]
6
- > This is an early beta. It hasn't been battle-tested yet. Please try it out and report any issues you find.
6
+ > This is a beta. It hasn't been battle-tested yet. Please try it out and report any issues you find.
7
7
 
8
8
  **[Try it out!](https://basecamp.github.io/lexxy/try-it)**
9
9
 
@@ -26,7 +26,7 @@ Visit the **[documentation site](https://basecamp.github.io/lexxy)**.
26
26
 
27
27
  ## Roadmap
28
28
 
29
- This is an early beta. Here's what's coming next:
29
+ This is a beta. Here's what's coming next:
30
30
 
31
31
  - [x] Configurable editors in Action Text: Choose your editor like you choose your database.
32
32
  - [x] More editing features:
@@ -6247,7 +6247,7 @@ purify.addHook("uponSanitizeElement", (node, data) => {
6247
6247
  }
6248
6248
  });
6249
6249
 
6250
- function buildConfig(allowedElements) {
6250
+ function buildConfig(allowedElements ) {
6251
6251
  const tagAttributes = {};
6252
6252
 
6253
6253
  for (const element of allowedElements) {
@@ -7551,15 +7551,12 @@ var Lexxy = {
7551
7551
  }
7552
7552
  };
7553
7553
 
7554
- function sanitize(html, allowedElements) {
7555
- return purify.sanitize(html, buildConfig(allowedElements))
7554
+ function setSanitizerConfig(allowedTags) {
7555
+ purify.clearConfig();
7556
+ purify.setConfig(buildConfig(allowedTags));
7556
7557
  }
7557
7558
 
7558
- // Sanitize HTML for custom attachment content (mentions, cards, etc.).
7559
- // Uses DOMPurify defaults to strip XSS vectors (scripts, event handlers)
7560
- // while preserving the richer tag set that server-rendered attachment
7561
- // content legitimately uses (e.g. <span>, <div>, <img>).
7562
- function sanitizeAttachmentContent(html) {
7559
+ function sanitize(html) {
7563
7560
  return purify.sanitize(html)
7564
7561
  }
7565
7562
 
@@ -7655,7 +7652,7 @@ class CustomActionTextAttachmentNode extends Fi {
7655
7652
  createDOM() {
7656
7653
  const figure = createElement(this.tagName, { "content-type": this.contentType, "data-lexxy-decorator": true });
7657
7654
 
7658
- figure.insertAdjacentHTML("beforeend", sanitizeAttachmentContent(this.innerHtml));
7655
+ figure.insertAdjacentHTML("beforeend", sanitize(this.innerHtml));
7659
7656
 
7660
7657
  const deleteButton = createElement("lexxy-node-delete-button");
7661
7658
  figure.appendChild(deleteButton);
@@ -8924,14 +8921,24 @@ function normalizeFilteredText(string) {
8924
8921
  .normalize("NFD").replace(/[\u0300-\u036f]/g, "") // Remove diacritics
8925
8922
  }
8926
8923
 
8927
- function filterMatches(text, potentialMatch) {
8928
- return normalizeFilteredText(text).includes(normalizeFilteredText(potentialMatch))
8924
+ function filterMatchPosition(text, potentialMatch) {
8925
+ const normalizedText = normalizeFilteredText(text);
8926
+ const normalizedMatch = normalizeFilteredText(potentialMatch);
8927
+
8928
+ if (!normalizedMatch) return 0
8929
+
8930
+ const match = normalizedText.match(new RegExp(`(?:^|\\b)${escapeForRegExp(normalizedMatch)}`));
8931
+ return match ? match.index : -1
8929
8932
  }
8930
8933
 
8931
8934
  function upcaseFirst(string) {
8932
8935
  return string.charAt(0).toUpperCase() + string.slice(1)
8933
8936
  }
8934
8937
 
8938
+ function escapeForRegExp(string) {
8939
+ return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
8940
+ }
8941
+
8935
8942
  // Parses a value that may arrive as a boolean or as a string (e.g. from DOM
8936
8943
  // getAttribute) into a proper boolean. Ensures "false" doesn't evaluate as truthy.
8937
8944
  function parseBoolean(value) {
@@ -13185,7 +13192,7 @@ class LexicalEditorElement extends HTMLElement {
13185
13192
  get value() {
13186
13193
  if (!this.cachedValue) {
13187
13194
  this.editor?.getEditorState().read(() => {
13188
- this.cachedValue = sanitize(g(this.editor, null), this.#allowedElements);
13195
+ this.cachedValue = sanitize(g(this.editor, null));
13189
13196
  });
13190
13197
  }
13191
13198
 
@@ -13244,6 +13251,7 @@ class LexicalEditorElement extends HTMLElement {
13244
13251
  this.#registerFocusEvents();
13245
13252
  this.#attachDebugHooks();
13246
13253
  this.#attachToolbar();
13254
+ this.#configureSanitizer();
13247
13255
  this.#loadInitialValue();
13248
13256
  this.#resetBeforeTurboCaches();
13249
13257
  }
@@ -13520,6 +13528,10 @@ class LexicalEditorElement extends HTMLElement {
13520
13528
  }
13521
13529
  }
13522
13530
 
13531
+ #configureSanitizer() {
13532
+ setSanitizerConfig(this.#allowedElements);
13533
+ }
13534
+
13523
13535
  get #allowedElements() {
13524
13536
  return this.#importableTags.concat(this.extensions.allowedElements)
13525
13537
  }
@@ -13819,7 +13831,7 @@ class LinkDropdown extends ToolbarDropdown {
13819
13831
  get #selectedLinkUrl() {
13820
13832
  return this.editor.getEditorState().read(() => {
13821
13833
  const linkNode = this.editorElement.selection.nearestNodeOfType(M$5);
13822
- return linkNode?.getUrl() ?? null
13834
+ return linkNode?.getURL() ?? ""
13823
13835
  })
13824
13836
  }
13825
13837
  }
@@ -13983,21 +13995,41 @@ class LocalFilterSource extends BaseSource {
13983
13995
  }
13984
13996
 
13985
13997
  #buildListItemsFromPromptItems(promptItems, filter) {
13986
- const listItems = [];
13987
13998
  this.promptItemByListItem = new WeakMap();
13988
13999
 
13989
- for (const promptItem of promptItems) {
13990
- if (listItems.length >= MAX_RENDERED_SUGGESTIONS$1) break
14000
+ if (!filter) {
14001
+ return this.#buildAllListItems(promptItems)
14002
+ }
13991
14003
 
14004
+ const matches = [];
14005
+ for (const promptItem of promptItems) {
13992
14006
  const searchableText = promptItem.getAttribute("search");
13993
-
13994
- if (!filter || filterMatches(searchableText, filter)) {
13995
- const listItem = this.buildListItemElementFor(promptItem);
13996
- this.promptItemByListItem.set(listItem, promptItem);
13997
- listItems.push(listItem);
14007
+ const position = filterMatchPosition(searchableText, filter);
14008
+ if (position >= 0) {
14009
+ matches.push({ promptItem, position });
13998
14010
  }
13999
14011
  }
14000
14012
 
14013
+ matches.sort((a, b) => a.position - b.position);
14014
+
14015
+ const listItems = [];
14016
+ for (const { promptItem } of matches) {
14017
+ if (listItems.length >= MAX_RENDERED_SUGGESTIONS$1) break
14018
+ const listItem = this.buildListItemElementFor(promptItem);
14019
+ this.promptItemByListItem.set(listItem, promptItem);
14020
+ listItems.push(listItem);
14021
+ }
14022
+ return listItems
14023
+ }
14024
+
14025
+ #buildAllListItems(promptItems) {
14026
+ const listItems = [];
14027
+ for (const promptItem of promptItems) {
14028
+ if (listItems.length >= MAX_RENDERED_SUGGESTIONS$1) break
14029
+ const listItem = this.buildListItemElementFor(promptItem);
14030
+ this.promptItemByListItem.set(listItem, promptItem);
14031
+ listItems.push(listItem);
14032
+ }
14001
14033
  return listItems
14002
14034
  }
14003
14035
  }
Binary file
Binary file