A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
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(/&/g, "&").replace(/<(|\/)(html|body|meta)[^>]*?>/ig, "").trim() ===
282 `<a href="${textPlain}">${textPlain}</a>` ||
283 textHTML.replace(/&/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