Vanessa 2023-04-26 19:24:18 +08:00
parent a46324df77
commit 42cddbff45
4 changed files with 131 additions and 33 deletions

View file

@ -84,7 +84,8 @@ export const insertHTML = (html: string, protyle: IProtyle, isBlock = false,
}
const tempElement = document.createElement("template");
// 需要再 spin 一次 https://github.com/siyuan-note/siyuan/issues/7118
tempElement.innerHTML = protyle.lute.SpinBlockDOM(html);
tempElement.innerHTML = protyle.lute.SpinBlockDOM(html) ||
html; // 空格会被 Spin 不再,需要使用原文
const editableElement = getContenteditableElement(blockElement);
// 使用 lute 方法会添加 p 元素,只有一个 p 元素或者只有一个字符串或者为 <u>b</u> 时的时候只拷贝内部
if (!isBlock) {

View file

@ -11,6 +11,7 @@ import {blockRender} from "../markdown/blockRender";
import {hideElements} from "../ui/hideElements";
import {hasClosestByAttribute} from "../util/hasClosest";
import {fetchPost, fetchSyncPost} from "../../util/fetch";
import {headingTurnIntoList, turnIntoTaskList} from "./turnIntoList";
export const input = async (protyle: IProtyle, blockElement: HTMLElement, range: Range, needRender = true) => {
if (!blockElement.parentElement) {
@ -41,13 +42,19 @@ export const input = async (protyle: IProtyle, blockElement: HTMLElement, range:
}
const wbrElement = document.createElement("wbr");
range.insertNode(wbrElement);
let id = blockElement.getAttribute("data-node-id");
const id = blockElement.getAttribute("data-node-id");
if (type !== "NodeCodeBlock" && (editElement.innerHTML.endsWith("\n<wbr>") || editElement.innerHTML.endsWith("\n<wbr>\n"))) {
// 软换行
updateTransaction(protyle, id, blockElement.outerHTML, blockElement.outerHTML.replace("\n<wbr>", "<wbr>"));
wbrElement.remove();
return;
}
if (turnIntoTaskList(protyle, type, blockElement, editElement)) {
return;
}
if (headingTurnIntoList(protyle, type, blockElement, editElement)) {
return;
}
// table、粗体 中也会有 br仅用于类似#a#,删除后会产生的 br
const brElement = blockElement.querySelector("br");
if (brElement && brElement.parentElement.tagName !== "TD" && brElement.parentElement.tagName !== "TH" && (
@ -80,9 +87,8 @@ export const input = async (protyle: IProtyle, blockElement: HTMLElement, range:
}
}
let html = blockElement.outerHTML;
let todoOldHTML = "";
let focusHR = false;
if (editElement.textContent === "---" && !blockElement.classList.contains("code-block")) {
if (editElement.textContent === "---" && type !== "NodeCodeBlock") {
html = `<div data-node-id="${id}" data-type="NodeThematicBreak" class="hr"><div></div></div>`;
const nextBlockElement = getNextBlock(editElement);
if (nextBlockElement) {
@ -94,23 +100,6 @@ export const input = async (protyle: IProtyle, blockElement: HTMLElement, range:
} else {
html += genEmptyBlock(false, true);
}
} else if (!blockElement.classList.contains("code-block") && (["[]", "[ ]", "[x]", "[X]", "【】", "【 】", "【x】", "【X】"].includes(editElement.textContent))) {
const isDone = editElement.textContent.indexOf("x") > -1 || editElement.textContent.indexOf("X") > -1;
if (blockElement.parentElement.classList.contains("li") &&
blockElement.parentElement.childElementCount === 3 // https://ld246.com/article/1659315815506
) {
// 仅有一项的列表才可转换
if (!blockElement.parentElement.parentElement.classList.contains("protyle-wysiwyg") && // https://ld246.com/article/1659315815506
blockElement.parentElement.parentElement.childElementCount === 2) {
html = `<div data-subtype="t" data-node-id="${blockElement.parentElement.parentElement.getAttribute("data-node-id")}" data-type="NodeList" class="list"><div data-marker="*" data-subtype="t" data-node-id="${blockElement.parentElement.getAttribute("data-node-id")}" data-type="NodeListItem" class="li${isDone ? " protyle-task--done" : ""}"><div class="protyle-action protyle-action--task" draggable="true"><svg><use xlink:href="#icon${isDone ? "C" : "Unc"}heck"></use></svg></div><div data-node-id="${id}" data-type="NodeParagraph" class="p"><div contenteditable="true" spellcheck="${window.siyuan.config.editor.spellcheck}"><wbr></div><div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div>`;
id = blockElement.parentElement.parentElement.getAttribute("data-node-id");
blockElement = blockElement.parentElement.parentElement;
todoOldHTML = blockElement.outerHTML;
}
} else {
html = `<div data-subtype="t" data-node-id="${id}" data-type="NodeList" class="list"><div data-marker="*" data-subtype="t" data-node-id="${Lute.NewNodeID()}" data-type="NodeListItem" class="li${isDone ? " protyle-task--done" : ""}"><div class="protyle-action protyle-action--task" draggable="true"><svg><use xlink:href="#icon${isDone ? "C" : "Unc"}heck"></use></svg></div><div data-node-id="${Lute.NewNodeID()}" data-type="NodeParagraph" class="p"><div contenteditable="true" spellcheck="${window.siyuan.config.editor.spellcheck}"><wbr></div><div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div>`;
todoOldHTML = blockElement.outerHTML;
}
} else {
if (trimStartText.startsWith("```") || trimStartText.startsWith("~~~") || trimStartText.startsWith("···") ||
trimStartText.indexOf("\n```") > -1 || trimStartText.indexOf("\n~~~") > -1 || trimStartText.indexOf("\n···") > -1) {
@ -137,7 +126,7 @@ export const input = async (protyle: IProtyle, blockElement: HTMLElement, range:
// 内容删空后使用上下键,光标无法到达 https://github.com/siyuan-note/siyuan/issues/4167 https://ld246.com/article/1636256333803
tempElement.content.childElementCount === 1 && getContenteditableElement(tempElement.content.firstElementChild)?.innerHTML === "<wbr>"
) &&
!(tempElement.content.childElementCount === 1 && tempElement.content.firstElementChild.classList.contains("code-block") && blockElement.classList.contains("code-block"))
!(tempElement.content.childElementCount === 1 && tempElement.content.firstElementChild.classList.contains("code-block") && type === "NodeCodeBlock")
) {
log("SpinBlockDOM", blockElement.outerHTML, "argument", protyle.options.debugger);
log("SpinBlockDOM", html, "result", protyle.options.debugger);
@ -216,10 +205,10 @@ export const input = async (protyle: IProtyle, blockElement: HTMLElement, range:
protyle.hint.render(protyle);
}
hideElements(["gutter"], protyle);
updateInput(html, protyle, id, type, todoOldHTML);
updateInput(html, protyle, id, type);
};
const updateInput = (html: string, protyle: IProtyle, id: string, type: string, oldHTML?: string) => {
const updateInput = (html: string, protyle: IProtyle, id: string, type: string) => {
const tempElement = document.createElement("template");
tempElement.innerHTML = html;
const doOperations: IOperation[] = [];
@ -237,7 +226,7 @@ const updateInput = (html: string, protyle: IProtyle, id: string, type: string,
});
undoOperations.push({
id,
data: protyle.wysiwyg.lastHTMLs[id] || oldHTML,
data: protyle.wysiwyg.lastHTMLs[id],
action: "update"
});
} else {

View file

@ -577,7 +577,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => {
event.preventDefault();
return;
}
const selectText = range.toString();
// 上下左右光标移动
if (!event.altKey && !event.shiftKey && !isCtrl(event) && !event.isComposing && (event.key.indexOf("Arrow") > -1)) {
// 需使用 editabled否则代码块会把语言字数算入
@ -637,7 +637,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => {
}
}
}
} else if (range.toString() === "" && (event.key === "ArrowDown" || event.key === "ArrowRight") && nodeElement.isSameNode(getLastBlock(protyle.wysiwyg.element.lastElementChild)) &&
} else if (selectText === "" && (event.key === "ArrowDown" || event.key === "ArrowRight") && nodeElement.isSameNode(getLastBlock(protyle.wysiwyg.element.lastElementChild)) &&
// 表格无法右移动 https://ld246.com/article/1631434502215
!hasClosestByMatchTag(range.startContainer, "TD") && !hasClosestByMatchTag(range.startContainer, "TH")) {
// 页面按向下/右箭头丢失焦点 https://ld246.com/article/1629954026096
@ -648,7 +648,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => {
event.preventDefault();
focusByRange(range);
}
} else if (range.toString() === "" && event.key === "ArrowLeft" && nodeElement.isSameNode(getFirstBlock(protyle.wysiwyg.element.firstElementChild))) {
} else if (selectText === "" && event.key === "ArrowLeft" && nodeElement.isSameNode(getFirstBlock(protyle.wysiwyg.element.firstElementChild))) {
// 页面向左箭头丢失焦点 https://github.com/siyuan-note/siyuan/issues/2768
const firstEditElement = getContenteditableElement(nodeElement);
if (firstEditElement && getSelectionOffset(firstEditElement, undefined, range).start === 0) {
@ -688,7 +688,6 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => {
return;
}
const selectText = range.toString();
// 删除,不可使用 !isCtrl(event),否则软删除回导致 https://github.com/siyuan-note/siyuan/issues/5607
// 不可使用 !event.shiftKey否则 https://ld246.com/article/1666434796806
if (!event.altKey && (event.key === "Backspace" || event.key === "Delete")) {
@ -699,8 +698,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => {
return;
}
// https://github.com/siyuan-note/siyuan/issues/6796
const previousSibling = hasPreviousSibling(range.startContainer) as HTMLElement;
if (range.toString() === "" && event.key === "Backspace" &&
if (selectText === "" && event.key === "Backspace" &&
range.startOffset === range.startContainer.textContent.length &&
range.startContainer.textContent.endsWith("\n" + Constants.ZWSP)) {
range.setStart(range.startContainer, range.startOffset - 1);
@ -709,6 +707,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => {
event.preventDefault();
return;
}
const previousSibling = hasPreviousSibling(range.startContainer) as HTMLElement;
// https://github.com/siyuan-note/siyuan/issues/5547
if (range.startOffset === 1 && range.startContainer.textContent === Constants.ZWSP &&
previousSibling && previousSibling.nodeType !== 3 &&
@ -843,7 +842,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => {
}
// 软换行
if (matchHotKey("⇧↩", event) && range.toString() === "") {
if (matchHotKey("⇧↩", event) && selectText === "") {
let startElement = range.startContainer as HTMLElement;
const nextSibling = hasNextSibling(startElement) as Element;
// 图片之前软换行
@ -1443,7 +1442,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => {
}
if (matchHotKey(window.siyuan.config.keymap.editor.general.copyPlainText.custom, event)) {
if (range.toString() === "") {
if (selectText === "") {
const selectsElement: HTMLElement[] = Array.from(protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select"));
let html = "";
if (selectsElement.length === 0) {

View file

@ -0,0 +1,109 @@
import {transaction} from "./transaction";
import {focusByWbr} from "../util/selection";
export const turnIntoTaskList = (protyle: IProtyle, type: string, blockElement: HTMLElement, editElement: HTMLElement) => {
if (type !== "NodeCodeBlock" &&
(
["[ ]", "[x]", "[X]", "【 】", "【x】", "【X】"].includes(editElement.innerHTML.substring(0, 3)) ||
["[]", "【】"].includes(editElement.innerHTML.substring(0, 2))
)
) {
const doOperation: IOperation[] = []
const undoOperation: IOperation[] = []
const contextStartIndex = (editElement.innerHTML.indexOf("]") || editElement.innerHTML.indexOf("]")) + 1;
const isDone = editElement.innerHTML.substring(1, 2).toLowerCase() === "x";
if (blockElement.parentElement.classList.contains("li") &&
blockElement.parentElement.childElementCount === 3 // https://ld246.com/article/1659315815506
) {
// 仅有一项的列表才可转换
if (!blockElement.parentElement.parentElement.classList.contains("protyle-wysiwyg") && // https://ld246.com/article/1659315815506
blockElement.parentElement.parentElement.childElementCount === 2) {
`<div data-subtype="t" data-node-id="${blockElement.parentElement.parentElement.getAttribute("data-node-id")}" data-type="NodeList" class="list"><div data-marker="*" data-subtype="t" data-node-id="${blockElement.parentElement.getAttribute("data-node-id")}" data-type="NodeListItem" class="li${isDone ? " protyle-task--done" : ""}"><div class="protyle-action protyle-action--task" draggable="true"><svg><use xlink:href="#icon${isDone ? "C" : "Unc"}heck"></use></svg></div><div data-node-id="${id}" data-type="NodeParagraph" class="p"><div contenteditable="true" spellcheck="${window.siyuan.config.editor.spellcheck}"><wbr></div><div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div>`;
}
return true;
} else {
const id = blockElement.getAttribute("data-node-id")
const newId = Lute.NewNodeID();
const emptyId = Lute.NewNodeID();
const liItemId = Lute.NewNodeID();
const oldHTML = blockElement.outerHTML;
editElement.innerHTML = editElement.innerHTML.substring(contextStartIndex);
transaction(protyle, [{
action: "update",
id,
data: blockElement.outerHTML,
}, {
action: "insert",
id: newId,
data: `<div data-subtype="t" data-node-id="${newId}" data-type="NodeList" class="list"><div data-marker="*" data-subtype="t" data-node-id="${liItemId}" data-type="NodeListItem" class="li${isDone ? " protyle-task--done" : ""}"><div class="protyle-action protyle-action--task" draggable="true"><svg><use xlink:href="#icon${isDone ? "C" : "Unc"}heck"></use></svg></div><div data-node-id="${emptyId}" data-type="NodeParagraph" class="p"><div contenteditable="true" spellcheck="${window.siyuan.config.editor.spellcheck}"></div><div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div>`,
previousID: id,
}, {
action: "move",
id,
previousID: emptyId,
}, {
action: "delete",
id: emptyId
}], [{
action: "update",
id,
data: oldHTML,
}, {
action: "move",
id,
previousID: newId,
}, {
action: "delete",
id: newId
}]);
blockElement.outerHTML = `<div data-subtype="t" data-node-id="${newId}" data-type="NodeList" class="list"><div data-marker="*" data-subtype="t" data-node-id="${liItemId}" data-type="NodeListItem" class="li${isDone ? " protyle-task--done" : ""}"><div class="protyle-action protyle-action--task" draggable="true"><svg><use xlink:href="#icon${isDone ? "C" : "Unc"}heck"></use></svg></div>${blockElement.outerHTML}<div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div>`
focusByWbr(protyle.wysiwyg.element, getSelection().getRangeAt(0))
return true;
}
}
return false
}
export const headingTurnIntoList = (protyle: IProtyle, type: string, blockElement: HTMLElement, editElement: HTMLElement) => {
if (type !== "NodeHeading" && ["* ", "- "].includes(editElement.innerHTML.substring(0, 2))) {
const id = blockElement.getAttribute("data-node-id")
const newId = Lute.NewNodeID();
const emptyId = Lute.NewNodeID();
const liItemId = Lute.NewNodeID();
const oldHTML = blockElement.outerHTML;
editElement.innerHTML = editElement.innerHTML.substring(2);
transaction(protyle, [{
action: "update",
id,
data: blockElement.outerHTML,
}, {
action: "insert",
id: newId,
data: `<div data-subtype="t" data-node-id="${newId}" data-type="NodeList" class="list"><div data-marker="*" data-subtype="t" data-node-id="${liItemId}" data-type="NodeListItem" class="li${isDone ? " protyle-task--done" : ""}"><div class="protyle-action protyle-action--task" draggable="true"><svg><use xlink:href="#icon${isDone ? "C" : "Unc"}heck"></use></svg></div><div data-node-id="${emptyId}" data-type="NodeParagraph" class="p"><div contenteditable="true" spellcheck="${window.siyuan.config.editor.spellcheck}"></div><div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div>`,
previousID: id,
}, {
action: "move",
id,
previousID: emptyId,
}, {
action: "delete",
id: emptyId
}], [{
action: "update",
id,
data: oldHTML,
}, {
action: "move",
id,
previousID: newId,
}, {
action: "delete",
id: newId
}]);
blockElement.outerHTML = `<div data-subtype="t" data-node-id="${newId}" data-type="NodeList" class="list"><div data-marker="*" data-subtype="t" data-node-id="${liItemId}" data-type="NodeListItem" class="li${isDone ? " protyle-task--done" : ""}"><div class="protyle-action protyle-action--task" draggable="true"><svg><use xlink:href="#icon${isDone ? "C" : "Unc"}heck"></use></svg></div>${blockElement.outerHTML}<div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div>`
focusByWbr(protyle.wysiwyg.element, getSelection().getRangeAt(0))
return true;
}
return false
}