Vanessa 2026-02-04 00:12:03 +08:00
parent 8bf4ed7fc4
commit 31e37ff3d0
3 changed files with 93 additions and 64 deletions

View file

@ -13,6 +13,7 @@ import {fontEvent, getFontNodeElements} from "../../protyle/toolbar/Font";
import {hideElements} from "../../protyle/ui/hideElements";
import {softEnter} from "../../protyle/wysiwyg/enter";
import {isInAndroid, isInHarmony} from "../../protyle/util/compatibility";
import {tabCodeBlock} from "../../protyle/wysiwyg/codeBlock";
let renderKeyboardToolbarTimeout: number;
let showUtil = false;
@ -346,14 +347,17 @@ const renderKeyboardToolbar = () => {
const dynamicElements = document.querySelectorAll("#keyboardToolbar .keyboard__dynamic");
const range = getSelection().getRangeAt(0);
const isProtyle = hasClosestByClassName(range.startContainer, "protyle-wysiwyg", true);
if (!isProtyle) {
const nodeElement = hasClosestBlock(range.startContainer);
if (!isProtyle || !nodeElement) {
dynamicElements[0].classList.add("fn__none");
dynamicElements[1].classList.add("fn__none");
return;
}
const selectText = range.toString();
if (selectText || dynamicElements[0].querySelector('[data-type="goinline"]').classList.contains("protyle-toolbar__item--current")) {
if (!nodeElement.classList.contains("code-block") &&
(selectText || dynamicElements[0].querySelector('[data-type="goinline"]').classList.contains("protyle-toolbar__item--current"))) {
dynamicElements[0].classList.add("fn__none");
dynamicElements[1].classList.remove("fn__none");
} else {
@ -374,16 +378,20 @@ const renderKeyboardToolbar = () => {
} else {
dynamicElements[0].querySelector('[data-type="redo"]').removeAttribute("disabled");
}
const nodeElement = hasClosestBlock(range.startContainer);
if (nodeElement) {
const outdentElement = dynamicElements[0].querySelector('[data-type="outdent"]');
if (nodeElement.parentElement.classList.contains("li")) {
outdentElement.classList.remove("fn__none");
outdentElement.nextElementSibling.classList.remove("fn__none");
} else {
outdentElement.classList.add("fn__none");
outdentElement.nextElementSibling.classList.add("fn__none");
}
const outdentElement = dynamicElements[0].querySelector('[data-type="outdent"]');
const goinlineElement = dynamicElements[0].querySelector('[data-type="goinline"]');
if (nodeElement.classList.contains("code-block")) {
goinlineElement.classList.add("fn__none");
} else {
goinlineElement.classList.remove("fn__none");
}
if (nodeElement.parentElement.classList.contains("li") ||
nodeElement.classList.contains("code-block")) {
outdentElement.classList.remove("fn__none");
outdentElement.nextElementSibling.classList.remove("fn__none");
} else {
outdentElement.classList.add("fn__none");
outdentElement.nextElementSibling.classList.add("fn__none");
}
}
@ -689,11 +697,23 @@ export const initKeyboardToolbar = () => {
activeBlur();
return;
} else if (type === "outdent") {
listOutdent(protyle, [nodeElement.parentElement], range);
if (nodeElement.classList.contains("code-block")) {
if (range.toString() !== "") {
tabCodeBlock(protyle, nodeElement, range, true);
}
} else {
listOutdent(protyle, [nodeElement.parentElement], range);
}
focusByRange(range);
return;
} else if (type === "indent") {
listIndent(protyle, [nodeElement.parentElement], range);
if (nodeElement.classList.contains("code-block")) {
if (range.toString() !== "") {
tabCodeBlock(protyle, nodeElement, range);
}
} else {
listIndent(protyle, [nodeElement.parentElement], range);
}
focusByRange(range);
return;
}

View file

@ -0,0 +1,56 @@
import {hasNextSibling} from "./getBlock";
import {setLastNodeRange} from "../util/selection";
import {updateTransaction} from "./transaction";
export const tabCodeBlock = (protyle: IProtyle, nodeElement: HTMLElement,
range: Range, outdent = false) => {
// https://github.com/siyuan-note/siyuan/issues/12650
if (!hasNextSibling(range.endContainer) && range.endContainer.textContent.endsWith("\n") && range.endOffset > 0) {
range.setEnd(range.endContainer, range.endOffset - 1);
}
const wbrElement = document.createElement("wbr");
range.insertNode(wbrElement);
range.setStartAfter(wbrElement);
const oldHTML = nodeElement.outerHTML;
let text = "";
const tabSpace = window.siyuan.config.editor.codeTabSpaces === 0 ? "\t" : "".padStart(window.siyuan.config.editor.codeTabSpaces, " ");
if (!outdent) {
range.extractContents().textContent.split("\n").forEach((item: string) => {
text += tabSpace + item + "\n";
});
} else {
range.extractContents().textContent.split("\n").forEach((item: string) => {
if (item.startsWith(tabSpace)) {
text += item.replace(tabSpace, "") + "\n";
} else {
text += item + "\n";
}
});
}
let language = nodeElement.querySelector(".protyle-action__language").textContent;
// 语言优先级处理 https://github.com/siyuan-note/siyuan/issues/14767
if (range.commonAncestorContainer.nodeType === 1) {
const snippetClassName = (range.commonAncestorContainer as HTMLElement).className;
if (snippetClassName.startsWith("language-")) {
language = snippetClassName.replace("language-", "");
// https://github.com/siyuan-note/siyuan/issues/14767
if (wbrElement.parentElement !== range.commonAncestorContainer) {
wbrElement.parentElement.after(wbrElement);
wbrElement.previousElementSibling.remove();
}
}
}
if (!window.hljs.getLanguage(language)) {
language = "plaintext";
}
wbrElement.insertAdjacentHTML("afterend", window.hljs.highlight(text.substr(0, text.length - 1), {
language,
ignoreIllegals: true
}).value + "<br>");
range.setStart(wbrElement.nextSibling, 0);
const brElement = wbrElement.parentElement.querySelector("br");
setLastNodeRange(brElement.previousSibling as Element, range, false);
brElement.remove();
updateTransaction(protyle, nodeElement.getAttribute("data-node-id"), nodeElement.outerHTML, oldHTML);
wbrElement.remove();
};

View file

@ -75,6 +75,7 @@ import {openLink} from "../../editor/openLink";
import {onlyProtyleCommand} from "../../boot/globalEvent/command/protyle";
import {AIChat} from "../../ai/chat";
import {updateCalloutType} from "./callout";
import {tabCodeBlock} from "./codeBlock";
export const getContentByInlineHTML = (range: Range, cb: (content: string) => void) => {
let html = "";
@ -1870,56 +1871,8 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => {
// tab 需等待 list 和 table 处理完成
if (event.key === "Tab" && isNotCtrl(event) && !event.altKey) {
event.preventDefault();
const tabSpace = window.siyuan.config.editor.codeTabSpaces === 0 ? "\t" : "".padStart(window.siyuan.config.editor.codeTabSpaces, " ");
if (nodeType === "NodeCodeBlock" && selectText !== "") {
// https://github.com/siyuan-note/siyuan/issues/12650
if (!hasNextSibling(range.endContainer) && range.endContainer.textContent.endsWith("\n") && range.endOffset > 0) {
range.setEnd(range.endContainer, range.endOffset - 1);
}
const wbrElement = document.createElement("wbr");
range.insertNode(wbrElement);
range.setStartAfter(wbrElement);
const oldHTML = nodeElement.outerHTML;
let text = "";
if (!event.shiftKey) {
range.extractContents().textContent.split("\n").forEach((item: string) => {
text += tabSpace + item + "\n";
});
} else {
range.extractContents().textContent.split("\n").forEach((item: string) => {
if (item.startsWith(tabSpace)) {
text += item.replace(tabSpace, "") + "\n";
} else {
text += item + "\n";
}
});
}
let language = nodeElement.querySelector(".protyle-action__language").textContent;
// 语言优先级处理 https://github.com/siyuan-note/siyuan/issues/14767
if (range.commonAncestorContainer.nodeType === 1) {
const snippetClassName = (range.commonAncestorContainer as HTMLElement).className;
if (snippetClassName.startsWith("language-")) {
language = snippetClassName.replace("language-", "");
// https://github.com/siyuan-note/siyuan/issues/14767
if (wbrElement.parentElement !== range.commonAncestorContainer) {
wbrElement.parentElement.after(wbrElement);
wbrElement.previousElementSibling.remove();
}
}
}
if (!window.hljs.getLanguage(language)) {
language = "plaintext";
}
wbrElement.insertAdjacentHTML("afterend", window.hljs.highlight(text.substr(0, text.length - 1), {
language,
ignoreIllegals: true
}).value + "<br>");
range.setStart(wbrElement.nextSibling, 0);
const brElement = wbrElement.parentElement.querySelector("br");
setLastNodeRange(brElement.previousSibling as Element, range, false);
brElement.remove();
updateTransaction(protyle, nodeElement.getAttribute("data-node-id"), nodeElement.outerHTML, oldHTML);
wbrElement.remove();
tabCodeBlock(protyle, nodeElement, range, event.shiftKey);
return;
}
if (!event.shiftKey) {
@ -1946,7 +1899,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => {
range.insertNode(wbrElement);
const oldHTML = nodeElement.outerHTML;
range.extractContents();
range.insertNode(document.createTextNode(tabSpace));
range.insertNode(document.createTextNode(window.siyuan.config.editor.codeTabSpaces === 0 ? "\t" : "".padStart(window.siyuan.config.editor.codeTabSpaces, " ")));
range.collapse(false);
focusByRange(range);
wbrElement.remove();