import {exportLayout, JSONToLayout, resetLayout, resizeTopBar} from "../layout/util"; import {resizeTabs} from "../layout/tabUtil"; import {setStorageVal} from "../protyle/util/compatibility"; /// #if !BROWSER import {ipcRenderer, webFrame} from "electron"; import * as fs from "fs"; import * as path from "path"; import {afterExport} from "../protyle/export/util"; import {onWindowsMsg} from "../window/onWindowsMsg"; /// #endif import {Constants} from "../constants"; import {appearance} from "../config/appearance"; import {fetchPost, fetchSyncPost} from "../util/fetch"; import {addGA, initAssets, setInlineStyle} from "../util/assets"; import {renderSnippet} from "../config/util/snippets"; import {checkFold, openFile, openFileById} from "../editor/util"; import {focusByRange} from "../protyle/util/selection"; import {exitSiYuan} from "../dialog/processSystem"; import {isWindow} from "../util/functions"; import {initStatus} from "../layout/status"; import {showMessage} from "../dialog/message"; import {replaceLocalPath} from "../editor/rename"; import {setTabPosition} from "../window/setHeader"; import {initBar} from "../layout/topBar"; import {openChangelog} from "./openChangelog"; import {getIdFromSYProtocol, isSYProtocol} from "../util/pathName"; import {App} from "../index"; import {initWindowEvent} from "./globalEvent/event"; import {sendGlobalShortcut} from "./globalEvent/keydown"; import {closeWindow} from "../window/closeWin"; const matchKeymap = (keymap: Record, key1: "general" | "editor", key2?: "general" | "insert" | "heading" | "list" | "table") => { if (key1 === "general") { if (!window.siyuan.config.keymap[key1]) { window.siyuan.config.keymap[key1] = keymap; return false; } } else { if (!window.siyuan.config.keymap[key1]) { window.siyuan.config.keymap[key1] = JSON.parse(JSON.stringify(Constants.SIYUAN_KEYMAP.editor)); return false; } if (!window.siyuan.config.keymap[key1][key2]) { window.siyuan.config.keymap[key1][key2] = keymap; return false; } } let match = true; Object.keys(keymap).forEach(key => { if (key1 === "general") { if (!window.siyuan.config.keymap[key1][key] || window.siyuan.config.keymap[key1][key].default !== keymap[key].default) { match = false; window.siyuan.config.keymap[key1][key] = keymap[key]; } } else { if (!window.siyuan.config.keymap[key1][key2][key] || window.siyuan.config.keymap[key1][key2][key].default !== keymap[key].default) { match = false; window.siyuan.config.keymap[key1][key2][key] = keymap[key]; } } }); return match; }; const hasKeymap = (keymap: Record, key1: "general" | "editor", key2?: "general" | "insert" | "heading" | "list" | "table") => { let match = true; if (key1 === "editor") { if (Object.keys(window.siyuan.config.keymap[key1][key2]).length !== Object.keys(Constants.SIYUAN_KEYMAP[key1][key2]).length) { Object.keys(window.siyuan.config.keymap[key1][key2]).forEach(item => { if (!Constants.SIYUAN_KEYMAP[key1][key2][item]) { match = false; delete window.siyuan.config.keymap[key1][key2][item]; } }); } } else { if (Object.keys(window.siyuan.config.keymap[key1]).length !== Object.keys(Constants.SIYUAN_KEYMAP[key1]).length) { Object.keys(window.siyuan.config.keymap[key1]).forEach(item => { if (!Constants.SIYUAN_KEYMAP[key1][item]) { match = false; delete window.siyuan.config.keymap[key1][item]; } }); } } return match; }; export const onGetConfig = (isStart: boolean, app: App) => { const matchKeymap1 = matchKeymap(Constants.SIYUAN_KEYMAP.general, "general"); const matchKeymap2 = matchKeymap(Constants.SIYUAN_KEYMAP.editor.general, "editor", "general"); const matchKeymap3 = matchKeymap(Constants.SIYUAN_KEYMAP.editor.insert, "editor", "insert"); const matchKeymap4 = matchKeymap(Constants.SIYUAN_KEYMAP.editor.heading, "editor", "heading"); const matchKeymap5 = matchKeymap(Constants.SIYUAN_KEYMAP.editor.list, "editor", "list"); const matchKeymap6 = matchKeymap(Constants.SIYUAN_KEYMAP.editor.table, "editor", "table"); const hasKeymap1 = hasKeymap(Constants.SIYUAN_KEYMAP.general, "general"); const hasKeymap2 = hasKeymap(Constants.SIYUAN_KEYMAP.editor.general, "editor", "general"); const hasKeymap3 = hasKeymap(Constants.SIYUAN_KEYMAP.editor.insert, "editor", "insert"); const hasKeymap4 = hasKeymap(Constants.SIYUAN_KEYMAP.editor.heading, "editor", "heading"); const hasKeymap5 = hasKeymap(Constants.SIYUAN_KEYMAP.editor.list, "editor", "list"); const hasKeymap6 = hasKeymap(Constants.SIYUAN_KEYMAP.editor.table, "editor", "table"); if (!window.siyuan.config.readonly && (!matchKeymap1 || !matchKeymap2 || !matchKeymap3 || !matchKeymap4 || !matchKeymap5 || !matchKeymap6 || !hasKeymap1 || !hasKeymap2 || !hasKeymap3 || !hasKeymap4 || !hasKeymap5 || !hasKeymap6)) { fetchPost("/api/setting/setKeymap", { data: window.siyuan.config.keymap }, () => { sendGlobalShortcut(app); }); } /// #if !BROWSER ipcRenderer.invoke(Constants.SIYUAN_INIT, { languages: window.siyuan.languages["_trayMenu"], workspaceDir: window.siyuan.config.system.workspaceDir, port: location.port }); webFrame.setZoomFactor(window.siyuan.storage[Constants.LOCAL_ZOOM]); /// #endif if (!window.siyuan.config.uiLayout || (window.siyuan.config.uiLayout && !window.siyuan.config.uiLayout.left)) { window.siyuan.config.uiLayout = Constants.SIYUAN_EMPTY_LAYOUT; } initWindowEvent(app); fetchPost("/api/system/getEmojiConf", {}, response => { window.siyuan.emojis = response.data as IEmoji[]; try { JSONToLayout(app, isStart); /// #if !BROWSER sendGlobalShortcut(app); /// #endif openChangelog(); } catch (e) { resetLayout(); } }); initBar(app); initStatus(); initWindow(app); appearance.onSetappearance(window.siyuan.config.appearance); initAssets(); setInlineStyle(); renderSnippet(); let resizeTimeout = 0; window.addEventListener("resize", () => { window.clearTimeout(resizeTimeout); resizeTimeout = window.setTimeout(() => { resizeTabs(); resizeTopBar(); }, 200); }); addGA(); }; const winOnMaxRestore = async () => { /// #if !BROWSER const maxBtnElement = document.getElementById("maxWindow"); const restoreBtnElement = document.getElementById("restoreWindow"); const isFullScreen = await ipcRenderer.invoke(Constants.SIYUAN_GET, { cmd: "isFullScreen", }); const isMaximized = await ipcRenderer.invoke(Constants.SIYUAN_GET, { cmd: "isMaximized", }); if (isMaximized || isFullScreen) { restoreBtnElement.style.display = "flex"; maxBtnElement.style.display = "none"; } else { restoreBtnElement.style.display = "none"; maxBtnElement.style.display = "flex"; } /// #endif }; const saveUI = () => { exportLayout({ reload: false, onlyData: false, errorExit: false }); }; export const unbindSaveUI = () => { window.removeEventListener("beforeunload", saveUI); window.removeEventListener("pagehide", saveUI); }; export const initWindow = async (app: App) => { /// #if !BROWSER const winOnClose = (close = false) => { exportLayout({ reload: false, cb() { if (window.siyuan.config.appearance.closeButtonBehavior === 1 && !close) { // 最小化 if ("windows" === window.siyuan.config.system.os) { ipcRenderer.send(Constants.SIYUAN_CONFIG_TRAY, { languages: window.siyuan.languages["_trayMenu"], }); } else { ipcRenderer.send(Constants.SIYUAN_CMD, "closeButtonBehavior"); } } else { exitSiYuan(); } }, onlyData: false, errorExit: true }); }; ipcRenderer.send(Constants.SIYUAN_EVENT, "onEvent"); ipcRenderer.on(Constants.SIYUAN_EVENT, (event, cmd) => { if (cmd === "focus") { if (getSelection().rangeCount > 0) { const range = getSelection().getRangeAt(0); const startNode = range.startContainer.childNodes[range.startOffset] as HTMLElement; if (startNode && startNode.nodeType !== 3 && (startNode.tagName === "TEXTAREA" || startNode.tagName === "INPUT")) { startNode.focus(); } else { focusByRange(getSelection().getRangeAt(0)); } } exportLayout({ reload: false, onlyData: false, errorExit: false }); window.siyuan.altIsPressed = false; window.siyuan.ctrlIsPressed = false; window.siyuan.shiftIsPressed = false; document.body.classList.remove("body--blur"); } else if (cmd === "blur") { document.body.classList.add("body--blur"); } else if (cmd === "enter-full-screen") { if ("darwin" === window.siyuan.config.system.os) { if (isWindow()) { setTabPosition(); } else { document.getElementById("toolbar").style.paddingLeft = "0"; } } else { winOnMaxRestore(); } } else if (cmd === "leave-full-screen") { if ("darwin" === window.siyuan.config.system.os) { if (isWindow()) { setTabPosition(); } else { document.getElementById("toolbar").setAttribute("style", ""); } } else { winOnMaxRestore(); } } else if (cmd === "maximize") { winOnMaxRestore(); } else if (cmd === "unmaximize") { winOnMaxRestore(); } }); if (!isWindow()) { ipcRenderer.on(Constants.SIYUAN_OPEN_URL, (event, url) => { let urlObj: URL; try { urlObj = new URL(url); if (urlObj.protocol !== "siyuan:") { return; } } catch (error) { return; } if (urlObj && urlObj.pathname.startsWith("//plugins/")) { const pluginNameType = urlObj.pathname.replace("//plugins/", ""); if (!pluginNameType) { return; } app.plugins.find(plugin => { if (pluginNameType.startsWith(plugin.name)) { // siyuan://plugins/plugin-name/foo?bar=baz plugin.eventBus.emit("open-siyuan-url-plugin", {url}); // https://github.com/siyuan-note/siyuan/pull/9256 if (pluginNameType.split("/")[0] !== plugin.name) { // siyuan://plugins/plugin-samplecustom_tab?title=自定义页签&icon=iconFace&data={"text": "This is the custom plugin tab I opened via protocol."} let data = urlObj.searchParams.get("data"); try { data = JSON.parse(data || "{}"); } catch (e) { console.log("Error open plugin tab with protocol:", e); } openFile({ app, custom: { title: urlObj.searchParams.get("title"), icon: urlObj.searchParams.get("icon"), data, id: pluginNameType }, }); } return true; } }); return; } if (urlObj && isSYProtocol(url)) { const id = getIdFromSYProtocol(url); const focus = urlObj.searchParams.get("focus") === "1"; fetchPost("/api/block/checkBlockExist", {id}, existResponse => { if (existResponse.data) { checkFold(id, (zoomIn) => { openFileById({ app, id, action: (zoomIn || focus) ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL] : [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT, Constants.CB_GET_ROOTSCROLL], zoomIn: zoomIn || focus }); }); ipcRenderer.send(Constants.SIYUAN_CMD, "show"); } app.plugins.forEach(plugin => { plugin.eventBus.emit("open-siyuan-url-block", { url, id, focus, exist: existResponse.data, }); }); }); return; } }); } ipcRenderer.on(Constants.SIYUAN_OPEN_FILE, (event, data) => { openFile(data); }); ipcRenderer.on(Constants.SIYUAN_SAVE_CLOSE, (event, close) => { if (isWindow()) { closeWindow(app); } else { winOnClose(close); } }); ipcRenderer.on(Constants.SIYUAN_SEND_WINDOWS, (e, ipcData: IWebSocketData) => { onWindowsMsg(ipcData); }); ipcRenderer.on(Constants.SIYUAN_HOTKEY, (e, data) => { let matchCommand = false; app.plugins.find(item => { item.commands.find(command => { if (command.globalCallback && data.hotkey === command.customHotkey) { matchCommand = true; command.globalCallback(); return true; } }); if (matchCommand) { return true; } }); }); ipcRenderer.on(Constants.SIYUAN_EXPORT_PDF, async (e, ipcData) => { const msgId = showMessage(window.siyuan.languages.exporting, -1); window.siyuan.storage[Constants.LOCAL_EXPORTPDF] = { removeAssets: ipcData.removeAssets, keepFold: ipcData.keepFold, mergeSubdocs: ipcData.mergeSubdocs, landscape: ipcData.pdfOptions.landscape, marginType: ipcData.pdfOptions.marginType, pageSize: ipcData.pdfOptions.pageSize, scale: ipcData.pdfOptions.scale, marginTop: ipcData.pdfOptions.margins.top, marginRight: ipcData.pdfOptions.margins.right, marginBottom: ipcData.pdfOptions.margins.bottom, marginLeft: ipcData.pdfOptions.margins.left, }; setStorageVal(Constants.LOCAL_EXPORTPDF, window.siyuan.storage[Constants.LOCAL_EXPORTPDF]); try { if (window.siyuan.config.export.pdfFooter.trim()) { const response = await fetchSyncPost("/api/template/renderSprig", {template: window.siyuan.config.export.pdfFooter}); ipcData.pdfOptions.displayHeaderFooter = true; ipcData.pdfOptions.headerTemplate = ""; ipcData.pdfOptions.footerTemplate = `
${response.data.replace("%pages", "").replace("%page", "")}
`; } const pdfData = await ipcRenderer.invoke(Constants.SIYUAN_GET, { cmd: "printToPDF", pdfOptions: ipcData.pdfOptions, webContentsId: ipcData.webContentsId }); fetchPost("/api/export/exportHTML", { id: ipcData.rootId, pdf: true, removeAssets: ipcData.removeAssets, merge: ipcData.mergeSubdocs, savePath: ipcData.filePaths[0] }, () => { const pdfFilePath = path.join(ipcData.filePaths[0], replaceLocalPath(ipcData.rootTitle) + ".pdf"); fs.writeFileSync(pdfFilePath, pdfData); ipcRenderer.send(Constants.SIYUAN_CMD, {cmd: "destroy", webContentsId: ipcData.webContentsId}); fetchPost("/api/export/processPDF", { id: ipcData.rootId, merge: ipcData.mergeSubdocs, path: pdfFilePath, removeAssets: ipcData.removeAssets, }, () => { afterExport(pdfFilePath, msgId); if (ipcData.removeAssets) { const removePromise = (dir: string) => { return new Promise(function (resolve) { //先读文件夹 fs.stat(dir, function (err, stat) { if (stat) { if (stat.isDirectory()) { fs.readdir(dir, function (err, files) { files = files.map(file => path.join(dir, file)); // a/b a/m Promise.all(files.map(file => removePromise(file))).then(function () { fs.rmdir(dir, resolve); }); }); } else { fs.unlink(dir, resolve); } } }); }); }; removePromise(path.join(ipcData.filePaths[0], "assets")); } }); }); } catch (e) { showMessage("Export PDF failed: " + e, 0, "error", msgId); ipcRenderer.send(Constants.SIYUAN_CMD, {cmd: "destroy", webContentsId: ipcData.webContentsId}); } ipcRenderer.send(Constants.SIYUAN_CMD, {cmd: "hide", webContentsId: ipcData.webContentsId}); }); if (isWindow()) { document.body.insertAdjacentHTML("beforeend", `
`); const pinElement = document.getElementById("pinWindow"); pinElement.addEventListener("click", () => { if (pinElement.getAttribute("aria-label") === window.siyuan.languages.pin) { pinElement.querySelector("use").setAttribute("xlink:href", "#iconUnpin"); pinElement.setAttribute("aria-label", window.siyuan.languages.unpin); ipcRenderer.send(Constants.SIYUAN_CMD, "setAlwaysOnTopTrue"); } else { pinElement.querySelector("use").setAttribute("xlink:href", "#iconPin"); pinElement.setAttribute("aria-label", window.siyuan.languages.pin); ipcRenderer.send(Constants.SIYUAN_CMD, "setAlwaysOnTopFalse"); } }); } if ("darwin" !== window.siyuan.config.system.os) { document.body.classList.add("body--win32"); // 添加窗口控件 const controlsHTML = `
`; if (isWindow()) { document.querySelector(".toolbar__window").insertAdjacentHTML("beforeend", controlsHTML); } else { document.getElementById("windowControls").innerHTML = controlsHTML; } const maxBtnElement = document.getElementById("maxWindow"); const restoreBtnElement = document.getElementById("restoreWindow"); restoreBtnElement.addEventListener("click", () => { ipcRenderer.send(Constants.SIYUAN_CMD, "restore"); }); maxBtnElement.addEventListener("click", () => { ipcRenderer.send(Constants.SIYUAN_CMD, "maximize"); }); winOnMaxRestore(); const minBtnElement = document.getElementById("minWindow"); const closeBtnElement = document.getElementById("closeWindow"); minBtnElement.addEventListener("click", () => { if (minBtnElement.classList.contains("window-controls__item--disabled")) { return; } ipcRenderer.send(Constants.SIYUAN_CMD, "minimize"); }); closeBtnElement.addEventListener("click", () => { if (isWindow()) { closeWindow(app); } else { winOnClose(); } }); } else { const toolbarElement = document.getElementById("toolbar"); const isFullScreen = await ipcRenderer.invoke(Constants.SIYUAN_GET, { cmd: "isFullScreen", }); if (isFullScreen && !isWindow()) { toolbarElement.style.paddingLeft = "0"; } } /// #else if (!isWindow()) { document.querySelector(".toolbar").classList.add("toolbar--browser"); } window.addEventListener("beforeunload", saveUI, false); window.addEventListener("pagehide", saveUI, false); /// #endif };