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 +4 -4
- data/README.md +2 -2
- data/app/assets/javascript/lexxy.js +53 -21
- data/app/assets/javascript/lexxy.js.br +0 -0
- data/app/assets/javascript/lexxy.js.gz +0 -0
- data/app/assets/javascript/lexxy.js.map +1 -1
- data/app/assets/javascript/lexxy.min.js +1 -1
- data/app/assets/javascript/lexxy.min.js.br +0 -0
- data/app/assets/javascript/lexxy.min.js.gz +0 -0
- data/lib/lexxy/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 35be558d191256d60a92fd727768d719247399dfa0f017d54560a4a7eb775666
|
|
4
|
+
data.tar.gz: 3f568f51b50c2cf790aa0ae1803dcd6350e22c9a09a354f0048473df1b41e831
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
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
|
|
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
|
|
7555
|
-
|
|
7554
|
+
function setSanitizerConfig(allowedTags) {
|
|
7555
|
+
purify.clearConfig();
|
|
7556
|
+
purify.setConfig(buildConfig(allowedTags));
|
|
7556
7557
|
}
|
|
7557
7558
|
|
|
7558
|
-
|
|
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",
|
|
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
|
|
8928
|
-
|
|
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)
|
|
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?.
|
|
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
|
-
|
|
13990
|
-
|
|
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 (
|
|
13995
|
-
|
|
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
|