import {openSearch} from "../search/spread"; import {exportLayout, getInstanceById, JSONToLayout, resetLayout, resizeDrag, resizeTabs} from "../layout/util"; import {hotKey2Electron, setStorageVal, updateHotkeyTip} from "../protyle/util/compatibility"; /// #if !BROWSER import {dialog, getCurrentWindow} from "@electron/remote"; import {ipcRenderer, OpenDialogReturnValue, webFrame} from "electron"; import * as fs from "fs"; import * as path from "path"; import {afterExport} from "../protyle/export/util"; /// #endif import {Constants} from "../constants"; import {appearance} from "../config/appearance"; import {globalShortcut} from "./globalShortcut"; import {fetchPost} from "./fetch"; import {mountHelp} from "./mount"; import {MenuItem} from "../menus/Menu"; import {addGA, initAssets, setInlineStyle, setMode} from "./assets"; import {renderSnippet} from "../config/util/snippets"; import {openFileById} from "../editor/util"; import {focusByRange} from "../protyle/util/selection"; import {exitSiYuan, processSync} from "../dialog/processSystem"; import {openSetting} from "../config"; import {getSearch, isWindow} from "./functions"; import {initStatus} from "../layout/status"; import {syncGuide} from "../sync/syncGuide"; import {showMessage} from "../dialog/message"; import {editor} from "../config/editor"; import {goBack, goForward} from "./backForward"; import {replaceLocalPath} from "../editor/rename"; import {workspaceMenu} from "../menus/workspace"; import {getWorkspaceName} from "./noRelyPCFunction"; import {setTabPosition} from "../window/setHeader"; import {Tab} from "../layout/Tab"; 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 setProxy = () => { /// #if !BROWSER if ("" === window.siyuan.config.system.networkProxy.scheme) { console.log("network proxy [system]"); return; } const session = getCurrentWindow().webContents.session; session.closeAllConnections().then(() => { const proxyURL = `${window.siyuan.config.system.networkProxy.scheme}://${window.siyuan.config.system.networkProxy.host}:${window.siyuan.config.system.networkProxy.port}`; session.setProxy({proxyRules: proxyURL}).then( () => console.log("network proxy [" + proxyURL + "]"), ); }); /// #endif }; export const onGetConfig = (isStart: boolean) => { 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 }, () => { /// #if !BROWSER ipcRenderer.send(Constants.SIYUAN_HOTKEY, { languages: window.siyuan.languages["_trayMenu"], id: getCurrentWindow().id, hotkey: hotKey2Electron(window.siyuan.config.keymap.general.toggleWin.custom) }); /// #endif }); } /// #if !BROWSER ipcRenderer.send(Constants.SIYUAN_INIT, { languages: window.siyuan.languages["_trayMenu"], workspaceDir: window.siyuan.config.system.workspaceDir, id: getCurrentWindow().id, port: location.port }); ipcRenderer.send(Constants.SIYUAN_HOTKEY, { languages: window.siyuan.languages["_trayMenu"], id: getCurrentWindow().id, hotkey: hotKey2Electron(window.siyuan.config.keymap.general.toggleWin.custom) }); 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; } globalShortcut(); fetchPost("/api/system/getEmojiConf", {}, response => { window.siyuan.emojis = response.data as IEmoji[]; try { JSONToLayout(isStart); } catch (e) { resetLayout(); } }); initBar(); initStatus(); initWindow(); appearance.onSetappearance(window.siyuan.config.appearance); initAssets(); renderSnippet(); setInlineStyle(); let resizeTimeout = 0; window.addEventListener("resize", () => { window.clearTimeout(resizeTimeout); resizeTimeout = window.setTimeout(() => { resizeTabs(); resizeDrag(); }, 200); }); if (window.siyuan.config.newbie) { mountHelp(); } addGA(); }; export const initBar = () => { const toolbarElement = document.getElementById("toolbar"); toolbarElement.innerHTML = `
${getWorkspaceName()}
开发版,使用前请进行备份 Development version, please backup before use
`; processSync(); toolbarElement.addEventListener("click", (event: MouseEvent) => { let target = event.target as HTMLElement; while (!target.classList.contains("toolbar")) { if (target.id === "barBack") { goBack(); event.stopPropagation(); break; } else if (target.id === "barForward") { goForward(); event.stopPropagation(); break; } else if (target.id === "barSync") { syncGuide(target); event.stopPropagation(); break; } else if (target.id === "barWorkspace") { workspaceMenu(target.getBoundingClientRect()); event.stopPropagation(); break; } else if (target.id === "barReadonly") { editor.setMode(); event.stopPropagation(); break; } else if (target.id === "barMode") { if (!window.siyuan.menus.menu.element.classList.contains("fn__none") && window.siyuan.menus.menu.element.getAttribute("data-name") === "barmode") { window.siyuan.menus.menu.remove(); return; } window.siyuan.menus.menu.remove(); window.siyuan.menus.menu.element.setAttribute("data-name", "barmode"); window.siyuan.menus.menu.append(new MenuItem({ label: window.siyuan.languages.themeLight, icon: "iconLight", current: window.siyuan.config.appearance.mode === 0 && !window.siyuan.config.appearance.modeOS, click: () => { setMode(0); } }).element); window.siyuan.menus.menu.append(new MenuItem({ label: window.siyuan.languages.themeDark, current: window.siyuan.config.appearance.mode === 1 && !window.siyuan.config.appearance.modeOS, icon: "iconDark", click: () => { setMode(1); } }).element); window.siyuan.menus.menu.append(new MenuItem({ label: window.siyuan.languages.themeOS, current: window.siyuan.config.appearance.modeOS, icon: "iconMode", click: () => { setMode(2); } }).element); const rect = target.getBoundingClientRect(); window.siyuan.menus.menu.popup({x: rect.right, y: rect.bottom}, true); event.stopPropagation(); break; } else if (target.id === "toolbarVIP") { const dialogSetting = openSetting(); dialogSetting.element.querySelector('.b3-tab-bar [data-name="account"]').dispatchEvent(new CustomEvent("click")); event.stopPropagation(); break; } else if (target.id === "barSearch") { openSearch(window.siyuan.config.keymap.general.globalSearch.custom); event.stopPropagation(); break; } target = target.parentElement; } }); setProxy(); }; const winOnFocus = () => { 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(false); window.siyuan.altIsPressed = false; window.siyuan.ctrlIsPressed = false; window.siyuan.shiftIsPressed = false; document.body.classList.remove("body--blur"); }; const winOnClose = (currentWindow: Electron.BrowserWindow, close = false) => { /// #if !BROWSER exportLayout(false, () => { if (window.siyuan.config.appearance.closeButtonBehavior === 1 && !close) { // 最小化 if ("windows" === window.siyuan.config.system.os) { ipcRenderer.send(Constants.SIYUAN_CONFIG_TRAY, { id: getCurrentWindow().id, languages: window.siyuan.languages["_trayMenu"], }); } else { if (currentWindow.isFullScreen()) { currentWindow.once("leave-full-screen", () => currentWindow.hide()); currentWindow.setFullScreen(false); } else { currentWindow.hide(); } } } else { exitSiYuan(); } }); /// #endif }; export const initWindow = () => { /// #if !BROWSER const currentWindow = getCurrentWindow(); currentWindow.on("focus", winOnFocus); currentWindow.on("blur", () => { document.body.classList.add("body--blur"); }); if (!isWindow()) { ipcRenderer.on(Constants.SIYUAN_OPENURL, (event, url) => { if (!/^siyuan:\/\/blocks\/\d{14}-\w{7}/.test(url)) { return; } const id = url.substr(16, 22); fetchPost("/api/block/checkBlockExist", {id}, existResponse => { if (existResponse.data) { openFileById({ id, action: [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT], zoomIn: getSearch("focus", url) === "1" }); ipcRenderer.send(Constants.SIYUAN_SHOW, getCurrentWindow().id); } }); }); ipcRenderer.on(Constants.SIYUAN_SAVE_CLOSE, (event, close) => { winOnClose(currentWindow, close); }); } ipcRenderer.on(Constants.SIYUAN_CLOSETAB, (e, ipcData) => { const tab = getInstanceById(ipcData); if (tab && tab instanceof Tab) { tab.parent.removeTab(ipcData); } }); ipcRenderer.on(Constants.SIYUAN_LOCK_SCREEN, () => { exportLayout(false, () => { fetchPost("/api/system/logoutAuth", {}, () => { window.location.reload(); }); }); }); ipcRenderer.on(Constants.SIYUAN_EXPORT_CLOSE, () => { window.siyuan.printWin.destroy(); }); ipcRenderer.on(Constants.SIYUAN_EXPORT_PDF, (e, ipcData) => { dialog.showOpenDialog({ title: window.siyuan.languages.export + " PDF", properties: ["createDirectory", "openDirectory"], }).then((result: OpenDialogReturnValue) => { if (result.canceled) { window.siyuan.printWin.destroy(); return; } 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, }; setStorageVal(Constants.LOCAL_EXPORTPDF, window.siyuan.storage[Constants.LOCAL_EXPORTPDF]); try { if (window.siyuan.config.export.addFooter) { ipcData.pdfOptions.displayHeaderFooter = true; ipcData.pdfOptions.headerTemplate = ""; ipcData.pdfOptions.footerTemplate = `
${window.siyuan.languages.exportBySiYuan}
`; } window.siyuan.printWin.webContents.printToPDF(ipcData.pdfOptions).then((pdfData) => { fetchPost("/api/export/exportHTML", { id: ipcData.rootId, pdf: true, removeAssets: ipcData.removeAssets, merge: ipcData.mergeSubdocs, savePath: result.filePaths[0] }, () => { const pdfFilePath = path.join(result.filePaths[0], replaceLocalPath(ipcData.rootTitle) + ".pdf"); fs.writeFileSync(pdfFilePath, pdfData); window.siyuan.printWin.destroy(); 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(result.filePaths[0], "assets")); } }); }); }).catch((error: string) => { showMessage("Export PDF error:" + error, 0, "error", msgId); window.siyuan.printWin.destroy(); }); } catch (e) { showMessage("Export PDF failed: " + e, 0, "error", msgId); window.siyuan.printWin.destroy(); } window.siyuan.printWin.hide(); }); }); window.addEventListener("beforeunload", () => { currentWindow.off("focus", winOnFocus); }, false); if (isWindow()) { document.body.insertAdjacentHTML("beforeend", `
`); const pinElement = document.getElementById("pinWindow"); pinElement.addEventListener("click", () => { pinElement.classList.toggle("toolbar__item--active"); if (pinElement.classList.contains("toolbar__item--active")) { pinElement.setAttribute("aria-label", window.siyuan.languages.unpin); currentWindow.setAlwaysOnTop(true, "pop-up-menu"); } else { pinElement.setAttribute("aria-label", window.siyuan.languages.pin); currentWindow.setAlwaysOnTop(false); } }); } if ("darwin" === window.siyuan.config.system.os) { document.getElementById("drag")?.addEventListener("dblclick", () => { if (currentWindow.isMaximized()) { currentWindow.unmaximize(); } else { currentWindow.maximize(); } }); const toolbarElement = document.getElementById("toolbar"); currentWindow.on("enter-full-screen", () => { if (isWindow()) { setTabPosition(); } else { toolbarElement.style.paddingLeft = "0"; } }); currentWindow.on("leave-full-screen", () => { if (isWindow()) { setTabPosition(); } else { toolbarElement.setAttribute("style", ""); } }); if (currentWindow.isFullScreen() && !isWindow()) { toolbarElement.style.paddingLeft = "0"; } return; } 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", () => { if (currentWindow.isFullScreen()) { currentWindow.setFullScreen(false); } else { currentWindow.unmaximize(); } }); maxBtnElement.addEventListener("click", () => { currentWindow.maximize(); }); const toggleMaxRestoreButtons = () => { if (currentWindow.isMaximized() || currentWindow.isFullScreen()) { restoreBtnElement.style.display = "flex"; maxBtnElement.style.display = "none"; } else { restoreBtnElement.style.display = "none"; maxBtnElement.style.display = "flex"; } }; toggleMaxRestoreButtons(); currentWindow.on("maximize", toggleMaxRestoreButtons); currentWindow.on("unmaximize", toggleMaxRestoreButtons); currentWindow.on("enter-full-screen", () => { restoreBtnElement.style.display = "flex"; maxBtnElement.style.display = "none"; }); currentWindow.on("leave-full-screen", toggleMaxRestoreButtons); const minBtnElement = document.getElementById("minWindow"); const closeBtnElement = document.getElementById("closeWindow"); minBtnElement.addEventListener("click", () => { if (minBtnElement.classList.contains("window-controls__item--disabled")) { return; } currentWindow.minimize(); }); closeBtnElement.addEventListener("click", () => { if (isWindow()) { currentWindow.destroy(); } else { winOnClose(currentWindow); } }); /// #else if (!isWindow()) { document.querySelector(".toolbar").classList.add("toolbar--browser"); window.addEventListener("beforeunload", () => { exportLayout(false); }, false); window.addEventListener("pagehide", () => { exportLayout(false); }, false); } /// #endif };