@37signals/lexxy 0.9.7-beta → 0.9.9-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 +188 -59
- package/dist/lexxy_helpers.esm.js +5 -1
- package/package.json +1 -1
package/dist/lexxy.esm.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { createElement, extractPlainTextFromHtml, createAttachmentFigure, isPreviewableImage, dispatch, parseHtml, addBlockSpacing, generateDomId } from './lexxy_helpers.esm.js';
|
|
1
|
+
import { isActiveAndVisible, createElement, extractPlainTextFromHtml, createAttachmentFigure, isPreviewableImage, dispatch, parseHtml, addBlockSpacing, generateDomId } from './lexxy_helpers.esm.js';
|
|
2
2
|
export { highlightCode } from './lexxy_helpers.esm.js';
|
|
3
3
|
import DOMPurify from 'dompurify';
|
|
4
4
|
import { getStyleObjectFromCSS, getCSSFromStyleObject, $isAtNodeEnd, $getSelectionStyleValueForProperty, $patchStyleText, $setBlocksType, $forEachSelectedTextNode, $ensureForwardRangeSelection } from '@lexical/selection';
|
|
5
5
|
import { SKIP_DOM_SELECTION_TAG, $getSelection, $isRangeSelection, DecoratorNode, $createParagraphNode, $getNodeByKey, $isTextNode, $createRangeSelection, $setSelection, $createTextNode, HISTORY_MERGE_TAG, SKIP_SCROLL_INTO_VIEW_TAG, $createNodeSelection, $isDecoratorNode, $isLineBreakNode, $isElementNode, TextNode, createCommand, createState, defineExtension, COMMAND_PRIORITY_NORMAL, $getState, $setState, $hasUpdateTag, PASTE_TAG, FORMAT_TEXT_COMMAND, UNDO_COMMAND, REDO_COMMAND, PASTE_COMMAND, COMMAND_PRIORITY_LOW, KEY_ARROW_RIGHT_COMMAND, KEY_TAB_COMMAND, OUTDENT_CONTENT_COMMAND, INDENT_CONTENT_COMMAND, $getEditor, $getNearestRootOrShadowRoot, $isNodeSelection, $getRoot, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_UP_COMMAND, KEY_ARROW_DOWN_COMMAND, DELETE_CHARACTER_COMMAND, SELECTION_CHANGE_COMMAND, CLICK_COMMAND, isDOMNode, $getNearestNodeFromDOMNode, ParagraphNode, $isRootOrShadowRoot, ElementNode, $splitNode, $isParagraphNode, $createLineBreakNode, $isRootNode, $getChildCaretAtIndex, RootNode, COMMAND_PRIORITY_HIGH, DRAGSTART_COMMAND, DROP_COMMAND, INSERT_PARAGRAPH_COMMAND, CLEAR_HISTORY_COMMAND, $addUpdateTag, KEY_ENTER_COMMAND, COMMAND_PRIORITY_CRITICAL, KEY_SPACE_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DOWN_COMMAND } from 'lexical';
|
|
6
6
|
import { buildEditorFromExtensions } from '@lexical/extension';
|
|
7
7
|
import { ListNode, ListItemNode, $getListDepth, INSERT_UNORDERED_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, $isListItemNode, $isListNode, registerList } from '@lexical/list';
|
|
8
|
-
import { $createAutoLinkNode, $toggleLink,
|
|
9
|
-
import { $getNearestNodeOfType, $wrapNodeInElement, $lastToFirstIterator, mergeRegister, $insertFirst, $unwrapAndFilterDescendants, $firstToLastIterator, $getNearestBlockElementAncestorOrThrow, $descendantsMatching } from '@lexical/utils';
|
|
8
|
+
import { LinkNode, $createAutoLinkNode, $toggleLink, $createLinkNode, AutoLinkNode, $isLinkNode } from '@lexical/link';
|
|
9
|
+
import { $getNearestNodeOfType, $wrapNodeInElement, $lastToFirstIterator, mergeRegister, $insertFirst, $unwrapAndFilterDescendants, $firstToLastIterator, $getNearestBlockElementAncestorOrThrow, $descendantsMatching, IS_APPLE } from '@lexical/utils';
|
|
10
10
|
import { registerPlainText } from '@lexical/plain-text';
|
|
11
11
|
import { RichTextExtension, $isQuoteNode, $isHeadingNode, $createHeadingNode, $createQuoteNode, QuoteNode, HeadingNode, registerRichText } from '@lexical/rich-text';
|
|
12
12
|
import { $generateNodesFromDOM, $generateHtmlFromNodes } from '@lexical/html';
|
|
@@ -209,10 +209,6 @@ class NextElementFinder {
|
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
function isActiveAndVisible(element) {
|
|
213
|
-
return element && !element.disabled && element.checkVisibility()
|
|
214
|
-
}
|
|
215
|
-
|
|
216
212
|
var ToolbarIcons = {
|
|
217
213
|
"bold":
|
|
218
214
|
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
@@ -486,7 +482,8 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
486
482
|
}
|
|
487
483
|
|
|
488
484
|
#handleEditorFocus = () => {
|
|
489
|
-
this.#focusableItems
|
|
485
|
+
const firstVisible = this.#focusableItems.find(isActiveAndVisible);
|
|
486
|
+
if (firstVisible) firstVisible.tabIndex = 0;
|
|
490
487
|
}
|
|
491
488
|
|
|
492
489
|
#handleEditorBlur = () => {
|
|
@@ -579,12 +576,6 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
579
576
|
}
|
|
580
577
|
}
|
|
581
578
|
|
|
582
|
-
#toolbarIsOverflowing() {
|
|
583
|
-
// Safari can report inconsistent clientWidth values on more than 100% window zoom level,
|
|
584
|
-
// that was affecting the toolbar overflow calculation. We're adding +1 to get around this issue.
|
|
585
|
-
return (this.scrollWidth - this.#overflow.clientWidth) > this.clientWidth + 1
|
|
586
|
-
}
|
|
587
|
-
|
|
588
579
|
#refreshToolbarOverflow = () => {
|
|
589
580
|
this.#resetToolbarOverflow();
|
|
590
581
|
this.#compactMenu();
|
|
@@ -597,29 +588,46 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
597
588
|
this.#overflowMenu.toggleAttribute("disabled", !isOverflowing);
|
|
598
589
|
}
|
|
599
590
|
|
|
591
|
+
// Separates layout reads from DOM writes to avoid forced reflows during init.
|
|
592
|
+
// Measures every button's right edge in a single read pass, figures out which
|
|
593
|
+
// buttons overflow using math, and then moves them in a single write pass.
|
|
594
|
+
// The previous implementation interleaved `scrollWidth`/`clientWidth` reads with
|
|
595
|
+
// `prepend()` writes inside a loop, forcing one full browser reflow per button.
|
|
600
596
|
#compactMenu() {
|
|
601
|
-
const buttons = this.#buttons
|
|
602
|
-
|
|
597
|
+
const buttons = this.#buttons;
|
|
598
|
+
if (buttons.length === 0) return
|
|
603
599
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
600
|
+
const availableWidth = this.clientWidth + 1; // +1 for Safari zoom rounding
|
|
601
|
+
const buttonRightEdges = buttons.map(button => button.offsetLeft + button.offsetWidth);
|
|
602
|
+
|
|
603
|
+
let firstOverflowing = -1;
|
|
604
|
+
for (let i = 0; i < buttons.length; i++) {
|
|
605
|
+
if (buttonRightEdges[i] > availableWidth) {
|
|
606
|
+
firstOverflowing = i;
|
|
610
607
|
break
|
|
611
608
|
}
|
|
612
609
|
}
|
|
610
|
+
|
|
611
|
+
if (firstOverflowing === -1) return
|
|
612
|
+
|
|
613
|
+
// Move one extra button to reserve space for the overflow control, which is
|
|
614
|
+
// `display: none` until we show it — matching the previous implementation's
|
|
615
|
+
// "move one more after it stops overflowing" behaviour.
|
|
616
|
+
const overflowIndex = Math.max(0, firstOverflowing - 1);
|
|
617
|
+
const overflowButtons = buttons.slice(overflowIndex).reverse();
|
|
618
|
+
for (const button of overflowButtons) {
|
|
619
|
+
this.#overflowMenu.prepend(button);
|
|
620
|
+
}
|
|
613
621
|
}
|
|
614
622
|
|
|
615
623
|
#resetToolbarOverflow() {
|
|
616
624
|
const items = Array.from(this.#overflowMenu.children);
|
|
617
625
|
items.sort((a, b) => this.#itemPosition(b) - this.#itemPosition(a));
|
|
618
626
|
|
|
619
|
-
|
|
627
|
+
for (const item of items) {
|
|
620
628
|
const nextItem = this.querySelector(`[data-position="${this.#itemPosition(item) + 1}"]`) ?? this.#overflow;
|
|
621
629
|
this.insertBefore(item, nextItem);
|
|
622
|
-
}
|
|
630
|
+
}
|
|
623
631
|
}
|
|
624
632
|
|
|
625
633
|
#itemPosition(item) {
|
|
@@ -1525,26 +1533,38 @@ class StyleCanonicalizer {
|
|
|
1525
1533
|
|
|
1526
1534
|
#resolveCannonicalValue(value) {
|
|
1527
1535
|
let index = this.#computedAllowedValues.indexOf(value);
|
|
1528
|
-
index
|
|
1536
|
+
if (index === -1) {
|
|
1537
|
+
index = this.#computedAllowedValues.indexOf(computeStyleValues(this._property, [ value ])[0]);
|
|
1538
|
+
}
|
|
1529
1539
|
return index === -1 ? null : this._allowedValues[index]
|
|
1530
1540
|
}
|
|
1531
1541
|
|
|
1532
1542
|
get #computedAllowedValues() {
|
|
1533
|
-
return this._computedAllowedValues ||= this._allowedValues
|
|
1534
|
-
value => getComputedStyleForProperty(this._property, value)
|
|
1535
|
-
)
|
|
1543
|
+
return this._computedAllowedValues ||= computeStyleValues(this._property, this._allowedValues)
|
|
1536
1544
|
}
|
|
1537
1545
|
}
|
|
1538
1546
|
|
|
1539
|
-
|
|
1540
|
-
|
|
1547
|
+
// Separates DOM writes from layout reads to avoid forced reflows. All resolver
|
|
1548
|
+
// elements are built inside a fragment, attached once, then read in a single pass.
|
|
1549
|
+
// Reading `getComputedStyle` after a write forces the browser to recompute layout,
|
|
1550
|
+
// so interleaving writes and reads inside a loop turns one reflow into N.
|
|
1551
|
+
function computeStyleValues(property, values) {
|
|
1552
|
+
const fragment = document.createDocumentFragment();
|
|
1553
|
+
|
|
1554
|
+
const elements = values.map(value => {
|
|
1555
|
+
const element = createElement("span", { style: `display: none; ${property}: ${value};` });
|
|
1556
|
+
fragment.appendChild(element);
|
|
1557
|
+
return element
|
|
1558
|
+
});
|
|
1559
|
+
|
|
1560
|
+
document.body.appendChild(fragment);
|
|
1541
1561
|
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
element.remove();
|
|
1562
|
+
const computed = elements.map(element =>
|
|
1563
|
+
window.getComputedStyle(element).getPropertyValue(property)
|
|
1564
|
+
);
|
|
1546
1565
|
|
|
1547
|
-
|
|
1566
|
+
elements.forEach(element => element.remove());
|
|
1567
|
+
return computed
|
|
1548
1568
|
}
|
|
1549
1569
|
|
|
1550
1570
|
class LexxyExtension {
|
|
@@ -2162,7 +2182,9 @@ class CommandDispatcher {
|
|
|
2162
2182
|
const selection = $getSelection();
|
|
2163
2183
|
if (!$isRangeSelection(selection)) return
|
|
2164
2184
|
|
|
2165
|
-
|
|
2185
|
+
const anchorNode = selection.anchor.getNode();
|
|
2186
|
+
|
|
2187
|
+
if (selection.isCollapsed() && !$getNearestNodeOfType(anchorNode, LinkNode)) {
|
|
2166
2188
|
const autoLinkNode = $createAutoLinkNode(url);
|
|
2167
2189
|
const textNode = $createTextNode(url);
|
|
2168
2190
|
autoLinkNode.append(textNode);
|
|
@@ -2542,14 +2564,24 @@ function normalizeFilteredText(string) {
|
|
|
2542
2564
|
.normalize("NFD").replace(/[\u0300-\u036f]/g, "") // Remove diacritics
|
|
2543
2565
|
}
|
|
2544
2566
|
|
|
2545
|
-
function
|
|
2546
|
-
|
|
2567
|
+
function filterMatchPosition(text, potentialMatch) {
|
|
2568
|
+
const normalizedText = normalizeFilteredText(text);
|
|
2569
|
+
const normalizedMatch = normalizeFilteredText(potentialMatch);
|
|
2570
|
+
|
|
2571
|
+
if (!normalizedMatch) return 0
|
|
2572
|
+
|
|
2573
|
+
const match = normalizedText.match(new RegExp(`(?:^|\\b)${escapeForRegExp(normalizedMatch)}`));
|
|
2574
|
+
return match ? match.index : -1
|
|
2547
2575
|
}
|
|
2548
2576
|
|
|
2549
2577
|
function upcaseFirst(string) {
|
|
2550
2578
|
return string.charAt(0).toUpperCase() + string.slice(1)
|
|
2551
2579
|
}
|
|
2552
2580
|
|
|
2581
|
+
function escapeForRegExp(string) {
|
|
2582
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2553
2585
|
// Parses a value that may arrive as a boolean or as a string (e.g. from DOM
|
|
2554
2586
|
// getAttribute) into a proper boolean. Ensures "false" doesn't evaluate as truthy.
|
|
2555
2587
|
function parseBoolean(value) {
|
|
@@ -6468,6 +6500,10 @@ class FormatEscapeExtension extends LexxyExtension {
|
|
|
6468
6500
|
return this.editorElement.supportsRichText
|
|
6469
6501
|
}
|
|
6470
6502
|
|
|
6503
|
+
get allowedElements() {
|
|
6504
|
+
return [ { tag: "li", attributes: [ "value" ] } ]
|
|
6505
|
+
}
|
|
6506
|
+
|
|
6471
6507
|
get lexicalExtension() {
|
|
6472
6508
|
return defineExtension({
|
|
6473
6509
|
name: "lexxy/format-escape",
|
|
@@ -6541,6 +6577,65 @@ function $handleArrowDownInCodeBlock(event) {
|
|
|
6541
6577
|
return false
|
|
6542
6578
|
}
|
|
6543
6579
|
|
|
6580
|
+
class LinkOpenerExtension extends LexxyExtension {
|
|
6581
|
+
get enabled() {
|
|
6582
|
+
return this.editorElement.supportsRichText
|
|
6583
|
+
}
|
|
6584
|
+
|
|
6585
|
+
get lexicalExtension() {
|
|
6586
|
+
return defineExtension({
|
|
6587
|
+
name: "lexxy/link-opener",
|
|
6588
|
+
register: () => {
|
|
6589
|
+
return mergeRegister(
|
|
6590
|
+
registerEventListener(window, "keydown", this.#update.bind(this)),
|
|
6591
|
+
registerEventListener(window, "keyup", this.#update.bind(this)),
|
|
6592
|
+
registerEventListener(window, "blur", this.#disable.bind(this)),
|
|
6593
|
+
registerEventListener(window, "focus", this.#refresh.bind(this))
|
|
6594
|
+
)
|
|
6595
|
+
}
|
|
6596
|
+
})
|
|
6597
|
+
}
|
|
6598
|
+
|
|
6599
|
+
#update(event) {
|
|
6600
|
+
if (this.#isModified(event)) {
|
|
6601
|
+
this.#enable();
|
|
6602
|
+
} else {
|
|
6603
|
+
this.#disable();
|
|
6604
|
+
}
|
|
6605
|
+
}
|
|
6606
|
+
|
|
6607
|
+
#refresh() {
|
|
6608
|
+
// Chrome dispatches events without modifier keys *for a while* after changing tabs
|
|
6609
|
+
setTimeout(() => {
|
|
6610
|
+
window.addEventListener("mousemove", this.#update.bind(this), { once: true });
|
|
6611
|
+
}, 200);
|
|
6612
|
+
}
|
|
6613
|
+
|
|
6614
|
+
#isModified(event) {
|
|
6615
|
+
return IS_APPLE ? event.metaKey : event.ctrlKey
|
|
6616
|
+
}
|
|
6617
|
+
|
|
6618
|
+
#enable() {
|
|
6619
|
+
for (const anchor of this.#anchors) {
|
|
6620
|
+
anchor.setAttribute("contenteditable", "false");
|
|
6621
|
+
anchor.setAttribute("target", "_blank");
|
|
6622
|
+
anchor.setAttribute("rel", "noopener noreferrer");
|
|
6623
|
+
}
|
|
6624
|
+
}
|
|
6625
|
+
|
|
6626
|
+
#disable() {
|
|
6627
|
+
for (const anchor of this.#anchors) {
|
|
6628
|
+
anchor.removeAttribute("contenteditable");
|
|
6629
|
+
anchor.removeAttribute("target");
|
|
6630
|
+
anchor.removeAttribute("rel");
|
|
6631
|
+
}
|
|
6632
|
+
}
|
|
6633
|
+
|
|
6634
|
+
get #anchors() {
|
|
6635
|
+
return this.editorElement.editorContentElement?.querySelectorAll("a") ?? []
|
|
6636
|
+
}
|
|
6637
|
+
}
|
|
6638
|
+
|
|
6544
6639
|
class LexicalEditorElement extends HTMLElement {
|
|
6545
6640
|
static formAssociated = true
|
|
6546
6641
|
static debug = false
|
|
@@ -6645,7 +6740,8 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
6645
6740
|
TrixContentExtension,
|
|
6646
6741
|
TablesExtension,
|
|
6647
6742
|
AttachmentsExtension,
|
|
6648
|
-
FormatEscapeExtension
|
|
6743
|
+
FormatEscapeExtension,
|
|
6744
|
+
LinkOpenerExtension
|
|
6649
6745
|
]
|
|
6650
6746
|
}
|
|
6651
6747
|
|
|
@@ -6895,7 +6991,9 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
6895
6991
|
}
|
|
6896
6992
|
|
|
6897
6993
|
#handleTurboBeforeCache = (event) => {
|
|
6898
|
-
this
|
|
6994
|
+
if (!this.closest("[data-turbo-permanent]")) {
|
|
6995
|
+
this.#reset();
|
|
6996
|
+
}
|
|
6899
6997
|
}
|
|
6900
6998
|
|
|
6901
6999
|
#synchronizeWithChanges() {
|
|
@@ -7168,19 +7266,30 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
7168
7266
|
]
|
|
7169
7267
|
}
|
|
7170
7268
|
|
|
7269
|
+
// Builds one resolver element per CSS value inside a hidden container, attaches
|
|
7270
|
+
// the container in a single DOM write, then reads all computed values in one pass
|
|
7271
|
+
// — triggering at most one forced reflow. The previous implementation interleaved
|
|
7272
|
+
// setProperty/getComputedStyle/removeProperty on the same element, forcing a style
|
|
7273
|
+
// recalc on every iteration during editor initialization.
|
|
7171
7274
|
#resolveColors(property, cssValues) {
|
|
7172
|
-
const
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7178
|
-
|
|
7179
|
-
|
|
7180
|
-
return { name: cssValue, value }
|
|
7275
|
+
const container = document.createElement("span");
|
|
7276
|
+
container.style.display = "none";
|
|
7277
|
+
|
|
7278
|
+
const resolvers = cssValues.map(cssValue => {
|
|
7279
|
+
const element = document.createElement("span");
|
|
7280
|
+
element.style.setProperty(property, cssValue);
|
|
7281
|
+
container.appendChild(element);
|
|
7282
|
+
return { element, name: cssValue }
|
|
7181
7283
|
});
|
|
7182
7284
|
|
|
7183
|
-
|
|
7285
|
+
this.appendChild(container);
|
|
7286
|
+
|
|
7287
|
+
const resolved = resolvers.map(({ element, name }) => ({
|
|
7288
|
+
name,
|
|
7289
|
+
value: window.getComputedStyle(element).getPropertyValue(property)
|
|
7290
|
+
}));
|
|
7291
|
+
|
|
7292
|
+
container.remove();
|
|
7184
7293
|
return resolved
|
|
7185
7294
|
}
|
|
7186
7295
|
|
|
@@ -7370,7 +7479,7 @@ class LinkDropdown extends ToolbarDropdown {
|
|
|
7370
7479
|
get #selectedLinkUrl() {
|
|
7371
7480
|
return this.editor.getEditorState().read(() => {
|
|
7372
7481
|
const linkNode = this.editorElement.selection.nearestNodeOfType(LinkNode);
|
|
7373
|
-
return linkNode?.
|
|
7482
|
+
return linkNode?.getURL() ?? ""
|
|
7374
7483
|
})
|
|
7375
7484
|
}
|
|
7376
7485
|
}
|
|
@@ -7534,21 +7643,41 @@ class LocalFilterSource extends BaseSource {
|
|
|
7534
7643
|
}
|
|
7535
7644
|
|
|
7536
7645
|
#buildListItemsFromPromptItems(promptItems, filter) {
|
|
7537
|
-
const listItems = [];
|
|
7538
7646
|
this.promptItemByListItem = new WeakMap();
|
|
7539
7647
|
|
|
7540
|
-
|
|
7541
|
-
|
|
7648
|
+
if (!filter) {
|
|
7649
|
+
return this.#buildAllListItems(promptItems)
|
|
7650
|
+
}
|
|
7542
7651
|
|
|
7652
|
+
const matches = [];
|
|
7653
|
+
for (const promptItem of promptItems) {
|
|
7543
7654
|
const searchableText = promptItem.getAttribute("search");
|
|
7544
|
-
|
|
7545
|
-
if (
|
|
7546
|
-
|
|
7547
|
-
this.promptItemByListItem.set(listItem, promptItem);
|
|
7548
|
-
listItems.push(listItem);
|
|
7655
|
+
const position = filterMatchPosition(searchableText, filter);
|
|
7656
|
+
if (position >= 0) {
|
|
7657
|
+
matches.push({ promptItem, position });
|
|
7549
7658
|
}
|
|
7550
7659
|
}
|
|
7551
7660
|
|
|
7661
|
+
matches.sort((a, b) => a.position - b.position);
|
|
7662
|
+
|
|
7663
|
+
const listItems = [];
|
|
7664
|
+
for (const { promptItem } of matches) {
|
|
7665
|
+
if (listItems.length >= MAX_RENDERED_SUGGESTIONS$1) break
|
|
7666
|
+
const listItem = this.buildListItemElementFor(promptItem);
|
|
7667
|
+
this.promptItemByListItem.set(listItem, promptItem);
|
|
7668
|
+
listItems.push(listItem);
|
|
7669
|
+
}
|
|
7670
|
+
return listItems
|
|
7671
|
+
}
|
|
7672
|
+
|
|
7673
|
+
#buildAllListItems(promptItems) {
|
|
7674
|
+
const listItems = [];
|
|
7675
|
+
for (const promptItem of promptItems) {
|
|
7676
|
+
if (listItems.length >= MAX_RENDERED_SUGGESTIONS$1) break
|
|
7677
|
+
const listItem = this.buildListItemElementFor(promptItem);
|
|
7678
|
+
this.promptItemByListItem.set(listItem, promptItem);
|
|
7679
|
+
listItems.push(listItem);
|
|
7680
|
+
}
|
|
7552
7681
|
return listItems
|
|
7553
7682
|
}
|
|
7554
7683
|
}
|
|
@@ -82,6 +82,10 @@ function extractPlainTextFromHtml(innerHtml = "") {
|
|
|
82
82
|
return parseHtml(innerHtml).body.textContent.trim()
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
function isActiveAndVisible(element) {
|
|
86
|
+
return element && !element.disabled && element.checkVisibility()
|
|
87
|
+
}
|
|
88
|
+
|
|
85
89
|
function highlightCode() {
|
|
86
90
|
const elements = document.querySelectorAll("pre[data-language]");
|
|
87
91
|
|
|
@@ -216,4 +220,4 @@ function collectTextNodes(root) {
|
|
|
216
220
|
return nodes
|
|
217
221
|
}
|
|
218
222
|
|
|
219
|
-
export { addBlockSpacing, createAttachmentFigure, createElement, dispatch, extractPlainTextFromHtml, generateDomId, highlightCode, isPreviewableImage, parseHtml };
|
|
223
|
+
export { addBlockSpacing, createAttachmentFigure, createElement, dispatch, extractPlainTextFromHtml, generateDomId, highlightCode, isActiveAndVisible, isPreviewableImage, parseHtml };
|