diff --git a/app/src/config/keymap.ts b/app/src/config/keymap.ts
index 0335c068c..6b642afa0 100644
--- a/app/src/config/keymap.ts
+++ b/app/src/config/keymap.ts
@@ -464,7 +464,8 @@ export const keymap = {
let hasConflict = false;
const isAssistKey = ["⌘", "⇧", "⌥", "⌃"].includes(keymapStr.substr(keymapStr.length - 1, 1));
if (isAssistKey ||
- ["⌘A", "⌘X", "⌘C", "⌘V", "⌘-", "⌘=", "⌘0", "⇧⌘V", "⌘/", "⇧↑", "⇧↓", "⇧→", "⇧←", "⇧⇥", "⌃D", "⇧⌘→", "⇧⌘←", "⌘Home", "⌘End", "⇧↩", "↩", "PageUp", "PageDown", "⌫", "⌦", "Escape"].includes(keymapStr) ||
+ ["⌘A", "⌘X", "⌘C", "⌘V", "⌘-", "⌘=", "⌘0", "⇧⌘V", "⌘/", "⇧↑", "⇧↓", "⇧→", "⇧←", "⇧⇥", "⌃D", "⇧⌘→",
+ "⇧⌘←", "⌘Home", "⌘End", "⇧↩", "⌥↩", "↩", "PageUp", "PageDown", "⌫", "⌦", "Escape"].includes(keymapStr) ||
// 跳转到下/上一个编辑页签不能包含 ctrl, 否则不能监听到 keyup
(isMac() && keys[0] === "general" && ["goToEditTabNext", "goToEditTabPrev"].includes(keys[1]) && keymapStr.includes("⌘"))
) {
diff --git a/app/src/constants.ts b/app/src/constants.ts
index dc2e37fdd..8a4cb65fa 100644
--- a/app/src/constants.ts
+++ b/app/src/constants.ts
@@ -343,7 +343,7 @@ export abstract class Constants {
// 冲突不使用 "⌘S/Q"
// "⌘", "⇧", "⌥", "⌃"
// "⌘A", "⌘X", "⌘C", "⌘V", "⌘-", "⌘=", "⌘0", "⇧⌘V", "⌘/", "⇧↑", "⇧↓", "⇧→", "⇧←", "⇧⇥", "⌃D", "⇧⌘→", "⇧⌘←",
- // "⌘Home", "⌘End", "⇧↩", "↩", "PageUp", "PageDown", "⌫", "⌦", "Escape" 不可自定义
+ // "⌘Home", "⌘End", "⇧↩", "⌥↩", "↩", "PageUp", "PageDown", "⌫", "⌦", "Escape" 不可自定义
public static readonly SIYUAN_KEYMAP: Config.IKeymap = {
general: {
mainMenu: {default: "⌥\\", custom: "⌥\\"},
diff --git a/app/src/protyle/toolbar/index.ts b/app/src/protyle/toolbar/index.ts
index 4ab7da9a9..d0fbd62f6 100644
--- a/app/src/protyle/toolbar/index.ts
+++ b/app/src/protyle/toolbar/index.ts
@@ -15,7 +15,7 @@ import {
import {hasClosestBlock, hasClosestByAttribute, hasClosestByClassName, hasClosestByTag} from "../util/hasClosest";
import {Link} from "./Link";
import {setPosition} from "../../util/setPosition";
-import {updateTransaction} from "../wysiwyg/transaction";
+import {transaction, updateTransaction} from "../wysiwyg/transaction";
import {Constants} from "../../constants";
import {copyPlainText, openByMobile, readClipboard, setStorageVal} from "../util/compatibility";
import {upDownHint} from "../../util/upDownHint";
@@ -1321,17 +1321,14 @@ export class Toolbar {
});
}
- public showCodeLanguage(protyle: IProtyle, languageElement: HTMLElement) {
- const nodeElement = hasClosestBlock(languageElement);
+ public showCodeLanguage(protyle: IProtyle, languageElements: HTMLElement[]) {
+ const nodeElement = hasClosestBlock(languageElements[0]);
if (!nodeElement) {
return;
}
hideElements(["hint"], protyle);
window.siyuan.menus.menu.remove();
this.range = getEditorRange(nodeElement);
- const id = nodeElement.getAttribute("data-node-id");
- let oldHtml = nodeElement.outerHTML;
-
let html = `
${window.siyuan.languages.clear}
`;
const hljsLanguages = Constants.ALIAS_CODE_LANGUAGES.concat(window.hljs?.listLanguages() ?? []).sort();
hljsLanguages.forEach((item, index) => {
@@ -1354,7 +1351,7 @@ export class Toolbar {
}
upDownHint(listElement, event);
if (event.key === "Enter") {
- oldHtml = this.updateLanguage(languageElement, protyle, id, nodeElement, oldHtml, this.subElement.querySelector(".b3-list-item--focus").textContent);
+ this.updateLanguage(languageElements, protyle, this.subElement.querySelector(".b3-list-item--focus").textContent);
event.preventDefault();
event.stopPropagation();
return;
@@ -1410,13 +1407,13 @@ export class Toolbar {
if (!listElement) {
return;
}
- oldHtml = this.updateLanguage(languageElement, protyle, id, nodeElement, oldHtml, listElement.textContent);
+ this.updateLanguage(languageElements, protyle, listElement.textContent);
});
this.subElement.style.zIndex = (++window.siyuan.zIndex).toString();
this.subElement.classList.remove("fn__none");
this.subElementCloseCB = undefined;
/// #if !MOBILE
- const nodeRect = languageElement.getBoundingClientRect();
+ const nodeRect = languageElements[0].getBoundingClientRect();
setPosition(this.subElement, nodeRect.left, nodeRect.bottom, nodeRect.height);
/// #else
setPosition(this.subElement, 0, 0);
@@ -1841,28 +1838,46 @@ ${item.name}
}
}
- private updateLanguage(languageElement: HTMLElement, protyle: IProtyle, id: string, nodeElement: HTMLElement, oldHtml: string, selectedLang: string) {
- languageElement.textContent = selectedLang === window.siyuan.languages.clear ? "" : selectedLang;
- if (!Constants.SIYUAN_RENDER_CODE_LANGUAGES.includes(languageElement.textContent)) {
- window.siyuan.storage[Constants.LOCAL_CODELANG] = languageElement.textContent;
+ private updateLanguage(languageElement: HTMLElement[], protyle: IProtyle, selectedLang: string) {
+ const currentLang = selectedLang === window.siyuan.languages.clear ? "" : selectedLang;
+ if (!Constants.SIYUAN_RENDER_CODE_LANGUAGES.includes(currentLang)) {
+ window.siyuan.storage[Constants.LOCAL_CODELANG] = currentLang;
setStorageVal(Constants.LOCAL_CODELANG, window.siyuan.storage[Constants.LOCAL_CODELANG]);
}
- const editElement = getContenteditableElement(nodeElement);
- if (Constants.SIYUAN_RENDER_CODE_LANGUAGES.includes(languageElement.textContent)) {
- nodeElement.dataset.content = editElement.textContent.trim();
- nodeElement.dataset.subtype = languageElement.textContent;
- nodeElement.className = "render-node";
- nodeElement.innerHTML = `${Constants.ZWSP}
`;
- processRender(nodeElement);
- } else {
- (editElement as HTMLElement).textContent = editElement.textContent;
- editElement.parentElement.removeAttribute("data-render");
- highlightRender(nodeElement);
- }
- nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss"));
- updateTransaction(protyle, id, nodeElement.outerHTML, oldHtml);
+ const doOperations: IOperation[] = [];
+ const undoOperations: IOperation[] = [];
+ languageElement.forEach(item => {
+ const nodeElement = hasClosestBlock(item);
+ if (nodeElement) {
+ const id = nodeElement.getAttribute("data-node-id");
+ undoOperations.push({
+ id,
+ data: nodeElement.outerHTML,
+ action: "update"
+ });
+ item.textContent = selectedLang === window.siyuan.languages.clear ? "" : selectedLang;
+ const editElement = getContenteditableElement(nodeElement);
+ if (Constants.SIYUAN_RENDER_CODE_LANGUAGES.includes(currentLang)) {
+ nodeElement.dataset.content = editElement.textContent.trim();
+ nodeElement.dataset.subtype = currentLang;
+ nodeElement.className = "render-node";
+ nodeElement.innerHTML = `${Constants.ZWSP}
`;
+ processRender(nodeElement);
+ } else {
+ (editElement as HTMLElement).textContent = editElement.textContent;
+ editElement.parentElement.removeAttribute("data-render");
+ highlightRender(nodeElement);
+ }
+ nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss"));
+ doOperations.push({
+ id,
+ data: nodeElement.outerHTML,
+ action: "update"
+ });
+ }
+ });
+ transaction(protyle, doOperations, undoOperations);
this.subElement.classList.add("fn__none");
focusByRange(this.range);
- return nodeElement.outerHTML;
}
}
diff --git a/app/src/protyle/wysiwyg/index.ts b/app/src/protyle/wysiwyg/index.ts
index ba5ff183a..c78f83ab6 100644
--- a/app/src/protyle/wysiwyg/index.ts
+++ b/app/src/protyle/wysiwyg/index.ts
@@ -2506,7 +2506,7 @@ export class WYSIWYG {
const languageElement = hasClosestByClassName(event.target, "protyle-action__language");
if (languageElement && !protyle.disabled && !ctrlIsPressed) {
- protyle.toolbar.showCodeLanguage(protyle, languageElement);
+ protyle.toolbar.showCodeLanguage(protyle, [languageElement]);
event.stopPropagation();
event.preventDefault();
return;
diff --git a/app/src/protyle/wysiwyg/keydown.ts b/app/src/protyle/wysiwyg/keydown.ts
index 68683b7b5..cd3df62dc 100644
--- a/app/src/protyle/wysiwyg/keydown.ts
+++ b/app/src/protyle/wysiwyg/keydown.ts
@@ -988,6 +988,29 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => {
return;
}
+ // 代码块语言选择 https://github.com/siyuan-note/siyuan/issues/14126
+ if (matchHotKey("⌥↩", event) && selectText === "") {
+ const selectElements = Array.from(protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select"));
+ if (selectElements.length === 0 && nodeElement.classList.contains("code-block")) {
+ selectElements.push(nodeElement);
+ }
+ if (selectElements.length > 0) {
+ const otherElement = selectElements.find(item => {
+ return !item.classList.contains("code-block");
+ });
+ if (!otherElement) {
+ const languageElements: HTMLElement[] = [];
+ selectElements.forEach(item => {
+ languageElements.push(item.querySelector(".protyle-action__language"));
+ });
+ protyle.toolbar.showCodeLanguage(protyle, languageElements);
+ event.stopPropagation();
+ event.preventDefault();
+ return;
+ }
+ }
+ }
+
// 回车
if (isNotCtrl(event) && event.key === "Enter") {
if (event.altKey) {