|
@@ -15,1037 +15,1039 @@
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
const {
|
|
|
- app,
|
|
|
- BrowserWindow,
|
|
|
- shell,
|
|
|
- Menu,
|
|
|
- screen,
|
|
|
- ipcMain,
|
|
|
- globalShortcut,
|
|
|
- Tray,
|
|
|
-} = require('electron')
|
|
|
-const path = require('path')
|
|
|
-const fs = require('fs')
|
|
|
-const net = require('net')
|
|
|
-const fetch = require('electron-fetch').default
|
|
|
-process.noAsar = true
|
|
|
-const appDir = path.dirname(app.getAppPath())
|
|
|
-const isDevEnv = process.env.NODE_ENV === 'development'
|
|
|
-const appVer = app.getVersion()
|
|
|
-const confDir = path.join(app.getPath('home'), '.config', 'siyuan')
|
|
|
-const windowStatePath = path.join(confDir, 'windowState.json')
|
|
|
-let bootWindow
|
|
|
-let firstOpen = false
|
|
|
-let workspaces = [] // workspaceDir, id, browserWindow, tray
|
|
|
-let kernelPort = 6806
|
|
|
-require('@electron/remote/main').initialize()
|
|
|
+ app,
|
|
|
+ BrowserWindow,
|
|
|
+ shell,
|
|
|
+ Menu,
|
|
|
+ screen,
|
|
|
+ ipcMain,
|
|
|
+ globalShortcut,
|
|
|
+ Tray,
|
|
|
+} = require("electron");
|
|
|
+const path = require("path");
|
|
|
+const fs = require("fs");
|
|
|
+const net = require("net");
|
|
|
+const fetch = require("electron-fetch").default;
|
|
|
+process.noAsar = true;
|
|
|
+const appDir = path.dirname(app.getAppPath());
|
|
|
+const isDevEnv = process.env.NODE_ENV === "development";
|
|
|
+const appVer = app.getVersion();
|
|
|
+const confDir = path.join(app.getPath("home"), ".config", "siyuan");
|
|
|
+const windowStatePath = path.join(confDir, "windowState.json");
|
|
|
+let bootWindow;
|
|
|
+let firstOpen = false;
|
|
|
+let workspaces = []; // workspaceDir, id, browserWindow, tray
|
|
|
+let kernelPort = 6806;
|
|
|
+require("@electron/remote/main").initialize();
|
|
|
|
|
|
if (!app.requestSingleInstanceLock()) {
|
|
|
- app.quit()
|
|
|
- return
|
|
|
+ app.quit();
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
- firstOpen = !fs.existsSync(path.join(confDir, 'workspace.json'))
|
|
|
- if (!fs.existsSync(confDir)) {
|
|
|
- fs.mkdirSync(confDir, {mode: 0o755, recursive: true})
|
|
|
- }
|
|
|
+ firstOpen = !fs.existsSync(path.join(confDir, "workspace.json"));
|
|
|
+ if (!fs.existsSync(confDir)) {
|
|
|
+ fs.mkdirSync(confDir, {mode: 0o755, recursive: true});
|
|
|
+ }
|
|
|
} catch (e) {
|
|
|
- console.error(e)
|
|
|
- require('electron').dialog.showErrorBox('创建配置目录失败 Failed to create config directory',
|
|
|
- '思源需要在用户家目录下创建配置文件夹(~/.config/siyuan),请确保该路径具有写入权限。\n\nSiYuan needs to create a configuration folder (~/.config/siyuan) in the user\'s home directory. Please make sure that the path has write permissions.')
|
|
|
- app.exit()
|
|
|
+ console.error(e);
|
|
|
+ require("electron").dialog.showErrorBox("创建配置目录失败 Failed to create config directory",
|
|
|
+ "思源需要在用户家目录下创建配置文件夹(~/.config/siyuan),请确保该路径具有写入权限。\n\nSiYuan needs to create a configuration folder (~/.config/siyuan) in the user's home directory. Please make sure that the path has write permissions.");
|
|
|
+ app.exit();
|
|
|
}
|
|
|
|
|
|
const getServer = (port = kernelPort) => {
|
|
|
- return 'http://127.0.0.1:' + port
|
|
|
-}
|
|
|
+ return "http://127.0.0.1:" + port;
|
|
|
+};
|
|
|
|
|
|
const sleep = (ms) => {
|
|
|
- return new Promise(resolve => setTimeout(resolve, ms))
|
|
|
-}
|
|
|
+ return new Promise(resolve => setTimeout(resolve, ms));
|
|
|
+};
|
|
|
|
|
|
const showErrorWindow = (title, content) => {
|
|
|
- let errorHTMLPath = path.join(appDir, 'app', 'electron', 'error.html')
|
|
|
- if (isDevEnv) {
|
|
|
- errorHTMLPath = path.join(appDir, 'electron', 'error.html')
|
|
|
- }
|
|
|
- const errWindow = new BrowserWindow({
|
|
|
- width: screen.getPrimaryDisplay().size.width / 2,
|
|
|
- height: screen.getPrimaryDisplay().workAreaSize.height / 2,
|
|
|
- frame: false,
|
|
|
- icon: path.join(appDir, 'stage', 'icon-large.png'),
|
|
|
- webPreferences: {
|
|
|
- nodeIntegration: true,
|
|
|
- webviewTag: true,
|
|
|
- webSecurity: false,
|
|
|
- contextIsolation: false,
|
|
|
- },
|
|
|
- })
|
|
|
- require('@electron/remote/main').enable(errWindow.webContents)
|
|
|
- errWindow.loadFile(errorHTMLPath, {
|
|
|
- query: {
|
|
|
- home: app.getPath('home'),
|
|
|
- v: appVer,
|
|
|
- title: title,
|
|
|
- content: content,
|
|
|
- icon: path.join(appDir, 'stage', 'icon-large.png'),
|
|
|
- },
|
|
|
- })
|
|
|
- errWindow.show()
|
|
|
-}
|
|
|
+ let errorHTMLPath = path.join(appDir, "app", "electron", "error.html");
|
|
|
+ if (isDevEnv) {
|
|
|
+ errorHTMLPath = path.join(appDir, "electron", "error.html");
|
|
|
+ }
|
|
|
+ const errWindow = new BrowserWindow({
|
|
|
+ width: screen.getPrimaryDisplay().size.width / 2,
|
|
|
+ height: screen.getPrimaryDisplay().workAreaSize.height / 2,
|
|
|
+ frame: false,
|
|
|
+ icon: path.join(appDir, "stage", "icon-large.png"),
|
|
|
+ webPreferences: {
|
|
|
+ nodeIntegration: true,
|
|
|
+ webviewTag: true,
|
|
|
+ webSecurity: false,
|
|
|
+ contextIsolation: false,
|
|
|
+ },
|
|
|
+ });
|
|
|
+ require("@electron/remote/main").enable(errWindow.webContents);
|
|
|
+ errWindow.loadFile(errorHTMLPath, {
|
|
|
+ query: {
|
|
|
+ home: app.getPath("home"),
|
|
|
+ v: appVer,
|
|
|
+ title: title,
|
|
|
+ content: content,
|
|
|
+ icon: path.join(appDir, "stage", "icon-large.png"),
|
|
|
+ },
|
|
|
+ });
|
|
|
+ errWindow.show();
|
|
|
+};
|
|
|
|
|
|
const writeLog = (out) => {
|
|
|
- console.log(out)
|
|
|
- const logFile = path.join(confDir, 'app.log')
|
|
|
- let log = ''
|
|
|
- const maxLogLines = 1024
|
|
|
- try {
|
|
|
- if (fs.existsSync(logFile)) {
|
|
|
- log = fs.readFileSync(logFile).toString()
|
|
|
- let lines = log.split('\n')
|
|
|
- if (maxLogLines < lines.length) {
|
|
|
- log = lines.slice(maxLogLines / 2, maxLogLines).join('\n') + '\n'
|
|
|
- }
|
|
|
+ console.log(out);
|
|
|
+ const logFile = path.join(confDir, "app.log");
|
|
|
+ let log = "";
|
|
|
+ const maxLogLines = 1024;
|
|
|
+ try {
|
|
|
+ if (fs.existsSync(logFile)) {
|
|
|
+ log = fs.readFileSync(logFile).toString();
|
|
|
+ let lines = log.split("\n");
|
|
|
+ if (maxLogLines < lines.length) {
|
|
|
+ log = lines.slice(maxLogLines / 2, maxLogLines).join("\n") + "\n";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ out = out.toString();
|
|
|
+ out = new Date().toISOString().replace(/T/, " ").replace(/\..+/, "") + " " +
|
|
|
+ out;
|
|
|
+ log += out + "\n";
|
|
|
+ fs.writeFileSync(logFile, log);
|
|
|
+ } catch (e) {
|
|
|
+ console.error(e);
|
|
|
}
|
|
|
- out = out.toString()
|
|
|
- out = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '') + ' ' +
|
|
|
- out
|
|
|
- log += out + '\n'
|
|
|
- fs.writeFileSync(logFile, log)
|
|
|
- } catch (e) {
|
|
|
- console.error(e)
|
|
|
- }
|
|
|
-}
|
|
|
+};
|
|
|
|
|
|
const boot = () => {
|
|
|
- // 恢复主窗体状态
|
|
|
- let oldWindowState = {}
|
|
|
- try {
|
|
|
- oldWindowState = JSON.parse(fs.readFileSync(windowStatePath, 'utf8'))
|
|
|
- } catch (e) {
|
|
|
- fs.writeFileSync(windowStatePath, '{}')
|
|
|
- }
|
|
|
- let defaultWidth
|
|
|
- let defaultHeight
|
|
|
- let workArea
|
|
|
- try {
|
|
|
- defaultWidth = screen.getPrimaryDisplay().size.width
|
|
|
- defaultHeight = screen.getPrimaryDisplay().workAreaSize.height
|
|
|
- workArea = screen.getPrimaryDisplay().workArea
|
|
|
- } catch (e) {
|
|
|
- console.error(e)
|
|
|
- }
|
|
|
- const windowState = Object.assign({}, {
|
|
|
- isMaximized: true,
|
|
|
- fullscreen: false,
|
|
|
- isDevToolsOpened: false,
|
|
|
- x: 0, y: 0,
|
|
|
- width: defaultWidth,
|
|
|
- height: defaultHeight,
|
|
|
- }, oldWindowState)
|
|
|
+ // 恢复主窗体状态
|
|
|
+ let oldWindowState = {};
|
|
|
+ try {
|
|
|
+ oldWindowState = JSON.parse(fs.readFileSync(windowStatePath, "utf8"));
|
|
|
+ } catch (e) {
|
|
|
+ fs.writeFileSync(windowStatePath, "{}");
|
|
|
+ }
|
|
|
+ let defaultWidth;
|
|
|
+ let defaultHeight;
|
|
|
+ let workArea;
|
|
|
+ try {
|
|
|
+ defaultWidth = screen.getPrimaryDisplay().size.width;
|
|
|
+ defaultHeight = screen.getPrimaryDisplay().workAreaSize.height;
|
|
|
+ workArea = screen.getPrimaryDisplay().workArea;
|
|
|
+ } catch (e) {
|
|
|
+ console.error(e);
|
|
|
+ }
|
|
|
+ const windowState = Object.assign({}, {
|
|
|
+ isMaximized: true,
|
|
|
+ fullscreen: false,
|
|
|
+ isDevToolsOpened: false,
|
|
|
+ x: 0, y: 0,
|
|
|
+ width: defaultWidth,
|
|
|
+ height: defaultHeight,
|
|
|
+ }, oldWindowState);
|
|
|
|
|
|
- let x = windowState.x
|
|
|
- let y = windowState.y
|
|
|
- if (workArea) {
|
|
|
- // 窗口大小等同于或大于 workArea 时,缩小会隐藏到左下角
|
|
|
- if (windowState.width >= workArea.width || windowState.height >=
|
|
|
- workArea.height) {
|
|
|
- windowState.width = Math.min(defaultWidth, workArea.width)
|
|
|
- windowState.height = Math.min(defaultHeight, workArea.height)
|
|
|
+ let x = windowState.x;
|
|
|
+ let y = windowState.y;
|
|
|
+ if (workArea) {
|
|
|
+ // 窗口大小等同于或大于 workArea 时,缩小会隐藏到左下角
|
|
|
+ if (windowState.width >= workArea.width || windowState.height >=
|
|
|
+ workArea.height) {
|
|
|
+ windowState.width = Math.min(defaultWidth, workArea.width);
|
|
|
+ windowState.height = Math.min(defaultHeight, workArea.height);
|
|
|
+ }
|
|
|
+ if (x > workArea.width) {
|
|
|
+ x = 0;
|
|
|
+ }
|
|
|
+ if (y > workArea.height) {
|
|
|
+ y = 0;
|
|
|
+ }
|
|
|
}
|
|
|
- if (x > workArea.width) {
|
|
|
- x = 0
|
|
|
+ if (windowState.width < 400) {
|
|
|
+ windowState.width = 400;
|
|
|
}
|
|
|
- if (y > workArea.height) {
|
|
|
- y = 0
|
|
|
+ if (windowState.height < 300) {
|
|
|
+ windowState.height = 300;
|
|
|
}
|
|
|
- }
|
|
|
- if (windowState.width < 400) {
|
|
|
- windowState.width = 400
|
|
|
- }
|
|
|
- if (windowState.height < 300) {
|
|
|
- windowState.height = 300
|
|
|
- }
|
|
|
- if (x < 0) {
|
|
|
- x = 0
|
|
|
- }
|
|
|
- if (y < 0) {
|
|
|
- y = 0
|
|
|
- }
|
|
|
+ if (x < 0) {
|
|
|
+ x = 0;
|
|
|
+ }
|
|
|
+ if (y < 0) {
|
|
|
+ y = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建主窗体
|
|
|
+ const currentWindow = new BrowserWindow({
|
|
|
+ show: false,
|
|
|
+ backgroundColor: "#FFF", // 桌面端主窗体背景色设置为 `#FFF` Fix https://github.com/siyuan-note/siyuan/issues/4544
|
|
|
+ width: windowState.width,
|
|
|
+ height: windowState.height,
|
|
|
+ minWidth: 493,
|
|
|
+ minHeight: 376,
|
|
|
+ x,
|
|
|
+ y,
|
|
|
+ fullscreenable: true,
|
|
|
+ fullscreen: windowState.fullscreen,
|
|
|
+ trafficLightPosition: {x: 8, y: 8},
|
|
|
+ webPreferences: {
|
|
|
+ nodeIntegration: true,
|
|
|
+ webviewTag: true,
|
|
|
+ webSecurity: false,
|
|
|
+ contextIsolation: false,
|
|
|
+ },
|
|
|
+ frame: "darwin" === process.platform,
|
|
|
+ titleBarStyle: "hidden",
|
|
|
+ icon: path.join(appDir, "stage", "icon-large.png"),
|
|
|
+ });
|
|
|
+ require("@electron/remote/main").enable(currentWindow.webContents);
|
|
|
+ currentWindow.webContents.userAgent = "SiYuan/" + appVer +
|
|
|
+ " https://b3log.org/siyuan Electron";
|
|
|
|
|
|
- // 创建主窗体
|
|
|
- const currentWindow = new BrowserWindow({
|
|
|
- show: false,
|
|
|
- backgroundColor: '#FFF', // 桌面端主窗体背景色设置为 `#FFF` Fix https://github.com/siyuan-note/siyuan/issues/4544
|
|
|
- width: windowState.width,
|
|
|
- height: windowState.height,
|
|
|
- minWidth: 493,
|
|
|
- minHeight: 376,
|
|
|
- x,
|
|
|
- y,
|
|
|
- fullscreenable: true,
|
|
|
- fullscreen: windowState.fullscreen,
|
|
|
- trafficLightPosition: {x: 8, y: 8},
|
|
|
- webPreferences: {
|
|
|
- nodeIntegration: true,
|
|
|
- webviewTag: true,
|
|
|
- webSecurity: false,
|
|
|
- contextIsolation: false,
|
|
|
- },
|
|
|
- frame: 'darwin' === process.platform,
|
|
|
- titleBarStyle: 'hidden',
|
|
|
- icon: path.join(appDir, 'stage', 'icon-large.png'),
|
|
|
- })
|
|
|
- require('@electron/remote/main').enable(currentWindow.webContents)
|
|
|
- currentWindow.webContents.userAgent = 'SiYuan/' + appVer +
|
|
|
- ' https://b3log.org/siyuan Electron'
|
|
|
+ currentWindow.webContents.session.setSpellCheckerLanguages(["en-US"]);
|
|
|
|
|
|
- currentWindow.webContents.session.setSpellCheckerLanguages(['en-US'])
|
|
|
+ // 发起互联网服务请求时绕过安全策略 https://github.com/siyuan-note/siyuan/issues/5516
|
|
|
+ currentWindow.webContents.session.webRequest.onBeforeSendHeaders(
|
|
|
+ (details, cb) => {
|
|
|
+ if (-1 < details.url.indexOf("bili")) {
|
|
|
+ // B 站不移除 Referer https://github.com/siyuan-note/siyuan/issues/94
|
|
|
+ cb({requestHeaders: details.requestHeaders});
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- // 发起互联网服务请求时绕过安全策略 https://github.com/siyuan-note/siyuan/issues/5516
|
|
|
- currentWindow.webContents.session.webRequest.onBeforeSendHeaders(
|
|
|
- (details, cb) => {
|
|
|
- if (-1 < details.url.indexOf('bili')) {
|
|
|
- // B 站不移除 Referer https://github.com/siyuan-note/siyuan/issues/94
|
|
|
- cb({requestHeaders: details.requestHeaders})
|
|
|
- return
|
|
|
- }
|
|
|
+ for (let key in details.requestHeaders) {
|
|
|
+ if ("referer" === key.toLowerCase()) {
|
|
|
+ delete details.requestHeaders[key];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ cb({requestHeaders: details.requestHeaders});
|
|
|
+ });
|
|
|
+ currentWindow.webContents.session.webRequest.onHeadersReceived(
|
|
|
+ (details, cb) => {
|
|
|
+ for (let key in details.responseHeaders) {
|
|
|
+ if ("x-frame-options" === key.toLowerCase()) {
|
|
|
+ delete details.responseHeaders[key];
|
|
|
+ } else if ("content-security-policy" === key.toLowerCase()) {
|
|
|
+ delete details.responseHeaders[key];
|
|
|
+ } else if ("access-control-allow-origin" === key.toLowerCase()) {
|
|
|
+ delete details.responseHeaders[key];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ cb({responseHeaders: details.responseHeaders});
|
|
|
+ });
|
|
|
|
|
|
- for (let key in details.requestHeaders) {
|
|
|
- if ('referer' === key.toLowerCase()) {
|
|
|
- delete details.requestHeaders[key]
|
|
|
+ currentWindow.webContents.on("did-finish-load", () => {
|
|
|
+ let siyuanOpenURL;
|
|
|
+ if ("win32" === process.platform || "linux" === process.platform) {
|
|
|
+ siyuanOpenURL = process.argv.find((arg) => arg.startsWith("siyuan://"));
|
|
|
}
|
|
|
- }
|
|
|
- cb({requestHeaders: details.requestHeaders})
|
|
|
- })
|
|
|
- currentWindow.webContents.session.webRequest.onHeadersReceived(
|
|
|
- (details, cb) => {
|
|
|
- for (let key in details.responseHeaders) {
|
|
|
- if ('x-frame-options' === key.toLowerCase()) {
|
|
|
- delete details.responseHeaders[key]
|
|
|
- } else if ('content-security-policy' === key.toLowerCase()) {
|
|
|
- delete details.responseHeaders[key]
|
|
|
- } else if ('access-control-allow-origin' === key.toLowerCase()) {
|
|
|
- delete details.responseHeaders[key]
|
|
|
+ if (siyuanOpenURL) {
|
|
|
+ if (currentWindow.isMinimized()) {
|
|
|
+ currentWindow.restore();
|
|
|
+ }
|
|
|
+ if (!currentWindow.isVisible()) {
|
|
|
+ currentWindow.show();
|
|
|
+ }
|
|
|
+ currentWindow.focus();
|
|
|
+ setTimeout(() => { // 等待界面js执行完毕
|
|
|
+ writeLog(siyuanOpenURL);
|
|
|
+ currentWindow.webContents.send("siyuan-openurl", siyuanOpenURL);
|
|
|
+ }, 2000);
|
|
|
}
|
|
|
- }
|
|
|
- cb({responseHeaders: details.responseHeaders})
|
|
|
- })
|
|
|
+ });
|
|
|
|
|
|
- currentWindow.webContents.on('did-finish-load', () => {
|
|
|
- let siyuanOpenURL
|
|
|
- if ('win32' === process.platform || 'linux' === process.platform) {
|
|
|
- siyuanOpenURL = process.argv.find((arg) => arg.startsWith('siyuan://'))
|
|
|
+ if (windowState.isDevToolsOpened) {
|
|
|
+ currentWindow.webContents.openDevTools({mode: "bottom"});
|
|
|
}
|
|
|
- if (siyuanOpenURL) {
|
|
|
- if (currentWindow.isMinimized()) {
|
|
|
- currentWindow.restore()
|
|
|
- }
|
|
|
- if (!currentWindow.isVisible()) {
|
|
|
- currentWindow.show()
|
|
|
- }
|
|
|
- currentWindow.focus()
|
|
|
- setTimeout(() => { // 等待界面js执行完毕
|
|
|
- writeLog(siyuanOpenURL)
|
|
|
- currentWindow.webContents.send('siyuan-openurl', siyuanOpenURL)
|
|
|
- }, 2000)
|
|
|
- }
|
|
|
- })
|
|
|
-
|
|
|
- if (windowState.isDevToolsOpened) {
|
|
|
- currentWindow.webContents.openDevTools({mode: 'bottom'})
|
|
|
- }
|
|
|
|
|
|
- // 主界面事件监听
|
|
|
- currentWindow.once('ready-to-show', () => {
|
|
|
- currentWindow.show()
|
|
|
- if (windowState.isMaximized) {
|
|
|
- currentWindow.maximize()
|
|
|
- } else {
|
|
|
- currentWindow.unmaximize()
|
|
|
- }
|
|
|
- if (bootWindow && !bootWindow.isDestroyed()) {
|
|
|
- bootWindow.destroy()
|
|
|
- }
|
|
|
- })
|
|
|
+ // 主界面事件监听
|
|
|
+ currentWindow.once("ready-to-show", () => {
|
|
|
+ currentWindow.show();
|
|
|
+ if (windowState.isMaximized) {
|
|
|
+ currentWindow.maximize();
|
|
|
+ } else {
|
|
|
+ currentWindow.unmaximize();
|
|
|
+ }
|
|
|
+ if (bootWindow && !bootWindow.isDestroyed()) {
|
|
|
+ bootWindow.destroy();
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
- // 加载主界面
|
|
|
- currentWindow.loadURL(getServer() + '/stage/build/app/index.html?v=' +
|
|
|
- new Date().getTime())
|
|
|
+ // 加载主界面
|
|
|
+ currentWindow.loadURL(getServer() + "/stage/build/app/index.html?v=" +
|
|
|
+ new Date().getTime());
|
|
|
|
|
|
- // 菜单
|
|
|
- const productName = 'SiYuan'
|
|
|
- const template = [
|
|
|
- {
|
|
|
- label: productName,
|
|
|
- submenu: [
|
|
|
+ // 菜单
|
|
|
+ const productName = "SiYuan";
|
|
|
+ const template = [
|
|
|
{
|
|
|
- label: `About ${productName}`,
|
|
|
- role: 'about',
|
|
|
+ label: productName,
|
|
|
+ submenu: [
|
|
|
+ {
|
|
|
+ label: `About ${productName}`,
|
|
|
+ role: "about",
|
|
|
+ },
|
|
|
+ {type: "separator"},
|
|
|
+ {role: "services"},
|
|
|
+ {type: "separator"},
|
|
|
+ {
|
|
|
+ label: `Hide ${productName}`,
|
|
|
+ role: "hide",
|
|
|
+ },
|
|
|
+ {role: "hideOthers"},
|
|
|
+ {role: "unhide"},
|
|
|
+ {type: "separator"},
|
|
|
+ {
|
|
|
+ label: `Quit ${productName}`,
|
|
|
+ role: "quit",
|
|
|
+ },
|
|
|
+ ],
|
|
|
},
|
|
|
- {type: 'separator'},
|
|
|
- {role: 'services'},
|
|
|
- {type: 'separator'},
|
|
|
{
|
|
|
- label: `Hide ${productName}`,
|
|
|
- role: 'hide',
|
|
|
+ role: "editMenu",
|
|
|
+ submenu: [
|
|
|
+ {role: "cut"},
|
|
|
+ {role: "copy"},
|
|
|
+ {role: "paste"},
|
|
|
+ {role: "pasteAndMatchStyle", accelerator: "CmdOrCtrl+Shift+C"},
|
|
|
+ {role: "selectAll"},
|
|
|
+ ],
|
|
|
},
|
|
|
- {role: 'hideOthers'},
|
|
|
- {role: 'unhide'},
|
|
|
- {type: 'separator'},
|
|
|
{
|
|
|
- label: `Quit ${productName}`,
|
|
|
- role: 'quit',
|
|
|
+ role: "windowMenu",
|
|
|
+ submenu: [
|
|
|
+ {role: "minimize"},
|
|
|
+ {role: "zoom"},
|
|
|
+ {role: "togglefullscreen"},
|
|
|
+ {type: "separator"},
|
|
|
+ {role: "toggledevtools"},
|
|
|
+ {type: "separator"},
|
|
|
+ {role: "front"},
|
|
|
+ ],
|
|
|
},
|
|
|
- ],
|
|
|
- },
|
|
|
- {
|
|
|
- role: 'editMenu',
|
|
|
- submenu: [
|
|
|
- {role: 'cut'},
|
|
|
- {role: 'copy'},
|
|
|
- {role: 'paste'},
|
|
|
- {role: 'pasteAndMatchStyle', accelerator: 'CmdOrCtrl+Shift+C'},
|
|
|
- {role: 'selectAll'},
|
|
|
- ],
|
|
|
- },
|
|
|
- {
|
|
|
- role: 'windowMenu',
|
|
|
- submenu: [
|
|
|
- {role: 'minimize'},
|
|
|
- {role: 'zoom'},
|
|
|
- {role: 'togglefullscreen'},
|
|
|
- {type: 'separator'},
|
|
|
- {role: 'toggledevtools'},
|
|
|
- {type: 'separator'},
|
|
|
- {role: 'front'},
|
|
|
- ],
|
|
|
- },
|
|
|
- ]
|
|
|
- const menu = Menu.buildFromTemplate(template)
|
|
|
- Menu.setApplicationMenu(menu)
|
|
|
- // 当前页面链接使用浏览器打开
|
|
|
- currentWindow.webContents.on('will-navigate', (event, url) => {
|
|
|
- const currentURL = new URL(event.sender.getURL())
|
|
|
- if (url.startsWith(getServer(currentURL.port))) {
|
|
|
- return
|
|
|
- }
|
|
|
+ ];
|
|
|
+ const menu = Menu.buildFromTemplate(template);
|
|
|
+ Menu.setApplicationMenu(menu);
|
|
|
+ // 当前页面链接使用浏览器打开
|
|
|
+ currentWindow.webContents.on("will-navigate", (event, url) => {
|
|
|
+ const currentURL = new URL(event.sender.getURL());
|
|
|
+ if (url.startsWith(getServer(currentURL.port))) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- event.preventDefault()
|
|
|
- shell.openExternal(url)
|
|
|
- })
|
|
|
+ event.preventDefault();
|
|
|
+ shell.openExternal(url);
|
|
|
+ });
|
|
|
|
|
|
- currentWindow.on('close', (event) => {
|
|
|
- if (currentWindow && !currentWindow.isDestroyed()) {
|
|
|
- currentWindow.webContents.send('siyuan-save-close', false)
|
|
|
- }
|
|
|
- event.preventDefault()
|
|
|
- })
|
|
|
- workspaces.push({
|
|
|
- browserWindow: currentWindow,
|
|
|
- id: currentWindow.id,
|
|
|
- })
|
|
|
-}
|
|
|
+ currentWindow.on("close", (event) => {
|
|
|
+ if (currentWindow && !currentWindow.isDestroyed()) {
|
|
|
+ currentWindow.webContents.send("siyuan-save-close", false);
|
|
|
+ }
|
|
|
+ event.preventDefault();
|
|
|
+ });
|
|
|
+ workspaces.push({
|
|
|
+ browserWindow: currentWindow,
|
|
|
+ id: currentWindow.id,
|
|
|
+ });
|
|
|
+};
|
|
|
|
|
|
const showWindow = (wnd) => {
|
|
|
- if (!wnd || wnd.isDestroyed()) {
|
|
|
- return
|
|
|
- }
|
|
|
+ if (!wnd || wnd.isDestroyed()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- if (wnd.isMinimized()) {
|
|
|
- wnd.restore()
|
|
|
- }
|
|
|
- if (!wnd.isVisible()) {
|
|
|
- wnd.show()
|
|
|
- }
|
|
|
- wnd.focus()
|
|
|
-}
|
|
|
+ if (wnd.isMinimized()) {
|
|
|
+ wnd.restore();
|
|
|
+ }
|
|
|
+ if (!wnd.isVisible()) {
|
|
|
+ wnd.show();
|
|
|
+ }
|
|
|
+ wnd.focus();
|
|
|
+};
|
|
|
|
|
|
const initKernel = (workspace, port, lang) => {
|
|
|
- return new Promise(async (resolve) => {
|
|
|
- bootWindow = new BrowserWindow({
|
|
|
- width: screen.getPrimaryDisplay().size.width / 2,
|
|
|
- height: screen.getPrimaryDisplay().workAreaSize.height / 2,
|
|
|
- frame: false,
|
|
|
- icon: path.join(appDir, 'stage', 'icon-large.png'),
|
|
|
- transparent: 'linux' !== process.platform,
|
|
|
- })
|
|
|
+ return new Promise(async (resolve) => {
|
|
|
+ bootWindow = new BrowserWindow({
|
|
|
+ width: screen.getPrimaryDisplay().size.width / 2,
|
|
|
+ height: screen.getPrimaryDisplay().workAreaSize.height / 2,
|
|
|
+ frame: false,
|
|
|
+ icon: path.join(appDir, "stage", "icon-large.png"),
|
|
|
+ transparent: "linux" !== process.platform,
|
|
|
+ });
|
|
|
|
|
|
- const kernelName = 'win32' === process.platform
|
|
|
- ? 'SiYuan-Kernel.exe'
|
|
|
- : 'SiYuan-Kernel'
|
|
|
- const kernelPath = path.join(appDir, 'kernel', kernelName)
|
|
|
- if (!fs.existsSync(kernelPath)) {
|
|
|
- showErrorWindow('⚠️ 内核文件丢失 Kernel is missing',
|
|
|
- `<div>内核可执行文件丢失,请重新安装思源,并将思源加入杀毒软件信任列表。</div><div>The kernel binary is not found, please reinstall SiYuan and add SiYuan into the trust list of your antivirus software.</div>`)
|
|
|
- bootWindow.destroy()
|
|
|
- resolve(false)
|
|
|
- return
|
|
|
- }
|
|
|
+ const kernelName = "win32" === process.platform
|
|
|
+ ? "SiYuan-Kernel.exe"
|
|
|
+ : "SiYuan-Kernel";
|
|
|
+ const kernelPath = path.join(appDir, "kernel", kernelName);
|
|
|
+ if (!fs.existsSync(kernelPath)) {
|
|
|
+ showErrorWindow("⚠️ 内核文件丢失 Kernel is missing",
|
|
|
+ "<div>内核可执行文件丢失,请重新安装思源,并将思源加入杀毒软件信任列表。</div><div>The kernel binary is not found, please reinstall SiYuan and add SiYuan into the trust list of your antivirus software.</div>");
|
|
|
+ bootWindow.destroy();
|
|
|
+ resolve(false);
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- if (!isDevEnv || workspaces.length > 0) {
|
|
|
- if (port && '' !== port) {
|
|
|
- kernelPort = port
|
|
|
- } else {
|
|
|
- const getAvailablePort = () => {
|
|
|
- // https://gist.github.com/mikeal/1840641
|
|
|
- return new Promise((portResolve, portReject) => {
|
|
|
- const server = net.createServer()
|
|
|
- server.on('error', error => {
|
|
|
- writeLog(error)
|
|
|
- kernelPort = ''
|
|
|
- portReject()
|
|
|
- })
|
|
|
- server.listen(0, () => {
|
|
|
- kernelPort = server.address().port
|
|
|
- server.close(() => portResolve(kernelPort))
|
|
|
- })
|
|
|
- })
|
|
|
+ if (!isDevEnv || workspaces.length > 0) {
|
|
|
+ if (port && "" !== port) {
|
|
|
+ kernelPort = port;
|
|
|
+ } else {
|
|
|
+ const getAvailablePort = () => {
|
|
|
+ // https://gist.github.com/mikeal/1840641
|
|
|
+ return new Promise((portResolve, portReject) => {
|
|
|
+ const server = net.createServer();
|
|
|
+ server.on("error", error => {
|
|
|
+ writeLog(error);
|
|
|
+ kernelPort = "";
|
|
|
+ portReject();
|
|
|
+ });
|
|
|
+ server.listen(0, () => {
|
|
|
+ kernelPort = server.address().port;
|
|
|
+ server.close(() => portResolve(kernelPort));
|
|
|
+ });
|
|
|
+ });
|
|
|
+ };
|
|
|
+ await getAvailablePort();
|
|
|
+ }
|
|
|
}
|
|
|
- await getAvailablePort()
|
|
|
- }
|
|
|
- }
|
|
|
- writeLog('got kernel port [' + kernelPort + ']')
|
|
|
- if (!kernelPort) {
|
|
|
- bootWindow.destroy()
|
|
|
- resolve(false)
|
|
|
- return
|
|
|
- }
|
|
|
- const cmds = ['--port', kernelPort, '--wd', appDir]
|
|
|
- if (isDevEnv && workspaces.length === 0) {
|
|
|
- cmds.push('--mode', 'dev')
|
|
|
- }
|
|
|
- if (workspace && '' !== workspace) {
|
|
|
- cmds.push('--workspace', workspace)
|
|
|
- }
|
|
|
- if (port && '' !== port) {
|
|
|
- cmds.push('--port', port)
|
|
|
- }
|
|
|
- if (lang && '' !== lang) {
|
|
|
- cmds.push('--lang', lang)
|
|
|
- }
|
|
|
- let cmd = `ui version [${appVer}], booting kernel [${kernelPath} ${cmds.join(
|
|
|
- ' ')}]`
|
|
|
- writeLog(cmd)
|
|
|
- let kernelProcessPid = ''
|
|
|
- if (!isDevEnv || workspaces.length > 0) {
|
|
|
- const cp = require('child_process')
|
|
|
- const kernelProcess = cp.spawn(kernelPath,
|
|
|
- cmds, {
|
|
|
- detached: false, // 桌面端内核进程不再以游离模式拉起 https://github.com/siyuan-note/siyuan/issues/6336
|
|
|
- stdio: 'ignore',
|
|
|
- },
|
|
|
- )
|
|
|
- kernelProcessPid = kernelProcess.pid
|
|
|
- writeLog('booted kernel process [pid=' + kernelProcessPid + ', port=' +
|
|
|
- kernelPort + ']')
|
|
|
+ writeLog("got kernel port [" + kernelPort + "]");
|
|
|
+ if (!kernelPort) {
|
|
|
+ bootWindow.destroy();
|
|
|
+ resolve(false);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const cmds = ["--port", kernelPort, "--wd", appDir];
|
|
|
+ if (isDevEnv && workspaces.length === 0) {
|
|
|
+ cmds.push("--mode", "dev");
|
|
|
+ }
|
|
|
+ if (workspace && "" !== workspace) {
|
|
|
+ cmds.push("--workspace", workspace);
|
|
|
+ }
|
|
|
+ if (port && "" !== port) {
|
|
|
+ cmds.push("--port", port);
|
|
|
+ }
|
|
|
+ if (lang && "" !== lang) {
|
|
|
+ cmds.push("--lang", lang);
|
|
|
+ }
|
|
|
+ let cmd = `ui version [${appVer}], booting kernel [${kernelPath} ${cmds.join(
|
|
|
+ " ")}]`;
|
|
|
+ writeLog(cmd);
|
|
|
+ let kernelProcessPid = "";
|
|
|
+ if (!isDevEnv || workspaces.length > 0) {
|
|
|
+ const cp = require("child_process");
|
|
|
+ const kernelProcess = cp.spawn(kernelPath,
|
|
|
+ cmds, {
|
|
|
+ detached: false, // 桌面端内核进程不再以游离模式拉起 https://github.com/siyuan-note/siyuan/issues/6336
|
|
|
+ stdio: "ignore",
|
|
|
+ },
|
|
|
+ );
|
|
|
+ kernelProcessPid = kernelProcess.pid;
|
|
|
+ writeLog("booted kernel process [pid=" + kernelProcessPid + ", port=" +
|
|
|
+ kernelPort + "]");
|
|
|
|
|
|
- kernelProcess.on('close', (code) => {
|
|
|
- writeLog(`kernel [pid=${kernelProcessPid}] exited with code [${code}]`)
|
|
|
- if (0 !== code) {
|
|
|
- switch (code) {
|
|
|
- case 20:
|
|
|
- showErrorWindow('⚠️ 数据库被锁定 The database is locked',
|
|
|
- `<div>数据库文件正在被其他进程占用,请检查是否同时存在多个内核进程(SiYuan Kernel)服务相同的工作空间。</div><div>The database file is being occupied by other processes, please check whether there are multiple kernel processes (SiYuan Kernel) serving the same workspace at the same time.</div>`)
|
|
|
- break
|
|
|
- case 21:
|
|
|
- showErrorWindow('⚠️ 监听端口 ' + kernelPort +
|
|
|
- ' 失败 Failed to listen to port ' + kernelPort,
|
|
|
- '<div>监听 ' + kernelPort +
|
|
|
- ' 端口失败,请确保程序拥有网络权限并不受防火墙和杀毒软件阻止。</div><div>Failed to listen to port ' +
|
|
|
- kernelPort +
|
|
|
- ', please make sure the program has network permissions and is not blocked by firewalls and antivirus software.</div>')
|
|
|
- break
|
|
|
- case 22:
|
|
|
- showErrorWindow(
|
|
|
- '⚠️ 创建配置目录失败 Failed to create config directory',
|
|
|
- `<div>思源需要在用户家目录下创建配置文件夹(~/.config/siyuan),请确保该路径具有写入权限。</div><div>SiYuan needs to create a configuration folder (~/.config/siyuan) in the user\'s home directory. Please make sure that the path has write permissions.</div>`)
|
|
|
- break
|
|
|
- case 23:
|
|
|
- showErrorWindow(
|
|
|
- '⚠️ 无法读写块树文件 Failed to access blocktree file',
|
|
|
- `<div>块树文件正在被其他程序锁定或者已经损坏,请删除 工作空间/temp/ 文件夹后重启</div><div>The block tree file is being locked by another program or is corrupted, please delete the workspace/temp/ folder and restart.</div>`)
|
|
|
- break
|
|
|
- case 24: // 工作空间已被锁定,尝试切换到第一个打开的工作空间
|
|
|
- if (workspaces && 0 < workspaces.length) {
|
|
|
- showWindow(workspaces[0].browserWindow)
|
|
|
- }
|
|
|
+ kernelProcess.on("close", (code) => {
|
|
|
+ writeLog(`kernel [pid=${kernelProcessPid}] exited with code [${code}]`);
|
|
|
+ if (0 !== code) {
|
|
|
+ switch (code) {
|
|
|
+ case 20:
|
|
|
+ showErrorWindow("⚠️ 数据库被锁定 The database is locked",
|
|
|
+ "<div>数据库文件正在被其他进程占用,请检查是否同时存在多个内核进程(SiYuan Kernel)服务相同的工作空间。</div><div>The database file is being occupied by other processes, please check whether there are multiple kernel processes (SiYuan Kernel) serving the same workspace at the same time.</div>");
|
|
|
+ break;
|
|
|
+ case 21:
|
|
|
+ showErrorWindow("⚠️ 监听端口 " + kernelPort +
|
|
|
+ " 失败 Failed to listen to port " + kernelPort,
|
|
|
+ "<div>监听 " + kernelPort +
|
|
|
+ " 端口失败,请确保程序拥有网络权限并不受防火墙和杀毒软件阻止。</div><div>Failed to listen to port " +
|
|
|
+ kernelPort +
|
|
|
+ ", please make sure the program has network permissions and is not blocked by firewalls and antivirus software.</div>");
|
|
|
+ break;
|
|
|
+ case 22:
|
|
|
+ showErrorWindow(
|
|
|
+ "⚠️ 创建配置目录失败 Failed to create config directory",
|
|
|
+ "<div>思源需要在用户家目录下创建配置文件夹(~/.config/siyuan),请确保该路径具有写入权限。</div><div>SiYuan needs to create a configuration folder (~/.config/siyuan) in the user\'s home directory. Please make sure that the path has write permissions.</div>");
|
|
|
+ break;
|
|
|
+ case 23:
|
|
|
+ showErrorWindow(
|
|
|
+ "⚠️ 无法读写块树文件 Failed to access blocktree file",
|
|
|
+ "<div>块树文件正在被其他程序锁定或者已经损坏,请删除 工作空间/temp/ 文件夹后重启</div><div>The block tree file is being locked by another program or is corrupted, please delete the workspace/temp/ folder and restart.</div>");
|
|
|
+ break;
|
|
|
+ case 24: // 工作空间已被锁定,尝试切换到第一个打开的工作空间
|
|
|
+ if (workspaces && 0 < workspaces.length) {
|
|
|
+ showWindow(workspaces[0].browserWindow);
|
|
|
+ }
|
|
|
|
|
|
- showErrorWindow(
|
|
|
- '⚠️ 工作空间已被锁定 The workspace is locked',
|
|
|
- `<div>该工作空间正在被使用。</div><div>The workspace is in use.</div>`)
|
|
|
- break
|
|
|
- case 25:
|
|
|
- showErrorWindow(
|
|
|
- '⚠️ 创建工作空间目录失败 Failed to create workspace directory',
|
|
|
- `<div>创建工作空间目录失败。</div><div>Failed to create workspace directory.</div>`)
|
|
|
- break
|
|
|
- case 0:
|
|
|
- case 1: // Fatal error
|
|
|
- break
|
|
|
- default:
|
|
|
- showErrorWindow(
|
|
|
- '⚠️ 内核因未知原因退出 The kernel exited for unknown reasons',
|
|
|
- `<div>思源内核因未知原因退出 [code=${code}],请尝试重启操作系统后再启动思源。如果该问题依然发生,请检查杀毒软件是否阻止思源内核启动。</div>
|
|
|
-<div>SiYuan Kernel exited for unknown reasons [code=${code}], please try to reboot your operating system and then start SiYuan again. If occurs this problem still, please check your anti-virus software whether kill the SiYuan Kernel.</div>`)
|
|
|
- break
|
|
|
- }
|
|
|
+ showErrorWindow(
|
|
|
+ "⚠️ 工作空间已被锁定 The workspace is locked",
|
|
|
+ "<div>该工作空间正在被使用。</div><div>The workspace is in use.</div>");
|
|
|
+ break;
|
|
|
+ case 25:
|
|
|
+ showErrorWindow(
|
|
|
+ "⚠️ 创建工作空间目录失败 Failed to create workspace directory",
|
|
|
+ "<div>创建工作空间目录失败。</div><div>Failed to create workspace directory.</div>");
|
|
|
+ break;
|
|
|
+ case 0:
|
|
|
+ case 1: // Fatal error
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ showErrorWindow(
|
|
|
+ "⚠️ 内核因未知原因退出 The kernel exited for unknown reasons",
|
|
|
+ `<div>思源内核因未知原因退出 [code=${code}],请尝试重启操作系统后再启动思源。如果该问题依然发生,请检查杀毒软件是否阻止思源内核启动。</div>
|
|
|
+<div>SiYuan Kernel exited for unknown reasons [code=${code}], please try to reboot your operating system and then start SiYuan again. If occurs this problem still, please check your anti-virus software whether kill the SiYuan Kernel.</div>`);
|
|
|
+ break;
|
|
|
+ }
|
|
|
|
|
|
- bootWindow.destroy()
|
|
|
- resolve(false)
|
|
|
+ bootWindow.destroy();
|
|
|
+ resolve(false);
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
- })
|
|
|
- }
|
|
|
|
|
|
- let gotVersion = false
|
|
|
- let apiData
|
|
|
- let count = 0
|
|
|
- writeLog('checking kernel version')
|
|
|
- while (!gotVersion) {
|
|
|
- try {
|
|
|
- const apiResult = await fetch(getServer() + '/api/system/version')
|
|
|
- apiData = await apiResult.json()
|
|
|
- gotVersion = true
|
|
|
- bootWindow.setResizable(false)
|
|
|
- bootWindow.loadURL(getServer() + '/appearance/boot/index.html')
|
|
|
- bootWindow.show()
|
|
|
- } catch (e) {
|
|
|
- writeLog('get kernel version failed: ' + e.message)
|
|
|
- await sleep(100)
|
|
|
- } finally {
|
|
|
- count++
|
|
|
- if (14 < count) {
|
|
|
- writeLog('get kernel ver failed')
|
|
|
+ let gotVersion = false;
|
|
|
+ let apiData;
|
|
|
+ let count = 0;
|
|
|
+ writeLog("checking kernel version");
|
|
|
+ while (!gotVersion && count < 15) {
|
|
|
+ try {
|
|
|
+ const apiResult = await fetch(getServer() + "/api/system/version");
|
|
|
+ apiData = await apiResult.json();
|
|
|
+ gotVersion = true;
|
|
|
+ bootWindow.setResizable(false);
|
|
|
+ bootWindow.loadURL(getServer() + "/appearance/boot/index.html");
|
|
|
+ bootWindow.show();
|
|
|
+ } catch (e) {
|
|
|
+ writeLog("get kernel version failed: " + e.message);
|
|
|
+ await sleep(100);
|
|
|
+ } finally {
|
|
|
+ count++;
|
|
|
+ if (14 < count) {
|
|
|
+ writeLog("get kernel ver failed");
|
|
|
+ showErrorWindow(
|
|
|
+ "⚠️ 获取内核服务端口失败 Failed to get kernel serve port",
|
|
|
+ "<div>获取内核服务端口失败,请确保程序拥有网络权限并不受防火墙和杀毒软件阻止。</div><div>Failed to get kernel serve port, please make sure the program has network permissions and is not blocked by firewalls and antivirus software.</div>");
|
|
|
+ bootWindow.destroy();
|
|
|
+ resolve(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- showErrorWindow(
|
|
|
- '⚠️ 获取内核服务端口失败 Failed to get kernel serve port',
|
|
|
- '<div>获取内核服务端口失败,请确保程序拥有网络权限并不受防火墙和杀毒软件阻止。</div><div>Failed to get kernel serve port, please make sure the program has network permissions and is not blocked by firewalls and antivirus software.</div>')
|
|
|
- bootWindow.destroy()
|
|
|
- resolve(false)
|
|
|
- return
|
|
|
+ if (!gotVersion) {
|
|
|
+ return;
|
|
|
}
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
- if (0 === apiData.code) {
|
|
|
- writeLog('got kernel version [' + apiData.data + ']')
|
|
|
- if (!isDevEnv && apiData.data !== appVer) {
|
|
|
- writeLog(
|
|
|
- `kernel [${apiData.data}] is running, shutdown it now and then start kernel [${appVer}]`)
|
|
|
- fetch(getServer() + '/api/system/exit', {method: 'POST'})
|
|
|
- bootWindow.destroy()
|
|
|
- resolve(false)
|
|
|
- } else {
|
|
|
- let progressing = false
|
|
|
- while (!progressing) {
|
|
|
- try {
|
|
|
- const progressResult = await fetch(
|
|
|
- getServer() + '/api/system/bootProgress')
|
|
|
- const progressData = await progressResult.json()
|
|
|
- if (progressData.data.progress >= 100) {
|
|
|
- resolve(true)
|
|
|
- progressing = true
|
|
|
+ if (0 === apiData.code) {
|
|
|
+ writeLog("got kernel version [" + apiData.data + "]");
|
|
|
+ if (!isDevEnv && apiData.data !== appVer) {
|
|
|
+ writeLog(
|
|
|
+ `kernel [${apiData.data}] is running, shutdown it now and then start kernel [${appVer}]`);
|
|
|
+ fetch(getServer() + "/api/system/exit", {method: "POST"});
|
|
|
+ bootWindow.destroy();
|
|
|
+ resolve(false);
|
|
|
} else {
|
|
|
- await sleep(100)
|
|
|
+ let progressing = false;
|
|
|
+ while (!progressing) {
|
|
|
+ try {
|
|
|
+ const progressResult = await fetch(
|
|
|
+ getServer() + "/api/system/bootProgress");
|
|
|
+ const progressData = await progressResult.json();
|
|
|
+ if (progressData.data.progress >= 100) {
|
|
|
+ resolve(true);
|
|
|
+ progressing = true;
|
|
|
+ } else {
|
|
|
+ await sleep(100);
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ writeLog("get boot progress failed: " + e.message);
|
|
|
+ fetch(getServer() + "/api/system/exit", {method: "POST"});
|
|
|
+ bootWindow.destroy();
|
|
|
+ resolve(false);
|
|
|
+ progressing = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- } catch (e) {
|
|
|
- writeLog('get boot progress failed: ' + e.message)
|
|
|
- fetch(getServer() + '/api/system/exit', {method: 'POST'})
|
|
|
- bootWindow.destroy()
|
|
|
- resolve(false)
|
|
|
- progressing = true
|
|
|
- }
|
|
|
+ } else {
|
|
|
+ writeLog(`get kernel version failed: ${apiData.code}, ${apiData.msg}`);
|
|
|
+ resolve(false);
|
|
|
}
|
|
|
- }
|
|
|
- } else {
|
|
|
- writeLog(`get kernel version failed: ${apiData.code}, ${apiData.msg}`)
|
|
|
- resolve(false)
|
|
|
- }
|
|
|
- })
|
|
|
-}
|
|
|
+ });
|
|
|
+};
|
|
|
|
|
|
-app.setAsDefaultProtocolClient('siyuan')
|
|
|
+app.setAsDefaultProtocolClient("siyuan");
|
|
|
|
|
|
-app.commandLine.appendSwitch('disable-web-security')
|
|
|
-app.commandLine.appendSwitch('auto-detect', 'false')
|
|
|
-app.commandLine.appendSwitch('no-proxy-server')
|
|
|
-app.commandLine.appendSwitch('enable-features', 'PlatformHEVCDecoderSupport')
|
|
|
+app.commandLine.appendSwitch("disable-web-security");
|
|
|
+app.commandLine.appendSwitch("auto-detect", "false");
|
|
|
+app.commandLine.appendSwitch("no-proxy-server");
|
|
|
+app.commandLine.appendSwitch("enable-features", "PlatformHEVCDecoderSupport");
|
|
|
|
|
|
-app.setPath('userData', app.getPath('userData') + '-Electron') // `~/.config` 下 Electron 相关文件夹名称改为 `SiYuan-Electron` https://github.com/siyuan-note/siyuan/issues/3349
|
|
|
+app.setPath("userData", app.getPath("userData") + "-Electron"); // `~/.config` 下 Electron 相关文件夹名称改为 `SiYuan-Electron` https://github.com/siyuan-note/siyuan/issues/3349
|
|
|
|
|
|
app.whenReady().then(() => {
|
|
|
|
|
|
- let resetWindowStateOnRestart = false
|
|
|
- const resetTrayMenu = (tray, lang, mainWindow) => {
|
|
|
- const trayMenuTemplate = [
|
|
|
- {
|
|
|
- label: mainWindow.isVisible()
|
|
|
- ? lang.hideWindow
|
|
|
- : lang.showWindow,
|
|
|
- click: () => {
|
|
|
- showHideWindow(tray, lang, mainWindow)
|
|
|
- },
|
|
|
- },
|
|
|
- {
|
|
|
- label: lang.officialWebsite,
|
|
|
- click: () => {
|
|
|
- shell.openExternal('https://b3log.org/siyuan/')
|
|
|
- },
|
|
|
- },
|
|
|
- {
|
|
|
- label: lang.openSource,
|
|
|
- click: () => {
|
|
|
- shell.openExternal('https://github.com/siyuan-note/siyuan')
|
|
|
- },
|
|
|
- },
|
|
|
- {
|
|
|
- label: lang.resetWindow,
|
|
|
- type: 'checkbox',
|
|
|
- click: v => {
|
|
|
- resetWindowStateOnRestart = v.checked
|
|
|
- mainWindow.webContents.send('siyuan-save-close', true)
|
|
|
- },
|
|
|
- },
|
|
|
- {
|
|
|
- label: lang.quit,
|
|
|
- click: () => {
|
|
|
- mainWindow.webContents.send('siyuan-save-close', true)
|
|
|
- },
|
|
|
- },
|
|
|
- ]
|
|
|
+ let resetWindowStateOnRestart = false;
|
|
|
+ const resetTrayMenu = (tray, lang, mainWindow) => {
|
|
|
+ const trayMenuTemplate = [
|
|
|
+ {
|
|
|
+ label: mainWindow.isVisible()
|
|
|
+ ? lang.hideWindow
|
|
|
+ : lang.showWindow,
|
|
|
+ click: () => {
|
|
|
+ showHideWindow(tray, lang, mainWindow);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: lang.officialWebsite,
|
|
|
+ click: () => {
|
|
|
+ shell.openExternal("https://b3log.org/siyuan/");
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: lang.openSource,
|
|
|
+ click: () => {
|
|
|
+ shell.openExternal("https://github.com/siyuan-note/siyuan");
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: lang.resetWindow,
|
|
|
+ type: "checkbox",
|
|
|
+ click: v => {
|
|
|
+ resetWindowStateOnRestart = v.checked;
|
|
|
+ mainWindow.webContents.send("siyuan-save-close", true);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: lang.quit,
|
|
|
+ click: () => {
|
|
|
+ mainWindow.webContents.send("siyuan-save-close", true);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ];
|
|
|
|
|
|
- if ('win32' === process.platform) {
|
|
|
- // Windows 端支持窗口置顶 https://github.com/siyuan-note/siyuan/issues/6860
|
|
|
- trayMenuTemplate.splice(1, 0, {
|
|
|
- label: mainWindow.isAlwaysOnTop()
|
|
|
- ? lang.cancelWindowTop
|
|
|
- : lang.setWindowTop,
|
|
|
- click: () => {
|
|
|
- if (!mainWindow.isAlwaysOnTop()) {
|
|
|
- mainWindow.setAlwaysOnTop(true)
|
|
|
- } else {
|
|
|
- mainWindow.setAlwaysOnTop(false)
|
|
|
- }
|
|
|
- resetTrayMenu(tray, lang, mainWindow)
|
|
|
- },
|
|
|
- })
|
|
|
- }
|
|
|
- const contextMenu = Menu.buildFromTemplate(trayMenuTemplate)
|
|
|
- tray.setContextMenu(contextMenu)
|
|
|
- }
|
|
|
+ if ("win32" === process.platform) {
|
|
|
+ // Windows 端支持窗口置顶 https://github.com/siyuan-note/siyuan/issues/6860
|
|
|
+ trayMenuTemplate.splice(1, 0, {
|
|
|
+ label: mainWindow.isAlwaysOnTop()
|
|
|
+ ? lang.cancelWindowTop
|
|
|
+ : lang.setWindowTop,
|
|
|
+ click: () => {
|
|
|
+ if (!mainWindow.isAlwaysOnTop()) {
|
|
|
+ mainWindow.setAlwaysOnTop(true);
|
|
|
+ } else {
|
|
|
+ mainWindow.setAlwaysOnTop(false);
|
|
|
+ }
|
|
|
+ resetTrayMenu(tray, lang, mainWindow);
|
|
|
+ },
|
|
|
+ });
|
|
|
+ }
|
|
|
+ const contextMenu = Menu.buildFromTemplate(trayMenuTemplate);
|
|
|
+ tray.setContextMenu(contextMenu);
|
|
|
+ };
|
|
|
|
|
|
- const hideWindow = (wnd) => {
|
|
|
- // 通过 `Alt+M` 最小化后焦点回到先前的窗口 https://github.com/siyuan-note/siyuan/issues/7275
|
|
|
- wnd.minimize()
|
|
|
- wnd.hide()
|
|
|
- }
|
|
|
+ const hideWindow = (wnd) => {
|
|
|
+ // 通过 `Alt+M` 最小化后焦点回到先前的窗口 https://github.com/siyuan-note/siyuan/issues/7275
|
|
|
+ wnd.minimize();
|
|
|
+ wnd.hide();
|
|
|
+ };
|
|
|
|
|
|
- const showHideWindow = (tray, lang, mainWindow) => {
|
|
|
- if (!mainWindow.isVisible()) {
|
|
|
- if (mainWindow.isMinimized()) {
|
|
|
- mainWindow.restore()
|
|
|
- }
|
|
|
- mainWindow.show()
|
|
|
- } else {
|
|
|
- hideWindow(mainWindow)
|
|
|
- }
|
|
|
+ const showHideWindow = (tray, lang, mainWindow) => {
|
|
|
+ if (!mainWindow.isVisible()) {
|
|
|
+ if (mainWindow.isMinimized()) {
|
|
|
+ mainWindow.restore();
|
|
|
+ }
|
|
|
+ mainWindow.show();
|
|
|
+ } else {
|
|
|
+ hideWindow(mainWindow);
|
|
|
+ }
|
|
|
|
|
|
- resetTrayMenu(tray, lang, mainWindow)
|
|
|
- }
|
|
|
+ resetTrayMenu(tray, lang, mainWindow);
|
|
|
+ };
|
|
|
|
|
|
- ipcMain.on('siyuan-first-quit', () => {
|
|
|
- app.exit()
|
|
|
- })
|
|
|
- ipcMain.on('siyuan-show', (event, id) => {
|
|
|
- showWindow(BrowserWindow.fromId(id))
|
|
|
- })
|
|
|
- ipcMain.on('siyuan-config-tray', (event, data) => {
|
|
|
- workspaces.find(item => {
|
|
|
- if (item.id === data.id) {
|
|
|
- hideWindow(item.browserWindow)
|
|
|
- if ('win32' === process.platform || 'linux' === process.platform) {
|
|
|
- resetTrayMenu(item.tray, data.languages, item.browserWindow)
|
|
|
- }
|
|
|
- return true
|
|
|
- }
|
|
|
- })
|
|
|
- })
|
|
|
- ipcMain.on('siyuan-closetab', (event, data) => {
|
|
|
- BrowserWindow.getAllWindows().forEach(item => {
|
|
|
- item.webContents.send('siyuan-closetab', data)
|
|
|
- })
|
|
|
- })
|
|
|
- ipcMain.on('siyuan-export-pdf', (event, data) => {
|
|
|
- BrowserWindow.fromId(data.id).webContents.send('siyuan-export-pdf', data)
|
|
|
- })
|
|
|
- ipcMain.on('siyuan-export-close', (event, id) => {
|
|
|
- BrowserWindow.fromId(id).webContents.send('siyuan-export-close', id)
|
|
|
- })
|
|
|
- ipcMain.on('siyuan-export-prevent', (event, id) => {
|
|
|
- BrowserWindow.fromId(id).webContents.on('will-navigate', (event, url) => {
|
|
|
- const currentURL = new URL(event.sender.getURL())
|
|
|
- if (url.startsWith(getServer(currentURL.port))) {
|
|
|
- return
|
|
|
- }
|
|
|
+ ipcMain.on("siyuan-first-quit", () => {
|
|
|
+ app.exit();
|
|
|
+ });
|
|
|
+ ipcMain.on("siyuan-show", (event, id) => {
|
|
|
+ showWindow(BrowserWindow.fromId(id));
|
|
|
+ });
|
|
|
+ ipcMain.on("siyuan-config-tray", (event, data) => {
|
|
|
+ workspaces.find(item => {
|
|
|
+ if (item.id === data.id) {
|
|
|
+ hideWindow(item.browserWindow);
|
|
|
+ if ("win32" === process.platform || "linux" === process.platform) {
|
|
|
+ resetTrayMenu(item.tray, data.languages, item.browserWindow);
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ ipcMain.on("siyuan-closetab", (event, data) => {
|
|
|
+ BrowserWindow.getAllWindows().forEach(item => {
|
|
|
+ item.webContents.send("siyuan-closetab", data);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ ipcMain.on("siyuan-export-pdf", (event, data) => {
|
|
|
+ BrowserWindow.fromId(data.id).webContents.send("siyuan-export-pdf", data);
|
|
|
+ });
|
|
|
+ ipcMain.on("siyuan-export-close", (event, id) => {
|
|
|
+ BrowserWindow.fromId(id).webContents.send("siyuan-export-close", id);
|
|
|
+ });
|
|
|
+ ipcMain.on("siyuan-export-prevent", (event, id) => {
|
|
|
+ BrowserWindow.fromId(id).webContents.on("will-navigate", (event, url) => {
|
|
|
+ const currentURL = new URL(event.sender.getURL());
|
|
|
+ if (url.startsWith(getServer(currentURL.port))) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- event.preventDefault()
|
|
|
- shell.openExternal(url)
|
|
|
- })
|
|
|
- })
|
|
|
- ipcMain.on('siyuan-quit', (event, id) => {
|
|
|
- const mainWindow = BrowserWindow.fromId(id)
|
|
|
- let tray
|
|
|
- workspaces.find((item, index) => {
|
|
|
- if (item.id === id) {
|
|
|
- if (workspaces.length > 1) {
|
|
|
- mainWindow.destroy()
|
|
|
+ event.preventDefault();
|
|
|
+ shell.openExternal(url);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ ipcMain.on("siyuan-quit", (event, id) => {
|
|
|
+ const mainWindow = BrowserWindow.fromId(id);
|
|
|
+ let tray;
|
|
|
+ workspaces.find((item, index) => {
|
|
|
+ if (item.id === id) {
|
|
|
+ if (workspaces.length > 1) {
|
|
|
+ mainWindow.destroy();
|
|
|
+ }
|
|
|
+ tray = item.tray;
|
|
|
+ workspaces.splice(index, 1);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ if (tray && "win32" === process.platform) {
|
|
|
+ tray.destroy();
|
|
|
}
|
|
|
- tray = item.tray
|
|
|
- workspaces.splice(index, 1)
|
|
|
- return true
|
|
|
- }
|
|
|
- })
|
|
|
- if (tray && 'win32' === process.platform) {
|
|
|
- tray.destroy()
|
|
|
- }
|
|
|
- if (workspaces.length === 0) {
|
|
|
- try {
|
|
|
- if (resetWindowStateOnRestart) {
|
|
|
- fs.writeFileSync(windowStatePath, '{}')
|
|
|
- } else {
|
|
|
- const bounds = mainWindow.getBounds()
|
|
|
- fs.writeFileSync(windowStatePath, JSON.stringify({
|
|
|
- isMaximized: mainWindow.isMaximized(),
|
|
|
- fullscreen: mainWindow.isFullScreen(),
|
|
|
- isDevToolsOpened: mainWindow.webContents.isDevToolsOpened(),
|
|
|
- x: bounds.x,
|
|
|
- y: bounds.y,
|
|
|
- width: bounds.width,
|
|
|
- height: bounds.height,
|
|
|
- }))
|
|
|
+ if (workspaces.length === 0) {
|
|
|
+ try {
|
|
|
+ if (resetWindowStateOnRestart) {
|
|
|
+ fs.writeFileSync(windowStatePath, "{}");
|
|
|
+ } else {
|
|
|
+ const bounds = mainWindow.getBounds();
|
|
|
+ fs.writeFileSync(windowStatePath, JSON.stringify({
|
|
|
+ isMaximized: mainWindow.isMaximized(),
|
|
|
+ fullscreen: mainWindow.isFullScreen(),
|
|
|
+ isDevToolsOpened: mainWindow.webContents.isDevToolsOpened(),
|
|
|
+ x: bounds.x,
|
|
|
+ y: bounds.y,
|
|
|
+ width: bounds.width,
|
|
|
+ height: bounds.height,
|
|
|
+ }));
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ writeLog(e);
|
|
|
+ }
|
|
|
+ app.exit();
|
|
|
+ globalShortcut.unregisterAll();
|
|
|
+ writeLog("exited ui");
|
|
|
}
|
|
|
- } catch (e) {
|
|
|
- writeLog(e)
|
|
|
- }
|
|
|
- app.exit()
|
|
|
- globalShortcut.unregisterAll()
|
|
|
- writeLog('exited ui')
|
|
|
- }
|
|
|
- })
|
|
|
- ipcMain.on('siyuan-openwindow', (event, data) => {
|
|
|
- const win = new BrowserWindow({
|
|
|
- show: true,
|
|
|
- backgroundColor: '#FFF',
|
|
|
- trafficLightPosition: {x: 8, y: 13},
|
|
|
- width: screen.getPrimaryDisplay().size.width * 0.7,
|
|
|
- height: screen.getPrimaryDisplay().size.height * 0.9,
|
|
|
- minWidth: 493,
|
|
|
- minHeight: 376,
|
|
|
- fullscreenable: true,
|
|
|
- frame: 'darwin' === process.platform,
|
|
|
- icon: path.join(appDir, 'stage', 'icon-large.png'),
|
|
|
- titleBarStyle: 'hidden',
|
|
|
- webPreferences: {
|
|
|
- contextIsolation: false,
|
|
|
- nodeIntegration: true,
|
|
|
- webviewTag: true,
|
|
|
- webSecurity: false,
|
|
|
- },
|
|
|
- })
|
|
|
- win.loadURL(data)
|
|
|
- require('@electron/remote/main').enable(win.webContents)
|
|
|
- })
|
|
|
- ipcMain.on('siyuan-open-workspace', (event, data) => {
|
|
|
- const foundWorkspace = workspaces.find((item, index) => {
|
|
|
- if (item.workspaceDir === data.workspace) {
|
|
|
- showWindow(item.browserWindow)
|
|
|
- return true
|
|
|
- }
|
|
|
- })
|
|
|
- if (!foundWorkspace) {
|
|
|
- initKernel(data.workspace, '', data.lang).then((isSucc) => {
|
|
|
- if (isSucc) {
|
|
|
- boot()
|
|
|
+ });
|
|
|
+ ipcMain.on("siyuan-openwindow", (event, data) => {
|
|
|
+ const win = new BrowserWindow({
|
|
|
+ show: true,
|
|
|
+ backgroundColor: "#FFF",
|
|
|
+ trafficLightPosition: {x: 8, y: 13},
|
|
|
+ width: screen.getPrimaryDisplay().size.width * 0.7,
|
|
|
+ height: screen.getPrimaryDisplay().size.height * 0.9,
|
|
|
+ minWidth: 493,
|
|
|
+ minHeight: 376,
|
|
|
+ fullscreenable: true,
|
|
|
+ frame: "darwin" === process.platform,
|
|
|
+ icon: path.join(appDir, "stage", "icon-large.png"),
|
|
|
+ titleBarStyle: "hidden",
|
|
|
+ webPreferences: {
|
|
|
+ contextIsolation: false,
|
|
|
+ nodeIntegration: true,
|
|
|
+ webviewTag: true,
|
|
|
+ webSecurity: false,
|
|
|
+ },
|
|
|
+ });
|
|
|
+ win.loadURL(data);
|
|
|
+ require("@electron/remote/main").enable(win.webContents);
|
|
|
+ });
|
|
|
+ ipcMain.on("siyuan-open-workspace", (event, data) => {
|
|
|
+ const foundWorkspace = workspaces.find((item) => {
|
|
|
+ if (item.workspaceDir === data.workspace) {
|
|
|
+ showWindow(item.browserWindow);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ if (!foundWorkspace) {
|
|
|
+ initKernel(data.workspace, "", data.lang).then((isSucc) => {
|
|
|
+ if (isSucc) {
|
|
|
+ boot();
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
- })
|
|
|
- }
|
|
|
- })
|
|
|
- ipcMain.on('siyuan-init', async (event, data) => {
|
|
|
- const exitWS = workspaces.find(item => {
|
|
|
- if (data.id === item.id && item.workspaceDir) {
|
|
|
- return true
|
|
|
- }
|
|
|
- })
|
|
|
- if (exitWS) {
|
|
|
- return
|
|
|
- }
|
|
|
- let tray
|
|
|
- if ('win32' === process.platform || 'linux' === process.platform) {
|
|
|
- // 系统托盘
|
|
|
- tray = new Tray(path.join(appDir, 'stage', 'icon-large.png'))
|
|
|
- tray.setToolTip(`${path.basename(data.workspaceDir)} - SiYuan v${appVer}`)
|
|
|
- const mainWindow = BrowserWindow.fromId(data.id)
|
|
|
- resetTrayMenu(tray, data.languages, mainWindow)
|
|
|
- tray.on('click', () => {
|
|
|
- showHideWindow(tray, data.languages, mainWindow)
|
|
|
- })
|
|
|
- }
|
|
|
- workspaces.find(item => {
|
|
|
- if (data.id === item.id) {
|
|
|
- item.workspaceDir = data.workspaceDir
|
|
|
- item.tray = tray
|
|
|
- return true
|
|
|
- }
|
|
|
- })
|
|
|
- await fetch(getServer(data.port) + '/api/system/uiproc?pid=' + process.pid,
|
|
|
- {method: 'POST'})
|
|
|
- })
|
|
|
- ipcMain.on('siyuan-hotkey', (event, data) => {
|
|
|
- globalShortcut.unregisterAll()
|
|
|
- if (!data.hotkey) {
|
|
|
- return
|
|
|
- }
|
|
|
- globalShortcut.register(data.hotkey, () => {
|
|
|
- workspaces.forEach(item => {
|
|
|
- const mainWindow = item.browserWindow
|
|
|
- if (mainWindow.isMinimized()) {
|
|
|
- mainWindow.restore()
|
|
|
- if (!mainWindow.isVisible()) {
|
|
|
- mainWindow.show()
|
|
|
- }
|
|
|
- } else {
|
|
|
- if (mainWindow.isVisible()) {
|
|
|
- if (1 === workspaces.length) { // 改进 `Alt+M` 激活窗口 https://github.com/siyuan-note/siyuan/issues/7258
|
|
|
- if (!mainWindow.isFocused()) {
|
|
|
- mainWindow.show()
|
|
|
- } else {
|
|
|
- hideWindow(mainWindow)
|
|
|
- }
|
|
|
- } else {
|
|
|
- hideWindow(mainWindow)
|
|
|
+ });
|
|
|
+ ipcMain.on("siyuan-init", async (event, data) => {
|
|
|
+ const exitWS = workspaces.find(item => {
|
|
|
+ if (data.id === item.id && item.workspaceDir) {
|
|
|
+ return true;
|
|
|
}
|
|
|
- } else {
|
|
|
- mainWindow.show()
|
|
|
- }
|
|
|
+ });
|
|
|
+ if (exitWS) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ let tray;
|
|
|
+ if ("win32" === process.platform || "linux" === process.platform) {
|
|
|
+ // 系统托盘
|
|
|
+ tray = new Tray(path.join(appDir, "stage", "icon-large.png"));
|
|
|
+ tray.setToolTip(`${path.basename(data.workspaceDir)} - SiYuan v${appVer}`);
|
|
|
+ const mainWindow = BrowserWindow.fromId(data.id);
|
|
|
+ resetTrayMenu(tray, data.languages, mainWindow);
|
|
|
+ tray.on("click", () => {
|
|
|
+ showHideWindow(tray, data.languages, mainWindow);
|
|
|
+ });
|
|
|
}
|
|
|
+ workspaces.find(item => {
|
|
|
+ if (data.id === item.id) {
|
|
|
+ item.workspaceDir = data.workspaceDir;
|
|
|
+ item.tray = tray;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ await fetch(getServer(data.port) + "/api/system/uiproc?pid=" + process.pid,
|
|
|
+ {method: "POST"});
|
|
|
+ });
|
|
|
+ ipcMain.on("siyuan-hotkey", (event, data) => {
|
|
|
+ globalShortcut.unregisterAll();
|
|
|
+ if (!data.hotkey) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ globalShortcut.register(data.hotkey, () => {
|
|
|
+ workspaces.forEach(item => {
|
|
|
+ const mainWindow = item.browserWindow;
|
|
|
+ if (mainWindow.isMinimized()) {
|
|
|
+ mainWindow.restore();
|
|
|
+ if (!mainWindow.isVisible()) {
|
|
|
+ mainWindow.show();
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (mainWindow.isVisible()) {
|
|
|
+ if (1 === workspaces.length) { // 改进 `Alt+M` 激活窗口 https://github.com/siyuan-note/siyuan/issues/7258
|
|
|
+ if (!mainWindow.isFocused()) {
|
|
|
+ mainWindow.show();
|
|
|
+ } else {
|
|
|
+ hideWindow(mainWindow);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ hideWindow(mainWindow);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ mainWindow.show();
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- if ('win32' === process.platform || 'linux' === process.platform) {
|
|
|
- resetTrayMenu(item.tray, data.languages, mainWindow)
|
|
|
+ if ("win32" === process.platform || "linux" === process.platform) {
|
|
|
+ resetTrayMenu(item.tray, data.languages, mainWindow);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ });
|
|
|
+ ipcMain.on("siyuan-lock-screen", () => {
|
|
|
+ BrowserWindow.getAllWindows().forEach(item => {
|
|
|
+ item.webContents.send("siyuan-lock-screen");
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ if (firstOpen) {
|
|
|
+ const firstOpenWindow = new BrowserWindow({
|
|
|
+ width: screen.getPrimaryDisplay().size.width * 0.6,
|
|
|
+ height: screen.getPrimaryDisplay().workAreaSize.height * 0.8,
|
|
|
+ frame: false,
|
|
|
+ icon: path.join(appDir, "stage", "icon-large.png"),
|
|
|
+ transparent: "linux" !== process.platform,
|
|
|
+ webPreferences: {
|
|
|
+ nodeIntegration: true,
|
|
|
+ webviewTag: true,
|
|
|
+ webSecurity: false,
|
|
|
+ contextIsolation: false,
|
|
|
+ },
|
|
|
+ });
|
|
|
+ require("@electron/remote/main").enable(firstOpenWindow.webContents);
|
|
|
+ let initHTMLPath = path.join(appDir, "app", "electron", "init.html");
|
|
|
+ if (isDevEnv) {
|
|
|
+ initHTMLPath = path.join(appDir, "electron", "init.html");
|
|
|
}
|
|
|
- })
|
|
|
- })
|
|
|
- })
|
|
|
- ipcMain.on('siyuan-lock-screen', () => {
|
|
|
- BrowserWindow.getAllWindows().forEach(item => {
|
|
|
- item.webContents.send('siyuan-lock-screen')
|
|
|
- })
|
|
|
- })
|
|
|
|
|
|
- if (firstOpen) {
|
|
|
- const firstOpenWindow = new BrowserWindow({
|
|
|
- width: screen.getPrimaryDisplay().size.width * 0.6,
|
|
|
- height: screen.getPrimaryDisplay().workAreaSize.height * 0.8,
|
|
|
- frame: false,
|
|
|
- icon: path.join(appDir, 'stage', 'icon-large.png'),
|
|
|
- transparent: 'linux' !== process.platform,
|
|
|
- webPreferences: {
|
|
|
- nodeIntegration: true,
|
|
|
- webviewTag: true,
|
|
|
- webSecurity: false,
|
|
|
- contextIsolation: false,
|
|
|
- },
|
|
|
- })
|
|
|
- require('@electron/remote/main').enable(firstOpenWindow.webContents)
|
|
|
- let initHTMLPath = path.join(appDir, 'app', 'electron', 'init.html')
|
|
|
- if (isDevEnv) {
|
|
|
- initHTMLPath = path.join(appDir, 'electron', 'init.html')
|
|
|
- }
|
|
|
+ // 改进桌面端初始化时使用的外观语言 https://github.com/siyuan-note/siyuan/issues/6803
|
|
|
+ let languages = app.getPreferredSystemLanguages();
|
|
|
+ let language = languages && 0 < languages.length && "zh-Hans-CN" ===
|
|
|
+ languages[0] ? "zh_CN" : "en_US";
|
|
|
+ firstOpenWindow.loadFile(
|
|
|
+ initHTMLPath, {
|
|
|
+ query: {
|
|
|
+ lang: language,
|
|
|
+ home: app.getPath("home"),
|
|
|
+ v: appVer,
|
|
|
+ icon: path.join(appDir, "stage", "icon-large.png"),
|
|
|
+ },
|
|
|
+ });
|
|
|
+ firstOpenWindow.show();
|
|
|
+ // 初始化启动
|
|
|
+ ipcMain.on("siyuan-first-init", (event, data) => {
|
|
|
+ initKernel(data.workspace, "", data.lang).then((isSucc) => {
|
|
|
+ if (isSucc) {
|
|
|
+ boot();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ firstOpenWindow.destroy();
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ const getArg = (name) => {
|
|
|
+ for (let i = 0; i < process.argv.length; i++) {
|
|
|
+ if (process.argv[i].startsWith(name)) {
|
|
|
+ return process.argv[i].split("=")[1];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
- // 改进桌面端初始化时使用的外观语言 https://github.com/siyuan-note/siyuan/issues/6803
|
|
|
- let languages = app.getPreferredSystemLanguages()
|
|
|
- let language = languages && 0 < languages.length && 'zh-Hans-CN' ===
|
|
|
- languages[0] ? 'zh_CN' : 'en_US'
|
|
|
- firstOpenWindow.loadFile(
|
|
|
- initHTMLPath, {
|
|
|
- query: {
|
|
|
- lang: language,
|
|
|
- home: app.getPath('home'),
|
|
|
- v: appVer,
|
|
|
- icon: path.join(appDir, 'stage', 'icon-large.png'),
|
|
|
- },
|
|
|
- })
|
|
|
- firstOpenWindow.show()
|
|
|
- // 初始化启动
|
|
|
- ipcMain.on('siyuan-first-init', (event, data) => {
|
|
|
- initKernel(data.workspace, '', data.lang).then((isSucc) => {
|
|
|
- if (isSucc) {
|
|
|
- boot()
|
|
|
+ const workspace = getArg("--workspace");
|
|
|
+ if (workspace) {
|
|
|
+ writeLog("got arg [--workspace=" + workspace + "]");
|
|
|
}
|
|
|
- })
|
|
|
- firstOpenWindow.destroy()
|
|
|
- })
|
|
|
- } else {
|
|
|
- const getArg = (name) => {
|
|
|
- for (let i = 0; i < process.argv.length; i++) {
|
|
|
- if (process.argv[i].startsWith(name)) {
|
|
|
- return process.argv[i].split('=')[1]
|
|
|
+ const port = getArg("--port");
|
|
|
+ if (port) {
|
|
|
+ writeLog("got arg [--port=" + port + "]");
|
|
|
}
|
|
|
- }
|
|
|
+ initKernel(workspace, port, "").then((isSucc) => {
|
|
|
+ if (isSucc) {
|
|
|
+ boot();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+app.on("open-url", (event, url) => { // for macOS
|
|
|
+ if (url.startsWith("siyuan://")) {
|
|
|
+ workspaces.forEach(item => {
|
|
|
+ if (item.browserWindow && !item.browserWindow.isDestroyed()) {
|
|
|
+ item.browserWindow.webContents.send("siyuan-openurl", url);
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
+});
|
|
|
|
|
|
- const workspace = getArg('--workspace')
|
|
|
+app.on("second-instance", (event, argv) => {
|
|
|
+ writeLog("second-instance [" + argv + "]");
|
|
|
+ let workspace = argv.find((arg) => arg.startsWith("--workspace="));
|
|
|
if (workspace) {
|
|
|
- writeLog('got arg [--workspace=' + workspace + ']')
|
|
|
+ workspace = workspace.split("=")[1];
|
|
|
+ writeLog("got second-instance arg [--workspace=" + workspace + "]");
|
|
|
}
|
|
|
- const port = getArg('--port')
|
|
|
+ let port = argv.find((arg) => arg.startsWith("--port="));
|
|
|
if (port) {
|
|
|
- writeLog('got arg [--port=' + port + ']')
|
|
|
+ port = port.split("=")[1];
|
|
|
+ writeLog("got second-instance arg [--port=" + port + "]");
|
|
|
+ } else {
|
|
|
+ port = 0;
|
|
|
+ }
|
|
|
+ const foundWorkspace = workspaces.find(item => {
|
|
|
+ if (item.browserWindow && !item.browserWindow.isDestroyed()) {
|
|
|
+ if (workspace && workspace === item.workspaceDir) {
|
|
|
+ showWindow(item.browserWindow);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ if (foundWorkspace) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (workspace) {
|
|
|
+ initKernel(workspace, port, "").then((isSucc) => {
|
|
|
+ if (isSucc) {
|
|
|
+ boot();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return;
|
|
|
}
|
|
|
- initKernel(workspace, port, '').then((isSucc) => {
|
|
|
- if (isSucc) {
|
|
|
- boot()
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
-})
|
|
|
|
|
|
-app.on('open-url', (event, url) => { // for macOS
|
|
|
- if (url.startsWith('siyuan://')) {
|
|
|
+ const siyuanURL = argv.find((arg) => arg.startsWith("siyuan://"));
|
|
|
workspaces.forEach(item => {
|
|
|
- if (item.browserWindow && !item.browserWindow.isDestroyed()) {
|
|
|
- item.browserWindow.webContents.send('siyuan-openurl', url)
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
-})
|
|
|
+ if (item.browserWindow && !item.browserWindow.isDestroyed() && siyuanURL) {
|
|
|
+ item.browserWindow.webContents.send("siyuan-openurl", siyuanURL);
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
-app.on('second-instance', (event, argv) => {
|
|
|
- writeLog('second-instance [' + argv + ']')
|
|
|
- let workspace = argv.find((arg) => arg.startsWith('--workspace='))
|
|
|
- if (workspace) {
|
|
|
- workspace = workspace.split('=')[1]
|
|
|
- writeLog('got second-instance arg [--workspace=' + workspace + ']')
|
|
|
- }
|
|
|
- let port = argv.find((arg) => arg.startsWith('--port='))
|
|
|
- if (port) {
|
|
|
- port = port.split('=')[1]
|
|
|
- writeLog('got second-instance arg [--port=' + port + ']')
|
|
|
- } else {
|
|
|
- port = 0
|
|
|
- }
|
|
|
- const foundWorkspace = workspaces.find(item => {
|
|
|
- if (item.browserWindow && !item.browserWindow.isDestroyed()) {
|
|
|
- if (workspace && workspace === item.workspaceDir) {
|
|
|
- showWindow(item.browserWindow)
|
|
|
- return true
|
|
|
- }
|
|
|
+ if (!siyuanURL && 0 < workspaces.length) {
|
|
|
+ showWindow(workspaces[0].browserWindow);
|
|
|
}
|
|
|
- })
|
|
|
- if (foundWorkspace) {
|
|
|
- return
|
|
|
- }
|
|
|
- if (workspace) {
|
|
|
- initKernel(workspace, port, '').then((isSucc) => {
|
|
|
- if (isSucc) {
|
|
|
- boot()
|
|
|
- }
|
|
|
- })
|
|
|
- return
|
|
|
- }
|
|
|
+});
|
|
|
|
|
|
- const siyuanURL = argv.find((arg) => arg.startsWith('siyuan://'))
|
|
|
- workspaces.forEach(item => {
|
|
|
- if (item.browserWindow && !item.browserWindow.isDestroyed() && siyuanURL) {
|
|
|
- item.browserWindow.webContents.send('siyuan-openurl', siyuanURL)
|
|
|
+app.on("activate", () => {
|
|
|
+ if (workspaces.length > 0) {
|
|
|
+ const mainWindow = workspaces[0].browserWindow;
|
|
|
+ if (mainWindow && !mainWindow.isDestroyed()) {
|
|
|
+ mainWindow.show();
|
|
|
+ }
|
|
|
}
|
|
|
- })
|
|
|
-
|
|
|
- if (!siyuanURL && 0 < workspaces.length) {
|
|
|
- showWindow(workspaces[0].browserWindow)
|
|
|
- }
|
|
|
-})
|
|
|
-
|
|
|
-app.on('activate', () => {
|
|
|
- if (workspaces.length > 0) {
|
|
|
- const mainWindow = workspaces[0].browserWindow
|
|
|
- if (mainWindow && !mainWindow.isDestroyed()) {
|
|
|
- mainWindow.show()
|
|
|
+ if (BrowserWindow.getAllWindows().length === 0) {
|
|
|
+ boot();
|
|
|
}
|
|
|
- }
|
|
|
- if (BrowserWindow.getAllWindows().length === 0) {
|
|
|
- boot()
|
|
|
- }
|
|
|
-})
|
|
|
+});
|
|
|
|
|
|
// 在编辑器内打开链接的处理,比如 iframe 上的打开链接。
|
|
|
-app.on('web-contents-created', (webContentsCreatedEvent, contents) => {
|
|
|
- contents.setWindowOpenHandler((details) => {
|
|
|
- shell.openExternal(details.url)
|
|
|
- return {action: 'deny'}
|
|
|
- })
|
|
|
-})
|
|
|
+app.on("web-contents-created", (webContentsCreatedEvent, contents) => {
|
|
|
+ contents.setWindowOpenHandler((details) => {
|
|
|
+ shell.openExternal(details.url);
|
|
|
+ return {action: "deny"};
|
|
|
+ });
|
|
|
+});
|
|
|
|
|
|
-app.on('before-quit', (event) => {
|
|
|
- workspaces.forEach(item => {
|
|
|
- if (item.browserWindow && !item.browserWindow.isDestroyed()) {
|
|
|
- event.preventDefault()
|
|
|
- item.browserWindow.webContents.send('siyuan-save-close', true)
|
|
|
- }
|
|
|
- })
|
|
|
-})
|
|
|
+app.on("before-quit", (event) => {
|
|
|
+ workspaces.forEach(item => {
|
|
|
+ if (item.browserWindow && !item.browserWindow.isDestroyed()) {
|
|
|
+ event.preventDefault();
|
|
|
+ item.browserWindow.webContents.send("siyuan-save-close", true);
|
|
|
+ }
|
|
|
+ });
|
|
|
+});
|
|
|
|
|
|
-const {powerMonitor} = require('electron')
|
|
|
+const {powerMonitor} = require("electron");
|
|
|
|
|
|
-powerMonitor.on('suspend', () => {
|
|
|
- writeLog('system suspend')
|
|
|
-})
|
|
|
+powerMonitor.on("suspend", () => {
|
|
|
+ writeLog("system suspend");
|
|
|
+});
|
|
|
|
|
|
-powerMonitor.on('resume', async () => {
|
|
|
- // 桌面端系统休眠唤醒后判断网络连通性后再执行数据同步 https://github.com/siyuan-note/siyuan/issues/6687
|
|
|
- writeLog('system resume')
|
|
|
- const isOnline = async () => {
|
|
|
- try {
|
|
|
- const result = await fetch('https://www.baidu.com', {timeout: 1000})
|
|
|
- return 200 === result.status
|
|
|
- } catch (e) {
|
|
|
- try {
|
|
|
- const result = await fetch('https://icanhazip.com', {timeout: 1000})
|
|
|
- return 200 === result.status
|
|
|
- } catch (e) {
|
|
|
- return false
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- let online = false
|
|
|
- for (let i = 0; i < 7; i++) {
|
|
|
- if (await isOnline()) {
|
|
|
- online = true
|
|
|
- break
|
|
|
- }
|
|
|
+powerMonitor.on("resume", async () => {
|
|
|
+ // 桌面端系统休眠唤醒后判断网络连通性后再执行数据同步 https://github.com/siyuan-note/siyuan/issues/6687
|
|
|
+ writeLog("system resume");
|
|
|
+ const isOnline = async () => {
|
|
|
+ try {
|
|
|
+ const result = await fetch("https://www.baidu.com", {timeout: 1000});
|
|
|
+ return 200 === result.status;
|
|
|
+ } catch (e) {
|
|
|
+ try {
|
|
|
+ const result = await fetch("https://icanhazip.com", {timeout: 1000});
|
|
|
+ return 200 === result.status;
|
|
|
+ } catch (e) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+ let online = false;
|
|
|
+ for (let i = 0; i < 7; i++) {
|
|
|
+ if (await isOnline()) {
|
|
|
+ online = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
|
|
|
- writeLog('network is offline')
|
|
|
- await sleep(1000)
|
|
|
- }
|
|
|
+ writeLog("network is offline");
|
|
|
+ await sleep(1000);
|
|
|
+ }
|
|
|
|
|
|
- if (!online) {
|
|
|
- writeLog('network is offline, do not sync after system resume')
|
|
|
- return
|
|
|
- }
|
|
|
+ if (!online) {
|
|
|
+ writeLog("network is offline, do not sync after system resume");
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- workspaces.forEach(item => {
|
|
|
- const currentURL = new URL(item.browserWindow.getURL())
|
|
|
- const server = getServer(currentURL.port)
|
|
|
- writeLog(
|
|
|
- 'sync after system resume [' + server + '/api/sync/performSync' + ']')
|
|
|
- fetch(server + '/api/sync/performSync', {method: 'POST'})
|
|
|
- })
|
|
|
-})
|
|
|
+ workspaces.forEach(item => {
|
|
|
+ const currentURL = new URL(item.browserWindow.getURL());
|
|
|
+ const server = getServer(currentURL.port);
|
|
|
+ writeLog(
|
|
|
+ "sync after system resume [" + server + "/api/sync/performSync" + "]");
|
|
|
+ fetch(server + "/api/sync/performSync", {method: "POST"});
|
|
|
+ });
|
|
|
+});
|
|
|
|
|
|
-powerMonitor.on('shutdown', () => {
|
|
|
- writeLog('system shutdown')
|
|
|
- workspaces.forEach(item => {
|
|
|
- const currentURL = new URL(item.browserWindow.getURL())
|
|
|
- fetch(getServer(currentURL.port) + '/api/system/exit', {method: 'POST'})
|
|
|
- })
|
|
|
-})
|
|
|
+powerMonitor.on("shutdown", () => {
|
|
|
+ writeLog("system shutdown");
|
|
|
+ workspaces.forEach(item => {
|
|
|
+ const currentURL = new URL(item.browserWindow.getURL());
|
|
|
+ fetch(getServer(currentURL.port) + "/api/system/exit", {method: "POST"});
|
|
|
+ });
|
|
|
+});
|