A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
at lambda-fork/main 598 lines 29 kB view raw
1import {Constants} from "../../constants"; 2import {uploadFiles, uploadLocalFiles} from "../upload"; 3import {processPasteCode, processRender} from "./processCode"; 4import {getLocalFiles, getTextSiyuanFromTextHTML, readText} from "./compatibility"; 5import {hasClosestBlock, hasClosestByAttribute, hasClosestByClassName} from "./hasClosest"; 6import {getEditorRange} from "./selection"; 7import {blockRender} from "../render/blockRender"; 8import {highlightRender} from "../render/highlightRender"; 9import {fetchPost} from "../../util/fetch"; 10import {isDynamicRef, isFileAnnotation} from "../../util/functions"; 11import {insertHTML} from "./insertHTML"; 12import {scrollCenter} from "../../util/highlightById"; 13import {hideElements} from "../ui/hideElements"; 14import {avRender} from "../render/av/render"; 15import {cellScrollIntoView, getCellText} from "../render/av/cell"; 16import {getContenteditableElement} from "../wysiwyg/getBlock"; 17 18export const getTextStar = (blockElement: HTMLElement) => { 19 const dataType = blockElement.dataset.type; 20 let refText = ""; 21 if (["NodeHeading", "NodeParagraph"].includes(dataType)) { 22 refText = getContenteditableElement(blockElement).innerHTML; 23 } else { 24 if ("NodeHTMLBlock" === dataType) { 25 refText = "HTML"; 26 } else if ("NodeAttributeView" === dataType) { 27 refText = blockElement.querySelector(".av__title").textContent || window.siyuan.languages.database; 28 } else if ("NodeThematicBreak" === dataType) { 29 refText = window.siyuan.languages.line; 30 } else if ("NodeIFrame" === dataType) { 31 refText = "IFrame"; 32 } else if ("NodeWidget" === dataType) { 33 refText = window.siyuan.languages.widget; 34 } else if ("NodeVideo" === dataType) { 35 refText = window.siyuan.languages.video; 36 } else if ("NodeAudio" === dataType) { 37 refText = window.siyuan.languages.audio; 38 } else if (["NodeCodeBlock", "NodeTable"].includes(dataType)) { 39 refText = getPlainText(blockElement); 40 } else if (blockElement.classList.contains("render-node")) { 41 // 需在嵌入块后,代码块前 42 refText += blockElement.dataset.subtype || Lute.UnEscapeHTMLStr(blockElement.getAttribute("data-content")); 43 } else if (["NodeBlockquote", "NodeList", "NodeSuperBlock", "NodeListItem"].includes(dataType)) { 44 Array.from(blockElement.querySelectorAll("[data-node-id]")).find((item: HTMLElement) => { 45 if (!["NodeBlockquote", "NodeList", "NodeSuperBlock", "NodeListItem"].includes(item.getAttribute("data-type"))) { 46 refText = getTextStar(blockElement.querySelector("[data-node-id]")); 47 return true; 48 } 49 }); 50 if (refText) { 51 return refText; 52 } 53 } 54 } 55 return refText + ` <span data-type="block-ref" data-subtype="s" data-id="${blockElement.getAttribute("data-node-id")}">*</span>`; 56}; 57 58export const getPlainText = (blockElement: HTMLElement, isNested = false) => { 59 let text = ""; 60 const dataType = blockElement.dataset.type; 61 if ("NodeHTMLBlock" === dataType) { 62 text += Lute.UnEscapeHTMLStr(blockElement.querySelector("protyle-html").getAttribute("data-content")); 63 } else if ("NodeAttributeView" === dataType) { 64 blockElement.querySelectorAll(".av__row").forEach(rowElement => { 65 rowElement.querySelectorAll(".av__cell").forEach((cellElement: HTMLElement) => { 66 text += getCellText(cellElement) + " "; 67 }); 68 text += "\n"; 69 }); 70 text = text.trimEnd(); 71 } else if ("NodeThematicBreak" === dataType) { 72 text += "---"; 73 } else if ("NodeIFrame" === dataType || "NodeWidget" === dataType) { 74 text += blockElement.querySelector("iframe").getAttribute("src"); 75 } else if ("NodeVideo" === dataType) { 76 text += blockElement.querySelector("video").getAttribute("src"); 77 } else if ("NodeAudio" === dataType) { 78 text += blockElement.querySelector("audio").getAttribute("src"); 79 } else if (blockElement.classList.contains("render-node")) { 80 // 需在嵌入块后,代码块前 81 text += Lute.UnEscapeHTMLStr(blockElement.getAttribute("data-content")); 82 } else if (["NodeHeading", "NodeParagraph", "NodeCodeBlock"].includes(dataType)) { 83 text += blockElement.querySelector("[spellcheck]").textContent; 84 } else if (dataType === "NodeTable") { 85 blockElement.querySelectorAll("th, td").forEach((item) => { 86 text += item.textContent.trim() + "\t"; 87 if (!item.nextElementSibling) { 88 text = text.slice(0, -1) + "\n"; 89 } 90 }); 91 text = text.slice(0, -1); 92 } else if (!isNested && ["NodeBlockquote", "NodeList", "NodeSuperBlock", "NodeListItem"].includes(dataType)) { 93 blockElement.querySelectorAll("[data-node-id]").forEach((item: HTMLElement) => { 94 const nestedText = getPlainText(item, true); 95 text += nestedText ? nestedText + "\n" : ""; 96 }); 97 } 98 return text; 99}; 100 101export const pasteEscaped = async (protyle: IProtyle, nodeElement: Element) => { 102 try { 103 let clipText = await readText() || ""; 104 // 删掉 <span data-type\="text".*>text</span> 标签,只保留文本 105 clipText = clipText.replace(/<span data-type="text".*?>(.*?)<\/span>/g, "$1"); 106 107 // https://github.com/siyuan-note/siyuan/issues/5446 108 // A\B\C\D\ 109 // E 110 // task-blog-2~default~baiduj 无法原义粘贴含有 `~foo~` 的文本 https://github.com/siyuan-note/siyuan/issues/5523 111 112 // 这里必须多加一个反斜杆,因为 Lute 在进行 Markdown 嵌套节点转换平铺标记节点时会剔除 Backslash 节点, 113 // 多加入的一个反斜杆会作为文本节点保留下来,后续 Spin 时刚好用于转义标记符 114 clipText = clipText.replace(/\\/g, "\\\\") 115 .replace(/\*/g, "\\*") 116 .replace(/_/g, "\\_") 117 .replace(/\[/g, "\\[") 118 .replace(/]/g, "\\]") 119 .replace(/!/g, "\\!") 120 .replace(/`/g, "\\`") 121 .replace(/</g, "\\<") 122 .replace(/>/g, "\\>") 123 .replace(/&/g, "\\&") 124 .replace(/~/g, "\\~") 125 .replace(/\{/g, "\\{") 126 .replace(/}/g, "\\}") 127 .replace(/\(/g, "\\(") 128 .replace(/\)/g, "\\)") 129 .replace(/=/g, "\\=") 130 .replace(/#/g, "\\#") 131 .replace(/\$/g, "\\$") 132 .replace(/\^/g, "\\^") 133 .replace(/\|/g, "\\|") 134 .replace(/\./g, "\\."); 135 // 转义文本不能使用 DOM 结构 https://github.com/siyuan-note/siyuan/issues/11778 136 paste(protyle, {textPlain: clipText, textHTML: "", target: nodeElement as HTMLElement}); 137 } catch (e) { 138 console.log(e); 139 } 140}; 141 142export const pasteAsPlainText = async (protyle: IProtyle) => { 143 let localFiles: string[] = []; 144 /// #if !BROWSER 145 localFiles = await getLocalFiles(); 146 if (localFiles.length > 0) { 147 uploadLocalFiles(localFiles, protyle, false); 148 return; 149 } 150 /// #endif 151 if (localFiles.length === 0) { 152 // Inline-level elements support pasted as plain text https://github.com/siyuan-note/siyuan/issues/8010 153 let textPlain = await readText() || ""; 154 if (getSelection().rangeCount > 0) { 155 const range = getSelection().getRangeAt(0); 156 if (hasClosestByAttribute(range.startContainer, "data-type", "code") || hasClosestByClassName(range.startContainer, "hljs")) { 157 insertHTML(textPlain.replace(/\u200D```/g, "```").replace(/```/g, "\u200D```"), protyle); 158 return; 159 } 160 } 161 // 对一些内置需要解析的 HTML 标签进行内部转移 Improve sub/sup pasting as plain text https://github.com/siyuan-note/siyuan/issues/12155 162 textPlain = textPlain.replace(/<sub>/g, "__@sub@__").replace(/<\/sub>/g, "__@/sub@__"); 163 textPlain = textPlain.replace(/<sup>/g, "__@sup@__").replace(/<\/sup>/g, "__@/sup@__"); 164 textPlain = textPlain.replace(/<kbd>/g, "__@kbd@__").replace(/<\/kbd>/g, "__@/kbd@__"); 165 textPlain = textPlain.replace(/<u>/g, "__@u@__").replace(/<\/u>/g, "__@/u@__"); 166 167 // 删掉 <span data-type\="text".*>text</span> 标签,只保留文本 168 textPlain = textPlain.replace(/<span data-type="text".*?>(.*?)<\/span>/g, "$1"); 169 170 // 对 HTML 标签进行内部转义,避免被 Lute 解析以后变为小写 https://github.com/siyuan-note/siyuan/issues/10620 171 textPlain = textPlain.replace(/</g, ";;;lt;;;").replace(/>/g, ";;;gt;;;"); 172 173 // 反转义内置需要解析的 HTML 标签 174 textPlain = textPlain.replace(/__@sub@__/g, "<sub>").replace(/__@\/sub@__/g, "</sub>"); 175 textPlain = textPlain.replace(/__@sup@__/g, "<sup>").replace(/__@\/sup@__/g, "</sup>"); 176 textPlain = textPlain.replace(/__@kbd@__/g, "<kbd>").replace(/__@\/kbd@__/g, "</kbd>"); 177 textPlain = textPlain.replace(/__@u@__/g, "<u>").replace(/__@\/u@__/g, "</u>"); 178 179 enableLuteMarkdownSyntax(protyle); 180 const content = protyle.lute.BlockDOM2EscapeMarkerContent(protyle.lute.Md2BlockDOM(textPlain)); 181 restoreLuteMarkdownSyntax(protyle); 182 183 // insertHTML 会进行内部反转义 184 insertHTML(content, protyle, false, false, true); 185 } 186}; 187 188export const enableLuteMarkdownSyntax = (protyle: IProtyle) => { 189 protyle.lute.SetInlineAsterisk(true); 190 protyle.lute.SetGFMStrikethrough(true); 191 protyle.lute.SetInlineMath(true); 192 protyle.lute.SetSub(true); 193 protyle.lute.SetSup(true); 194 protyle.lute.SetTag(true); 195 protyle.lute.SetInlineUnderscore(true); 196}; 197 198export const restoreLuteMarkdownSyntax = (protyle: IProtyle) => { 199 protyle.lute.SetInlineAsterisk(window.siyuan.config.editor.markdown.inlineAsterisk); 200 protyle.lute.SetGFMStrikethrough(window.siyuan.config.editor.markdown.inlineStrikethrough); 201 protyle.lute.SetInlineMath(window.siyuan.config.editor.markdown.inlineMath); 202 protyle.lute.SetSub(window.siyuan.config.editor.markdown.inlineSub); 203 protyle.lute.SetSup(window.siyuan.config.editor.markdown.inlineSup); 204 protyle.lute.SetTag(window.siyuan.config.editor.markdown.inlineTag); 205 protyle.lute.SetInlineUnderscore(window.siyuan.config.editor.markdown.inlineUnderscore); 206 protyle.lute.SetMark(window.siyuan.config.editor.markdown.inlineMark); 207}; 208 209const readLocalFile = async (protyle: IProtyle, localFiles: string[]) => { 210 if (protyle && protyle.app && protyle.app.plugins) { 211 for (let i = 0; i < protyle.app.plugins.length; i++) { 212 const response: { files: string[] } = await new Promise((resolve) => { 213 const emitResult = protyle.app.plugins[i].eventBus.emit("paste", { 214 protyle, 215 resolve, 216 textHTML: "", 217 textPlain: "", 218 siyuanHTML: "", 219 files: localFiles 220 }); 221 if (emitResult) { 222 resolve(undefined); 223 } 224 }); 225 if (response?.files) { 226 localFiles = response.files; 227 } 228 } 229 } 230 uploadLocalFiles(localFiles, protyle, true); 231}; 232 233export const paste = async (protyle: IProtyle, event: (ClipboardEvent | DragEvent | IClipboardData) & { 234 target: HTMLElement 235}) => { 236 if ("clipboardData" in event || "dataTransfer" in event) { 237 event.stopPropagation(); 238 event.preventDefault(); 239 } 240 let textHTML: string; 241 let textPlain: string; 242 let siyuanHTML: string; 243 let files: FileList | DataTransferItemList | File[]; 244 if ("clipboardData" in event) { 245 textHTML = event.clipboardData.getData("text/html"); 246 textPlain = event.clipboardData.getData("text/plain"); 247 siyuanHTML = event.clipboardData.getData("text/siyuan"); 248 files = event.clipboardData.files; 249 } else if ("dataTransfer" in event) { 250 textHTML = event.dataTransfer.getData("text/html"); 251 textPlain = event.dataTransfer.getData("text/plain"); 252 siyuanHTML = event.dataTransfer.getData("text/siyuan"); 253 if (event.dataTransfer.types[0] === "Files") { 254 files = event.dataTransfer.items; 255 } 256 } else { 257 if (event.localFiles?.length > 0) { 258 readLocalFile(protyle, event.localFiles); 259 return; 260 } 261 textHTML = event.textHTML; 262 textPlain = event.textPlain; 263 siyuanHTML = event.siyuanHTML; 264 files = event.files; 265 } 266 267 // Improve the pasting of selected text in PDF rectangular annotation https://github.com/siyuan-note/siyuan/issues/11629 268 textPlain = textPlain.replace(/\r\n|\r|\u2028|\u2029/g, "\n"); 269 270 /// #if !BROWSER 271 if (!siyuanHTML && !textHTML && !textPlain && ("clipboardData" in event)) { 272 const localFiles: string[] = await getLocalFiles(); 273 if (localFiles.length > 0) { 274 readLocalFile(protyle, localFiles); 275 return; 276 } 277 } 278 /// #endif 279 280 // 浏览器地址栏拷贝处理 281 if (textHTML.replace(/&amp;/g, "&").replace(/<(|\/)(html|body|meta)[^>]*?>/ig, "").trim() === 282 `<a href="${textPlain}">${textPlain}</a>` || 283 textHTML.replace(/&amp;/g, "&").replace(/<(|\/)(html|body|meta)[^>]*?>/ig, "").trim() === 284 `<!--StartFragment--><a href="${textPlain}">${textPlain}</a><!--EndFragment-->`) { 285 textHTML = ""; 286 } 287 // 复制标题及其下方块使用 writeText,需将 textPlain 转换为 textHTML 288 if (textPlain.endsWith(Constants.ZWSP) && !textHTML && !siyuanHTML) { 289 siyuanHTML = textPlain.substr(0, textPlain.length - 1); 290 } 291 // 复制/剪切折叠标题需获取 siyuanHTML 292 if (textHTML && textPlain && !siyuanHTML) { 293 const textObj = getTextSiyuanFromTextHTML(textHTML); 294 siyuanHTML = textObj.textSiyuan; 295 textHTML = textObj.textHtml; 296 } 297 // 剪切复制中首位包含空格或仅有空格 https://github.com/siyuan-note/siyuan/issues/5667 298 if (!siyuanHTML) { 299 // process word 300 const doc = new DOMParser().parseFromString(textHTML, "text/html"); 301 if (doc.body && doc.body.innerHTML) { 302 textHTML = doc.body.innerHTML; 303 } 304 // windows 剪切板 305 if (textHTML.startsWith("\n<!--StartFragment-->") && textHTML.endsWith("<!--EndFragment-->\n\n")) { 306 textHTML = doc.body.innerHTML.trim().replace("<!--StartFragment-->", "").replace("<!--EndFragment-->", ""); 307 } 308 textHTML = Lute.Sanitize(textHTML); 309 } 310 311 if (protyle && protyle.app && protyle.app.plugins) { 312 for (let i = 0; i < protyle.app.plugins.length; i++) { 313 const response: IObject & { files: FileList } = await new Promise((resolve) => { 314 const emitResult = protyle.app.plugins[i].eventBus.emit("paste", { 315 protyle, 316 resolve, 317 textHTML, 318 textPlain, 319 siyuanHTML, 320 files 321 }); 322 if (emitResult) { 323 resolve(undefined); 324 } 325 }); 326 327 if (response?.textHTML) { 328 textHTML = response.textHTML; 329 } 330 if (response?.textPlain) { 331 textPlain = response.textPlain; 332 } 333 if (response?.siyuanHTML) { 334 siyuanHTML = response.siyuanHTML; 335 } 336 if (response?.files) { 337 files = response.files as FileList; 338 } 339 } 340 } 341 342 const nodeElement = hasClosestBlock(event.target); 343 if (!nodeElement) { 344 if (files && files.length > 0) { 345 uploadFiles(protyle, files); 346 } 347 return; 348 } 349 protyle.hint.enableExtend = Constants.BLOCK_HINT_KEYS.includes(protyle.hint.splitChar); 350 hideElements(protyle.hint.enableExtend ? ["select"] : ["select", "hint"], protyle); 351 protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--hl").forEach(item => { 352 item.classList.remove("protyle-wysiwyg--hl"); 353 }); 354 const code = processPasteCode(textHTML, textPlain, protyle); 355 const range = getEditorRange(protyle.wysiwyg.element); 356 if (nodeElement.getAttribute("data-type") === "NodeCodeBlock" || 357 protyle.toolbar.getCurrentType(range).includes("code")) { 358 // https://github.com/siyuan-note/siyuan/issues/13552 359 insertHTML(textPlain.replace(/\u200D```/g, "```").replace(/```/g, "\u200D```"), protyle); 360 return; 361 } else if (siyuanHTML) { 362 // 编辑器内部粘贴 363 const tempElement = document.createElement("div"); 364 tempElement.innerHTML = siyuanHTML; 365 if (range.toString()) { 366 let types: string[] = []; 367 let linkElement: HTMLElement; 368 if (tempElement.childNodes.length === 1 && tempElement.childElementCount === 1) { 369 types = (tempElement.firstElementChild.getAttribute("data-type") || "").split(" "); 370 if ((types.includes("block-ref") || types.includes("a"))) { 371 linkElement = tempElement.firstElementChild as HTMLElement; 372 } 373 } 374 if (!linkElement) { 375 const linkTemp = document.createElement("template"); 376 linkTemp.innerHTML = protyle.lute.SpinBlockDOM(siyuanHTML); 377 if (linkTemp.content.firstChild.nodeType !== 3 && linkTemp.content.firstElementChild.classList.contains("p")) { 378 linkTemp.innerHTML = linkTemp.content.firstElementChild.firstElementChild.innerHTML.trim(); 379 } 380 if (linkTemp.content.childNodes.length === 1 && linkTemp.content.childElementCount === 1) { 381 types = (linkTemp.content.firstElementChild.getAttribute("data-type") || "").split(" "); 382 if ((types.includes("block-ref") || types.includes("a"))) { 383 linkElement = linkTemp.content.firstElementChild as HTMLElement; 384 } 385 } 386 } 387 388 if (types.includes("block-ref")) { 389 const refElement = protyle.toolbar.setInlineMark(protyle, "block-ref", "range", { 390 type: "id", 391 color: `${linkElement.dataset.id}${Constants.ZWSP}s${Constants.ZWSP}${range.toString()}` 392 }); 393 if (refElement[0]) { 394 protyle.toolbar.range.selectNodeContents(refElement[0]); 395 } 396 return; 397 } 398 if (types.includes("a")) { 399 protyle.toolbar.setInlineMark(protyle, "a", "range", { 400 type: "a", 401 color: `${linkElement.dataset.href}${Constants.ZWSP}${range.toString()}` 402 }); 403 return; 404 } 405 } 406 let isBlock = false; 407 tempElement.querySelectorAll("[data-node-id]").forEach((e) => { 408 const newId = Lute.NewNodeID(); 409 e.setAttribute("data-node-id", newId); 410 e.removeAttribute(Constants.CUSTOM_RIFF_DECKS); 411 e.classList.remove("protyle-wysiwyg--select", "protyle-wysiwyg--hl"); 412 e.setAttribute("updated", newId.split("-")[0]); 413 e.removeAttribute("refcount"); 414 isBlock = true; 415 }); 416 if (nodeElement.classList.contains("table")) { 417 isBlock = false; 418 } 419 // 从历史中复制后粘贴 420 tempElement.querySelectorAll('[contenteditable="false"][spellcheck]').forEach((e) => { 421 e.setAttribute("contenteditable", "true"); 422 }); 423 let tempInnerHTML = tempElement.innerHTML; 424 if (!nodeElement.classList.contains("av") && tempInnerHTML.startsWith("[[{") && tempInnerHTML.endsWith("}]]")) { 425 try { 426 const json = JSON.parse(tempInnerHTML); 427 if (json.length > 0 && json[0].length > 0 && json[0][0].id && json[0][0].type) { 428 insertHTML(textPlain, protyle, isBlock); 429 } else { 430 insertHTML(tempInnerHTML, protyle, isBlock); 431 } 432 } catch (e) { 433 insertHTML(tempInnerHTML, protyle, isBlock); 434 } 435 } else { 436 if (-1 < tempInnerHTML.indexOf("NodeHTMLBlock")) { 437 // 复制 HTML 块粘贴出来的不是 HTML 块 https://github.com/siyuan-note/siyuan/issues/12994 438 tempInnerHTML = Lute.UnEscapeHTMLStr(tempInnerHTML); 439 } 440 441 // https://github.com/siyuan-note/siyuan/issues/13552 442 tempInnerHTML = tempInnerHTML.replace(/\u200D```/g, "```"); 443 444 insertHTML(tempInnerHTML, protyle, isBlock, false, true); 445 } 446 blockRender(protyle, protyle.wysiwyg.element); 447 processRender(protyle.wysiwyg.element); 448 highlightRender(protyle.wysiwyg.element); 449 avRender(protyle.wysiwyg.element, protyle); 450 } else if (code) { 451 if (!code.startsWith('<div data-type="NodeCodeBlock" class="code-block" data-node-id="')) { 452 // 原有代码在行内元素中粘贴会嵌套 453 insertHTML(code, protyle); 454 } else { 455 insertHTML(code, protyle, true, false, true); 456 highlightRender(protyle.wysiwyg.element); 457 } 458 hideElements(["hint"], protyle); 459 } else { 460 let isHTML = false; 461 if (textHTML.replace("<!--StartFragment--><!--EndFragment-->", "").trim() !== "") { 462 textHTML = textHTML.replace("<!--StartFragment-->", "").replace("<!--EndFragment-->", "").trim(); 463 if (files && files.length === 1 && ( 464 textHTML.startsWith("<img") || // 浏览器上复制单个图片 465 (textHTML.startsWith("<table") && textHTML.indexOf("<img") > -1) // Excel 或者浏览器中复制带有图片的表格 466 )) { 467 isHTML = false; 468 } else { 469 // 需注意 Edge 中的划选不应识别为图片 https://github.com/siyuan-note/siyuan/issues/7021 470 isHTML = true; 471 } 472 473 const textHTMLLowercase = textHTML.toLowerCase(); 474 if (textPlain && "" !== textPlain.trim() && (textHTML.startsWith("<span") || textHTML.startsWith("<br")) && 475 (0 > textHTMLLowercase.indexOf("class=\"katex") && 0 > textHTMLLowercase.indexOf("class=\"math") && 476 0 > textHTMLLowercase.indexOf("</a>") && 0 > textHTMLLowercase.indexOf("</img>") && 0 > textHTMLLowercase.indexOf("</code>") && 477 0 > textHTMLLowercase.indexOf("</b>") && 0 > textHTMLLowercase.indexOf("</strong>") && 478 0 > textHTMLLowercase.indexOf("</i>") && 0 > textHTMLLowercase.indexOf("</em>") && 479 0 > textHTMLLowercase.indexOf("</ol>") && 0 > textHTMLLowercase.indexOf("</ul>") && 480 0 > textHTMLLowercase.indexOf("</table>") && 0 > textHTMLLowercase.indexOf("</blockquote>") && 481 0 > textHTMLLowercase.indexOf("</h1>") && 0 > textHTMLLowercase.indexOf("</h2>") && 482 0 > textHTMLLowercase.indexOf("</h3>") && 0 > textHTMLLowercase.indexOf("</h4>") && 483 0 > textHTMLLowercase.indexOf("</h5>") && 0 > textHTMLLowercase.indexOf("</h6>"))) { 484 // 豆包复制粘贴问题 https://github.com/siyuan-note/siyuan/issues/13265 https://github.com/siyuan-note/siyuan/issues/14313 485 isHTML = false; 486 } 487 } 488 if (isHTML) { 489 const tempElement = document.createElement("div"); 490 tempElement.innerHTML = textHTML; 491 tempElement.querySelectorAll("[style]").forEach((e) => { 492 e.removeAttribute("style"); 493 }); 494 // 移除空的 A 标签 495 tempElement.querySelectorAll("a").forEach((e) => { 496 if (e.innerHTML.trim() === "") { 497 e.remove(); 498 } 499 }); 500 // https://github.com/siyuan-note/siyuan/issues/14625#issuecomment-2869618067 501 let linkElement; 502 if (tempElement.childElementCount === 1 && tempElement.childNodes.length === 1) { 503 if (tempElement.firstElementChild.tagName === "A") { 504 linkElement = tempElement.firstElementChild; 505 } else if (tempElement.firstElementChild.tagName === "P" && 506 tempElement.firstElementChild.childElementCount === 1 && 507 tempElement.firstElementChild.childNodes.length === 1 && 508 tempElement.firstElementChild.firstElementChild.tagName === "A") { 509 linkElement = tempElement.firstElementChild.firstElementChild; 510 } 511 } 512 if (linkElement) { 513 const selectText = range.toString(); 514 const aElements = protyle.toolbar.setInlineMark(protyle, "a", "range", { 515 type: "a", 516 color: `${linkElement.getAttribute("href")}${Constants.ZWSP}${selectText || linkElement.textContent}` 517 }); 518 if (!selectText) { 519 if (aElements[0].lastChild) { 520 // https://github.com/siyuan-note/siyuan/issues/15801 521 range.setEnd(aElements[0].lastChild, aElements[0].lastChild.textContent.length); 522 } 523 range.collapse(false); 524 } 525 return; 526 } 527 fetchPost("/api/lute/html2BlockDOM", { 528 dom: tempElement.innerHTML 529 }, (response) => { 530 insertHTML(response.data, protyle, false, false, true); 531 protyle.wysiwyg.element.querySelectorAll('[data-type~="block-ref"]').forEach(item => { 532 if (item.textContent === "") { 533 fetchPost("/api/block/getRefText", {id: item.getAttribute("data-id")}, (response) => { 534 item.innerHTML = response.data; 535 }); 536 } 537 }); 538 blockRender(protyle, protyle.wysiwyg.element); 539 processRender(protyle.wysiwyg.element); 540 highlightRender(protyle.wysiwyg.element); 541 avRender(protyle.wysiwyg.element, protyle); 542 scrollCenter(protyle, undefined, false, "smooth"); 543 }); 544 return; 545 } else if (files && files.length > 0) { 546 uploadFiles(protyle, files); 547 return; 548 } else if (textPlain.trim() !== "" && (files && files.length === 0 || !files)) { 549 if (range.toString() !== "") { 550 const firstLine = textPlain.split("\n")[0]; 551 if (isDynamicRef(textPlain)) { 552 const refElement = protyle.toolbar.setInlineMark(protyle, "block-ref", "range", { 553 type: "id", 554 // range 不能 escape,否则 https://github.com/siyuan-note/siyuan/issues/8359 555 color: `${textPlain.substring(2, 22 + 2)}${Constants.ZWSP}s${Constants.ZWSP}${range.toString()}` 556 }); 557 if (refElement[0]) { 558 protyle.toolbar.range.selectNodeContents(refElement[0]); 559 } 560 return; 561 } else if (isFileAnnotation(firstLine)) { 562 protyle.toolbar.setInlineMark(protyle, "file-annotation-ref", "range", { 563 type: "file-annotation-ref", 564 color: firstLine.substring(2).replace(/ ".+">>$/, "") 565 }); 566 return; 567 } else { 568 // https://github.com/siyuan-note/siyuan/issues/8475 569 const linkDest = textPlain.startsWith("assets/") ? textPlain : protyle.lute.GetLinkDest(textPlain); 570 if (linkDest) { 571 protyle.toolbar.setInlineMark(protyle, "a", "range", { 572 type: "a", 573 color: linkDest 574 }); 575 return; 576 } 577 } 578 } 579 580 // https://github.com/siyuan-note/siyuan/issues/13552 581 textPlain = textPlain.replace(/\u200D```/g, "```"); 582 583 const textPlainDom = protyle.lute.Md2BlockDOM(textPlain); 584 insertHTML(textPlainDom, protyle, false, false, true); 585 } 586 blockRender(protyle, protyle.wysiwyg.element); 587 processRender(protyle.wysiwyg.element); 588 highlightRender(protyle.wysiwyg.element); 589 avRender(protyle.wysiwyg.element, protyle); 590 } 591 const selectCellElement = nodeElement.querySelector(".av__cell--select"); 592 if (nodeElement.classList.contains("av") && selectCellElement) { 593 cellScrollIntoView(nodeElement, selectCellElement); 594 } else { 595 scrollCenter(protyle, undefined, false, "smooth"); 596 } 597}; 598