效果是在面包屑旁添加一个全屏按钮,快捷切换页签的全屏状态:
JS 片段:
// 在面包屑旁添加全屏按钮,快捷切换全屏 JS片段
// author by JeffreyChen https://ld246.com/article/1731698559408
// 参考 @wilsons 的方法进行了优化 https://ld246.com/article/1731683038390/comment/1731693866270#comments ,不再需要全屏切换快捷键
(function() {
function addFullScreenButton(protyleElement) {
// 检查该 protyle 是否已经有了 fullScreen_simulate 按钮
if (protyleElement.querySelector('.fullScreen_simulate')) {
return; // 如果已存在,直接返回
}
let mode = protyleElement.querySelector('.protyle-breadcrumb .block__icon[data-type="readonly"]');
if (mode) {
mode.insertAdjacentHTML(
"beforebegin",
'<button class="fullScreen_simulate block__icon fn__flex-center ariaLabel" aria-label="全屏切换"></button>'
);
let fullScreenBtn = protyleElement.querySelector(".fullScreen_simulate");
fullScreenBtn.innerHTML = `<svg><use xlink:href="#iconFullscreen"></use></svg>`;
fullScreenBtn.addEventListener("click", function (e) {
// 获取 .layout-tab-container > .protyle .protyle-breadcrumb__space 元素
const breadcrumbSpace = protyleElement.querySelector('.protyle-breadcrumb__space');
// 如果元素存在,则模拟点击,聚焦当前页签
if (breadcrumbSpace) {
breadcrumbSpace.click();
}
toggleFullScreen(protyleElement, fullScreenBtn); // 切换全屏状态
});
}
}
// 切换全屏状态的函数
function toggleFullScreen(protyle, fullScreenBtn) {
if (!window.siyuan.editorIsFullscreen) {
enterFullScreen(protyle, fullScreenBtn);
} else {
exitFullScreen(protyle, fullScreenBtn);
}
}
function enterFullScreen(protyle, fullScreenBtn) {
protyle.classList.add("fullscreen");
window.siyuan.editorIsFullscreen = true;
updateFullScreenButton(fullScreenBtn, true); // 更新按钮
}
function exitFullScreen(protyle, fullScreenBtn) {
protyle.classList.remove("fullscreen");
window.siyuan.editorIsFullscreen = false;
updateFullScreenButton(fullScreenBtn, false); // 更新按钮
}
function updateFullScreenButton(fullScreenBtn, isFullScreen) {
const iconUse = fullScreenBtn.querySelector('use');
// 切换图标
iconUse.setAttribute('xlink:href', isFullScreen ? '#iconFullscreenExit' : '#iconFullscreen');
fullScreenBtn.setAttribute('aria-label', isFullScreen ? '退出全屏' : '全屏');
}
// 定期检查 .layout__center 是否存在于 DOM 中
function checkForLayoutCenter() {
const targetNode = document.querySelector('.layout__center');
if (targetNode) {
startObserving(targetNode);
// 立即检查一次 protyle-breadcrumb
checkProtyleElements(targetNode);
} else {
setTimeout(checkForLayoutCenter, 200);
}
}
function checkProtyleElements(targetNode) {
const protyles = targetNode.querySelectorAll('.layout-tab-container > .protyle');
protyles.forEach(protyle => {
addFullScreenButton(protyle);
});
}
function startObserving(targetNode) {
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
// 检查添加的节点是否是 .protyle 元素
if (node.nodeType === 1 && node.matches('.layout-tab-container > .protyle')) {
addFullScreenButton(node);
}
});
} else if (mutation.type === 'attributes' && mutation.target.matches('.layout-tab-container > .protyle')) {
// 如果已有的 protyle 的类名发生变化,尝试添加全屏按钮
addFullScreenButton(mutation.target);
}
});
});
// 配置并开始观察
const config = { childList: true, subtree: true, attributes: true };
observer.observe(targetNode, config);
}
checkForLayoutCenter();
})();
旧方案(全屏切换需要设置快捷键)
// 在面包屑旁添加全屏按钮,快捷切换全屏 JS片段
// author by JeffreyChen https://ld246.com/article/1731698559408
// 注意:需要为全屏切换设置一个快捷键
(function() {
function addFullScreenButton(protyleElement) {
// 检查该 protyle 是否已经有了 fullScreen_simulate 按钮
if (protyleElement.querySelector('.fullScreen_simulate')) {
return; // 如果已存在,直接返回
}
let mode = protyleElement.querySelector('.protyle-breadcrumb .block__icon[data-type="readonly"]');
if (mode) {
mode.insertAdjacentHTML(
"beforebegin",
'<button class="fullScreen_simulate block__icon fn__flex-center ariaLabel" aria-label="全屏切换"></button>'
);
let fullScreenBtn = protyleElement.querySelector(".fullScreen_simulate");
fullScreenBtn.innerHTML = `<svg><use xlink:href="#iconFullscreen"></use></svg>`;
fullScreenBtn.addEventListener("click", function (e) {
// 获取 .layout-tab-container > .protyle .protyle-breadcrumb__space 元素
const breadcrumbSpace = protyleElement.querySelector('.protyle-breadcrumb__space');
// 如果元素存在,则模拟点击,聚焦当前页签
if (breadcrumbSpace) {
breadcrumbSpace.click();
}
dispatchKeyEvent();
// 切换图标
const iconUse = fullScreenBtn.querySelector('use');
if (iconUse.getAttribute('xlink:href') === '#iconFullscreen') {
iconUse.setAttribute('xlink:href', '#iconFullscreenExit');
} else {
iconUse.setAttribute('xlink:href', '#iconFullscreen');
}
});
}
}
function dispatchKeyEvent() {
let keyInit = parseHotKeyStr(window.top.siyuan.config.keymap.editor.general.fullscreen.custom);
keyInit["bubbles"] = true;
let keydownEvent = new KeyboardEvent('keydown', keyInit);
document.getElementsByTagName("body")[0].dispatchEvent(keydownEvent);
let keyUpEvent = new KeyboardEvent('keyup', keyInit);
document.getElementsByTagName("body")[0].dispatchEvent(keyUpEvent);
}
/**
* @param {*} hotkeyStr 思源hotkey格式 Refer: https://github.com/siyuan-note/siyuan/blob/d0f011b1a5b12e5546421f8bd442606bf0b5ad86/app/src/protyle/util/hotKey.ts#L4
* @returns KeyboardEventInit Refer: https://developer.mozilla.org/zh-CN/docs/Web/API/KeyboardEvent/KeyboardEvent
*/
function parseHotKeyStr(hotkeyStr) {
let result = {
ctrlKey: false,
altKey: false,
metaKey: false,
shiftKey: false,
key: 'A',
keyCode: 0
}
if (hotkeyStr == "" || hotkeyStr == undefined || hotkeyStr == null) {
console.error("解析快捷键设置失败", hotkeyStr);
throw new Error("解析快捷键设置失败");
}
let onlyKey = hotkeyStr;
if (hotkeyStr.indexOf("⌘") != -1) {
result.ctrlKey = true;
onlyKey = onlyKey.replace("⌘", "");
}
if (hotkeyStr.indexOf("⌥") != -1) {
result.altKey = true;
onlyKey = onlyKey.replace("⌥", "");
}
if (hotkeyStr.indexOf("⇧") != -1) {
result.shiftKey = true;
onlyKey = onlyKey.replace("⇧", "");
}
// 未处理 windows btn (MetaKey)
result.key = onlyKey;
// 在https://github.com/siyuan-note/siyuan/commit/70acd57c4b4701b973a8ca93fadf6c003b24c789#diff-558f9f531a326d2fd53151e3fc250ac4bd545452ba782b0c7c18765a37a4e2cc
// 更改中,思源改为使用keyCode判断快捷键按下事件,这里进行了对应的转换
// 另请参考该提交中涉及的文件
result.keyCode = keyCodeList[result.key];
console.assert(result.keyCode != undefined, `keyCode转换错误,key为${result.key}`);
switch (result.key) {
case "→": {
result.key = "ArrowRight";
break;
}
case "←": {
result.key = "ArrowLeft";
break;
}
case "↑": {
result.key = "ArrowUp";
break;
}
case "↓": {
result.key = "ArrowDown";
break;
}
case "⌦": {
result.key = "Delete";
break;
}
case "⌫": {
result.key = "Backspace";
break;
}
case "↩": {
result.key = "Enter";
break;
}
}
return result;
}
const keyCodeList = {
"⌫": 8,
"⇥": 9,
"↩": 13,
"⇧": 16,
"⌘": 91,
"⌥": 18,
"Pause": 19,
"CapsLock": 20,
"Escape": 27,
" ": 32,
"PageUp": 33,
"PageDown": 34,
"End": 35,
"Home": 36,
"←": 37,
"↑": 38,
"→": 39,
"↓": 40,
"PrintScreen": 44,
"Insert": 45,
"⌦": 46,
"0": 48,
"1": 49,
"2": 50,
"3": 51,
"4": 52,
"5": 53,
"6": 54,
"7": 55,
"8": 56,
"9": 57,
"A": 65,
"B": 66,
"C": 67,
"D": 68,
"E": 69,
"F": 70,
"G": 71,
"H": 72,
"I": 73,
"J": 74,
"K": 75,
"L": 76,
"M": 77,
"N": 78,
"O": 79,
"P": 80,
"Q": 81,
"R": 82,
"S": 83,
"T": 84,
"U": 85,
"V": 86,
"W": 87,
"X": 88,
"Y": 89,
"Z": 90,
"ContextMenu": 93,
"MyComputer": 182,
"MyCalculator": 183,
";": 186,
"=": 187,
",": 188,
"-": 189,
".": 190,
"/": 191,
"`": 192,
"[": 219,
"\\": 220,
"]": 221,
"'": 222,
"*": 106,
"+": 107,
"-": 109,
".": 110,
"/": 111,
"F1": 112,
"F2": 113,
"F3": 114,
"F4": 115,
"F5": 116,
"F6": 117,
"F7": 118,
"F8": 119,
"F9": 120,
"F10": 121,
"F11": 122,
"F12": 123,
"NumLock": 144,
"ScrollLock": 145
};
// 定期检查 .layout__center 是否存在于 DOM 中
function checkForLayoutCenter() {
const targetNode = document.querySelector('.layout__center');
if (targetNode) {
startObserving(targetNode);
// 立即检查一次 protyle-breadcrumb
checkProtyleElements(targetNode);
} else {
setTimeout(checkForLayoutCenter, 200);
}
}
function checkProtyleElements(targetNode) {
const protyles = targetNode.querySelectorAll('.layout-tab-container > .protyle');
protyles.forEach(protyle => {
addFullScreenButton(protyle);
});
}
function startObserving(targetNode) {
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
// 检查添加的节点是否是 .protyle 元素
if (node.nodeType === 1 && node.matches('.layout-tab-container > .protyle')) {
addFullScreenButton(node);
}
});
} else if (mutation.type === 'attributes' && mutation.target.matches('.layout-tab-container > .protyle')) {
// 如果已有的 protyle 的类名发生变化,尝试添加全屏按钮
addFullScreenButton(mutation.target);
}
});
});
// 配置并开始观察
const config = { childList: true, subtree: true, attributes: true };
observer.observe(targetNode, config);
}
checkForLayoutCenter();
})();
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于