diff --git a/app/src/mobile/util/keyboardToolbar.ts b/app/src/mobile/util/keyboardToolbar.ts
index 215249025..696ade80c 100644
--- a/app/src/mobile/util/keyboardToolbar.ts
+++ b/app/src/mobile/util/keyboardToolbar.ts
@@ -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;
}
diff --git a/app/src/protyle/wysiwyg/codeBlock.ts b/app/src/protyle/wysiwyg/codeBlock.ts
new file mode 100644
index 000000000..b148c75aa
--- /dev/null
+++ b/app/src/protyle/wysiwyg/codeBlock.ts
@@ -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 + "
");
+ 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();
+};
diff --git a/app/src/protyle/wysiwyg/keydown.ts b/app/src/protyle/wysiwyg/keydown.ts
index 4f52b53d1..3a8504bd1 100644
--- a/app/src/protyle/wysiwyg/keydown.ts
+++ b/app/src/protyle/wysiwyg/keydown.ts
@@ -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 + "
");
- 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();