diff --git a/app/src/boot/globalEvent/keydown.ts b/app/src/boot/globalEvent/keydown.ts index 5d120efc6..67fc76a49 100644 --- a/app/src/boot/globalEvent/keydown.ts +++ b/app/src/boot/globalEvent/keydown.ts @@ -76,6 +76,7 @@ import {historyKeydown} from "../../history/keydown"; import {zoomOut} from "../../menus/protyle"; import {openSearchAV} from "../../protyle/render/av/relation"; import * as dayjs from "dayjs"; +import {getPlainText} from "../../protyle/util/paste"; const switchDialogEvent = (app: App, event: MouseEvent) => { event.preventDefault(); @@ -488,10 +489,7 @@ const editKeydown = (app: App, event: KeyboardEvent) => { selectsElement.push(nodeElement); } selectsElement.forEach(item => { - // 不能使用 [contenteditable="true"], 否则嵌入块无法复制 - item.querySelectorAll("[spellcheck]").forEach(editItem => { - html += editItem.textContent + "\n"; - }); + html += getPlainText(item) + "\n"; }); copyPlainText(html.trimEnd()); } else { diff --git a/app/src/protyle/gutter/index.ts b/app/src/protyle/gutter/index.ts index 9c7fa4c1d..32c42f958 100644 --- a/app/src/protyle/gutter/index.ts +++ b/app/src/protyle/gutter/index.ts @@ -9,7 +9,14 @@ import {getIconByType} from "../../editor/getIcon"; import {enterBack, iframeMenu, setFold, tableMenu, videoMenu, zoomOut} from "../../menus/protyle"; import {MenuItem} from "../../menus/Menu"; import {copySubMenu, openAttr, openWechatNotify} from "../../menus/commonMenuItem"; -import {copyPlainText, isMac, isOnlyMeta, openByMobile, updateHotkeyTip, writeText} from "../util/compatibility"; +import { + copyPlainText, + isMac, + isOnlyMeta, + openByMobile, + updateHotkeyTip, + writeText +} from "../util/compatibility"; import { transaction, turnsIntoOneTransaction, @@ -47,6 +54,7 @@ import {emitOpenMenu} from "../../plugin/EventBus"; import {insertAttrViewBlockAnimation} from "../render/av/row"; import {avContextmenu} from "../render/av/action"; import {openSearchAV} from "../render/av/relation"; +import {getPlainText} from "../util/paste"; export class Gutter { public element: HTMLElement; @@ -749,10 +757,8 @@ export class Gutter { accelerator: window.siyuan.config.keymap.editor.general.copyPlainText.custom, click() { let html = ""; - selectsElement.forEach(item => { - item.querySelectorAll("[spellcheck]").forEach(editItem => { - html += editItem.textContent + "\n"; - }); + selectsElement.forEach((item: HTMLElement) => { + html += getPlainText(item) + "\n"; }); copyPlainText(html.trimEnd()); focusBlock(selectsElement[0]); @@ -1223,11 +1229,7 @@ export class Gutter { label: window.siyuan.languages.copyPlainText, accelerator: window.siyuan.config.keymap.editor.general.copyPlainText.custom, click() { - let text = ""; - nodeElement.querySelectorAll("[spellcheck]").forEach(item => { - text += item.textContent + "\n"; - }); - copyPlainText(text.trimEnd()); + copyPlainText(getPlainText(nodeElement as HTMLElement).trimEnd()); focusBlock(nodeElement); } }, { diff --git a/app/src/protyle/util/paste.ts b/app/src/protyle/util/paste.ts index e12ea5bee..b2f8c0366 100644 --- a/app/src/protyle/util/paste.ts +++ b/app/src/protyle/util/paste.ts @@ -15,7 +15,42 @@ import {insertHTML} from "./insertHTML"; import {scrollCenter} from "../../util/highlightById"; import {hideElements} from "../ui/hideElements"; import {avRender} from "../render/av/render"; -import {cellScrollIntoView} from "../render/av/cell"; +import {cellScrollIntoView, getCellText} from "../render/av/cell"; + +export const getPlainText = (blockElement: HTMLElement, isNested = false) => { + let text = "" + const dataType = blockElement.dataset.type + if ("NodeHTMLBlock" === dataType) { + text += Lute.UnEscapeHTMLStr(blockElement.querySelector("protyle-html").getAttribute("data-content")) + } else if ("NodeAttributeView" === dataType) { + blockElement.querySelectorAll(".av__row").forEach(rowElement => { + rowElement.querySelectorAll(".av__cell").forEach((cellElement: HTMLElement) => { + text += getCellText(cellElement) + " "; + }) + text += "\n"; + }) + text = text.trimEnd() + } else if ("NodeThematicBreak" === dataType) { + text += "---"; + } else if ("NodeIFrame" === dataType || "NodeWidget" === dataType) { + text += blockElement.querySelector("iframe").getAttribute("src") + } else if ("NodeVideo" === dataType) { + text += blockElement.querySelector("video").getAttribute("src") + } else if ("NodeAudio" === dataType) { + text += blockElement.querySelector("audio").getAttribute("src") + } else if (blockElement.classList.contains("render-node")) { + // 需在嵌入块后,代码块前 + text += Lute.UnEscapeHTMLStr(blockElement.getAttribute("data-content")) + } else if (["NodeHeading", "NodeParagraph", "NodeCodeBlock", "NodeTable"].includes(dataType)) { + text += blockElement.querySelector("[spellcheck]").textContent; + } else if (!isNested && ["NodeBlockquote", "NodeList", "NodeSuperBlock", "NodeListItem"].includes(dataType)) { + blockElement.querySelectorAll("[data-node-id]").forEach((item: HTMLElement) => { + const nestedText = getPlainText(item, true); + text += nestedText ? nestedText + "\n" : ""; + }) + } + return text; +} export const pasteEscaped = async (protyle: IProtyle, nodeElement: Element) => { try {