@37signals/lexxy 0.7.0-beta → 0.7.2-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.
- package/dist/lexxy.esm.js +434 -231
- package/dist/lexxy_helpers.esm.js +75 -0
- package/dist/stylesheets/lexxy-content.css +0 -8
- package/dist/stylesheets/lexxy-editor.css +39 -21
- package/package.json +5 -2
package/dist/lexxy.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import 'prismjs';
|
|
2
2
|
import 'prismjs/components/prism-clike';
|
|
3
3
|
import 'prismjs/components/prism-markup';
|
|
4
4
|
import 'prismjs/components/prism-markup-templating';
|
|
@@ -10,12 +10,15 @@ import 'prismjs/components/prism-json';
|
|
|
10
10
|
import 'prismjs/components/prism-diff';
|
|
11
11
|
import DOMPurify from 'dompurify';
|
|
12
12
|
import { getStyleObjectFromCSS, getCSSFromStyleObject, $getSelectionStyleValueForProperty, $patchStyleText } from '@lexical/selection';
|
|
13
|
-
import { $isTextNode, TextNode, $isRangeSelection, SKIP_DOM_SELECTION_TAG, $getSelection, DecoratorNode, $getNodeByKey, HISTORY_MERGE_TAG, FORMAT_TEXT_COMMAND, $createTextNode, $isRootOrShadowRoot, UNDO_COMMAND, REDO_COMMAND, PASTE_COMMAND, COMMAND_PRIORITY_LOW, KEY_TAB_COMMAND, COMMAND_PRIORITY_NORMAL, OUTDENT_CONTENT_COMMAND, INDENT_CONTENT_COMMAND, $isNodeSelection, $getRoot, $isLineBreakNode, $isElementNode, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ARROW_UP_COMMAND, KEY_ARROW_DOWN_COMMAND, KEY_DELETE_COMMAND, KEY_BACKSPACE_COMMAND, SELECTION_CHANGE_COMMAND, $createNodeSelection, $setSelection, $createParagraphNode, KEY_ENTER_COMMAND, COMMAND_PRIORITY_HIGH, $isParagraphNode, $insertNodes, $createLineBreakNode, CLEAR_HISTORY_COMMAND, $addUpdateTag,
|
|
13
|
+
import { $isTextNode, TextNode, $isRangeSelection, SKIP_DOM_SELECTION_TAG, $getSelection, DecoratorNode, $getNodeByKey, HISTORY_MERGE_TAG, FORMAT_TEXT_COMMAND, $createTextNode, $isRootOrShadowRoot, UNDO_COMMAND, REDO_COMMAND, PASTE_COMMAND, COMMAND_PRIORITY_LOW, KEY_TAB_COMMAND, COMMAND_PRIORITY_NORMAL, OUTDENT_CONTENT_COMMAND, INDENT_CONTENT_COMMAND, $isNodeSelection, $getRoot, $isLineBreakNode, $isElementNode, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ARROW_UP_COMMAND, KEY_ARROW_DOWN_COMMAND, KEY_DELETE_COMMAND, KEY_BACKSPACE_COMMAND, SELECTION_CHANGE_COMMAND, $createNodeSelection, $setSelection, $createParagraphNode, KEY_ENTER_COMMAND, COMMAND_PRIORITY_HIGH, $isParagraphNode, $insertNodes, $createLineBreakNode, createCommand, createState, defineExtension, $setState, $getState, $hasUpdateTag, PASTE_TAG, CLEAR_HISTORY_COMMAND, $addUpdateTag, BLUR_COMMAND, FOCUS_COMMAND, KEY_DOWN_COMMAND, KEY_SPACE_COMMAND } from 'lexical';
|
|
14
14
|
import { $isListNode, $isListItemNode, INSERT_UNORDERED_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, ListNode, $getListDepth, $createListNode, ListItemNode, registerList } from '@lexical/list';
|
|
15
|
-
import { $isQuoteNode, $isHeadingNode, $createQuoteNode, $createHeadingNode, QuoteNode, HeadingNode, registerRichText } from '@lexical/rich-text';
|
|
15
|
+
import { $isQuoteNode, $isHeadingNode, $createQuoteNode, $createHeadingNode, RichTextExtension, QuoteNode, HeadingNode, registerRichText } from '@lexical/rich-text';
|
|
16
16
|
import { $isCodeNode, CodeNode, normalizeCodeLang, CodeHighlightNode, registerCodeHighlighting, CODE_LANGUAGE_FRIENDLY_NAME_MAP } from '@lexical/code';
|
|
17
17
|
import { $isLinkNode, $createAutoLinkNode, $toggleLink, $createLinkNode, LinkNode, AutoLinkNode } from '@lexical/link';
|
|
18
18
|
import { $getTableCellNodeFromLexicalNode, TableNode, INSERT_TABLE_COMMAND, $insertTableRowAtSelection, $insertTableColumnAtSelection, $deleteTableRowAtSelection, $deleteTableColumnAtSelection, $findTableNode, TableCellNode, TableRowNode, registerTablePlugin, registerTableSelectionObserver, setScrollableTablesActive, $getTableRowIndexFromTableCellNode, $getTableColumnIndexFromTableCellNode, $getElementForTableNode, $isTableCellNode, TableCellHeaderStates } from '@lexical/table';
|
|
19
|
+
import { createElement, createAttachmentFigure, isPreviewableImage, dispatchCustomEvent, parseHtml, dispatch, generateDomId } from './lexxy_helpers.esm.js';
|
|
20
|
+
export { highlightCode as highlightAll, highlightCode } from './lexxy_helpers.esm.js';
|
|
21
|
+
import { buildEditorFromExtensions } from '@lexical/extension';
|
|
19
22
|
import { registerPlainText } from '@lexical/plain-text';
|
|
20
23
|
import { $generateNodesFromDOM, $generateHtmlFromNodes } from '@lexical/html';
|
|
21
24
|
import { registerMarkdownShortcuts, TRANSFORMERS } from '@lexical/markdown';
|
|
@@ -61,8 +64,15 @@ class Configuration {
|
|
|
61
64
|
}
|
|
62
65
|
}
|
|
63
66
|
|
|
67
|
+
function range(from, to) {
|
|
68
|
+
return [ ...Array(1 + to - from).keys() ].map(i => i + from)
|
|
69
|
+
}
|
|
70
|
+
|
|
64
71
|
const global = new Configuration({
|
|
65
|
-
attachmentTagName: "action-text-attachment"
|
|
72
|
+
attachmentTagName: "action-text-attachment",
|
|
73
|
+
attachmentContentTypeNamespace: "actiontext",
|
|
74
|
+
authenticatedUploads: false,
|
|
75
|
+
extensions: []
|
|
66
76
|
});
|
|
67
77
|
|
|
68
78
|
const presets = new Configuration({
|
|
@@ -72,6 +82,16 @@ const presets = new Configuration({
|
|
|
72
82
|
multiLine: true,
|
|
73
83
|
richText: true,
|
|
74
84
|
toolbar: true,
|
|
85
|
+
highlight: {
|
|
86
|
+
buttons: {
|
|
87
|
+
color: range(1, 9).map(n => `var(--highlight-${n})`),
|
|
88
|
+
"background-color": range(1, 9).map(n => `var(--highlight-bg-${n})`),
|
|
89
|
+
},
|
|
90
|
+
permit: {
|
|
91
|
+
color: [],
|
|
92
|
+
"background-color": []
|
|
93
|
+
}
|
|
94
|
+
}
|
|
75
95
|
}
|
|
76
96
|
});
|
|
77
97
|
|
|
@@ -166,15 +186,21 @@ function isPrintableCharacter(event) {
|
|
|
166
186
|
return event.key.length === 1
|
|
167
187
|
}
|
|
168
188
|
|
|
169
|
-
function extendTextNodeConversion(conversionName,
|
|
189
|
+
function extendTextNodeConversion(conversionName, ...callbacks) {
|
|
170
190
|
return extendConversion(TextNode, conversionName, (conversionOutput, element) => ({
|
|
171
191
|
...conversionOutput,
|
|
172
192
|
forChild: (lexicalNode, parentNode) => {
|
|
173
193
|
const originalForChild = conversionOutput?.forChild ?? (x => x);
|
|
174
194
|
let childNode = originalForChild(lexicalNode, parentNode);
|
|
175
195
|
|
|
176
|
-
|
|
177
|
-
|
|
196
|
+
|
|
197
|
+
if ($isTextNode(childNode)) {
|
|
198
|
+
childNode = callbacks.reduce(
|
|
199
|
+
(childNode, callback) => callback(childNode, element) ?? childNode,
|
|
200
|
+
childNode
|
|
201
|
+
);
|
|
202
|
+
return childNode
|
|
203
|
+
}
|
|
178
204
|
}
|
|
179
205
|
}))
|
|
180
206
|
}
|
|
@@ -206,6 +232,58 @@ function hasHighlightStyles(cssOrStyles) {
|
|
|
206
232
|
return !!(styles.color || styles["background-color"])
|
|
207
233
|
}
|
|
208
234
|
|
|
235
|
+
class StyleCanonicalizer {
|
|
236
|
+
constructor(property, allowedValues= []) {
|
|
237
|
+
this._property = property;
|
|
238
|
+
this._allowedValues = allowedValues;
|
|
239
|
+
this._canonicalValues = this.#allowedValuesIdentityObject;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
applyCanonicalization(css) {
|
|
243
|
+
const styles = { ...getStyleObjectFromCSS(css) };
|
|
244
|
+
|
|
245
|
+
styles[this._property] = this.getCanonicalAllowedValue(styles[this._property]);
|
|
246
|
+
if (!styles[this._property]) {
|
|
247
|
+
delete styles[this._property];
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return getCSSFromStyleObject(styles)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
getCanonicalAllowedValue(value) {
|
|
254
|
+
return this._canonicalValues[value] ||= this.#resolveCannonicalValue(value)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Private
|
|
258
|
+
|
|
259
|
+
get #allowedValuesIdentityObject() {
|
|
260
|
+
return this._allowedValues.reduce((object, value) => ({ ...object, [value]: value }), {})
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
#resolveCannonicalValue(value) {
|
|
264
|
+
let index = this.#computedAllowedValues.indexOf(value);
|
|
265
|
+
index ||= this.#computedAllowedValues.indexOf(getComputedStyleForProperty(this._property, value));
|
|
266
|
+
return index === -1 ? null : this._allowedValues[index]
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
get #computedAllowedValues() {
|
|
270
|
+
return this._computedAllowedValues ||= this._allowedValues.map(
|
|
271
|
+
value => getComputedStyleForProperty(this._property, value)
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function getComputedStyleForProperty(property, value) {
|
|
277
|
+
const style = `${property}: ${value};`;
|
|
278
|
+
|
|
279
|
+
// the element has to be attached to the DOM have computed styles
|
|
280
|
+
const element = document.body.appendChild(createElement("span", { style: "display: none;" + style }));
|
|
281
|
+
const computedStyle = window.getComputedStyle(element).getPropertyValue(property);
|
|
282
|
+
element.remove();
|
|
283
|
+
|
|
284
|
+
return computedStyle
|
|
285
|
+
}
|
|
286
|
+
|
|
209
287
|
function handleRollingTabIndex(elements, event) {
|
|
210
288
|
const previousActiveElement = document.activeElement;
|
|
211
289
|
|
|
@@ -629,8 +707,8 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
629
707
|
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M7.65422 0.711575C7.1856 0.242951 6.42579 0.242951 5.95717 0.711575C5.48853 1.18021 5.48853 1.94 5.95717 2.40864L8.70864 5.16011L2.85422 11.0145C1.44834 12.4204 1.44833 14.6998 2.85422 16.1057L7.86011 21.1115C9.26599 22.5174 11.5454 22.5174 12.9513 21.1115L19.6542 14.4087C20.1228 13.94 20.1228 13.1802 19.6542 12.7115L11.8544 4.91171L11.2542 4.31158L7.65422 0.711575ZM4.55127 12.7115L10.4057 6.85716L17.1087 13.56H4.19981C4.19981 13.253 4.31696 12.9459 4.55127 12.7115ZM23.6057 20.76C23.6057 22.0856 22.5311 23.16 21.2057 23.16C19.8802 23.16 18.8057 22.0856 18.8057 20.76C18.8057 19.5408 19.8212 18.5339 20.918 17.4462C21.0135 17.3516 21.1096 17.2563 21.2057 17.16C21.3018 17.2563 21.398 17.3516 21.4935 17.4462C22.5903 18.5339 23.6057 19.5408 23.6057 20.76Z"/></svg>
|
|
630
708
|
</summary>
|
|
631
709
|
<lexxy-highlight-dropdown class="lexxy-editor__toolbar-dropdown-content">
|
|
632
|
-
<div data-button-group="color"
|
|
633
|
-
<div data-button-group="background-color"
|
|
710
|
+
<div data-button-group="color"></div>
|
|
711
|
+
<div data-button-group="background-color"></div>
|
|
634
712
|
<button data-command="removeHighlight" class="lexxy-editor__toolbar-dropdown-reset">Remove all coloring</button>
|
|
635
713
|
</lexxy-highlight-dropdown>
|
|
636
714
|
</details>
|
|
@@ -773,59 +851,6 @@ var theme = {
|
|
|
773
851
|
}
|
|
774
852
|
};
|
|
775
853
|
|
|
776
|
-
function createElement(name, properties, content = "") {
|
|
777
|
-
const element = document.createElement(name);
|
|
778
|
-
for (const [ key, value ] of Object.entries(properties || {})) {
|
|
779
|
-
if (key in element) {
|
|
780
|
-
element[key] = value;
|
|
781
|
-
} else if (value !== null && value !== undefined) {
|
|
782
|
-
element.setAttribute(key, value);
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
if (content) {
|
|
786
|
-
element.innerHTML = content;
|
|
787
|
-
}
|
|
788
|
-
return element
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
function parseHtml(html) {
|
|
792
|
-
const parser = new DOMParser();
|
|
793
|
-
return parser.parseFromString(html, "text/html")
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
function createAttachmentFigure(contentType, isPreviewable, fileName) {
|
|
797
|
-
const extension = fileName ? fileName.split(".").pop().toLowerCase() : "unknown";
|
|
798
|
-
return createElement("figure", {
|
|
799
|
-
className: `attachment attachment--${isPreviewable ? "preview" : "file"} attachment--${extension}`,
|
|
800
|
-
"data-content-type": contentType
|
|
801
|
-
})
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
function isPreviewableImage(contentType) {
|
|
805
|
-
return contentType.startsWith("image/") && !contentType.includes("svg")
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
function dispatchCustomEvent(element, name, detail) {
|
|
809
|
-
const event = new CustomEvent(name, {
|
|
810
|
-
detail: detail,
|
|
811
|
-
bubbles: true,
|
|
812
|
-
});
|
|
813
|
-
element.dispatchEvent(event);
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
function sanitize(html) {
|
|
817
|
-
return DOMPurify.sanitize(html, buildConfig())
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
function dispatch(element, eventName, detail = null, cancelable = false) {
|
|
821
|
-
return element.dispatchEvent(new CustomEvent(eventName, { bubbles: true, detail, cancelable }))
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
function generateDomId(prefix) {
|
|
825
|
-
const randomPart = Math.random().toString(36).slice(2, 10);
|
|
826
|
-
return `${prefix}-${randomPart}`
|
|
827
|
-
}
|
|
828
|
-
|
|
829
854
|
function bytesToHumanSize(bytes) {
|
|
830
855
|
if (bytes === 0) return "0 B"
|
|
831
856
|
const sizes = [ "B", "KB", "MB", "GB", "TB", "PB" ];
|
|
@@ -849,9 +874,9 @@ class ActionTextAttachmentNode extends DecoratorNode {
|
|
|
849
874
|
|
|
850
875
|
static importDOM() {
|
|
851
876
|
return {
|
|
852
|
-
[
|
|
877
|
+
[this.TAG_NAME]: () => {
|
|
853
878
|
return {
|
|
854
|
-
conversion: () => ({
|
|
879
|
+
conversion: (attachment) => ({
|
|
855
880
|
node: new ActionTextAttachmentNode({
|
|
856
881
|
sgid: attachment.getAttribute("sgid"),
|
|
857
882
|
src: attachment.getAttribute("url"),
|
|
@@ -864,13 +889,12 @@ class ActionTextAttachmentNode extends DecoratorNode {
|
|
|
864
889
|
width: attachment.getAttribute("width"),
|
|
865
890
|
height: attachment.getAttribute("height")
|
|
866
891
|
})
|
|
867
|
-
}),
|
|
868
|
-
priority: 1
|
|
892
|
+
}), priority: 1
|
|
869
893
|
}
|
|
870
894
|
},
|
|
871
|
-
"img": (
|
|
895
|
+
"img": () => {
|
|
872
896
|
return {
|
|
873
|
-
conversion: () => ({
|
|
897
|
+
conversion: (img) => ({
|
|
874
898
|
node: new ActionTextAttachmentNode({
|
|
875
899
|
src: img.getAttribute("src"),
|
|
876
900
|
caption: img.getAttribute("alt") || "",
|
|
@@ -878,32 +902,37 @@ class ActionTextAttachmentNode extends DecoratorNode {
|
|
|
878
902
|
width: img.getAttribute("width"),
|
|
879
903
|
height: img.getAttribute("height")
|
|
880
904
|
})
|
|
881
|
-
}),
|
|
882
|
-
priority: 1
|
|
905
|
+
}), priority: 1
|
|
883
906
|
}
|
|
884
907
|
},
|
|
885
|
-
"video": (
|
|
886
|
-
const videoSource = video.getAttribute("src") || video.querySelector("source")?.src;
|
|
887
|
-
const fileName = videoSource?.split("/")?.pop();
|
|
888
|
-
const contentType = video.querySelector("source")?.getAttribute("content-type") || "video/*";
|
|
889
|
-
|
|
908
|
+
"video": () => {
|
|
890
909
|
return {
|
|
891
|
-
conversion: () =>
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
910
|
+
conversion: (video) => {
|
|
911
|
+
const videoSource = video.getAttribute("src") || video.querySelector("source")?.src;
|
|
912
|
+
const fileName = videoSource?.split("/")?.pop();
|
|
913
|
+
const contentType = video.querySelector("source")?.getAttribute("content-type") || "video/*";
|
|
914
|
+
|
|
915
|
+
return {
|
|
916
|
+
node: new ActionTextAttachmentNode({
|
|
917
|
+
src: videoSource,
|
|
918
|
+
fileName: fileName,
|
|
919
|
+
contentType: contentType
|
|
920
|
+
})
|
|
921
|
+
}
|
|
922
|
+
}, priority: 1
|
|
899
923
|
}
|
|
900
924
|
}
|
|
901
925
|
}
|
|
902
926
|
}
|
|
903
927
|
|
|
904
|
-
|
|
928
|
+
static get TAG_NAME() {
|
|
929
|
+
return Lexxy.global.get("attachmentTagName")
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
constructor({ tagName, sgid, src, previewable, altText, caption, contentType, fileName, fileSize, width, height }, key) {
|
|
905
933
|
super(key);
|
|
906
934
|
|
|
935
|
+
this.tagName = tagName || ActionTextAttachmentNode.TAG_NAME;
|
|
907
936
|
this.sgid = sgid;
|
|
908
937
|
this.src = src;
|
|
909
938
|
this.previewable = previewable;
|
|
@@ -919,7 +948,7 @@ class ActionTextAttachmentNode extends DecoratorNode {
|
|
|
919
948
|
createDOM() {
|
|
920
949
|
const figure = this.createAttachmentFigure();
|
|
921
950
|
|
|
922
|
-
figure.addEventListener("click", (
|
|
951
|
+
figure.addEventListener("click", () => {
|
|
923
952
|
this.#select(figure);
|
|
924
953
|
});
|
|
925
954
|
|
|
@@ -939,7 +968,7 @@ class ActionTextAttachmentNode extends DecoratorNode {
|
|
|
939
968
|
}
|
|
940
969
|
|
|
941
970
|
getTextContent() {
|
|
942
|
-
return `[${
|
|
971
|
+
return `[${this.caption || this.fileName}]\n\n`
|
|
943
972
|
}
|
|
944
973
|
|
|
945
974
|
isInline() {
|
|
@@ -947,7 +976,7 @@ class ActionTextAttachmentNode extends DecoratorNode {
|
|
|
947
976
|
}
|
|
948
977
|
|
|
949
978
|
exportDOM() {
|
|
950
|
-
const attachment = createElement(
|
|
979
|
+
const attachment = createElement(this.tagName, {
|
|
951
980
|
sgid: this.sgid,
|
|
952
981
|
previewable: this.previewable || null,
|
|
953
982
|
url: this.src,
|
|
@@ -968,6 +997,7 @@ class ActionTextAttachmentNode extends DecoratorNode {
|
|
|
968
997
|
return {
|
|
969
998
|
type: "action_text_attachment",
|
|
970
999
|
version: 1,
|
|
1000
|
+
tagName: this.tagName,
|
|
971
1001
|
sgid: this.sgid,
|
|
972
1002
|
src: this.src,
|
|
973
1003
|
previewable: this.previewable,
|
|
@@ -1105,8 +1135,9 @@ class ActionTextAttachmentUploadNode extends ActionTextAttachmentNode {
|
|
|
1105
1135
|
return null
|
|
1106
1136
|
}
|
|
1107
1137
|
|
|
1108
|
-
constructor(
|
|
1109
|
-
|
|
1138
|
+
constructor(node, key) {
|
|
1139
|
+
const { file, uploadUrl, blobUrlTemplate, editor, progress } = node;
|
|
1140
|
+
super({ ...node, contentType: file.type }, key);
|
|
1110
1141
|
this.file = file;
|
|
1111
1142
|
this.uploadUrl = uploadUrl;
|
|
1112
1143
|
this.blobUrlTemplate = blobUrlTemplate;
|
|
@@ -1191,11 +1222,17 @@ class ActionTextAttachmentUploadNode extends ActionTextAttachmentNode {
|
|
|
1191
1222
|
|
|
1192
1223
|
async #startUpload(progressBar, figure) {
|
|
1193
1224
|
const { DirectUpload } = await import('@rails/activestorage');
|
|
1225
|
+
const shouldAuthenticateUploads = Lexxy.global.get("authenticatedUploads");
|
|
1194
1226
|
|
|
1195
1227
|
const upload = new DirectUpload(this.file, this.uploadUrl, this);
|
|
1196
1228
|
|
|
1197
1229
|
upload.delegate = {
|
|
1230
|
+
directUploadWillCreateBlobWithXHR: (request) => {
|
|
1231
|
+
if (shouldAuthenticateUploads) request.withCredentials = true;
|
|
1232
|
+
},
|
|
1198
1233
|
directUploadWillStoreFileWithXHR: (request) => {
|
|
1234
|
+
if (shouldAuthenticateUploads) request.withCredentials = true;
|
|
1235
|
+
|
|
1199
1236
|
request.upload.addEventListener("progress", (event) => {
|
|
1200
1237
|
this.editor.update(() => {
|
|
1201
1238
|
progressBar.value = Math.round(event.loaded / event.total * 100);
|
|
@@ -1226,11 +1263,12 @@ class ActionTextAttachmentUploadNode extends ActionTextAttachmentNode {
|
|
|
1226
1263
|
const image = figure.querySelector("img");
|
|
1227
1264
|
|
|
1228
1265
|
const src = this.blobUrlTemplate
|
|
1229
|
-
|
|
1230
|
-
|
|
1266
|
+
.replace(":signed_id", blob.signed_id)
|
|
1267
|
+
.replace(":filename", encodeURIComponent(blob.filename));
|
|
1231
1268
|
const latest = $getNodeByKey(this.getKey());
|
|
1232
1269
|
if (latest) {
|
|
1233
1270
|
latest.replace(new ActionTextAttachmentNode({
|
|
1271
|
+
tagName: this.tagName,
|
|
1234
1272
|
sgid: blob.attachable_sgid,
|
|
1235
1273
|
src: blob.previewable ? blob.url : src,
|
|
1236
1274
|
altText: blob.filename,
|
|
@@ -1769,7 +1807,14 @@ class Selection {
|
|
|
1769
1807
|
|
|
1770
1808
|
placeCursorAtTheEnd() {
|
|
1771
1809
|
this.editor.update(() => {
|
|
1772
|
-
$getRoot()
|
|
1810
|
+
const root = $getRoot();
|
|
1811
|
+
const lastDescendant = root.getLastDescendant();
|
|
1812
|
+
|
|
1813
|
+
if (lastDescendant && $isTextNode(lastDescendant)) {
|
|
1814
|
+
lastDescendant.selectEnd();
|
|
1815
|
+
} else {
|
|
1816
|
+
root.selectEnd();
|
|
1817
|
+
}
|
|
1773
1818
|
});
|
|
1774
1819
|
}
|
|
1775
1820
|
|
|
@@ -2384,6 +2429,10 @@ class Selection {
|
|
|
2384
2429
|
}
|
|
2385
2430
|
}
|
|
2386
2431
|
|
|
2432
|
+
function sanitize(html) {
|
|
2433
|
+
return DOMPurify.sanitize(html, buildConfig())
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2387
2436
|
// Prevent the hardcoded background color
|
|
2388
2437
|
// A background color value is set by Lexical if background is null:
|
|
2389
2438
|
// https://github.com/facebook/lexical/blob/5bbbe849bd229e1db0e7b536e6a919520ada7bb2/packages/lexical-table/src/LexicalTableCellNode.ts#L187
|
|
@@ -2474,15 +2523,15 @@ class CustomActionTextAttachmentNode extends DecoratorNode {
|
|
|
2474
2523
|
}
|
|
2475
2524
|
|
|
2476
2525
|
static importDOM() {
|
|
2526
|
+
|
|
2477
2527
|
return {
|
|
2478
|
-
[
|
|
2479
|
-
|
|
2480
|
-
if (!attachment.getAttribute("content")) {
|
|
2528
|
+
[this.TAG_NAME]: (element) => {
|
|
2529
|
+
if (!element.getAttribute("content")) {
|
|
2481
2530
|
return null
|
|
2482
2531
|
}
|
|
2483
2532
|
|
|
2484
2533
|
return {
|
|
2485
|
-
conversion: () => {
|
|
2534
|
+
conversion: (attachment) => {
|
|
2486
2535
|
// Preserve initial space if present since Lexical removes it
|
|
2487
2536
|
const nodes = [];
|
|
2488
2537
|
const previousSibling = attachment.previousSibling;
|
|
@@ -2492,7 +2541,7 @@ class CustomActionTextAttachmentNode extends DecoratorNode {
|
|
|
2492
2541
|
|
|
2493
2542
|
nodes.push(new CustomActionTextAttachmentNode({
|
|
2494
2543
|
sgid: attachment.getAttribute("sgid"),
|
|
2495
|
-
innerHtml: JSON.parse(content),
|
|
2544
|
+
innerHtml: JSON.parse(attachment.getAttribute("content")),
|
|
2496
2545
|
contentType: attachment.getAttribute("content-type")
|
|
2497
2546
|
}));
|
|
2498
2547
|
|
|
@@ -2506,16 +2555,23 @@ class CustomActionTextAttachmentNode extends DecoratorNode {
|
|
|
2506
2555
|
}
|
|
2507
2556
|
}
|
|
2508
2557
|
|
|
2509
|
-
|
|
2558
|
+
static get TAG_NAME() {
|
|
2559
|
+
return Lexxy.global.get("attachmentTagName")
|
|
2560
|
+
}
|
|
2561
|
+
|
|
2562
|
+
constructor({ tagName, sgid, contentType, innerHtml }, key) {
|
|
2510
2563
|
super(key);
|
|
2511
2564
|
|
|
2565
|
+
const contentTypeNamespace = Lexxy.global.get("attachmentContentTypeNamespace");
|
|
2566
|
+
|
|
2567
|
+
this.tagName = tagName || CustomActionTextAttachmentNode.TAG_NAME;
|
|
2512
2568
|
this.sgid = sgid;
|
|
2513
|
-
this.contentType = contentType ||
|
|
2569
|
+
this.contentType = contentType || `application/vnd.${contentTypeNamespace}.unknown`;
|
|
2514
2570
|
this.innerHtml = innerHtml;
|
|
2515
2571
|
}
|
|
2516
2572
|
|
|
2517
2573
|
createDOM() {
|
|
2518
|
-
const figure = createElement(
|
|
2574
|
+
const figure = createElement(this.tagName, { "content-type": this.contentType, "data-lexxy-decorator": true });
|
|
2519
2575
|
|
|
2520
2576
|
figure.addEventListener("click", (event) => {
|
|
2521
2577
|
dispatchCustomEvent(figure, "lexxy:internal:select-node", { key: this.getKey() });
|
|
@@ -2539,7 +2595,7 @@ class CustomActionTextAttachmentNode extends DecoratorNode {
|
|
|
2539
2595
|
}
|
|
2540
2596
|
|
|
2541
2597
|
exportDOM() {
|
|
2542
|
-
const attachment = createElement(
|
|
2598
|
+
const attachment = createElement(this.tagName, {
|
|
2543
2599
|
sgid: this.sgid,
|
|
2544
2600
|
content: JSON.stringify(this.innerHtml),
|
|
2545
2601
|
"content-type": this.contentType
|
|
@@ -2552,6 +2608,7 @@ class CustomActionTextAttachmentNode extends DecoratorNode {
|
|
|
2552
2608
|
return {
|
|
2553
2609
|
type: "custom_action_text_attachment",
|
|
2554
2610
|
version: 1,
|
|
2611
|
+
tagName: this.tagName,
|
|
2555
2612
|
sgid: this.sgid,
|
|
2556
2613
|
contentType: this.contentType,
|
|
2557
2614
|
innerHtml: this.innerHtml
|
|
@@ -3702,133 +3759,203 @@ class Clipboard {
|
|
|
3702
3759
|
}
|
|
3703
3760
|
}
|
|
3704
3761
|
|
|
3705
|
-
class
|
|
3706
|
-
|
|
3707
|
-
|
|
3762
|
+
class Extensions {
|
|
3763
|
+
|
|
3764
|
+
constructor(lexxyElement) {
|
|
3765
|
+
this.lexxyElement = lexxyElement;
|
|
3708
3766
|
|
|
3709
|
-
this.#
|
|
3767
|
+
this.enabledExtensions = this.#initializeExtensions();
|
|
3710
3768
|
}
|
|
3711
3769
|
|
|
3712
|
-
|
|
3713
|
-
this.
|
|
3714
|
-
this.#toggleSelectionStyles(styles);
|
|
3715
|
-
});
|
|
3770
|
+
get lexicalExtensions() {
|
|
3771
|
+
return this.enabledExtensions.map(ext => ext.lexicalExtension).filter(Boolean)
|
|
3716
3772
|
}
|
|
3717
3773
|
|
|
3718
|
-
|
|
3719
|
-
this
|
|
3774
|
+
initializeToolbars() {
|
|
3775
|
+
if (this.#lexxyToolbar) {
|
|
3776
|
+
this.enabledExtensions.forEach(ext => ext.initializeToobar(this.#lexxyToolbar));
|
|
3777
|
+
}
|
|
3720
3778
|
}
|
|
3721
3779
|
|
|
3722
|
-
#
|
|
3723
|
-
return this.
|
|
3724
|
-
this.#syncHighlightWithStyle(textNode);
|
|
3725
|
-
})
|
|
3780
|
+
get #lexxyToolbar() {
|
|
3781
|
+
return this.lexxyElement.toolbar
|
|
3726
3782
|
}
|
|
3727
3783
|
|
|
3728
|
-
#
|
|
3729
|
-
const
|
|
3730
|
-
if (!$isRangeSelection(selection)) return
|
|
3784
|
+
#initializeExtensions() {
|
|
3785
|
+
const extensionDefinitions = Lexxy.global.get("extensions");
|
|
3731
3786
|
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3787
|
+
return extensionDefinitions.map(
|
|
3788
|
+
extension => new extension(this.lexxyElement)
|
|
3789
|
+
).filter(extension => extension.enabled)
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
|
|
3793
|
+
const TOGGLE_HIGHLIGHT_COMMAND = createCommand();
|
|
3794
|
+
|
|
3795
|
+
const hasPastedStylesState = createState("hasPastedStyles", {
|
|
3796
|
+
parse: (value) => value || false
|
|
3797
|
+
});
|
|
3798
|
+
|
|
3799
|
+
const HighlightExtension = defineExtension({
|
|
3800
|
+
dependencies: [ RichTextExtension ],
|
|
3801
|
+
name: "lexxy/highlight",
|
|
3802
|
+
config: {
|
|
3803
|
+
color: { buttons: [], permit: [] },
|
|
3804
|
+
"background-color": { buttons: [], permit: [] }
|
|
3805
|
+
},
|
|
3806
|
+
html: {
|
|
3807
|
+
import: {
|
|
3808
|
+
mark: $markConversion
|
|
3736
3809
|
}
|
|
3810
|
+
},
|
|
3811
|
+
register(editor, config) {
|
|
3812
|
+
const canonicalizers = buildCanonicalizers(config);
|
|
3737
3813
|
|
|
3738
|
-
|
|
3814
|
+
editor.registerCommand(TOGGLE_HIGHLIGHT_COMMAND, $toggleSelectionStyles, COMMAND_PRIORITY_NORMAL);
|
|
3815
|
+
editor.registerNodeTransform(TextNode, $syncHighlightWithStyle);
|
|
3816
|
+
editor.registerNodeTransform(TextNode, (textNode) => $canonicalizePastedStyles(textNode, canonicalizers));
|
|
3739
3817
|
}
|
|
3818
|
+
});
|
|
3740
3819
|
|
|
3741
|
-
|
|
3742
|
-
|
|
3820
|
+
function $applyHighlightStyle(textNode, element) {
|
|
3821
|
+
const elementStyles = {
|
|
3822
|
+
color: element.style?.color,
|
|
3823
|
+
"background-color": element.style?.backgroundColor
|
|
3824
|
+
};
|
|
3825
|
+
|
|
3826
|
+
if ($hasUpdateTag(PASTE_TAG)) { $setPastedStyles(textNode); }
|
|
3827
|
+
const highlightStyle = getCSSFromStyleObject(elementStyles);
|
|
3828
|
+
|
|
3829
|
+
if (highlightStyle.length) {
|
|
3830
|
+
return textNode.setStyle(textNode.getStyle() + highlightStyle)
|
|
3743
3831
|
}
|
|
3832
|
+
}
|
|
3744
3833
|
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3834
|
+
function $markConversion() {
|
|
3835
|
+
return {
|
|
3836
|
+
conversion: extendTextNodeConversion("mark", $applyHighlightStyle),
|
|
3837
|
+
priority: 1
|
|
3749
3838
|
}
|
|
3750
3839
|
}
|
|
3751
3840
|
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3841
|
+
function buildCanonicalizers(config) {
|
|
3842
|
+
return [
|
|
3843
|
+
new StyleCanonicalizer("color", [ ...config.buttons.color, ...config.permit.color ]),
|
|
3844
|
+
new StyleCanonicalizer("background-color", [ ...config.buttons["background-color"], ...config.permit["background-color"] ])
|
|
3845
|
+
]
|
|
3846
|
+
}
|
|
3847
|
+
|
|
3848
|
+
function $toggleSelectionStyles(styles) {
|
|
3849
|
+
const selection = $getSelection();
|
|
3850
|
+
if (!$isRangeSelection(selection)) return
|
|
3851
|
+
|
|
3852
|
+
const patch = {};
|
|
3853
|
+
for (const property in styles) {
|
|
3854
|
+
const oldValue = $getSelectionStyleValueForProperty(selection, property);
|
|
3855
|
+
patch[property] = toggleOrReplace(oldValue, styles[property]);
|
|
3755
3856
|
}
|
|
3756
3857
|
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3858
|
+
$patchStyleText(selection, patch);
|
|
3859
|
+
}
|
|
3860
|
+
|
|
3861
|
+
function toggleOrReplace(oldValue, newValue) {
|
|
3862
|
+
return oldValue === newValue ? null : newValue
|
|
3863
|
+
}
|
|
3864
|
+
|
|
3865
|
+
function $syncHighlightWithStyle(textNode) {
|
|
3866
|
+
if (hasHighlightStyles(textNode.getStyle()) !== textNode.hasFormat("highlight")) {
|
|
3867
|
+
textNode.toggleFormat("highlight");
|
|
3764
3868
|
}
|
|
3765
3869
|
}
|
|
3766
3870
|
|
|
3767
|
-
function
|
|
3768
|
-
|
|
3769
|
-
|
|
3871
|
+
function $canonicalizePastedStyles(textNode, canonicalizers = []) {
|
|
3872
|
+
if ($hasPastedStyles(textNode)) {
|
|
3873
|
+
$setPastedStyles(textNode, false);
|
|
3770
3874
|
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3875
|
+
const canonicalizedCSS = canonicalizers.reduce((css, canonicalizer) => {
|
|
3876
|
+
return canonicalizer.applyCanonicalization(css)
|
|
3877
|
+
}, textNode.getStyle());
|
|
3774
3878
|
|
|
3775
|
-
|
|
3776
|
-
if (!textNode.hasFormat("highlight")) textNode.toggleFormat("highlight");
|
|
3777
|
-
return textNode.setStyle(textNode.getStyle() + highlightStyle)
|
|
3879
|
+
textNode.setStyle(canonicalizedCSS);
|
|
3778
3880
|
}
|
|
3779
3881
|
}
|
|
3780
3882
|
|
|
3781
|
-
|
|
3883
|
+
function $setPastedStyles(textNode, value = true) {
|
|
3884
|
+
$setState(textNode, hasPastedStylesState, value);
|
|
3885
|
+
}
|
|
3886
|
+
|
|
3887
|
+
function $hasPastedStyles(textNode) {
|
|
3888
|
+
return $getState(textNode, hasPastedStylesState)
|
|
3889
|
+
}
|
|
3890
|
+
|
|
3891
|
+
class Highlighter {
|
|
3782
3892
|
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
return this.config("trix-text", { extends: TextNode })
|
|
3893
|
+
constructor(editorElement) {
|
|
3894
|
+
this.editorElement = editorElement;
|
|
3786
3895
|
}
|
|
3787
3896
|
|
|
3788
|
-
|
|
3789
|
-
return
|
|
3790
|
-
|
|
3897
|
+
get editor() {
|
|
3898
|
+
return this.editorElement.editor
|
|
3899
|
+
}
|
|
3900
|
+
|
|
3901
|
+
get lexicalExtension() {
|
|
3902
|
+
return [ HighlightExtension, this.editorElement.config.get("highlight") ]
|
|
3903
|
+
}
|
|
3904
|
+
|
|
3905
|
+
toggle(styles) {
|
|
3906
|
+
this.editor.dispatchCommand(TOGGLE_HIGHLIGHT_COMMAND, styles);
|
|
3907
|
+
}
|
|
3908
|
+
|
|
3909
|
+
remove() {
|
|
3910
|
+
this.toggle({ "color": null, "background-color": null });
|
|
3911
|
+
}
|
|
3912
|
+
}
|
|
3913
|
+
|
|
3914
|
+
const TRIX_LANGUAGE_ATTR = "language";
|
|
3915
|
+
|
|
3916
|
+
const TrixContentExtension = defineExtension({
|
|
3917
|
+
name: "lexxy/trix-content",
|
|
3918
|
+
html: {
|
|
3919
|
+
import: {
|
|
3791
3920
|
em: (element) => onlyStyledElements(element, {
|
|
3792
|
-
conversion: extendTextNodeConversion("i", applyHighlightStyle),
|
|
3921
|
+
conversion: extendTextNodeConversion("i", $applyHighlightStyle),
|
|
3793
3922
|
priority: 1
|
|
3794
3923
|
}),
|
|
3795
3924
|
span: (element) => onlyStyledElements(element, {
|
|
3796
|
-
conversion: extendTextNodeConversion("mark", applyHighlightStyle),
|
|
3925
|
+
conversion: extendTextNodeConversion("mark", $applyHighlightStyle),
|
|
3797
3926
|
priority: 1
|
|
3798
3927
|
}),
|
|
3799
3928
|
strong: (element) => onlyStyledElements(element, {
|
|
3800
|
-
conversion: extendTextNodeConversion("b", applyHighlightStyle),
|
|
3929
|
+
conversion: extendTextNodeConversion("b", $applyHighlightStyle),
|
|
3801
3930
|
priority: 1
|
|
3802
3931
|
}),
|
|
3803
|
-
// del => s
|
|
3804
3932
|
del: () => ({
|
|
3805
|
-
conversion: extendTextNodeConversion("s", applyStrikethrough),
|
|
3933
|
+
conversion: extendTextNodeConversion("s", $applyStrikethrough, $applyHighlightStyle),
|
|
3806
3934
|
priority: 1
|
|
3807
3935
|
}),
|
|
3808
|
-
// read "language" attribute and normalize
|
|
3809
3936
|
pre: (element) => onlyPreLanguageElements(element, {
|
|
3810
|
-
conversion: extendConversion(CodeNode, "pre", applyLanguage),
|
|
3937
|
+
conversion: extendConversion(CodeNode, "pre", $applyLanguage),
|
|
3811
3938
|
priority: 1
|
|
3812
3939
|
})
|
|
3813
3940
|
}
|
|
3814
3941
|
}
|
|
3815
|
-
}
|
|
3942
|
+
});
|
|
3816
3943
|
|
|
3817
3944
|
function onlyStyledElements(element, conversion) {
|
|
3818
3945
|
const elementHighlighted = element.style.color !== "" || element.style.backgroundColor !== "";
|
|
3819
3946
|
return elementHighlighted ? conversion : null
|
|
3820
3947
|
}
|
|
3821
3948
|
|
|
3822
|
-
function applyStrikethrough(textNode
|
|
3949
|
+
function $applyStrikethrough(textNode) {
|
|
3823
3950
|
if (!textNode.hasFormat("strikethrough")) textNode.toggleFormat("strikethrough");
|
|
3824
|
-
return
|
|
3951
|
+
return textNode
|
|
3825
3952
|
}
|
|
3826
3953
|
|
|
3827
3954
|
function onlyPreLanguageElements(element, conversion) {
|
|
3828
3955
|
return element.hasAttribute(TRIX_LANGUAGE_ATTR) ? conversion : null
|
|
3829
3956
|
}
|
|
3830
3957
|
|
|
3831
|
-
function applyLanguage(conversionOutput, element) {
|
|
3958
|
+
function $applyLanguage(conversionOutput, element) {
|
|
3832
3959
|
const language = normalizeCodeLang(element.getAttribute(TRIX_LANGUAGE_ATTR));
|
|
3833
3960
|
conversionOutput.node.setLanguage(language);
|
|
3834
3961
|
}
|
|
@@ -3852,11 +3979,14 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3852
3979
|
connectedCallback() {
|
|
3853
3980
|
this.id ??= generateDomId("lexxy-editor");
|
|
3854
3981
|
this.config = new EditorConfiguration(this);
|
|
3982
|
+
this.extensions = new Extensions(this);
|
|
3983
|
+
this.highlighter = new Highlighter(this);
|
|
3984
|
+
|
|
3855
3985
|
this.editor = this.#createEditor();
|
|
3986
|
+
|
|
3856
3987
|
this.contents = new Contents(this);
|
|
3857
3988
|
this.selection = new Selection(this);
|
|
3858
3989
|
this.clipboard = new Clipboard(this);
|
|
3859
|
-
this.highlighter = new Highlighter(this);
|
|
3860
3990
|
|
|
3861
3991
|
CommandDispatcher.configureFor(this);
|
|
3862
3992
|
this.#initialize();
|
|
@@ -3864,6 +3994,8 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3864
3994
|
requestAnimationFrame(() => dispatch(this, "lexxy:initialize"));
|
|
3865
3995
|
this.toggleAttribute("connected", true);
|
|
3866
3996
|
|
|
3997
|
+
this.#handleAutofocus();
|
|
3998
|
+
|
|
3867
3999
|
this.valueBeforeDisconnect = null;
|
|
3868
4000
|
}
|
|
3869
4001
|
|
|
@@ -3888,10 +4020,6 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3888
4020
|
this.editor.dispatchCommand(CLEAR_HISTORY_COMMAND, undefined);
|
|
3889
4021
|
}
|
|
3890
4022
|
|
|
3891
|
-
focus() {
|
|
3892
|
-
this.editor.focus();
|
|
3893
|
-
}
|
|
3894
|
-
|
|
3895
4023
|
toString() {
|
|
3896
4024
|
if (!this.cachedStringValue) {
|
|
3897
4025
|
this.editor?.getEditorState().read(() => {
|
|
@@ -3966,6 +4094,10 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3966
4094
|
return parseInt(this.editorContentElement?.getAttribute("tabindex") ?? "0")
|
|
3967
4095
|
}
|
|
3968
4096
|
|
|
4097
|
+
focus() {
|
|
4098
|
+
this.editor.focus(() => this.#onFocus());
|
|
4099
|
+
}
|
|
4100
|
+
|
|
3969
4101
|
get value() {
|
|
3970
4102
|
if (!this.cachedValue) {
|
|
3971
4103
|
this.editor?.getEditorState().read(() => {
|
|
@@ -4028,29 +4160,43 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4028
4160
|
}
|
|
4029
4161
|
|
|
4030
4162
|
#createEditor() {
|
|
4031
|
-
this.editorContentElement
|
|
4163
|
+
this.editorContentElement ||= this.#createEditorContentElement();
|
|
4032
4164
|
|
|
4033
|
-
const editor =
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4165
|
+
const editor = buildEditorFromExtensions({
|
|
4166
|
+
name: "lexxy/core",
|
|
4167
|
+
namespace: "Lexxy",
|
|
4168
|
+
theme: theme,
|
|
4169
|
+
nodes: this.#lexicalNodes
|
|
4037
4170
|
},
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
});
|
|
4171
|
+
...this.#lexicalExtensions
|
|
4172
|
+
);
|
|
4041
4173
|
|
|
4042
4174
|
editor.setRootElement(this.editorContentElement);
|
|
4043
4175
|
|
|
4044
4176
|
return editor
|
|
4045
4177
|
}
|
|
4046
4178
|
|
|
4179
|
+
get #lexicalExtensions() {
|
|
4180
|
+
const extensions = [ ];
|
|
4181
|
+
const richTextExtensions = [
|
|
4182
|
+
this.highlighter.lexicalExtension,
|
|
4183
|
+
TrixContentExtension
|
|
4184
|
+
];
|
|
4185
|
+
|
|
4186
|
+
if (this.supportsRichText) {
|
|
4187
|
+
extensions.push(...richTextExtensions);
|
|
4188
|
+
}
|
|
4189
|
+
|
|
4190
|
+
extensions.push(...this.extensions.lexicalExtensions);
|
|
4191
|
+
|
|
4192
|
+
return extensions
|
|
4193
|
+
}
|
|
4194
|
+
|
|
4047
4195
|
get #lexicalNodes() {
|
|
4048
4196
|
const nodes = [ CustomActionTextAttachmentNode ];
|
|
4049
4197
|
|
|
4050
4198
|
if (this.supportsRichText) {
|
|
4051
4199
|
nodes.push(
|
|
4052
|
-
TrixTextNode,
|
|
4053
|
-
HighlightNode,
|
|
4054
4200
|
QuoteNode,
|
|
4055
4201
|
HeadingNode,
|
|
4056
4202
|
ListNode,
|
|
@@ -4239,6 +4385,20 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4239
4385
|
this.editor.registerCommand(FOCUS_COMMAND, () => { dispatch(this, "lexxy:focus"); }, COMMAND_PRIORITY_NORMAL);
|
|
4240
4386
|
}
|
|
4241
4387
|
|
|
4388
|
+
#onFocus() {
|
|
4389
|
+
if (this.isEmpty) {
|
|
4390
|
+
this.selection.placeCursorAtTheEnd();
|
|
4391
|
+
}
|
|
4392
|
+
}
|
|
4393
|
+
|
|
4394
|
+
#handleAutofocus() {
|
|
4395
|
+
if (!document.querySelector(":focus")) {
|
|
4396
|
+
if (this.hasAttribute("autofocus") && document.querySelector("[autofocus]") === this) {
|
|
4397
|
+
this.focus();
|
|
4398
|
+
}
|
|
4399
|
+
}
|
|
4400
|
+
}
|
|
4401
|
+
|
|
4242
4402
|
#handleTables() {
|
|
4243
4403
|
if (this.supportsRichText) {
|
|
4244
4404
|
this.removeTableSelectionObserver = registerTableSelectionObserver(this.editor, true);
|
|
@@ -4353,6 +4513,10 @@ class ToolbarDropdown extends HTMLElement {
|
|
|
4353
4513
|
return this.closest("lexxy-toolbar")
|
|
4354
4514
|
}
|
|
4355
4515
|
|
|
4516
|
+
get editorElement() {
|
|
4517
|
+
return this.toolbar.editorElement
|
|
4518
|
+
}
|
|
4519
|
+
|
|
4356
4520
|
get editor() {
|
|
4357
4521
|
return this.toolbar.editor
|
|
4358
4522
|
}
|
|
@@ -4484,15 +4648,26 @@ const REMOVE_HIGHLIGHT_SELECTOR = "[data-command='removeHighlight']";
|
|
|
4484
4648
|
const NO_STYLE = Symbol("no_style");
|
|
4485
4649
|
|
|
4486
4650
|
class HighlightDropdown extends ToolbarDropdown {
|
|
4651
|
+
#initialized = false
|
|
4652
|
+
|
|
4487
4653
|
connectedCallback() {
|
|
4488
4654
|
super.connectedCallback();
|
|
4655
|
+
this.#registerToggleHandler();
|
|
4656
|
+
}
|
|
4657
|
+
|
|
4658
|
+
#ensureInitialized() {
|
|
4659
|
+
if (this.#initialized) return
|
|
4489
4660
|
|
|
4490
4661
|
this.#setUpButtons();
|
|
4491
|
-
this.#
|
|
4662
|
+
this.#registerButtonHandlers();
|
|
4663
|
+
this.#initialized = true;
|
|
4492
4664
|
}
|
|
4493
4665
|
|
|
4494
|
-
#
|
|
4666
|
+
#registerToggleHandler() {
|
|
4495
4667
|
this.container.addEventListener("toggle", this.#handleToggle.bind(this));
|
|
4668
|
+
}
|
|
4669
|
+
|
|
4670
|
+
#registerButtonHandlers() {
|
|
4496
4671
|
this.#colorButtons.forEach(button => button.addEventListener("click", this.#handleColorButtonClick.bind(this)));
|
|
4497
4672
|
this.querySelector(REMOVE_HIGHLIGHT_SELECTOR).addEventListener("click", this.#handleRemoveHighlightClick.bind(this));
|
|
4498
4673
|
}
|
|
@@ -4504,8 +4679,8 @@ class HighlightDropdown extends ToolbarDropdown {
|
|
|
4504
4679
|
}
|
|
4505
4680
|
|
|
4506
4681
|
#populateButtonGroup(buttonGroup) {
|
|
4507
|
-
const values = buttonGroup.dataset.values?.split("; ") || [];
|
|
4508
4682
|
const attribute = buttonGroup.dataset.buttonGroup;
|
|
4683
|
+
const values = this.editorElement.config.get(`highlight.buttons.${attribute}`) || [];
|
|
4509
4684
|
values.forEach((value, index) => {
|
|
4510
4685
|
buttonGroup.appendChild(this.#createButton(attribute, value, index));
|
|
4511
4686
|
});
|
|
@@ -4523,6 +4698,8 @@ class HighlightDropdown extends ToolbarDropdown {
|
|
|
4523
4698
|
|
|
4524
4699
|
#handleToggle({ newState }) {
|
|
4525
4700
|
if (newState === "open") {
|
|
4701
|
+
this.#ensureInitialized();
|
|
4702
|
+
|
|
4526
4703
|
this.editor.getEditorState().read(() => {
|
|
4527
4704
|
this.#updateColorButtonStates($getSelection());
|
|
4528
4705
|
});
|
|
@@ -4616,7 +4793,7 @@ class TableHandler extends HTMLElement {
|
|
|
4616
4793
|
}
|
|
4617
4794
|
|
|
4618
4795
|
get #tableHandlerButtons() {
|
|
4619
|
-
return Array.from(this.
|
|
4796
|
+
return Array.from(this.querySelectorAll("button, details > summary"))
|
|
4620
4797
|
}
|
|
4621
4798
|
|
|
4622
4799
|
#registerKeyboardShortcuts() {
|
|
@@ -4629,7 +4806,7 @@ class TableHandler extends HTMLElement {
|
|
|
4629
4806
|
|
|
4630
4807
|
#handleKeyDown = (event) => {
|
|
4631
4808
|
if ((event.ctrlKey || event.metaKey) && event.shiftKey && event.key === "F10") {
|
|
4632
|
-
const firstButton = this.
|
|
4809
|
+
const firstButton = this.querySelector("button, [tabindex]:not([tabindex='-1'])");
|
|
4633
4810
|
this.#setFocusStateOnSelectedCell();
|
|
4634
4811
|
firstButton?.focus();
|
|
4635
4812
|
} else if (event.key === "Escape") {
|
|
@@ -4654,22 +4831,16 @@ class TableHandler extends HTMLElement {
|
|
|
4654
4831
|
}
|
|
4655
4832
|
|
|
4656
4833
|
#setUpButtons() {
|
|
4657
|
-
this.
|
|
4658
|
-
|
|
4659
|
-
});
|
|
4660
|
-
|
|
4661
|
-
this.buttonsContainer.appendChild(this.#createRowButtonsContainer());
|
|
4662
|
-
this.buttonsContainer.appendChild(this.#createColumnButtonsContainer());
|
|
4834
|
+
this.appendChild(this.#createRowButtonsContainer());
|
|
4835
|
+
this.appendChild(this.#createColumnButtonsContainer());
|
|
4663
4836
|
|
|
4664
4837
|
this.moreMenu = this.#createMoreMenu();
|
|
4665
|
-
this.
|
|
4666
|
-
this.
|
|
4667
|
-
|
|
4668
|
-
this.#editorElement.appendChild(this.buttonsContainer);
|
|
4838
|
+
this.appendChild(this.moreMenu);
|
|
4839
|
+
this.addEventListener("keydown", this.#handleTableHandlerKeydown);
|
|
4669
4840
|
}
|
|
4670
4841
|
|
|
4671
4842
|
#showTableHandlerButtons() {
|
|
4672
|
-
this.
|
|
4843
|
+
this.style.display = "flex";
|
|
4673
4844
|
this.#closeMoreMenu();
|
|
4674
4845
|
|
|
4675
4846
|
this.#updateRowColumnCount();
|
|
@@ -4677,7 +4848,7 @@ class TableHandler extends HTMLElement {
|
|
|
4677
4848
|
}
|
|
4678
4849
|
|
|
4679
4850
|
#hideTableHandlerButtons() {
|
|
4680
|
-
this.
|
|
4851
|
+
this.style.display = "none";
|
|
4681
4852
|
this.#closeMoreMenu();
|
|
4682
4853
|
|
|
4683
4854
|
this.#setTableFocusState(false);
|
|
@@ -4693,8 +4864,8 @@ class TableHandler extends HTMLElement {
|
|
|
4693
4864
|
|
|
4694
4865
|
const relativeTop = tableRect.top - editorRect.top;
|
|
4695
4866
|
const relativeCenter = (tableRect.left + tableRect.right) / 2 - editorRect.left;
|
|
4696
|
-
this.
|
|
4697
|
-
this.
|
|
4867
|
+
this.style.top = `${relativeTop}px`;
|
|
4868
|
+
this.style.left = `${relativeCenter}px`;
|
|
4698
4869
|
}
|
|
4699
4870
|
|
|
4700
4871
|
#updateRowColumnCount() {
|
|
@@ -5561,30 +5732,57 @@ class LexicalPromptElement extends HTMLElement {
|
|
|
5561
5732
|
|
|
5562
5733
|
if (!promptItem) { return }
|
|
5563
5734
|
|
|
5564
|
-
const
|
|
5735
|
+
const templates = Array.from(promptItem.querySelectorAll("template[type='editor']"));
|
|
5565
5736
|
const stringToReplace = `${this.trigger}${this.#editorContents.textBackUntil(this.trigger)}`;
|
|
5566
5737
|
|
|
5567
5738
|
if (this.hasAttribute("insert-editable-text")) {
|
|
5568
|
-
this.#
|
|
5739
|
+
this.#insertTemplatesAsEditableText(templates, stringToReplace);
|
|
5569
5740
|
} else {
|
|
5570
|
-
this.#
|
|
5741
|
+
this.#insertTemplatesAsAttachments(templates, stringToReplace, promptItem.getAttribute("sgid"));
|
|
5571
5742
|
}
|
|
5572
5743
|
}
|
|
5573
5744
|
|
|
5574
|
-
#
|
|
5745
|
+
#insertTemplatesAsEditableText(templates, stringToReplace) {
|
|
5575
5746
|
this.#editor.update(() => {
|
|
5576
|
-
const nodes =
|
|
5747
|
+
const nodes = templates.flatMap(template => this.#buildEditableTextNodes(template));
|
|
5577
5748
|
this.#editorContents.replaceTextBackUntil(stringToReplace, nodes);
|
|
5578
5749
|
});
|
|
5579
5750
|
}
|
|
5580
5751
|
|
|
5581
|
-
#
|
|
5752
|
+
#buildEditableTextNodes(template) {
|
|
5753
|
+
return $generateNodesFromDOM(this.#editor, parseHtml(`${template.innerHTML}`))
|
|
5754
|
+
}
|
|
5755
|
+
|
|
5756
|
+
#insertTemplatesAsAttachments(templates, stringToReplace, fallbackSgid = null) {
|
|
5582
5757
|
this.#editor.update(() => {
|
|
5583
|
-
const
|
|
5584
|
-
this.#
|
|
5758
|
+
const attachmentNodes = this.#buildAttachmentNodes(templates, fallbackSgid);
|
|
5759
|
+
const spacedAttachmentNodes = attachmentNodes.flatMap(node => [ node, this.#getSpacerTextNode() ]).slice(0, -1);
|
|
5760
|
+
this.#editorContents.replaceTextBackUntil(stringToReplace, spacedAttachmentNodes);
|
|
5585
5761
|
});
|
|
5586
5762
|
}
|
|
5587
5763
|
|
|
5764
|
+
#buildAttachmentNodes(templates, fallbackSgid = null) {
|
|
5765
|
+
return templates.map(
|
|
5766
|
+
template => this.#buildAttachmentNode(
|
|
5767
|
+
template.innerHTML,
|
|
5768
|
+
template.getAttribute("content-type") || this.#defaultPromptContentType,
|
|
5769
|
+
template.getAttribute("sgid") || fallbackSgid
|
|
5770
|
+
))
|
|
5771
|
+
}
|
|
5772
|
+
|
|
5773
|
+
#getSpacerTextNode() {
|
|
5774
|
+
return $createTextNode(" ")
|
|
5775
|
+
}
|
|
5776
|
+
|
|
5777
|
+
get #defaultPromptContentType() {
|
|
5778
|
+
const attachmentContentTypeNamespace = Lexxy.global.get("attachmentContentTypeNamespace");
|
|
5779
|
+
return `application/vnd.${attachmentContentTypeNamespace}.${this.name}`
|
|
5780
|
+
}
|
|
5781
|
+
|
|
5782
|
+
#buildAttachmentNode(innerHtml, contentType, sgid) {
|
|
5783
|
+
return new CustomActionTextAttachmentNode({ sgid, contentType, innerHtml })
|
|
5784
|
+
}
|
|
5785
|
+
|
|
5588
5786
|
get #editorContents() {
|
|
5589
5787
|
return this.#editorElement.contents
|
|
5590
5788
|
}
|
|
@@ -5636,13 +5834,12 @@ class CodeLanguagePicker extends HTMLElement {
|
|
|
5636
5834
|
this.#updateCodeBlockLanguage(this.languagePickerElement.value);
|
|
5637
5835
|
});
|
|
5638
5836
|
|
|
5639
|
-
this.languagePickerElement.style.position = "absolute";
|
|
5640
5837
|
this.languagePickerElement.setAttribute("nonce", getNonce());
|
|
5641
|
-
this.
|
|
5838
|
+
this.appendChild(this.languagePickerElement);
|
|
5642
5839
|
}
|
|
5643
5840
|
|
|
5644
5841
|
#createLanguagePicker() {
|
|
5645
|
-
const selectElement = createElement("select", {
|
|
5842
|
+
const selectElement = createElement("select", { className: "lexxy-code-language-picker", "aria-label": "Pick a language…", name: "lexxy-code-language" });
|
|
5646
5843
|
|
|
5647
5844
|
for (const [ value, label ] of Object.entries(this.#languages)) {
|
|
5648
5845
|
const option = document.createElement("option");
|
|
@@ -5739,43 +5936,49 @@ class CodeLanguagePicker extends HTMLElement {
|
|
|
5739
5936
|
const editorRect = this.editorElement.getBoundingClientRect();
|
|
5740
5937
|
const relativeTop = codeRect.top - editorRect.top;
|
|
5741
5938
|
|
|
5742
|
-
this.
|
|
5939
|
+
this.style.top = `${relativeTop}px`;
|
|
5743
5940
|
}
|
|
5744
5941
|
|
|
5745
5942
|
#showLanguagePicker() {
|
|
5746
|
-
this.
|
|
5943
|
+
this.hidden = false;
|
|
5747
5944
|
}
|
|
5748
5945
|
|
|
5749
5946
|
#hideLanguagePicker() {
|
|
5750
|
-
this.
|
|
5947
|
+
this.hidden = true;
|
|
5751
5948
|
}
|
|
5752
5949
|
}
|
|
5753
5950
|
|
|
5754
5951
|
customElements.define("lexxy-code-language-picker", CodeLanguagePicker);
|
|
5755
5952
|
|
|
5756
|
-
|
|
5757
|
-
|
|
5953
|
+
class LexxyExtension {
|
|
5954
|
+
#editorElement
|
|
5758
5955
|
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
}
|
|
5762
|
-
}
|
|
5956
|
+
constructor(editorElement) {
|
|
5957
|
+
this.#editorElement = editorElement;
|
|
5958
|
+
}
|
|
5763
5959
|
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5960
|
+
get editorElement() {
|
|
5961
|
+
return this.#editorElement
|
|
5962
|
+
}
|
|
5767
5963
|
|
|
5768
|
-
|
|
5769
|
-
|
|
5964
|
+
get editorConfig() {
|
|
5965
|
+
return this.#editorElement.config
|
|
5966
|
+
}
|
|
5770
5967
|
|
|
5771
|
-
//
|
|
5772
|
-
|
|
5968
|
+
// optional: defaults to true
|
|
5969
|
+
get enabled() {
|
|
5970
|
+
return true
|
|
5971
|
+
}
|
|
5972
|
+
|
|
5973
|
+
get lexicalExtension() {
|
|
5974
|
+
return null
|
|
5975
|
+
}
|
|
5976
|
+
|
|
5977
|
+
initializeToolbar(_lexxyToolbar) {
|
|
5773
5978
|
|
|
5774
|
-
|
|
5775
|
-
const codeElement = createElement("code", { "data-language": language, innerHTML: highlightedHtml });
|
|
5776
|
-
preElement.replaceWith(codeElement);
|
|
5979
|
+
}
|
|
5777
5980
|
}
|
|
5778
5981
|
|
|
5779
5982
|
const configure = Lexxy.configure;
|
|
5780
5983
|
|
|
5781
|
-
export {
|
|
5984
|
+
export { ActionTextAttachmentNode, ActionTextAttachmentUploadNode, CustomActionTextAttachmentNode, LexxyExtension as Extension, HorizontalDividerNode, configure };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import Prism from 'prismjs';
|
|
2
|
+
|
|
3
|
+
function createElement(name, properties, content = "") {
|
|
4
|
+
const element = document.createElement(name);
|
|
5
|
+
for (const [ key, value ] of Object.entries(properties || {})) {
|
|
6
|
+
if (key in element) {
|
|
7
|
+
element[key] = value;
|
|
8
|
+
} else if (value !== null && value !== undefined) {
|
|
9
|
+
element.setAttribute(key, value);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
if (content) {
|
|
13
|
+
element.innerHTML = content;
|
|
14
|
+
}
|
|
15
|
+
return element
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function parseHtml(html) {
|
|
19
|
+
const parser = new DOMParser();
|
|
20
|
+
return parser.parseFromString(html, "text/html")
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function createAttachmentFigure(contentType, isPreviewable, fileName) {
|
|
24
|
+
const extension = fileName ? fileName.split(".").pop().toLowerCase() : "unknown";
|
|
25
|
+
return createElement("figure", {
|
|
26
|
+
className: `attachment attachment--${isPreviewable ? "preview" : "file"} attachment--${extension}`,
|
|
27
|
+
"data-content-type": contentType
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isPreviewableImage(contentType) {
|
|
32
|
+
return contentType.startsWith("image/") && !contentType.includes("svg")
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function dispatchCustomEvent(element, name, detail) {
|
|
36
|
+
const event = new CustomEvent(name, {
|
|
37
|
+
detail: detail,
|
|
38
|
+
bubbles: true,
|
|
39
|
+
});
|
|
40
|
+
element.dispatchEvent(event);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function dispatch(element, eventName, detail = null, cancelable = false) {
|
|
44
|
+
return element.dispatchEvent(new CustomEvent(eventName, { bubbles: true, detail, cancelable }))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function generateDomId(prefix) {
|
|
48
|
+
const randomPart = Math.random().toString(36).slice(2, 10);
|
|
49
|
+
return `${prefix}-${randomPart}`
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function highlightCode() {
|
|
53
|
+
const elements = document.querySelectorAll("pre[data-language]");
|
|
54
|
+
|
|
55
|
+
elements.forEach(preElement => {
|
|
56
|
+
highlightElement(preElement);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function highlightElement(preElement) {
|
|
61
|
+
const language = preElement.getAttribute("data-language");
|
|
62
|
+
let code = preElement.innerHTML.replace(/<br\s*\/?>/gi, "\n");
|
|
63
|
+
|
|
64
|
+
const grammar = Prism.languages?.[language];
|
|
65
|
+
if (!grammar) return
|
|
66
|
+
|
|
67
|
+
// unescape HTML entities in the code block
|
|
68
|
+
code = new DOMParser().parseFromString(code, "text/html").body.textContent || "";
|
|
69
|
+
|
|
70
|
+
const highlightedHtml = Prism.highlight(code, grammar, language);
|
|
71
|
+
const codeElement = createElement("code", { "data-language": language, innerHTML: highlightedHtml });
|
|
72
|
+
preElement.replaceWith(codeElement);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export { createAttachmentFigure, createElement, dispatch, dispatchCustomEvent, generateDomId, highlightCode, isPreviewableImage, parseHtml };
|
|
@@ -240,14 +240,6 @@
|
|
|
240
240
|
}
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
-
:where([data-lexical-cursor]) {
|
|
244
|
-
animation: blink 1s step-end infinite;
|
|
245
|
-
block-size: 1lh;
|
|
246
|
-
border-inline-start: 1px solid currentColor;
|
|
247
|
-
line-height: inherit;
|
|
248
|
-
margin-block: 1em;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
243
|
/* Attachments
|
|
252
244
|
/* ------------------------------------------------------------------------ */
|
|
253
245
|
|
|
@@ -102,6 +102,20 @@
|
|
|
102
102
|
outline: 2px dashed var(--lexxy-color-selected-dark);
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
:where([data-lexical-cursor]) {
|
|
106
|
+
animation: blink 1s infinite;
|
|
107
|
+
block-size: 1lh;
|
|
108
|
+
border-inline-start: 1.5px solid currentColor;
|
|
109
|
+
line-height: inherit;
|
|
110
|
+
margin-block: 0 var(--lexxy-content-margin);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
@keyframes blink {
|
|
114
|
+
0% { opacity: 1; }
|
|
115
|
+
60% { opacity: 1; }
|
|
116
|
+
100% { opacity: 0;}
|
|
117
|
+
}
|
|
118
|
+
|
|
105
119
|
/* Toolbar
|
|
106
120
|
/* -------------------------------------------------------------------------- */
|
|
107
121
|
|
|
@@ -381,7 +395,7 @@
|
|
|
381
395
|
/* Table handle buttons
|
|
382
396
|
/* -------------------------------------------------------------------------- */
|
|
383
397
|
|
|
384
|
-
:where(
|
|
398
|
+
:where(lexxy-table-handler) {
|
|
385
399
|
--button-size: 2.5lh;
|
|
386
400
|
|
|
387
401
|
color: var(--lexxy-color-ink-inverted);
|
|
@@ -502,28 +516,32 @@
|
|
|
502
516
|
/* Language picker
|
|
503
517
|
/* -------------------------------------------------------------------------- */
|
|
504
518
|
|
|
505
|
-
:where(
|
|
506
|
-
-webkit-appearance: none;
|
|
507
|
-
appearance: none;
|
|
508
|
-
background-color: var(--lexxy-color-canvas);
|
|
509
|
-
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m12 19.5c-.7 0-1.3-.3-1.7-.8l-9.8-11.1c-.7-.8-.6-1.9.2-2.6.8-.6 1.9-.6 2.5.2l8.6 9.8c0 .1.2.1.4 0l8.6-9.8c.7-.8 1.8-.9 2.6-.2s.9 1.8.2 2.6l-9.8 11.1c-.4.5-1.1.8-1.7.8z' fill='%23000'/%3E%3C/svg%3E");
|
|
510
|
-
background-position: center right 1ch;
|
|
511
|
-
background-repeat: no-repeat;
|
|
512
|
-
background-size: 1ch;
|
|
513
|
-
block-size: 1.5lh;
|
|
514
|
-
border: 1px solid var(--lexxy-color-ink-lighter);
|
|
515
|
-
border-radius: var(--lexxy-radius);
|
|
516
|
-
color: var(--lexxy-color-ink);
|
|
517
|
-
font-family: var(--lexxy-font-base);
|
|
518
|
-
font-size: var(--lexxy-text-small);
|
|
519
|
-
font-weight: normal;
|
|
519
|
+
:where(lexxy-code-language-picker) {
|
|
520
520
|
inset-inline-end: var(--lexxy-editor-padding);
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
521
|
+
position: absolute;
|
|
522
|
+
|
|
523
|
+
select {
|
|
524
|
+
-webkit-appearance: none;
|
|
525
|
+
appearance: none;
|
|
526
|
+
background-color: var(--lexxy-color-canvas);
|
|
527
|
+
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m12 19.5c-.7 0-1.3-.3-1.7-.8l-9.8-11.1c-.7-.8-.6-1.9.2-2.6.8-.6 1.9-.6 2.5.2l8.6 9.8c0 .1.2.1.4 0l8.6-9.8c.7-.8 1.8-.9 2.6-.2s.9 1.8.2 2.6l-9.8 11.1c-.4.5-1.1.8-1.7.8z' fill='%23000'/%3E%3C/svg%3E");
|
|
528
|
+
background-position: center right 1ch;
|
|
529
|
+
background-repeat: no-repeat;
|
|
530
|
+
background-size: 1ch;
|
|
531
|
+
block-size: 1.5lh;
|
|
532
|
+
border: 1px solid var(--lexxy-color-ink-lighter);
|
|
533
|
+
border-radius: var(--lexxy-radius);
|
|
534
|
+
color: var(--lexxy-color-ink);
|
|
535
|
+
font-family: var(--lexxy-font-base);
|
|
536
|
+
font-size: var(--lexxy-text-small);
|
|
537
|
+
font-weight: normal;
|
|
538
|
+
margin: 0.5ch 0.5ch 0 -0.5ch;
|
|
539
|
+
padding: 0 2ch 0 1ch;
|
|
540
|
+
text-align: start;
|
|
524
541
|
|
|
525
|
-
|
|
526
|
-
|
|
542
|
+
@media (prefers-color-scheme: dark) {
|
|
543
|
+
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m12 19.5c-.7 0-1.3-.3-1.7-.8l-9.8-11.1c-.7-.8-.6-1.9.2-2.6.8-.6 1.9-.6 2.5.2l8.6 9.8c0 .1.2.1.4 0l8.6-9.8c.7-.8 1.8-.9 2.6-.2s.9 1.8.2 2.6l-9.8 11.1c-.4.5-1.1.8-1.7.8z' fill='%23fff'/%3E%3C/svg%3E");
|
|
544
|
+
}
|
|
527
545
|
}
|
|
528
546
|
}
|
|
529
547
|
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@37signals/lexxy",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2-beta",
|
|
4
4
|
"description": "Lexxy - A modern rich text editor for Rails.",
|
|
5
5
|
"module": "dist/lexxy.esm.js",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"exports":
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./dist/lexxy.esm.js",
|
|
9
|
+
"./helpers": "./dist/lexxy_helpers.esm.js"
|
|
10
|
+
},
|
|
8
11
|
"files": [
|
|
9
12
|
"dist"
|
|
10
13
|
],
|