Browse Source

:art: fix https://github.com/siyuan-note/siyuan/issues/5382

Vanessa 2 years ago
parent
commit
3c04ad31a8

+ 4 - 3
app/src/block/util.ts

@@ -5,12 +5,15 @@ import {genListItemElement, updateListOrder} from "../protyle/wysiwyg/list";
 import {transaction, updateTransaction} from "../protyle/wysiwyg/transaction";
 import {scrollCenter} from "../util/highlightById";
 import {Constants} from "../constants";
+import {hideElements} from "../protyle/ui/hideElements";
 
 export const cancelSB = (protyle: IProtyle, nodeElement: Element) => {
     const doOperations: IOperation[] = [];
     const undoOperations: IOperation[] = [];
     let previousId = nodeElement.previousElementSibling ? nodeElement.previousElementSibling.getAttribute("data-node-id") : undefined;
     nodeElement.classList.remove("protyle-wysiwyg--select");
+    nodeElement.removeAttribute("select-start");
+    nodeElement.removeAttribute("select-end");
     const id = nodeElement.getAttribute("data-node-id");
     const sbElement = genSBElement(nodeElement.getAttribute("data-sb-layout"), id, nodeElement.lastElementChild.outerHTML);
     undoOperations.push({
@@ -85,9 +88,7 @@ export const insertEmptyBlock = (protyle: IProtyle, position: InsertPosition, id
             } else {
                 blockElement = selectElements[selectElements.length - 1];
             }
-            selectElements.forEach(item => {
-                item.classList.remove("protyle-wysiwyg--select");
-            });
+            hideElements(["select"], protyle);
         } else {
             blockElement = hasClosestBlock(range.startContainer) as HTMLElement;
             blockElement = getTopAloneElement(blockElement);

+ 3 - 3
app/src/protyle/gutter/index.ts

@@ -1333,7 +1333,7 @@ export class Gutter {
                 label: window.siyuan.languages["insert-before"],
                 accelerator: window.siyuan.config.keymap.editor.general.insertBefore.custom,
                 click() {
-                    nodeElement.classList.remove("protyle-wysiwyg--select");
+                    hideElements(["select"], protyle);
                     countBlockWord([], protyle.block.rootID);
                     insertEmptyBlock(protyle, "beforebegin", id);
                 }
@@ -1343,7 +1343,7 @@ export class Gutter {
                 label: window.siyuan.languages["insert-after"],
                 accelerator: window.siyuan.config.keymap.editor.general.insertAfter.custom,
                 click() {
-                    nodeElement.classList.remove("protyle-wysiwyg--select");
+                    hideElements(["select"], protyle);
                     countBlockWord([], protyle.block.rootID);
                     insertEmptyBlock(protyle, "afterend", id);
                 }
@@ -1353,7 +1353,7 @@ export class Gutter {
             label: window.siyuan.languages.jumpToParentNext,
             accelerator: window.siyuan.config.keymap.editor.general.jumpToParentNext.custom,
             click() {
-                nodeElement.classList.remove("protyle-wysiwyg--select");
+                hideElements(["select"], protyle);
                 jumpToParentNext(protyle, nodeElement);
             }
         }).element);

+ 2 - 0
app/src/protyle/ui/hideElements.ts

@@ -38,6 +38,8 @@ export const hideElements = (panels: string[], protyle?: IProtyle, focusHide = f
     if (panels.includes("select")) {
         protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select").forEach(item => {
             item.classList.remove("protyle-wysiwyg--select");
+            item.removeAttribute("select-start");
+            item.removeAttribute("select-end");
         });
     }
 };

+ 4 - 0
app/src/protyle/util/editorCommonEvent.ts

@@ -597,6 +597,8 @@ export const dropEvent = (protyle: IProtyle, editorElement: HTMLElement) => {
             });
             sourceElements.forEach(item => {
                 item.classList.remove("protyle-wysiwyg--select", "protyle-wysiwyg--hl");
+                item.removeAttribute("select-start");
+                item.removeAttribute("select-end");
                 // 反链提及有高亮,如果拖拽到正文的话,应移除
                 item.querySelectorAll('[data-type="search-mark"]').forEach(markItem => {
                     markItem.outerHTML = markItem.innerHTML;
@@ -778,6 +780,8 @@ export const dropEvent = (protyle: IProtyle, editorElement: HTMLElement) => {
         if (nodeElement) {
             if ((window.siyuan.dragElement?.getAttribute("data-selected-ids") || "").indexOf(nodeElement.getAttribute("data-node-id")) === -1) {
                 nodeElement.classList.remove("protyle-wysiwyg--select");
+                nodeElement.removeAttribute("select-start");
+                nodeElement.removeAttribute("select-end");
             }
             nodeElement.classList.remove("dragover__top", "dragover__bottom", "dragover__left", "dragover__right");
         }

+ 4 - 2
app/src/protyle/util/paste.ts

@@ -16,6 +16,7 @@ import {isDynamicRef, isFileAnnotation} from "../../util/functions";
 import {insertHTML} from "./insertHTML";
 import {scrollCenter} from "../../util/highlightById";
 import {getContenteditableElement} from "../wysiwyg/getBlock";
+import {hideElements} from "../ui/hideElements";
 
 const filterClipboardHint = (protyle: IProtyle, textPlain: string) => {
     let needRender = true;
@@ -173,8 +174,9 @@ export const paste = async (protyle: IProtyle, event: (ClipboardEvent | DragEven
         }
         return;
     }
-    protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select, .protyle-wysiwyg--hl").forEach(item => {
-        item.classList.remove("protyle-wysiwyg--select", "protyle-wysiwyg--hl");
+    hideElements(["select"], protyle);
+    protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--hl").forEach(item => {
+        item.classList.remove("protyle-wysiwyg--hl");
     });
     const code = processPasteCode(textHTML, textPlain);
     const range = getEditorRange(protyle.wysiwyg.element);

+ 2 - 3
app/src/protyle/util/selection.ts

@@ -7,6 +7,7 @@ import {
 } from "../wysiwyg/getBlock";
 import {hasClosestByAttribute, hasClosestByMatchTag} from "./hasClosest";
 import {countBlockWord, countSelectWord} from "../../layout/status";
+import {hideElements} from "../ui/hideElements";
 
 const selectIsEditor = (editor: Element, range?: Range) => {
     if (!range) {
@@ -113,9 +114,7 @@ export const selectAll = (protyle: IProtyle, nodeElement: Element, range: Range)
     if (protyle.wysiwyg.element.childElementCount === selectElements.length && selectElements[0].parentElement.isSameNode(protyle.wysiwyg.element)) {
         return true;
     }
-    selectElements.forEach(item => {
-        item.classList.remove("protyle-wysiwyg--select");
-    });
+    hideElements(["select"], protyle);
     const ids: string [] = [];
     Array.from(protyle.wysiwyg.element.children).forEach(item => {
         item.classList.add("protyle-wysiwyg--select");

+ 125 - 1
app/src/protyle/wysiwyg/commonHotkey.ts

@@ -1,11 +1,15 @@
 import {matchHotKey} from "../util/hotKey";
 import {fetchPost} from "../../util/fetch";
 import {writeText} from "../util/compatibility";
-import {focusByOffset, getSelectionOffset} from "../util/selection";
+import {focusByOffset, getSelectionOffset, setFirstNodeRange, setLastNodeRange} from "../util/selection";
 import {fullscreen, netImg2LocalAssets} from "../breadcrumb/action";
 import {setPadding} from "../ui/initUI";
 import {openBacklink, openGraph, openOutline} from "../../layout/dock/util";
 import {reloadProtyle} from "../util/reload";
+import {getContenteditableElement} from "./getBlock";
+import {hasClosestByMatchTag} from "../util/hasClosest";
+import {hideElements} from "../ui/hideElements";
+import {countBlockWord} from "../../layout/status";
 
 export const commonHotkey = (protyle: IProtyle, event: KeyboardEvent) => {
     const target = event.target as HTMLElement;
@@ -64,3 +68,123 @@ export const commonHotkey = (protyle: IProtyle, event: KeyboardEvent) => {
         }
     }
 };
+
+export const upSelect = (options: {
+    protyle: IProtyle,
+    event: KeyboardEvent,
+    nodeElement: HTMLElement,
+    editorElement: HTMLElement,
+    range: Range,
+    cb: (selectElements: NodeListOf<Element>) => void
+}) => {
+    const selectElements = options.protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select");
+    if (selectElements.length > 0) {
+        options.event.stopPropagation();
+        options.event.preventDefault();
+    } else {
+        const start = getSelectionOffset(options.nodeElement, options.editorElement, options.range).start;
+        if (start !== 0) {
+            const editElement = getContenteditableElement(options.nodeElement);
+            if (editElement.tagName === "TABLE") {
+                const cellElement = hasClosestByMatchTag(options.range.startContainer, "TH") || hasClosestByMatchTag(options.range.startContainer, "TD") || editElement.querySelector("th, td");
+                if (getSelectionOffset(cellElement, cellElement, options.range).start !== 0) {
+                    setFirstNodeRange(cellElement, options.range);
+                    options.event.stopPropagation();
+                    options.event.preventDefault();
+                    return;
+                }
+            } else {
+                const firstIndex = editElement.textContent.indexOf("\n");
+                if (firstIndex === -1 || start <= firstIndex || start === editElement.textContent.replace("\n", " ").indexOf("\n")) {
+                    setFirstNodeRange(editElement, options.range);
+                    options.event.stopPropagation();
+                    options.event.preventDefault();
+                    return;
+                } else {
+                    return;
+                }
+            }
+        }
+    }
+    options.range.collapse(true);
+    hideElements(["toolbar"], options.protyle);
+    if (selectElements.length === 0) {
+        options.nodeElement.classList.add("protyle-wysiwyg--select");
+    } else {
+        options.cb(selectElements);
+    }
+    const ids: string[] = [];
+    options.protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select").forEach(item => {
+        ids.push(item.getAttribute("data-node-id"));
+    });
+    countBlockWord(ids, options.protyle.block.rootID);
+    options.event.stopPropagation();
+    options.event.preventDefault();
+}
+
+export const downSelect = (options: {
+    protyle: IProtyle,
+    event: KeyboardEvent,
+    nodeElement: HTMLElement,
+    editorElement: HTMLElement,
+    range: Range,
+    cb: (selectElement: NodeListOf<Element>) => void
+}) => {
+    const selectElements = options.protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select");
+    if (selectElements.length > 0) {
+        options.event.stopPropagation();
+        options.event.preventDefault();
+    } else {
+        const editElement = getContenteditableElement(options.nodeElement);
+        const end = getSelectionOffset(options.nodeElement, options.editorElement, options.range).end;
+        if (end < editElement.textContent.length) {
+            if (end > editElement.textContent.lastIndexOf("\n")) {
+                setLastNodeRange(editElement, options.range, false);
+                options.event.stopPropagation();
+                options.event.preventDefault();
+                return;
+            } else {
+                return;
+            }
+        }
+    }
+    options.range.collapse(false);
+    hideElements(["toolbar"], options.protyle);
+    if (selectElements.length === 0) {
+        options.nodeElement.classList.add("protyle-wysiwyg--select");
+    } else {
+        options.cb(selectElements)
+    }
+    const ids: string[] = [];
+    options.protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select").forEach(item => {
+        ids.push(item.getAttribute("data-node-id"));
+    });
+    countBlockWord(ids, options.protyle.block.rootID);
+    options.event.stopPropagation();
+    options.event.preventDefault();
+}
+
+export const getStartEndElement = (selectElements: NodeListOf<Element>) => {
+    let startElement
+    let endElement
+    selectElements.forEach(item => {
+        if (item.getAttribute("select-start")) {
+            startElement = item
+        }
+        if (item.getAttribute("select-end")) {
+            endElement = item
+        }
+    });
+    if (!startElement) {
+        startElement = selectElements[0]
+        startElement.setAttribute("select-start", "true")
+    }
+    if (!endElement) {
+        endElement = selectElements[selectElements.length - 1]
+        endElement.setAttribute("select-end", "true")
+    }
+    return {
+        startElement,
+        endElement
+    }
+}

+ 2 - 1
app/src/protyle/wysiwyg/enter.ts

@@ -12,6 +12,7 @@ import {breakList, genListItemElement, listOutdent, updateListOrder} from "./lis
 import {highlightRender} from "../markdown/highlightRender";
 import {Constants} from "../../constants";
 import {scrollCenter} from "../../util/highlightById";
+import {hideElements} from "../ui/hideElements";
 
 const listEnter = (protyle: IProtyle, blockElement: HTMLElement, range: Range) => {
     const listItemElement = blockElement.parentElement;
@@ -193,7 +194,7 @@ export const enter = (blockElement: HTMLElement, range: Range, protyle: IProtyle
     if (!disableElement && blockElement.classList.contains("protyle-wysiwyg--select")) {
         setLastNodeRange(getContenteditableElement(blockElement), range, false);
         range.collapse(false);
-        blockElement.classList.remove("protyle-wysiwyg--select");
+        hideElements(["select"], protyle);
         return;
     }
     // https://github.com/siyuan-note/siyuan/issues/5471

+ 6 - 0
app/src/protyle/wysiwyg/index.ts

@@ -1935,15 +1935,21 @@ export class WYSIWYG {
                     ctrlElement = getTopAloneElement(ctrlElement) as HTMLElement;
                     if (ctrlElement.classList.contains("protyle-wysiwyg--select")) {
                         ctrlElement.classList.remove("protyle-wysiwyg--select");
+                        ctrlElement.removeAttribute("select-start");
+                        ctrlElement.removeAttribute("select-end");
                     } else {
                         ctrlElement.classList.add("protyle-wysiwyg--select");
                     }
                     ctrlElement.querySelectorAll(".protyle-wysiwyg--select").forEach(item => {
                         item.classList.remove("protyle-wysiwyg--select");
+                        item.removeAttribute("select-start");
+                        item.removeAttribute("select-end");
                     });
                     const ctrlParentElement = hasClosestByClassName(ctrlElement, "protyle-wysiwyg--select");
                     if (ctrlParentElement && !ctrlElement.isSameNode(ctrlParentElement)) {
                         ctrlParentElement.classList.remove("protyle-wysiwyg--select");
+                        ctrlParentElement.removeAttribute("select-start");
+                        ctrlParentElement.removeAttribute("select-end");
                     }
                     const ids: string[] = [];
                     protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select").forEach(item => {

+ 104 - 97
app/src/protyle/wysiwyg/keydown.ts

@@ -43,7 +43,7 @@ import {insertEmptyBlock, jumpToParentNext} from "../../block/util";
 import {isLocalPath} from "../../util/pathName";
 /// #if !MOBILE
 import {openBy, openFileById} from "../../editor/util";
-import {commonHotkey} from "./commonHotkey";
+import {commonHotkey, downSelect, getStartEndElement, upSelect} from "./commonHotkey";
 /// #endif
 import {linkMenu, refMenu, setFold, zoomOut} from "../../menus/protyle";
 import {removeEmbed} from "./removeEmbed";
@@ -145,9 +145,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => {
             if (selectElements.length > 0) {
                 event.preventDefault();
                 event.stopPropagation();
-                selectElements.forEach(item => {
-                    item.classList.remove("protyle-wysiwyg--select");
-                });
+                hideElements(["select"], protyle);
                 if (event.key === "ArrowDown") {
                     const currentSelectElement = selectElements[selectElements.length - 1] as HTMLElement;
                     let nextElement = getNextBlock(currentSelectElement) as HTMLElement;
@@ -286,109 +284,120 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => {
             }
         }
 
-        if (matchHotKey("⇧↑", event)) {
-            const selectElements = protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select");
-            if (selectElements.length > 0) {
-                event.stopPropagation();
-                event.preventDefault();
-            } else {
-                const start = getSelectionOffset(nodeElement, editorElement, range).start;
-                if (start !== 0) {
-                    const editElement = getContenteditableElement(nodeElement);
-                    if (editElement.tagName === "TABLE") {
-                        const cellElement = hasClosestByMatchTag(range.startContainer, "TH") || hasClosestByMatchTag(range.startContainer, "TD") || editElement.querySelector("th, td");
-                        if (getSelectionOffset(cellElement, cellElement, range).start !== 0) {
-                            setFirstNodeRange(cellElement, range);
-                            event.stopPropagation();
-                            event.preventDefault();
-                            return;
-                        }
-                    } else {
-                        const firstIndex = editElement.textContent.indexOf("\n");
-                        if (firstIndex === -1 || start <= firstIndex || start === editElement.textContent.replace("\n", " ").indexOf("\n")) {
-                            setFirstNodeRange(editElement, range);
-                            event.stopPropagation();
-                            event.preventDefault();
-                            return;
-                        } else {
-                            return;
+        if (matchHotKey("⌥⇧↑", event)) {
+            upSelect({
+                protyle, event, nodeElement, editorElement, range,
+                cb(selectElements) {
+                    const previousElement = selectElements[0].previousElementSibling as HTMLElement;
+                    if (previousElement && previousElement.getAttribute("data-node-id")) {
+                        previousElement.classList.add("protyle-wysiwyg--select");
+                        selectElements.forEach(item => {
+                            item.removeAttribute("select-end")
+                        })
+                        previousElement.setAttribute("select-end", "true");
+                        const top = previousElement.getBoundingClientRect().top - protyle.contentElement.getBoundingClientRect().top;
+                        if (top < 0) {
+                            protyle.contentElement.scrollTop = protyle.contentElement.scrollTop + top;
+                            protyle.scroll.lastScrollTop = protyle.contentElement.scrollTop + 1;
                         }
+                    } else if (!selectElements[0].parentElement.classList.contains("protyle-wysiwyg")) {
+                        hideElements(["select"], protyle);
+                        selectElements[0].parentElement.classList.add("protyle-wysiwyg--select");
                     }
                 }
-            }
-            range.collapse(true);
-            hideElements(["toolbar"], protyle);
-            if (selectElements.length === 0) {
-                nodeElement.classList.add("protyle-wysiwyg--select");
-            } else {
-                const previousElement = selectElements[0].previousElementSibling as HTMLElement;
-                if (previousElement && previousElement.getAttribute("data-node-id")) {
-                    previousElement.classList.add("protyle-wysiwyg--select");
-                    const top = previousElement.getBoundingClientRect().top - protyle.contentElement.getBoundingClientRect().top;
-                    if (top < 0) {
-                        protyle.contentElement.scrollTop = protyle.contentElement.scrollTop + top;
-                        protyle.scroll.lastScrollTop = protyle.contentElement.scrollTop + 1;
+            });
+            return;
+        }
+
+        if (matchHotKey("⌥⇧↓", event)) {
+            downSelect({
+                protyle, event, nodeElement, editorElement, range,
+                cb(selectElements) {
+                    const selectLastElement = selectElements[selectElements.length - 1];
+                    const nextElement = selectLastElement.nextElementSibling as HTMLElement;
+                    if (nextElement && nextElement.getAttribute("data-node-id")) {
+                        nextElement.classList.add("protyle-wysiwyg--select");
+                        selectElements.forEach(item => {
+                            item.removeAttribute("select-end")
+                        })
+                        nextElement.setAttribute("select-end", "true");
+                        const bottom = nextElement.getBoundingClientRect().bottom - protyle.contentElement.getBoundingClientRect().bottom;
+                        if (bottom > 0) {
+                            protyle.contentElement.scrollTop = protyle.contentElement.scrollTop + bottom;
+                            protyle.scroll.lastScrollTop = protyle.contentElement.scrollTop - 1;
+                        }
+                    } else if (!selectLastElement.parentElement.classList.contains("protyle-wysiwyg")) {
+                        hideElements(["select"], protyle);
+                        selectLastElement.parentElement.classList.add("protyle-wysiwyg--select");
                     }
-                } else if (!selectElements[0].parentElement.classList.contains("protyle-wysiwyg")) {
-                    hideElements(["select"], protyle);
-                    selectElements[0].parentElement.classList.add("protyle-wysiwyg--select");
                 }
-            }
-            const ids: string[] = [];
-            protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select").forEach(item => {
-                ids.push(item.getAttribute("data-node-id"));
-            });
-            countBlockWord(ids, protyle.block.rootID);
-            event.stopPropagation();
-            event.preventDefault();
+            })
             return;
         }
 
-        if (matchHotKey("⇧↓", event)) {
-            const selectElements = protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select");
-            if (selectElements.length > 0) {
-                event.stopPropagation();
-                event.preventDefault();
-            } else {
-                const editElement = getContenteditableElement(nodeElement);
-                const end = getSelectionOffset(nodeElement, editorElement, range).end;
-                if (end < editElement.textContent.length) {
-                    if (end > editElement.textContent.lastIndexOf("\n")) {
-                        setLastNodeRange(editElement, range, false);
-                        event.stopPropagation();
-                        event.preventDefault();
-                        return;
+        if (matchHotKey("⇧↑", event)) {
+            upSelect({
+                protyle, event, nodeElement, editorElement, range,
+                cb(selectElements) {
+                    const startEndElement = getStartEndElement(selectElements)
+                    if (startEndElement.startElement.getBoundingClientRect().top >= startEndElement.endElement.getBoundingClientRect().top) {
+                        const previousElement = startEndElement.endElement.previousElementSibling as HTMLElement;
+                        if (previousElement && previousElement.getAttribute("data-node-id")) {
+                            previousElement.classList.add("protyle-wysiwyg--select");
+                            previousElement.setAttribute("select-end", "true");
+                            startEndElement.endElement.removeAttribute("select-end");
+                            const top = previousElement.getBoundingClientRect().top - protyle.contentElement.getBoundingClientRect().top;
+                            if (top < 0) {
+                                protyle.contentElement.scrollTop = protyle.contentElement.scrollTop + top;
+                                protyle.scroll.lastScrollTop = protyle.contentElement.scrollTop + 1;
+                            }
+                        } else if (!startEndElement.endElement.parentElement.classList.contains("protyle-wysiwyg")) {
+                            hideElements(["select"], protyle);
+                            startEndElement.endElement.parentElement.classList.add("protyle-wysiwyg--select");
+                        }
                     } else {
-                        return;
+                        startEndElement.endElement.classList.remove("protyle-wysiwyg--select");
+                        startEndElement.endElement.removeAttribute("select-end");
+                        const previousElement = getPreviousBlock(startEndElement.endElement);
+                        if (previousElement) {
+                            previousElement.setAttribute("select-end", "true")
+                        }
                     }
                 }
-            }
-            range.collapse(false);
-            hideElements(["toolbar"], protyle);
-            const selectLastElement = selectElements[selectElements.length - 1];
-            if (selectElements.length === 0) {
-                nodeElement.classList.add("protyle-wysiwyg--select");
-            } else {
-                const nextElement = selectLastElement.nextElementSibling as HTMLElement;
-                if (nextElement && nextElement.getAttribute("data-node-id")) {
-                    nextElement.classList.add("protyle-wysiwyg--select");
-                    const bottom = nextElement.getBoundingClientRect().bottom - protyle.contentElement.getBoundingClientRect().bottom;
-                    if (bottom > 0) {
-                        protyle.contentElement.scrollTop = protyle.contentElement.scrollTop + bottom;
-                        protyle.scroll.lastScrollTop = protyle.contentElement.scrollTop - 1;
+            })
+            return;
+        }
+
+        if (matchHotKey("⇧↓", event)) {
+            downSelect({
+                protyle, event, nodeElement, editorElement, range,
+                cb(selectElements) {
+                    const startEndElement = getStartEndElement(selectElements)
+                    if (startEndElement.startElement.getBoundingClientRect().top <= startEndElement.endElement.getBoundingClientRect().top) {
+                        const nextElement = startEndElement.endElement.nextElementSibling as HTMLElement;
+                        if (nextElement && nextElement.getAttribute("data-node-id")) {
+                            nextElement.classList.add("protyle-wysiwyg--select");
+                            nextElement.setAttribute("select-end", "true");
+                            startEndElement.endElement.removeAttribute("select-end");
+                            const bottom = nextElement.getBoundingClientRect().bottom - protyle.contentElement.getBoundingClientRect().bottom;
+                            if (bottom > 0) {
+                                protyle.contentElement.scrollTop = protyle.contentElement.scrollTop + bottom;
+                                protyle.scroll.lastScrollTop = protyle.contentElement.scrollTop - 1;
+                            }
+                        } else if (!startEndElement.endElement.parentElement.classList.contains("protyle-wysiwyg")) {
+                            hideElements(["select"], protyle);
+                            startEndElement.endElement.parentElement.classList.add("protyle-wysiwyg--select");
+                        }
+                    } else {
+                        startEndElement.endElement.classList.remove("protyle-wysiwyg--select");
+                        startEndElement.endElement.removeAttribute("select-end");
+                        const nextElement = getNextBlock(startEndElement.endElement);
+                        if (nextElement) {
+                            nextElement.setAttribute("select-end", "true")
+                        }
                     }
-                } else if (!selectLastElement.parentElement.classList.contains("protyle-wysiwyg")) {
-                    hideElements(["select"], protyle);
-                    selectLastElement.parentElement.classList.add("protyle-wysiwyg--select");
                 }
-            }
-            const ids: string[] = [];
-            protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select").forEach(item => {
-                ids.push(item.getAttribute("data-node-id"));
-            });
-            countBlockWord(ids, protyle.block.rootID);
-            event.stopPropagation();
-            event.preventDefault();
+            })
             return;
         }
 
@@ -1121,9 +1130,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => {
                 // 防止 ESC 时选中当前块
                 window.siyuan.menus.menu.remove();
             } else {
-                protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select").forEach(item => {
-                    item.classList.remove("protyle-wysiwyg--select");
-                });
+                hideElements(["select"], protyle);
                 range.collapse(false);
                 nodeElement.classList.add("protyle-wysiwyg--select");
                 countBlockWord([nodeElement.getAttribute("data-node-id")], protyle.block.rootID);

+ 6 - 0
app/src/protyle/wysiwyg/list.ts

@@ -50,6 +50,8 @@ export const listIndent = (protyle: IProtyle, liItemElements: Element[], range:
     range.insertNode(document.createElement("wbr"));
     liItemElements.forEach(item => {
         item.classList.remove("protyle-wysiwyg--select");
+        item.removeAttribute("select-start");
+        item.removeAttribute("select-end");
     });
     const html = previousElement.parentElement.outerHTML;
     if (previousElement.lastElementChild.previousElementSibling.getAttribute("data-type") === "NodeList") {
@@ -297,6 +299,8 @@ export const listOutdent = (protyle: IProtyle, liItemElements: Element[], range:
         let previousElement: Element = liElement;
         liItemElements.forEach(item => {
             item.classList.remove("protyle-wysiwyg--select");
+            item.removeAttribute("select-start");
+            item.removeAttribute("select-end");
             Array.from(item.children).forEach((blockElement, index) => {
                 const id = blockElement.getAttribute("data-node-id");
                 if (!id) {
@@ -379,6 +383,8 @@ export const listOutdent = (protyle: IProtyle, liItemElements: Element[], range:
     const previousID = liItemElements[0].previousElementSibling?.getAttribute("data-node-id");
     liItemElements.forEach(item => {
         item.classList.remove("protyle-wysiwyg--select");
+        item.removeAttribute("select-start");
+        item.removeAttribute("select-end");
     });
     let startIndex;
     if (!liItemElements[0].previousElementSibling && liElement.getAttribute("data-subtype") === "o") {

+ 1 - 1
app/src/protyle/wysiwyg/remove.ts

@@ -183,8 +183,8 @@ export const removeBlock = (protyle: IProtyle, blockElement: Element, range: Ran
         let sideElement = selectElements[0].previousElementSibling || selectElements[selectElements.length - 1].nextElementSibling;
         let listElement: Element;
         let topParentElement: Element;
+        hideElements(["select"], protyle);
         selectElements.find((item: HTMLElement) => {
-            item.classList.remove("protyle-wysiwyg--select");
             const topElement = getTopAloneElement(item);
             topParentElement = topElement.parentElement;
             const id = topElement.getAttribute("data-node-id");

+ 6 - 0
app/src/protyle/wysiwyg/transaction.ts

@@ -638,6 +638,8 @@ export const turnsIntoOneTransaction = (options: { protyle: IProtyle, selectsEle
     let itemPreviousId: string;
     options.selectsElement.forEach((item, index) => {
         item.classList.remove("protyle-wysiwyg--select");
+        item.removeAttribute("select-start");
+        item.removeAttribute("select-end");
         const itemId = item.getAttribute("data-node-id");
         undoOperations.push({
             action: "move",
@@ -741,6 +743,8 @@ export const turnsIntoTransaction = (options: {
             setFold(options.protyle, item);
         }
         item.classList.remove("protyle-wysiwyg--select");
+        item.removeAttribute("select-start")
+        item.removeAttribute("select-end")
         html += item.outerHTML;
         const id = item.getAttribute("data-node-id");
         undoOperations.push({
@@ -858,6 +862,8 @@ export const updateBatchTransaction = (nodeElements: Element[], protyle: IProtyl
     nodeElements.forEach((element) => {
         const id = element.getAttribute("data-node-id");
         element.classList.remove("protyle-wysiwyg--select");
+        element.removeAttribute("select-start");
+        element.removeAttribute("select-end");
         undoOperations.push({
             action: "update",
             id,