From 9dba5dbb4a61c4c030fe755874cb45233fe67ab6 Mon Sep 17 00:00:00 2001 From: Vanessa Date: Tue, 21 Oct 2025 20:21:33 +0800 Subject: [PATCH] :art: https://github.com/siyuan-note/siyuan/issues/16176 --- app/src/layout/dock/Outline.ts | 413 +++++++++++++++++---------------- 1 file changed, 208 insertions(+), 205 deletions(-) diff --git a/app/src/layout/dock/Outline.ts b/app/src/layout/dock/Outline.ts index 6499da919..c4fb9223b 100644 --- a/app/src/layout/dock/Outline.ts +++ b/app/src/layout/dock/Outline.ts @@ -875,169 +875,128 @@ export class Outline extends Model { const currentLevel = this.getHeadingLevel(element); window.siyuan.menus.menu.remove(); window.siyuan.menus.menu.element.setAttribute("data-name", Constants.MENU_OUTLINE_CONTEXT); - // 升级 - if (currentLevel > 1) { - window.siyuan.menus.menu.append(new MenuItem({ - id: "upgrade", - icon: "iconUp", - label: window.siyuan.languages.upgrade, - click: () => { - const data = this.getProtyleAndBlockElement(element); - if (data) { - turnsIntoTransaction({ - protyle: data.protyle, - selectsElement: [data.blockElement], - type: "Blocks2Hs", - level: currentLevel - 1 - }); - } - } - }).element); - } - - // 降级 - if (currentLevel < 6) { - window.siyuan.menus.menu.append(new MenuItem({ - id: "downgrade", - icon: "iconDown", - label: window.siyuan.languages.downgrade, - click: () => { - const data = this.getProtyleAndBlockElement(element); - if (data) { - turnsIntoTransaction({ - protyle: data.protyle, - selectsElement: [data.blockElement], - type: "Blocks2Hs", - level: currentLevel + 1 - }); - } - } - }).element); - } - - // 带子标题转换 const id = element.getAttribute("data-node-id"); - checkFold(id, (zoomIn) => { - openFileById({ - app: this.app, - id, - action: zoomIn ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL, Constants.CB_GET_HTML, Constants.CB_GET_OUTLINE] : [Constants.CB_GET_FOCUS, Constants.CB_GET_OUTLINE, Constants.CB_GET_SETID, Constants.CB_GET_CONTEXT, Constants.CB_GET_HTML], - }); - }); - this.setCurrentById(id); - const headingSubMenu = []; - if (currentLevel !== 1) { - headingSubMenu.push(this.genHeadingTransform(id, 1)); - } - if (currentLevel !== 2) { - headingSubMenu.push(this.genHeadingTransform(id, 2)); - } - if (currentLevel !== 3) { - headingSubMenu.push(this.genHeadingTransform(id, 3)); - } - if (currentLevel !== 4) { - headingSubMenu.push(this.genHeadingTransform(id, 4)); - } - if (currentLevel !== 5) { - headingSubMenu.push(this.genHeadingTransform(id, 5)); - } - if (currentLevel !== 6) { - headingSubMenu.push(this.genHeadingTransform(id, 6)); - } - - if (headingSubMenu.length > 0) { - window.siyuan.menus.menu.append(new MenuItem({ - id: "tWithSubtitle", - type: "submenu", - icon: "iconRefresh", - label: window.siyuan.languages.tWithSubtitle, - submenu: headingSubMenu - }).element); - } - - window.siyuan.menus.menu.append(new MenuItem({id: "separator_1", type: "separator"}).element); - - // 在前面插入同级标题 - window.siyuan.menus.menu.append(new MenuItem({ - id: "insertSameLevelHeadingBefore", - icon: "iconBefore", - label: window.siyuan.languages.insertSameLevelHeadingBefore, - click: () => { - const data = this.getProtyleAndBlockElement(element); - const newId = Lute.NewNodeID(); - const html = `
${Constants.ZWSP}
`; - transaction(data.protyle, [{ - action: "insert", - data: html, - id: newId, - previousID: data.blockElement.previousElementSibling?.getAttribute("data-node-id"), - parentID: data.blockElement.parentElement.getAttribute("data-node-id") || data.protyle.block.parentID, - }], [{ - action: "delete", - id: newId - }]); - data.blockElement.insertAdjacentHTML("beforebegin", html); - data.blockElement.previousElementSibling.scrollIntoView(); - focusByWbr(data.blockElement.previousElementSibling, document.createRange()); + if (!window.siyuan.config.readonly) { + // 升级 + if (currentLevel > 1) { + window.siyuan.menus.menu.append(new MenuItem({ + id: "upgrade", + icon: "iconUp", + label: window.siyuan.languages.upgrade, + click: () => { + const data = this.getProtyleAndBlockElement(element); + if (data) { + turnsIntoTransaction({ + protyle: data.protyle, + selectsElement: [data.blockElement], + type: "Blocks2Hs", + level: currentLevel - 1 + }); + } + } + }).element); } - }).element); - // 在后面插入同级标题 - window.siyuan.menus.menu.append(new MenuItem({ - id: "insertSameLevelHeadingAfter", - icon: "iconAfter", - label: window.siyuan.languages.insertSameLevelHeadingAfter, - click: () => { - fetchPost("/api/block/getHeadingDeleteTransaction", { + // 降级 + if (currentLevel < 6) { + window.siyuan.menus.menu.append(new MenuItem({ + id: "downgrade", + icon: "iconDown", + label: window.siyuan.languages.downgrade, + click: () => { + const data = this.getProtyleAndBlockElement(element); + if (data) { + turnsIntoTransaction({ + protyle: data.protyle, + selectsElement: [data.blockElement], + type: "Blocks2Hs", + level: currentLevel + 1 + }); + } + } + }).element); + } + + // 带子标题转换 + checkFold(id, (zoomIn) => { + openFileById({ + app: this.app, id, - }, (deleteResponse) => { - const data = this.getProtyleAndBlockElement(element); - const previousID = deleteResponse.data.doOperations[deleteResponse.data.doOperations.length - 1].id; + action: zoomIn ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL, Constants.CB_GET_HTML, Constants.CB_GET_OUTLINE] : [Constants.CB_GET_FOCUS, Constants.CB_GET_OUTLINE, Constants.CB_GET_SETID, Constants.CB_GET_CONTEXT, Constants.CB_GET_HTML], + }); + }); + this.setCurrentById(id); + const headingSubMenu = []; + if (currentLevel !== 1) { + headingSubMenu.push(this.genHeadingTransform(id, 1)); + } + if (currentLevel !== 2) { + headingSubMenu.push(this.genHeadingTransform(id, 2)); + } + if (currentLevel !== 3) { + headingSubMenu.push(this.genHeadingTransform(id, 3)); + } + if (currentLevel !== 4) { + headingSubMenu.push(this.genHeadingTransform(id, 4)); + } + if (currentLevel !== 5) { + headingSubMenu.push(this.genHeadingTransform(id, 5)); + } + if (currentLevel !== 6) { + headingSubMenu.push(this.genHeadingTransform(id, 6)); + } + if (headingSubMenu.length > 0) { + window.siyuan.menus.menu.append(new MenuItem({ + id: "tWithSubtitle", + type: "submenu", + icon: "iconRefresh", + label: window.siyuan.languages.tWithSubtitle, + submenu: headingSubMenu + }).element); + } + + window.siyuan.menus.menu.append(new MenuItem({id: "separator_1", type: "separator"}).element); + + // 在前面插入同级标题 + window.siyuan.menus.menu.append(new MenuItem({ + id: "insertSameLevelHeadingBefore", + icon: "iconBefore", + label: window.siyuan.languages.insertSameLevelHeadingBefore, + click: () => { + const data = this.getProtyleAndBlockElement(element); const newId = Lute.NewNodeID(); const html = `
${Constants.ZWSP}
`; transaction(data.protyle, [{ action: "insert", data: html, id: newId, - previousID, + previousID: data.blockElement.previousElementSibling?.getAttribute("data-node-id"), + parentID: data.blockElement.parentElement.getAttribute("data-node-id") || data.protyle.block.parentID, }], [{ action: "delete", id: newId }]); - const previousElement = data.protyle.wysiwyg.element.querySelector(`[data-node-id="${previousID}"]`); - if (previousElement) { - previousElement.insertAdjacentHTML("afterend", html); - previousElement.nextElementSibling.scrollIntoView(); - focusByWbr(previousElement.nextElementSibling, document.createRange()); - } - }); - } - }).element); + data.blockElement.insertAdjacentHTML("beforebegin", html); + data.blockElement.previousElementSibling.scrollIntoView(); + focusByWbr(data.blockElement.previousElementSibling, document.createRange()); + } + }).element); - // 添加子标题 - if (currentLevel < 6) { // 只有当前级别小于6时才能添加子标题 + // 在后面插入同级标题 window.siyuan.menus.menu.append(new MenuItem({ - id: "addChildHeading", - icon: "iconAdd", - label: window.siyuan.languages.addChildHeading, + id: "insertSameLevelHeadingAfter", + icon: "iconAfter", + label: window.siyuan.languages.insertSameLevelHeadingAfter, click: () => { fetchPost("/api/block/getHeadingDeleteTransaction", { id, }, (deleteResponse) => { - let previousID = deleteResponse.data.doOperations[deleteResponse.data.doOperations.length - 1].id; - deleteResponse.data.undoOperations.find((operationsItem: IOperation, index: number) => { - const startIndex = operationsItem.data.indexOf(' data-subtype="h'); - if (startIndex > -1 && startIndex < 260 && parseInt(operationsItem.data.substring(startIndex + 16, startIndex + 17)) === currentLevel + 1) { - previousID = deleteResponse.data.undoOperations[index - 1].id; - return true; - } - }); - - const data = this.getProtyleAndBlockElement(element); + const previousID = deleteResponse.data.doOperations[deleteResponse.data.doOperations.length - 1].id; + const newId = Lute.NewNodeID(); - const html = `
${Constants.ZWSP}
`; + const html = `
${Constants.ZWSP}
`; transaction(data.protyle, [{ action: "insert", data: html, @@ -1056,9 +1015,52 @@ export class Outline extends Model { }); } }).element); - } - window.siyuan.menus.menu.append(new MenuItem({id: "separator_2", type: "separator"}).element); + // 添加子标题 + if (currentLevel < 6) { // 只有当前级别小于6时才能添加子标题 + window.siyuan.menus.menu.append(new MenuItem({ + id: "addChildHeading", + icon: "iconAdd", + label: window.siyuan.languages.addChildHeading, + click: () => { + fetchPost("/api/block/getHeadingDeleteTransaction", { + id, + }, (deleteResponse) => { + let previousID = deleteResponse.data.doOperations[deleteResponse.data.doOperations.length - 1].id; + deleteResponse.data.undoOperations.find((operationsItem: IOperation, index: number) => { + const startIndex = operationsItem.data.indexOf(' data-subtype="h'); + if (startIndex > -1 && startIndex < 260 && parseInt(operationsItem.data.substring(startIndex + 16, startIndex + 17)) === currentLevel + 1) { + previousID = deleteResponse.data.undoOperations[index - 1].id; + return true; + } + }); + + + const data = this.getProtyleAndBlockElement(element); + const newId = Lute.NewNodeID(); + const html = `
${Constants.ZWSP}
`; + transaction(data.protyle, [{ + action: "insert", + data: html, + id: newId, + previousID, + }], [{ + action: "delete", + id: newId + }]); + const previousElement = data.protyle.wysiwyg.element.querySelector(`[data-node-id="${previousID}"]`); + if (previousElement) { + previousElement.insertAdjacentHTML("afterend", html); + previousElement.nextElementSibling.scrollIntoView(); + focusByWbr(previousElement.nextElementSibling, document.createRange()); + } + }); + } + }).element); + } + + window.siyuan.menus.menu.append(new MenuItem({id: "separator_2", type: "separator"}).element); + } // 复制带子标题 window.siyuan.menus.menu.append(new MenuItem({ @@ -1082,28 +1084,66 @@ export class Outline extends Model { } }).element); - // 剪切带子标题 - window.siyuan.menus.menu.append(new MenuItem({ - id: "cutHeadings1", - icon: "iconCut", - label: `${window.siyuan.languages.cut} ${window.siyuan.languages.headings1}`, - click: () => { - const data = this.getProtyleAndBlockElement(element); - fetchPost("/api/block/getHeadingChildrenDOM", { - id, - removeFoldAttr: data.blockElement.getAttribute("fold") !== "1" - }, (response) => { - if (isInAndroid()) { - window.JSAndroid.writeHTMLClipboard(data.protyle.lute.BlockDOM2StdMd(response.data).trimEnd(), response.data + Constants.ZWSP); - } else if (isInHarmony()) { - window.JSHarmony.writeHTMLClipboard(data.protyle.lute.BlockDOM2StdMd(response.data).trimEnd(), response.data + Constants.ZWSP); - } else { - writeText(response.data + Constants.ZWSP); - } + if (!window.siyuan.config.readonly) { + // 剪切带子标题 + window.siyuan.menus.menu.append(new MenuItem({ + id: "cutHeadings1", + icon: "iconCut", + label: `${window.siyuan.languages.cut} ${window.siyuan.languages.headings1}`, + click: () => { + const data = this.getProtyleAndBlockElement(element); + fetchPost("/api/block/getHeadingChildrenDOM", { + id, + removeFoldAttr: data.blockElement.getAttribute("fold") !== "1" + }, (response) => { + if (isInAndroid()) { + window.JSAndroid.writeHTMLClipboard(data.protyle.lute.BlockDOM2StdMd(response.data).trimEnd(), response.data + Constants.ZWSP); + } else if (isInHarmony()) { + window.JSHarmony.writeHTMLClipboard(data.protyle.lute.BlockDOM2StdMd(response.data).trimEnd(), response.data + Constants.ZWSP); + } else { + writeText(response.data + Constants.ZWSP); + } + fetchPost("/api/block/getHeadingDeleteTransaction", { + id, + }, (deleteResponse) => { + deleteResponse.data.doOperations.forEach((operation: IOperation) => { + data.protyle.wysiwyg.element.querySelectorAll(`[data-node-id="${operation.id}"]`).forEach((itemElement: HTMLElement) => { + itemElement.remove(); + }); + }); + if (data.protyle.wysiwyg.element.childElementCount === 0) { + const newID = Lute.NewNodeID(); + const emptyElement = genEmptyElement(false, false, newID); + data.protyle.wysiwyg.element.insertAdjacentElement("afterbegin", emptyElement); + deleteResponse.data.doOperations.push({ + action: "insert", + data: emptyElement.outerHTML, + id: newID, + parentID: data.protyle.block.parentID + }); + deleteResponse.data.undoOperations.push({ + action: "delete", + id: newID, + }); + focusBlock(emptyElement); + } + transaction(data.protyle, deleteResponse.data.doOperations, deleteResponse.data.undoOperations); + }); + }); + } + }).element); + + // 删除 + window.siyuan.menus.menu.append(new MenuItem({ + id: "deleteHeadings1", + icon: "iconTrashcan", + label: `${window.siyuan.languages.delete} ${window.siyuan.languages.headings1}`, + click: () => { + const data = this.getProtyleAndBlockElement(element); fetchPost("/api/block/getHeadingDeleteTransaction", { id, - }, (deleteResponse) => { - deleteResponse.data.doOperations.forEach((operation: IOperation) => { + }, (response) => { + response.data.doOperations.forEach((operation: IOperation) => { data.protyle.wysiwyg.element.querySelectorAll(`[data-node-id="${operation.id}"]`).forEach((itemElement: HTMLElement) => { itemElement.remove(); }); @@ -1112,60 +1152,23 @@ export class Outline extends Model { const newID = Lute.NewNodeID(); const emptyElement = genEmptyElement(false, false, newID); data.protyle.wysiwyg.element.insertAdjacentElement("afterbegin", emptyElement); - deleteResponse.data.doOperations.push({ + response.data.doOperations.push({ action: "insert", data: emptyElement.outerHTML, id: newID, parentID: data.protyle.block.parentID }); - deleteResponse.data.undoOperations.push({ + response.data.undoOperations.push({ action: "delete", id: newID, }); focusBlock(emptyElement); } - transaction(data.protyle, deleteResponse.data.doOperations, deleteResponse.data.undoOperations); + transaction(data.protyle, response.data.doOperations, response.data.undoOperations); }); - }); - } - }).element); - - // 删除 - window.siyuan.menus.menu.append(new MenuItem({ - id: "deleteHeadings1", - icon: "iconTrashcan", - label: `${window.siyuan.languages.delete} ${window.siyuan.languages.headings1}`, - click: () => { - const data = this.getProtyleAndBlockElement(element); - fetchPost("/api/block/getHeadingDeleteTransaction", { - id, - }, (response) => { - response.data.doOperations.forEach((operation: IOperation) => { - data.protyle.wysiwyg.element.querySelectorAll(`[data-node-id="${operation.id}"]`).forEach((itemElement: HTMLElement) => { - itemElement.remove(); - }); - }); - if (data.protyle.wysiwyg.element.childElementCount === 0) { - const newID = Lute.NewNodeID(); - const emptyElement = genEmptyElement(false, false, newID); - data.protyle.wysiwyg.element.insertAdjacentElement("afterbegin", emptyElement); - response.data.doOperations.push({ - action: "insert", - data: emptyElement.outerHTML, - id: newID, - parentID: data.protyle.block.parentID - }); - response.data.undoOperations.push({ - action: "delete", - id: newID, - }); - focusBlock(emptyElement); - } - transaction(data.protyle, response.data.doOperations, response.data.undoOperations); - }); - } - }).element); - + } + }).element); + } window.siyuan.menus.menu.append(new MenuItem({id: "separator_3", type: "separator"}).element); // 展开子标题