Browse Source

:art: https://github.com/siyuan-note/siyuan/issues/8061

Vanessa 2 years ago
parent
commit
9cb6e9ec25

+ 27 - 8
app/src/card/newCardTab.ts

@@ -3,13 +3,15 @@ import {getInstanceById, getWndByLayout} from "../layout/util";
 import {Tab} from "../layout/Tab";
 import {Custom} from "../layout/dock/Custom";
 import {Dialog} from "../dialog";
-import {genCardHTML} from "./openCard";
+import {bindCardEvent, genCardHTML} from "./openCard";
 import {fetchPost} from "../util/fetch";
+import {Protyle} from "../protyle";
 
 export const newCardTab = (options: {
-    type: TCardType,
+    cardType: TCardType,
     id: string,
-    dialog: Dialog
+    dialog: Dialog,
+    title?: string
 }) => {
     let wnd: Wnd;
     const element = document.querySelector(".layout__wnd--active");
@@ -19,6 +21,7 @@ export const newCardTab = (options: {
     if (!wnd) {
         wnd = getWndByLayout(window.siyuan.layout.centerLayout);
     }
+    let editor: Protyle
     const tab = new Tab({
         icon: "iconRiffCard",
         title: window.siyuan.languages.spaceRepetition,
@@ -27,23 +30,39 @@ export const newCardTab = (options: {
                 type: "card",
                 tab,
                 data: {
-                    type: options.type,
+                    title: options.title,
+                    cardType: options.cardType,
                     id: options.id
                 },
                 init(element) {
-                    fetchPost(options.type === "all" ? "/api/riff/getRiffDueCards" :
-                        (options.type === "doc" ? "/api/riff/getTreeRiffDueCards" : "/api/riff/getNotebookRiffDueCards"), {
+                    fetchPost(options.cardType === "all" ? "/api/riff/getRiffDueCards" :
+                        (options.cardType === "doc" ? "/api/riff/getTreeRiffDueCards" : "/api/riff/getNotebookRiffDueCards"), {
                         rootID: options.id,
                         deckID: options.id,
                         notebook: options.id,
                     }, (response) => {
                         element.innerHTML = genCardHTML({
                             id: options.id,
-                            cardType: options.type,
-                            blocks: response.data,
+                            cardType: options.cardType,
+                            blocks: response.data.cards,
                             isTab: true,
                         });
+
+                        editor = bindCardEvent({
+                            element,
+                            id: options.id,
+                            title: options.title,
+                            cardType: options.cardType,
+                            blocks: response.data.cards,
+                            dialog: options.dialog,
+                        })
                     });
+                },
+                destroy() {
+                    editor.destroy();
+                },
+                resize(){
+                    editor.resize();
                 }
             });
             tab.addModel(custom);

+ 96 - 77
app/src/card/openCard.ts

@@ -33,8 +33,8 @@ export const genCardHTML = (options: {
             <svg><use xlink:href="#iconCloseRound"></use></svg>
         </div>` : `<div data-type="fullscreen" class="b3-tooltips b3-tooltips__sw block__icon block__icon--show" aria-label="${window.siyuan.languages.fullscreen}">
     <svg><use xlink:href="#iconFullscreen"></use></svg>
-</div><div class="fn__space"></div>
-<div data-type="sticktab" class="b3-tooltips b3-tooltips__sw block__icon block__icon--show" aria-label="${window.siyuan.languages.openInNewTab}">
+</div><div class="fn__space${options.isTab ? " fn__none" : ""}"></div>
+<div data-type="sticktab" class="b3-tooltips b3-tooltips__sw block__icon block__icon--show${options.isTab ? " fn__none" : ""}" aria-label="${window.siyuan.languages.openInNewTab}">
     <svg><use xlink:href="#iconLayoutRight"></use></svg>
 </div>`}
     </div>
@@ -54,35 +54,35 @@ export const genCardHTML = (options: {
     <div class="fn__flex card__action fn__none">
         <div>
             <span>${window.siyuan.languages.nextRound}</span>
-            <button data-type="-3" aria-label="0" class="b3-button b3-button--cancel b3-tooltips__s b3-tooltips">
+            <button data-type="-3" aria-label="0" class="b3-button b3-button--cancel b3-tooltips__n b3-tooltips">
                 <div>💤</div>
                 ${window.siyuan.languages.skip} (0)
             </button>
         </div>
         <div>
             <span></span>
-            <button data-type="0" aria-label="1 / j" class="b3-button b3-button--error b3-tooltips__s b3-tooltips">
+            <button data-type="0" aria-label="1 / j" class="b3-button b3-button--error b3-tooltips__n b3-tooltips">
                 <div>🙈</div>
                 ${window.siyuan.languages.cardRatingAgain} (1)
             </button>
         </div>
         <div>
             <span></span>
-            <button data-type="1" aria-label="2 / k" class="b3-button b3-button--warning b3-tooltips__s b3-tooltips">
+            <button data-type="1" aria-label="2 / k" class="b3-button b3-button--warning b3-tooltips__n b3-tooltips">
                 <div>😬</div>
                 ${window.siyuan.languages.cardRatingHard} (2)
             </button>
         </div>
         <div>
             <span></span>
-            <button data-type="2" aria-label="3 / l" class="b3-button b3-button--info b3-tooltips__s b3-tooltips">
+            <button data-type="2" aria-label="3 / l" class="b3-button b3-button--info b3-tooltips__n b3-tooltips">
                 <div>😊</div>
                 ${window.siyuan.languages.cardRatingGood} (3)
             </button>
         </div>
         <div>
             <span></span>
-            <button data-type="3" aria-label="4 / ;" class="b3-button b3-button--success b3-tooltips__s b3-tooltips">
+            <button data-type="3" aria-label="4 / ;" class="b3-button b3-button--success b3-tooltips__n b3-tooltips">
                 <div>🌈</div>
                 ${window.siyuan.languages.cardRatingEasy} (4)
             </button>
@@ -91,44 +91,16 @@ export const genCardHTML = (options: {
 </div>`;
 };
 
-export const openCard = () => {
-    fetchPost("/api/riff/getRiffDueCards", {deckID: ""}, (cardsResponse) => {
-        openCardByData(cardsResponse.data, "all");
-    });
-};
-
-export const openCardByData = (cardsData: {
-    cards: ICard[],
-    unreviewedCount: number
-}, cardType: TCardType, id?: string, title?: string) => {
-    const exit = window.siyuan.dialogs.find(item => {
-        if (item.element.getAttribute("data-key") === window.siyuan.config.keymap.general.riffCard.custom) {
-            item.destroy();
-            return true;
-        }
-    });
-    if (exit) {
-        return;
-    }
-
-    let blocks = cardsData.cards;
+export const bindCardEvent = (options: {
+    element: Element,
+    title?: string,
+    blocks: ICard[],
+    cardType: TCardType,
+    id?: string,
+    dialog: Dialog,
+}) => {
     let index = 0;
-    const dialog = new Dialog({
-        content: genCardHTML({id, cardType, blocks, isTab: false}),
-        width: isMobile() ? "100vw" : "80vw",
-        height: isMobile() ? "100vh" : "70vh",
-        destroyCallback() {
-            if (editor) {
-                editor.destroy();
-                if (window.siyuan.mobile) {
-                    window.siyuan.mobile.popEditor = null;
-                }
-            }
-        }
-    });
-    (dialog.element.querySelector(".b3-dialog__scrim") as HTMLElement).style.backgroundColor = "var(--b3-theme-background)";
-    (dialog.element.querySelector(".b3-dialog__container") as HTMLElement).style.maxWidth = "1024px";
-    const editor = new Protyle(dialog.element.querySelector("[data-type='render']") as HTMLElement, {
+    const editor = new Protyle(options.element.querySelector("[data-type='render']") as HTMLElement, {
         blockId: "",
         action: [Constants.CB_GET_ALL],
         render: {
@@ -145,20 +117,20 @@ export const openCardByData = (cardsData: {
     if (window.siyuan.config.editor.readOnly) {
         disabledProtyle(editor.protyle);
     }
-    if (blocks.length > 0) {
+    if (options.blocks.length > 0) {
         fetchPost("/api/filetree/getDoc", {
-            id: blocks[index].blockID,
+            id: options.blocks[index].blockID,
             mode: 0,
             size: Constants.SIZE_GET_MAX
         }, (response) => {
             onGet(response, editor.protyle, [Constants.CB_GET_ALL, Constants.CB_GET_HTML]);
         });
     }
-    (dialog.element.firstElementChild as HTMLElement).style.zIndex = "200";
-    dialog.element.setAttribute("data-key", window.siyuan.config.keymap.general.riffCard.custom);
-    const countElement = dialog.element.querySelector('[data-type="count"]');
-    const actionElements = dialog.element.querySelectorAll(".card__action");
-    const filterElement = dialog.element.querySelector('[data-type="filter"]');
+    (options.element.firstElementChild as HTMLElement).style.zIndex = "200";
+    options.element.setAttribute("data-key", window.siyuan.config.keymap.general.riffCard.custom);
+    const countElement = options.element.querySelector('[data-type="count"]');
+    const actionElements = options.element.querySelectorAll(".card__action");
+    const filterElement = options.element.querySelector('[data-type="filter"]');
     const fetchNewRound = () => {
         const currentCardType = filterElement.getAttribute("data-cardtype");
         fetchPost(currentCardType === "all" ? "/api/riff/getRiffDueCards" :
@@ -168,14 +140,14 @@ export const openCardByData = (cardsData: {
             notebook: filterElement.getAttribute("data-id"),
         }, (treeCards) => {
             index = 0;
-            blocks = treeCards.data.cards;
-            if (blocks.length > 0) {
+            options.blocks = treeCards.data.cards;
+            if (options.blocks.length > 0) {
                 nextCard({
                     countElement,
                     editor,
                     actionElements,
                     index,
-                    blocks
+                    blocks: options.blocks
                 });
             } else {
                 allDone(countElement, editor, actionElements);
@@ -183,7 +155,7 @@ export const openCardByData = (cardsData: {
         });
     };
 
-    dialog.element.addEventListener("click", (event) => {
+    options.element.addEventListener("click", (event: MouseEvent) => {
         const target = event.target as HTMLElement;
         let type = "";
         if (typeof event.detail === "string") {
@@ -205,8 +177,8 @@ export const openCardByData = (cardsData: {
         } else {
             const fullscreenElement = hasClosestByAttribute(target, "data-type", "fullscreen");
             if (fullscreenElement) {
-                fullscreen(dialog.element.querySelector(".card__main"),
-                    dialog.element.querySelector('[data-type="fullscreen"]'));
+                fullscreen(options.element.querySelector(".card__main"),
+                    options.element.querySelector('[data-type="fullscreen"]'));
                 event.stopPropagation();
                 event.preventDefault();
                 return;
@@ -214,9 +186,10 @@ export const openCardByData = (cardsData: {
             const sticktabElement = hasClosestByAttribute(target, "data-type", "sticktab");
             if (sticktabElement) {
                 newCardTab({
-                    type: filterElement.getAttribute("data-cardtype") as TCardType,
+                    cardType: filterElement.getAttribute("data-cardtype") as TCardType,
                     id: filterElement.getAttribute("data-id"),
-                    dialog,
+                    dialog: options.dialog,
+                    title: options.title
                 });
                 event.stopPropagation();
                 event.preventDefault();
@@ -224,7 +197,7 @@ export const openCardByData = (cardsData: {
             }
             const closeElement = hasClosestByAttribute(target, "data-type", "close");
             if (closeElement) {
-                dialog.destroy();
+                options.dialog.destroy();
                 event.stopPropagation();
                 event.preventDefault();
                 return;
@@ -253,16 +226,16 @@ export const openCardByData = (cardsData: {
                             }, [], undefined, window.siyuan.languages.specifyPath, true);
                         }
                     }).element);
-                    if (title || response.data.length > 0) {
+                    if (options.title || response.data.length > 0) {
                         window.siyuan.menus.menu.append(new MenuItem({type: "separator"}).element);
                     }
-                    if (title) {
+                    if (options.title) {
                         window.siyuan.menus.menu.append(new MenuItem({
                             iconHTML: Constants.ZWSP,
-                            label: escapeHtml(title),
+                            label: escapeHtml(options.title),
                             click() {
-                                filterElement.setAttribute("data-id", id);
-                                filterElement.setAttribute("data-cardtype", cardType);
+                                filterElement.setAttribute("data-id", options.id);
+                                filterElement.setAttribute("data-cardtype", options.cardType);
                                 fetchNewRound();
                             },
                         }).element);
@@ -301,7 +274,7 @@ export const openCardByData = (cardsData: {
                 type = buttonElement.getAttribute("data-type");
             }
         }
-        if (!type || !blocks[index]) {
+        if (!type || !options.blocks[index]) {
             return;
         }
         event.preventDefault();
@@ -315,7 +288,7 @@ export const openCardByData = (cardsData: {
             actionElements[0].classList.add("fn__none");
             actionElements[1].querySelectorAll(".b3-button").forEach((element, btnIndex) => {
                 if (btnIndex !== 0) {
-                    element.previousElementSibling.textContent = blocks[index].nextDues[btnIndex - 1];
+                    element.previousElementSibling.textContent = options.blocks[index].nextDues[btnIndex - 1];
                 }
             });
             actionElements[1].classList.remove("fn__none");
@@ -332,17 +305,17 @@ export const openCardByData = (cardsData: {
                     editor,
                     actionElements,
                     index,
-                    blocks
+                    blocks: options.blocks
                 });
             }
             return;
         }
         if (["0", "1", "2", "3", "-3"].includes(type) && actionElements[0].classList.contains("fn__none")) {
             fetchPost(type === "-3" ? "/api/riff/skipReviewRiffCard" : "/api/riff/reviewRiffCard", {
-                deckID: blocks[index].deckID,
-                cardID: blocks[index].cardID,
+                deckID: options.blocks[index].deckID,
+                cardID: options.blocks[index].cardID,
                 rating: parseInt(type),
-                reviewedCards: blocks
+                reviewedCards: options.blocks
             }, () => {
                 /// #if MOBILE
                 if (type !== "-3" &&
@@ -352,18 +325,18 @@ export const openCardByData = (cardsData: {
                 }
                 /// #endif
                 index++;
-                if (index > blocks.length - 1) {
+                if (index > options.blocks.length - 1) {
                     const currentCardType = filterElement.getAttribute("data-cardtype");
                     fetchPost(currentCardType === "all" ? "/api/riff/getRiffDueCards" :
                         (currentCardType === "doc" ? "/api/riff/getTreeRiffDueCards" : "/api/riff/getNotebookRiffDueCards"), {
                         rootID: filterElement.getAttribute("data-id"),
                         deckID: filterElement.getAttribute("data-id"),
                         notebook: filterElement.getAttribute("data-id"),
-                        reviewedCards: blocks
+                        reviewedCards: options.blocks
                     }, (result) => {
                         index = 0;
-                        blocks = result.data.cards;
-                        if (blocks.length === 0) {
+                        options.blocks = result.data.cards;
+                        if (options.blocks.length === 0) {
                             if (result.data.unreviewedCount > 0) {
                                 newRound(countElement, editor, actionElements, result.data.unreviewedCount);
                             } else {
@@ -375,7 +348,7 @@ export const openCardByData = (cardsData: {
                                 editor,
                                 actionElements,
                                 index,
-                                blocks
+                                blocks: options.blocks
                             });
                         }
                     });
@@ -386,11 +359,57 @@ export const openCardByData = (cardsData: {
                     editor,
                     actionElements,
                     index,
-                    blocks
+                    blocks: options.blocks
                 });
             });
         }
     });
+    return editor;
+};
+
+export const openCard = () => {
+    fetchPost("/api/riff/getRiffDueCards", {deckID: ""}, (cardsResponse) => {
+        openCardByData(cardsResponse.data, "all");
+    });
+};
+
+export const openCardByData = (cardsData: {
+    cards: ICard[],
+    unreviewedCount: number
+}, cardType: TCardType, id?: string, title?: string) => {
+    const exit = window.siyuan.dialogs.find(item => {
+        if (item.element.getAttribute("data-key") === window.siyuan.config.keymap.general.riffCard.custom) {
+            item.destroy();
+            return true;
+        }
+    });
+    if (exit) {
+        return;
+    }
+
+    const dialog = new Dialog({
+        content: genCardHTML({id, cardType, blocks: cardsData.cards, isTab: false}),
+        width: isMobile() ? "100vw" : "80vw",
+        height: isMobile() ? "100vh" : "70vh",
+        destroyCallback() {
+            if (editor) {
+                editor.destroy();
+                if (window.siyuan.mobile) {
+                    window.siyuan.mobile.popEditor = null;
+                }
+            }
+        }
+    });
+    (dialog.element.querySelector(".b3-dialog__scrim") as HTMLElement).style.backgroundColor = "var(--b3-theme-background)";
+    (dialog.element.querySelector(".b3-dialog__container") as HTMLElement).style.maxWidth = "1024px";
+    const editor = bindCardEvent({
+        element: dialog.element,
+        blocks: cardsData.cards,
+        title,
+        id,
+        cardType,
+        dialog
+    });
 };
 
 const nextCard = (options: {

+ 2 - 1
app/src/editor/util.ts

@@ -25,6 +25,7 @@ import {zoomOut} from "../menus/protyle";
 import {countBlockWord, countSelectWord} from "../layout/status";
 import {showMessage} from "../dialog/message";
 import {getSearch} from "../util/functions";
+import {resize} from "../protyle/util/resize";
 
 export const openFileById = async (options: {
     id: string,
@@ -390,7 +391,7 @@ export const updatePanelByEditor = (protyle?: IProtyle, focus = true, pushBackSt
             return;
         }
         title = protyle.title.editElement.textContent;
-        setPadding(protyle);
+        resize(protyle);
         if (focus) {
             if (protyle.toolbar.range) {
                 focusByRange(protyle.toolbar.range);

+ 4 - 0
app/src/layout/Wnd.ts

@@ -33,6 +33,7 @@ import {escapeHtml} from "../util/escape";
 import {isWindow} from "../util/functions";
 import {hideAllElements} from "../protyle/ui/hideElements";
 import {focusByOffset, getSelectionOffset} from "../protyle/util/selection";
+import {Custom} from "./dock/Custom";
 
 export class Wnd {
     public id: string;
@@ -646,6 +647,9 @@ export class Wnd {
                 model.pdfObject.pdfLoadingTask.destroy();
             }
         }
+        if (model instanceof Custom) {
+            model.destroy();
+        }
         model.send("closews", {});
     }
 

+ 19 - 3
app/src/layout/dock/Custom.ts

@@ -8,8 +8,10 @@ export class Custom extends Model {
     constructor(options: {
         tab: Tab,
         data: any,
+        destroy?: () => void,
+        resize?: () => void,
         type: string,   // 同一类型的唯一标识
-        init:(element:Element)=>void
+        init: (element: Element) => void
     }) {
         super({id: options.tab.id});
         if (window.siyuan.config.fileTree.openFilesUseCurrentTab) {
@@ -20,10 +22,24 @@ export class Custom extends Model {
         this.element.addEventListener("click", () => {
             setPanelFocus(this.element.parentElement.parentElement);
         });
-        this.update();
+        this.data = options.data;
+        this.destroy = options.destroy;
+        this.resize = options.resize;
+    }
+
+    public destroy() {
+        if (this.destroy) {
+            this.destroy();
+        }
+    }
+
+    public resize() {
+        if (this.resize) {
+            this.resize();
+        }
     }
 
     private update() {
-        this.element.innerHTML = `eee`
+        // TODO
     }
 }

+ 5 - 1
app/src/layout/getAll.ts

@@ -9,6 +9,7 @@ import {Search} from "../search";
 import {Files} from "./dock/Files";
 import {Bookmark} from "./dock/Bookmark";
 import {Tag} from "./dock/Tag";
+import {Custom} from "./dock/Custom";
 
 export const getAllModels = () => {
     const models: IModels = {
@@ -21,7 +22,8 @@ export const getAllModels = () => {
         inbox: [],
         files: [],
         bookmark: [],
-        tag: []
+        tag: [],
+        custom: [],
     };
     const getTabs = (layout: Layout) => {
         for (let i = 0; i < layout.children.length; i++) {
@@ -46,6 +48,8 @@ export const getAllModels = () => {
                     models.bookmark.push(model);
                 } else if (model instanceof Tag) {
                     models.tag.push(model);
+                } else if (model instanceof Custom) {
+                    models.custom.push(model);
                 }
             } else {
                 getTabs(item as Layout);

+ 6 - 28
app/src/layout/util.ts

@@ -532,34 +532,9 @@ export const resizeTabs = () => {
     resizeTimeout = window.setTimeout(() => {
         const models = getAllModels();
         models.editor.forEach((item) => {
-            if (item.editor && item.editor.protyle && item.element.parentElement) {
-                hideElements(["gutter"], item.editor.protyle);
-                setPadding(item.editor.protyle);
-                if (typeof echarts !== "undefined") {
-                    item.editor.protyle.wysiwyg.element.querySelectorAll('[data-subtype="echarts"], [data-subtype="mindmap"]').forEach((chartItem: HTMLElement) => {
-                        const chartInstance = echarts.getInstanceById(chartItem.firstElementChild.nextElementSibling.getAttribute("_echarts_instance_"));
-                        if (chartInstance) {
-                            chartInstance.resize();
-                        }
-                    });
-                }
-                // 保持光标位置不变 https://ld246.com/article/1673704873983/comment/1673765814595#comments
-                if (!item.element.classList.contains("fn__none") && item.editor.protyle.toolbar.range) {
-                    let rangeRect = item.editor.protyle.toolbar.range.getBoundingClientRect();
-                    if (rangeRect.height === 0) {
-                        const blockElement = hasClosestBlock(item.editor.protyle.toolbar.range.startContainer);
-                        if (blockElement) {
-                            rangeRect = blockElement.getBoundingClientRect();
-                        }
-                    }
-                    if (rangeRect.height === 0) {
-                        return;
-                    }
-                    const protyleRect = item.editor.protyle.element.getBoundingClientRect();
-                    if (protyleRect.top + 30 > rangeRect.top || protyleRect.bottom < rangeRect.bottom) {
-                        item.editor.protyle.toolbar.range.startContainer.parentElement.scrollIntoView(protyleRect.top > rangeRect.top);
-                    }
-                }
+            if (item.editor && item.editor.protyle &&
+                item.element.parentElement && !item.element.classList.contains("fn__none")) {
+                item.editor.resize();
             }
         });
         // https://github.com/siyuan-note/siyuan/issues/6250
@@ -572,6 +547,9 @@ export const resizeTabs = () => {
                 hideElements(["gutter"], editorItem.protyle);
             });
         });
+        models.custom.forEach(item => {
+            item.resize();
+        })
         pdfResize();
         hideAllElements(["gutter"]);
     }, 200);

+ 5 - 0
app/src/protyle/index.ts

@@ -26,6 +26,7 @@ import {disabledProtyle, enableProtyle, onGet} from "./util/onGet";
 import {reloadProtyle} from "./util/reload";
 import {renderBacklink} from "./wysiwyg/renderBacklink";
 import {setEmpty} from "../mobile/util/setEmpty";
+import {resize} from "./util/resize";
 
 export class Protyle {
 
@@ -271,4 +272,8 @@ export class Protyle {
     public destroy() {
         destroy(this.protyle);
     }
+
+    public resize() {
+        resize(this.protyle);
+    }
 }

+ 33 - 0
app/src/protyle/util/resize.ts

@@ -0,0 +1,33 @@
+import {hideElements} from "../ui/hideElements";
+import {setPadding} from "../ui/initUI";
+import {hasClosestBlock} from "./hasClosest";
+
+export const resize = (protyle: IProtyle) => {
+    hideElements(["gutter"], protyle);
+    setPadding(protyle);
+    if (typeof echarts !== "undefined") {
+        protyle.wysiwyg.element.querySelectorAll('[data-subtype="echarts"], [data-subtype="mindmap"]').forEach((chartItem: HTMLElement) => {
+            const chartInstance = echarts.getInstanceById(chartItem.firstElementChild.nextElementSibling.getAttribute("_echarts_instance_"));
+            if (chartInstance) {
+                chartInstance.resize();
+            }
+        });
+    }
+    // 保持光标位置不变 https://ld246.com/article/1673704873983/comment/1673765814595#comments
+    if (protyle.toolbar.range) {
+        let rangeRect = protyle.toolbar.range.getBoundingClientRect();
+        if (rangeRect.height === 0) {
+            const blockElement = hasClosestBlock(protyle.toolbar.range.startContainer);
+            if (blockElement) {
+                rangeRect = blockElement.getBoundingClientRect();
+            }
+        }
+        if (rangeRect.height === 0) {
+            return;
+        }
+        const protyleRect = protyle.element.getBoundingClientRect();
+        if (protyleRect.top + 30 > rangeRect.top || protyleRect.bottom < rangeRect.bottom) {
+            protyle.toolbar.range.startContainer.parentElement.scrollIntoView(protyleRect.top > rangeRect.top);
+        }
+    }
+};

+ 1 - 0
app/src/types/index.d.ts

@@ -658,6 +658,7 @@ declare interface IModels {
     tag: import("../layout/dock/Tag").Tag[]
     asset: import("../asset").Asset[]
     search: import("../search").Search[]
+    custom: import("../layout/dock/Custom").Custom[]
 }
 
 declare interface IMenu {