diff --git a/app/electron/init.html b/app/electron/init.html
index e28631878..e034fbb09 100644
--- a/app/electron/init.html
+++ b/app/electron/init.html
@@ -358,8 +358,10 @@
if (!fs.existsSync(initPath)) {
fs.mkdirSync(initPath, {mode: 0o755, recursive: true})
}
- const lang = document.querySelector('.lang').value
- ipcRenderer.send('siyuan-first-init', `${initPath}-${lang}`)
+ ipcRenderer.send('siyuan-first-init', {
+ workspace: initPath,
+ lang: document.querySelector('.lang').value
+ })
}
})
})
diff --git a/app/electron/main.js b/app/electron/main.js
index 11a7f1c83..98cec7dbf 100644
--- a/app/electron/main.js
+++ b/app/electron/main.js
@@ -27,7 +27,7 @@ const {
} = require('electron')
const path = require('path')
const fs = require('fs')
-const net = require("net");
+const net = require('net')
const fetch = require('electron-fetch').default
process.noAsar = true
const appDir = path.dirname(app.getAppPath())
@@ -35,14 +35,13 @@ 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 tray // 托盘必须使用全局变量,以防止被垃圾回收 https://www.electronjs.org/docs/faq#my-apps-windowtray-disappeared-after-a-few-minutes
-let mainWindow // 从托盘处激活报错 https://github.com/siyuan-note/siyuan/issues/769
let firstOpenWindow, bootWindow
-let closeButtonBehavior = 0
let siyuanOpenURL
let firstOpen = false
let resetWindowStateOnRestart = false
-const localhost = "127.0.0.1"
+let workspaces = []
+const localhost = '127.0.0.1'
+let kernelPort = 6806
require('@electron/remote/main').initialize()
if (!app.requestSingleInstanceLock()) {
@@ -50,6 +49,28 @@ if (!app.requestSingleInstanceLock()) {
return
}
+try {
+ 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()
+}
+
+const getServer = () => {
+ return 'http://' + localhost + ':' + kernelPort
+}
+
+const sleep = (ms) => {
+ return new Promise(resolve => setTimeout(resolve, ms))
+}
+
const showErrorWindow = (title, content) => {
let errorHTMLPath = path.join(appDir, 'app', 'electron', 'error.html')
if (isDevEnv) {
@@ -81,18 +102,6 @@ const showErrorWindow = (title, content) => {
errWindow.show()
}
-try {
- 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()
-}
-
const writeLog = (out) => {
console.log(out)
const logFile = path.join(confDir, 'app.log')
@@ -173,7 +182,7 @@ const boot = () => {
}
// 创建主窗体
- mainWindow = new BrowserWindow({
+ const currentWindow = new BrowserWindow({
show: false,
backgroundColor: '#FFF', // 桌面端主窗体背景色设置为 `#FFF` Fix https://github.com/siyuan-note/siyuan/issues/4544
width: windowState.width,
@@ -196,15 +205,14 @@ const boot = () => {
titleBarStyle: 'hidden',
icon: path.join(appDir, 'stage', 'icon-large.png'),
})
-
- require('@electron/remote/main').enable(mainWindow.webContents)
- mainWindow.webContents.userAgent = 'SiYuan/' + appVer +
+ require('@electron/remote/main').enable(currentWindow.webContents)
+ currentWindow.webContents.userAgent = 'SiYuan/' + appVer +
' https://b3log.org/siyuan Electron'
- mainWindow.webContents.session.setSpellCheckerLanguages(['en-US'])
+ currentWindow.webContents.session.setSpellCheckerLanguages(['en-US'])
// 发起互联网服务请求时绕过安全策略 https://github.com/siyuan-note/siyuan/issues/5516
- mainWindow.webContents.session.webRequest.onBeforeSendHeaders(
+ currentWindow.webContents.session.webRequest.onBeforeSendHeaders(
(details, cb) => {
if (-1 < details.url.indexOf('bili')) {
// B 站不移除 Referer https://github.com/siyuan-note/siyuan/issues/94
@@ -219,50 +227,51 @@ const boot = () => {
}
cb({requestHeaders: details.requestHeaders})
})
- mainWindow.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]
+ 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})
- })
+ cb({responseHeaders: details.responseHeaders})
+ })
- mainWindow.webContents.on('did-finish-load', () => {
+ currentWindow.webContents.on('did-finish-load', () => {
if ('win32' === process.platform || 'linux' === process.platform) {
siyuanOpenURL = process.argv.find((arg) => arg.startsWith('siyuan://'))
}
if (siyuanOpenURL) {
- if (mainWindow.isMinimized()) {
- mainWindow.restore()
+ if (currentWindow.isMinimized()) {
+ currentWindow.restore()
}
- if (!mainWindow.isVisible()) {
- mainWindow.show()
+ if (!currentWindow.isVisible()) {
+ currentWindow.show()
}
- mainWindow.focus()
+ currentWindow.focus()
setTimeout(() => { // 等待界面js执行完毕
writeLog(siyuanOpenURL)
- mainWindow.webContents.send('siyuan-openurl', siyuanOpenURL)
+ currentWindow.webContents.send('siyuan-openurl', siyuanOpenURL)
siyuanOpenURL = null
}, 2000)
}
})
if (windowState.isDevToolsOpened) {
- mainWindow.webContents.openDevTools({mode: 'bottom'})
+ currentWindow.webContents.openDevTools({mode: 'bottom'})
}
// 主界面事件监听
- mainWindow.once('ready-to-show', () => {
- mainWindow.show()
+ currentWindow.once('ready-to-show', () => {
+ currentWindow.show()
if (windowState.isMaximized) {
- mainWindow.maximize()
+ currentWindow.maximize()
} else {
- mainWindow.unmaximize()
+ currentWindow.unmaximize()
}
if (bootWindow && !bootWindow.isDestroyed()) {
bootWindow.destroy()
@@ -270,7 +279,7 @@ const boot = () => {
})
// 加载主界面
- mainWindow.loadURL(getServer() + '/stage/build/app/index.html?v=' +
+ currentWindow.loadURL(getServer() + '/stage/build/app/index.html?v=' +
new Date().getTime())
// 菜单
@@ -333,7 +342,7 @@ const boot = () => {
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
// 当前页面链接使用浏览器打开
- mainWindow.webContents.on('will-navigate', (event, url) => {
+ currentWindow.webContents.on('will-navigate', (event, url) => {
if (url.startsWith(getServer())) {
return
}
@@ -342,218 +351,48 @@ const boot = () => {
shell.openExternal(url)
})
- mainWindow.on('close', (event) => {
- if (mainWindow && !mainWindow.isDestroyed()) {
- mainWindow.webContents.send('siyuan-save-close', false)
+ currentWindow.on('close', (event) => {
+ if (currentWindow && !currentWindow.isDestroyed()) {
+ currentWindow.webContents.send('siyuan-save-close', false)
}
event.preventDefault()
})
- // 监听主题切换
- ipcMain.on('siyuan-config-theme', (event, theme) => {
- nativeTheme.themeSource = theme
+ workspaces.push({
+ browserWindow: currentWindow,
+ id: currentWindow.id,
})
- ipcMain.on('siyuan-config-close', (event, close) => {
- closeButtonBehavior = close
- })
- ipcMain.on('siyuan-config-tray', () => {
- mainWindow.hide()
- })
- ipcMain.on('siyuan-config-closetray', () => {
- if ('win32' === process.platform) {
- tray.destroy()
- }
- })
- ipcMain.on('siyuan-export-pdf', (event, data) => {
- mainWindow.webContents.send('siyuan-export-pdf', data)
- })
- ipcMain.on('siyuan-export-close', (event, data) => {
- mainWindow.webContents.send('siyuan-export-close', data)
- })
- ipcMain.on('siyuan-quit', () => {
- 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')
- })
-
- let trayMenu = {
- "showWindow": "Show Window",
- "hideWindow": "Hide Window",
- "setWindowTop": "Set Window top",
- "cancelWindowTop": "Cancel Window top",
- "officialWebsite": "Visit official website",
- "openSource": "Visit Project on Github",
- "resetWindow": "Reset Window on restart",
- "quit": "Quit application"
- }
- ipcMain.on('siyuan-init', async (event, languages) => {
- trayMenu = languages['_trayMenu'];
- resetTrayMenu()
- await fetch(getServer() + '/api/system/uiproc?pid=' + process.pid,
- {method: 'POST'})
- })
-
- const resetTrayMenu = () => {
- if ('win32' !== process.platform && 'linux' !== process.platform) {
- return
- }
-
- const trayMenuTemplate = buildTrayMenuTemplate()
- const contextMenu = Menu.buildFromTemplate(trayMenuTemplate)
- tray.setContextMenu(contextMenu)
- }
-
- const buildShowWndMenu = () => {
- const ret = {
- label: trayMenu.hideWindow,
- click: () => {
- showHideWnd()
- },
- }
-
- if (mainWindow.isVisible()) {
- ret.label = trayMenu.hideWindow
- } else {
- ret.label = trayMenu.showWindow
- }
- return ret
- }
-
- const showHideWnd = () => {
- if (!mainWindow.isVisible()) {
- if (mainWindow.isMinimized()) {
- mainWindow.restore()
- }
- mainWindow.show()
- } else {
- mainWindow.hide()
- }
-
- resetTrayMenu()
- }
-
- const buildSetWndTopMenu = () => {
- const ret = {
- label: trayMenu.setWindowTop,
- click: () => {
- setCancelWndTop()
- },
- }
- if (mainWindow.isAlwaysOnTop()) {
- ret.label = trayMenu.cancelWindowTop
- } else {
- ret.label = trayMenu.setWindowTop
- }
- return ret;
- }
- const setCancelWndTop = () => {
- if (!mainWindow.isAlwaysOnTop()) {
- mainWindow.setAlwaysOnTop(true)
- } else {
- mainWindow.setAlwaysOnTop(false)
- }
-
- resetTrayMenu()
- }
-
- const buildTrayMenuTemplate = () => {
- let ret = [
- buildShowWndMenu(),
- {
- label: trayMenu.officialWebsite,
- click: () => {
- shell.openExternal('https://b3log.org/siyuan/')
- },
- },
- {
- label: trayMenu.openSource,
- click: () => {
- shell.openExternal('https://github.com/siyuan-note/siyuan')
- },
- },
- {
- label: trayMenu.resetWindow,
- type: 'checkbox',
- click: v => {
- resetWindowStateOnRestart = v.checked
- mainWindow.webContents.send('siyuan-save-close', true)
- },
- },
- {
- label: trayMenu.quit,
- click: () => {
- mainWindow.webContents.send('siyuan-save-close', true)
- },
- }
- ]
-
- if ('win32' === process.platform) {
- // Windows 端支持窗口置顶 https://github.com/siyuan-note/siyuan/issues/6860
- ret.splice(1, 0, buildSetWndTopMenu())
- }
- return ret;
- }
-
- ipcMain.on('siyuan-hotkey', (event, hotkey) => {
- globalShortcut.unregisterAll()
- if (!hotkey) {
- return
- }
- globalShortcut.register(hotkey, () => {
- if (mainWindow.isMinimized()) {
- mainWindow.restore()
- if (!mainWindow.isVisible()) {
- mainWindow.show()
- }
- } else {
- if (mainWindow.isVisible()) {
- if (!mainWindow.isFocused()) {
- mainWindow.show()
- } else {
- mainWindow.hide()
- }
- } else {
- mainWindow.show()
- }
- }
-
- resetTrayMenu()
- })
- })
-
- if ('win32' === process.platform || 'linux' === process.platform) {
- // 系统托盘
-
- tray = new Tray(path.join(appDir, 'stage', 'icon-large.png'))
- tray.setToolTip('SiYuan v' + appVer)
-
- const trayMenuTemplate = buildTrayMenuTemplate()
- const contextMenu = Menu.buildFromTemplate(trayMenuTemplate)
- tray.setContextMenu(contextMenu)
- tray.on('click', () => {
- showHideWnd()
- })
- }
}
-const initKernel = (initData) => {
+const initKernel = (workspace, lang) => {
+ const getKernelPort = async () => {
+ // TODO if (isDevEnv) {
+ // writeLog("got kernel port [" + kernelPort + "]")
+ // return kernelPort
+ // }
+
+ // 改进桌面端拉起内核 https://github.com/siyuan-note/siyuan/issues/6894
+ const getAvailablePort = (port = kernelPort) => {
+ // https://gist.github.com/mikeal/1840641
+ let tryGetPortCount = 0
+ const server = net.createServer()
+ return new Promise((resolve, reject) => server.on('error', error => {
+ writeLog(error)
+ if (2048 < ++tryGetPortCount) {
+ writeLog('failed to get available port [tryCount=' + tryGetPortCount +
+ ', port=' + port + ']')
+ reject(error)
+ return
+ }
+ server.listen(++port)
+ }).on('listening', () => {
+ writeLog('found an available port [' + port + ']')
+ server.close(() => resolve(port))
+ }).listen(port, '127.0.0.1'))
+ }
+ kernelPort = await getAvailablePort()
+ writeLog('got kernel available port [' + kernelPort + ']')
+ return kernelPort
+ }
return new Promise(async (resolve) => {
bootWindow = new BrowserWindow({
width: screen.getPrimaryDisplay().size.width / 2,
@@ -566,7 +405,9 @@ const initKernel = (initData) => {
},
})
- const kernelName = 'win32' === process.platform ? 'SiYuan-Kernel.exe' : 'SiYuan-Kernel'
+ 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',
@@ -578,18 +419,19 @@ const initKernel = (initData) => {
const availablePort = await getKernelPort()
const cmds = ['--port', availablePort, '--wd', appDir]
- if (isDevEnv) {
- cmds.push('--mode', 'dev')
+ // TODO if (isDevEnv) {
+ // cmds.push('--mode', 'dev')
+ // }
+ if (workspace) {
+ cmds.push('--workspace', workspace)
+ cmds.push('--lang', lang)
}
- if (initData) {
- const initDatas = initData.split('-')
- cmds.push('--workspace', initDatas[0])
- cmds.push('--lang', initDatas[1])
- }
- let cmd = `ui version [${appVer}], booting kernel [${kernelPath} ${cmds.join(' ')}]`
+ let cmd = `ui version [${appVer}], booting kernel [${kernelPath} ${cmds.join(
+ ' ')}]`
writeLog(cmd)
- let kernelProcessPid = ""
- if (!isDevEnv) {
+ let kernelProcessPid = ''
+ // TODO
+ if (isDevEnv) {
const cp = require('child_process')
const kernelProcess = cp.spawn(kernelPath,
cmds, {
@@ -609,8 +451,12 @@ const initKernel = (initData) => {
`
数据库文件正在被其他进程占用,请检查是否同时存在多个内核进程(SiYuan Kernel)服务相同的工作空间。
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.
`)
break
case 21:
- showErrorWindow('⚠️ 监听端口 ' + kernelPort + ' 失败 Failed to listen to port ' + kernelPort,
- '监听 ' + kernelPort + ' 端口失败,请确保程序拥有网络权限并不受防火墙和杀毒软件阻止。
Failed to listen to port ' + kernelPort + ', please make sure the program has network permissions and is not blocked by firewalls and antivirus software.
')
+ showErrorWindow('⚠️ 监听端口 ' + kernelPort +
+ ' 失败 Failed to listen to port ' + kernelPort,
+ '监听 ' + kernelPort +
+ ' 端口失败,请确保程序拥有网络权限并不受防火墙和杀毒软件阻止。
Failed to listen to port ' +
+ kernelPort +
+ ', please make sure the program has network permissions and is not blocked by firewalls and antivirus software.
')
break
case 22:
showErrorWindow(
@@ -659,7 +505,8 @@ const initKernel = (initData) => {
if (14 < count) {
writeLog('get kernel ver failed')
- showErrorWindow('⚠️ 获取内核服务端口失败 Failed to get kernel serve port',
+ showErrorWindow(
+ '⚠️ 获取内核服务端口失败 Failed to get kernel serve port',
'获取内核服务端口失败,请确保程序拥有网络权限并不受防火墙和杀毒软件阻止。
Failed to get kernel serve port, please make sure the program has network permissions and is not blocked by firewalls and antivirus software.
')
bootWindow.destroy()
resolve(false)
@@ -680,7 +527,8 @@ const initKernel = (initData) => {
let progressing = false
while (!progressing) {
try {
- const progressResult = await fetch(getServer() + '/api/system/bootProgress')
+ const progressResult = await fetch(
+ getServer() + '/api/system/bootProgress')
const progressData = await progressResult.json()
if (progressData.data.progress >= 100) {
resolve(true)
@@ -714,9 +562,199 @@ 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.whenReady().then(() => {
+ const resetTrayMenu = (tray, lang, mainWindow) => {
+ const trayMenuTemplate = [
+ {
+ label: mainWindow.isVisible()
+ ? lang.hideWindow
+ : lang.showWindow,
+ click: () => {
+ showHideWnd(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)
+ }
+ const showHideWnd = (tray, lang, mainWindow) => {
+ if (!mainWindow.isVisible()) {
+ if (mainWindow.isMinimized()) {
+ mainWindow.restore()
+ }
+ mainWindow.show()
+ } else {
+ mainWindow.hide()
+ }
+
+ resetTrayMenu(tray, lang, mainWindow)
+ }
+
ipcMain.on('siyuan-first-quit', () => {
app.exit()
})
+ ipcMain.on('siyuan-show', (event, id) => {
+ const mainWindow = BrowserWindow.fromId(id)
+ if (mainWindow.isMinimized()) {
+ mainWindow.restore()
+ }
+ if (!mainWindow.isVisible()) {
+ mainWindow.show()
+ }
+ mainWindow.focus()
+ })
+ ipcMain.on('siyuan-config-theme', (event, theme) => {
+ nativeTheme.themeSource = theme
+ })
+ ipcMain.on('siyuan-config-tray', (event, id) => {
+ BrowserWindow.fromId(id).hide()
+ })
+ 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', data)
+ })
+ ipcMain.on('siyuan-quit', (id) => {
+ const mainWindow = BrowserWindow.fromId(id)
+ let tray
+ workspaces.find((item, index) => {
+ if (item.id === id) {
+ mainWindow.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,
+ }))
+ }
+ } catch (e) {
+ writeLog(e)
+ }
+ app.exit()
+ globalShortcut.unregisterAll()
+ writeLog('exited ui')
+ }
+ })
+ ipcMain.on('siyuan-open-workspace', (event, data) => {
+ initKernel(data.workspace, data.lang).then((isSucc) => {
+ if (isSucc) {
+ boot()
+ }
+ })
+ })
+ ipcMain.on('siyuan-init', async (event, data) => {
+ let tray
+ if ('win32' === process.platform || 'linux' === process.platform) {
+ // 系统托盘
+ tray = new Tray(path.join(appDir, 'stage', 'icon-large.png'))
+ tray.setToolTip('SiYuan v' + appVer)
+ const mainWindow = BrowserWindow.fromId(data.id)
+ resetTrayMenu(tray, data.languages, mainWindow)
+ tray.on('click', () => {
+ showHideWnd(tray, data.languages, mainWindow)
+ })
+ }
+ workspaces.find(item => {
+ if (data.id === item.id) {
+ item.workspaceDir = data.workspaceDir
+ item.tray = tray
+ return true
+ }
+ })
+ await fetch(getServer() + '/api/system/uiproc?pid=' + process.pid,
+ {method: 'POST'})
+ })
+ ipcMain.on('siyuan-hotkey', (event, data) => {
+ globalShortcut.unregisterAll()
+ if (!data.hotkey) {
+ return
+ }
+ globalShortcut.register(data.hotkey, () => {
+ const mainWindow = BrowserWindow.fromId(data.id)
+ if (mainWindow.isMinimized()) {
+ mainWindow.restore()
+ if (!mainWindow.isVisible()) {
+ mainWindow.show()
+ }
+ } else {
+ if (mainWindow.isVisible()) {
+ if (!mainWindow.isFocused()) {
+ mainWindow.show()
+ } else {
+ mainWindow.hide()
+ }
+ } else {
+ mainWindow.show()
+ }
+ }
+ workspaces.find(item => {
+ if (item.id === data.id) {
+ resetTrayMenu(item.tray, data.languages, mainWindow)
+ return true
+ }
+ })
+ })
+ })
if (firstOpen) {
firstOpenWindow = new BrowserWindow({
@@ -740,8 +778,9 @@ app.whenReady().then(() => {
}
// 改进桌面端初始化时使用的外观语言 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";
+ let languages = app.getPreferredSystemLanguages()
+ let language = languages && 0 < languages.length && 'zh-Hans-CN' ===
+ languages[0] ? 'zh_CN' : 'en_US'
firstOpenWindow.loadFile(
initHTMLPath, {
query: {
@@ -753,8 +792,8 @@ app.whenReady().then(() => {
})
firstOpenWindow.show()
// 初始化启动
- ipcMain.on('siyuan-first-init', (event, initData) => {
- initKernel(initData).then((isSucc) => {
+ ipcMain.on('siyuan-first-init', (event, data) => {
+ initKernel(data.workspace, data.lang).then((isSucc) => {
if (isSucc) {
boot()
}
@@ -773,34 +812,25 @@ app.whenReady().then(() => {
app.on('open-url', (event, url) => { // for macOS
if (url.startsWith('siyuan://')) {
siyuanOpenURL = url
- if (mainWindow && !mainWindow.isDestroyed()) {
- if (mainWindow.isMinimized()) {
- mainWindow.restore()
+ workspaces.forEach(item => {
+ if (item.browserWindow && !item.browserWindow.isDestroyed()) {
+ item.browserWindow.webContents.send('siyuan-openurl', url)
}
- if (!mainWindow.isVisible()) {
- mainWindow.show()
- }
- mainWindow.focus()
- mainWindow.webContents.send('siyuan-openurl', url)
- }
+ })
}
})
app.on('second-instance', (event, commandLine) => {
- if (mainWindow && !mainWindow.isDestroyed()) {
- if (mainWindow.isMinimized()) {
- mainWindow.restore()
+ workspaces.forEach(item => {
+ if (item.browserWindow && !item.browserWindow.isDestroyed()) {
+ item.browserWindow.webContents.send('siyuan-openurl',
+ commandLine.find((arg) => arg.startsWith('siyuan://')))
}
- if (!mainWindow.isVisible()) {
- mainWindow.show()
- }
- mainWindow.focus()
- mainWindow.webContents.send('siyuan-openurl',
- commandLine.find((arg) => arg.startsWith('siyuan://')))
- }
+ })
})
app.on('activate', () => {
+ const mainWindow = workspaces[0].browserWindow
if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.show()
}
@@ -818,10 +848,12 @@ app.on('web-contents-created', (webContentsCreatedEvent, contents) => {
})
app.on('before-quit', (event) => {
- if (mainWindow && !mainWindow.isDestroyed()) {
- event.preventDefault()
- mainWindow.webContents.send('siyuan-save-close', true)
- }
+ workspaces.forEach(item => {
+ if (item.browserWindow && !item.browserWindow.isDestroyed()) {
+ event.preventDefault()
+ item.browserWindow.webContents.send('siyuan-save-close', true)
+ }
+ })
})
const {powerMonitor} = require('electron')
@@ -832,23 +864,36 @@ powerMonitor.on('suspend', () => {
powerMonitor.on('resume', async () => {
writeLog('system resume')
+ const isOnline = async () => {
+ try {
+ const result = await fetch('https://icanhazip.com', {timeout: 1000})
+ return 200 === result.status
+ } catch (e) {
+ try {
+ const result = await fetch('https://www.baidu.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;
+ break
}
- writeLog("network is offline")
+ writeLog('network is offline')
await sleep(1000)
}
if (!online) {
- writeLog("network is offline, do not sync after system resume")
- return;
+ writeLog('network is offline, do not sync after system resume')
+ return
}
- writeLog("sync after system resume")
+ writeLog('sync after system resume')
// 桌面端系统休眠唤醒后同步延时 7s 后再执行 https://github.com/siyuan-note/siyuan/issues/6687
fetch(getServer() + '/api/sync/performSync', {method: 'POST'})
})
@@ -857,61 +902,3 @@ powerMonitor.on('shutdown', () => {
writeLog('system shutdown')
fetch(getServer() + '/api/system/exit', {method: 'POST'})
})
-
-const sleep = (ms) => {
- return new Promise(resolve => setTimeout(resolve, ms))
-}
-
-const isOnline = async () => {
- try {
- const result = await fetch("https://icanhazip.com", {timeout: 1000})
- return 200 === result.status
- } catch (e) {
- try {
- const result = await fetch("https://www.baidu.com", {timeout: 1000})
- return 200 === result.status
- } catch (e) {
- return false;
- }
- }
-}
-
-let kernelPort = 6806
-
-const getKernelPort = async () => {
- if (isDevEnv) {
- writeLog("got kernel port [" + kernelPort + "]")
- return kernelPort
- }
-
- // 改进桌面端拉起内核 https://github.com/siyuan-note/siyuan/issues/6894
- kernelPort = await getAvailablePort()
- writeLog("got kernel available port [" + kernelPort + "]")
- return kernelPort
-}
-
-let tryGetPortCount = 0
-const getAvailablePort = (port = kernelPort) => {
- // https://gist.github.com/mikeal/1840641
-
- const server = net.createServer()
- return new Promise((resolve, reject) => server
- .on('error', error => {
- writeLog(error)
- if (2048 < ++tryGetPortCount) {
- writeLog('failed to get available port [tryCount=' + tryGetPortCount + ', port=' + port + ']')
- reject(error)
- return
- }
- server.listen(++port)
- })
- .on('listening', () => {
- writeLog('found an available port [' + port + ']')
- server.close(() => resolve(port))
- })
- .listen(port, '127.0.0.1'))
-}
-
-const getServer = () => {
- return "http://" + localhost + ":" + kernelPort
-}
diff --git a/app/src/config/about.ts b/app/src/config/about.ts
index 88507fc8c..1c9f9d19e 100644
--- a/app/src/config/about.ts
+++ b/app/src/config/about.ts
@@ -1,6 +1,6 @@
import {Constants} from "../constants";
/// #if !BROWSER
-import {app, shell} from "electron";
+import {app, ipcRenderer, shell} from "electron";
import {dialog} from "@electron/remote";
/// #endif
import {isBrowser} from "../util/functions";
@@ -242,8 +242,9 @@ export const about = {
fetchPost("/api/system/setWorkspaceDir", {
path: workspace
}, () => {
- exportLayout(false, () => {
- exitSiYuan();
+ ipcRenderer.send(Constants.SIYUAN_OPEN_WORKSPACE, {
+ workspace,
+ lang: window.siyuan.config.appearance.lang
});
});
});
diff --git a/app/src/config/appearance.ts b/app/src/config/appearance.ts
index e85fb8f41..512e4100d 100644
--- a/app/src/config/appearance.ts
+++ b/app/src/config/appearance.ts
@@ -274,7 +274,6 @@ export const appearance = {
}
/// #if !BROWSER
ipcRenderer.send(Constants.SIYUAN_CONFIG_THEME, data.modeOS ? "system" : (data.mode === 1 ? "dark" : "light"));
- ipcRenderer.send(Constants.SIYUAN_CONFIG_CLOSE, data.closeButtonBehavior);
/// #endif
if (needLoadAsset) {
loadAssets(data);
diff --git a/app/src/config/keymap.ts b/app/src/config/keymap.ts
index ac601407b..96658e045 100644
--- a/app/src/config/keymap.ts
+++ b/app/src/config/keymap.ts
@@ -2,8 +2,11 @@ import {hotKey2Electron, isCtrl, isMac, updateHotkeyTip} from "../protyle/util/c
import {Constants} from "../constants";
import {showMessage} from "../dialog/message";
import {fetchPost} from "../util/fetch";
-import {ipcRenderer} from "electron";
import {exportLayout} from "../layout/util";
+/// #if !BROWSER
+import {getCurrentWindow} from "@electron/remote";
+import {ipcRenderer} from "electron";
+/// #endif
import {confirmDialog} from "../dialog/confirmDialog";
export const keymap = {
@@ -132,7 +135,11 @@ export const keymap = {
data
}, () => {
/// #if !BROWSER
- ipcRenderer.send(Constants.SIYUAN_HOTKEY, hotKey2Electron(window.siyuan.config.keymap.general.toggleWin.custom));
+ ipcRenderer.send(Constants.SIYUAN_HOTKEY, {
+ languages: window.siyuan.languages["_trayMenu"],
+ id: getCurrentWindow().id,
+ hotkey: hotKey2Electron(window.siyuan.config.keymap.general.toggleWin.custom)
+ });
/// #endif
});
},
@@ -219,7 +226,11 @@ export const keymap = {
}, () => {
window.location.reload();
/// #if !BROWSER
- ipcRenderer.send(Constants.SIYUAN_HOTKEY, hotKey2Electron(window.siyuan.config.keymap.general.toggleWin.custom));
+ ipcRenderer.send(Constants.SIYUAN_HOTKEY, {
+ languages: window.siyuan.languages["_trayMenu"],
+ id: getCurrentWindow().id,
+ hotkey: hotKey2Electron(window.siyuan.config.keymap.general.toggleWin.custom)
+ });
/// #endif
});
});
diff --git a/app/src/constants.ts b/app/src/constants.ts
index d0363fb36..64ca3ffac 100644
--- a/app/src/constants.ts
+++ b/app/src/constants.ts
@@ -20,10 +20,10 @@ export abstract class Constants {
public static readonly SIYUAN_DROP_EDITOR: string = "application/siyuan-editor";
// 渲染进程调主进程
+ public static readonly SIYUAN_SHOW: string = "siyuan-show";
public static readonly SIYUAN_CONFIG_THEME: string = "siyuan-config-theme";
- public static readonly SIYUAN_CONFIG_CLOSE: string = "siyuan-config-close";
public static readonly SIYUAN_CONFIG_TRAY: string = "siyuan-config-tray";
- public static readonly SIYUAN_CONFIG_CLOSETRAY: string = "siyuan-config-closetray";
+ public static readonly SIYUAN_OPEN_WORKSPACE: string = "siyuan-open-workspace";
public static readonly SIYUAN_QUIT: string = "siyuan-quit";
public static readonly SIYUAN_HOTKEY: string = "siyuan-hotkey";
public static readonly SIYUAN_INIT: string = "siyuan-init";
diff --git a/app/src/dialog/processSystem.ts b/app/src/dialog/processSystem.ts
index 38d87504d..d0b081bae 100644
--- a/app/src/dialog/processSystem.ts
+++ b/app/src/dialog/processSystem.ts
@@ -93,7 +93,6 @@ export const exitSiYuan = () => {
buttonElement.addEventListener("click", () => {
fetchPost("/api/system/exit", {force: true}, () => {
/// #if !BROWSER
- ipcRenderer.send(Constants.SIYUAN_CONFIG_CLOSETRAY);
ipcRenderer.send(Constants.SIYUAN_QUIT);
/// #else
if (["ios", "android"].includes(window.siyuan.config.system.container) && (window.webkit?.messageHandlers || window.JSAndroid)) {
@@ -118,7 +117,6 @@ export const exitSiYuan = () => {
}, 2000);
// 然后等待一段时间后再退出,避免界面主进程退出以后内核子进程被杀死
setTimeout(() => {
- ipcRenderer.send(Constants.SIYUAN_CONFIG_CLOSETRAY);
ipcRenderer.send(Constants.SIYUAN_QUIT);
}, 4000);
/// #endif
@@ -129,14 +127,12 @@ export const exitSiYuan = () => {
execInstallPkg: 1 // 0:默认检查新版本,1:不执行新版本安装,2:执行新版本安装
}, () => {
/// #if !BROWSER
- ipcRenderer.send(Constants.SIYUAN_CONFIG_CLOSETRAY);
ipcRenderer.send(Constants.SIYUAN_QUIT);
/// #endif
});
});
} else { // 正常退出
/// #if !BROWSER
- ipcRenderer.send(Constants.SIYUAN_CONFIG_CLOSETRAY);
ipcRenderer.send(Constants.SIYUAN_QUIT);
/// #else
if (["ios", "android"].includes(window.siyuan.config.system.container) && (window.webkit?.messageHandlers || window.JSAndroid)) {
diff --git a/app/src/protyle/export/index.ts b/app/src/protyle/export/index.ts
index bd26642a6..ddacddf54 100644
--- a/app/src/protyle/export/index.ts
+++ b/app/src/protyle/export/index.ts
@@ -416,11 +416,12 @@ const renderPDF = (id: string) => {
});
actionElement.querySelector('.b3-button--cancel').addEventListener('click', () => {
const {ipcRenderer} = require("electron");
- ipcRenderer.send("${Constants.SIYUAN_EXPORT_CLOSE}")
+ ipcRenderer.send("${Constants.SIYUAN_EXPORT_CLOSE}", getCurrentWindow().id)
});
actionElement.querySelector('.b3-button--text').addEventListener('click', () => {
const {ipcRenderer} = require("electron");
ipcRenderer.send("${Constants.SIYUAN_EXPORT_PDF}", {
+ id: getCurrentWindow().id,
pdfOptions:{
printBackground: true,
landscape: actionElement.querySelector("#landscape").checked,
diff --git a/app/src/util/assets.ts b/app/src/util/assets.ts
index f1fa7b854..3700a3f20 100644
--- a/app/src/util/assets.ts
+++ b/app/src/util/assets.ts
@@ -235,7 +235,6 @@ export const setMode = (modeElementValue: number) => {
window.siyuan.config.appearance = response.data;
/// #if !BROWSER
ipcRenderer.send(Constants.SIYUAN_CONFIG_THEME, response.data.modeOS ? "system" : (response.data.mode === 1 ? "dark" : "light"));
- ipcRenderer.send(Constants.SIYUAN_CONFIG_CLOSE, response.data.closeButtonBehavior);
/// #endif
loadAssets(response.data);
document.querySelector("#barMode use").setAttribute("xlink:href", `#icon${window.siyuan.config.appearance.modeOS ? "Mode" : (window.siyuan.config.appearance.mode === 0 ? "Light" : "Dark")}`);
diff --git a/app/src/util/fetch.ts b/app/src/util/fetch.ts
index 508079845..779de61fb 100644
--- a/app/src/util/fetch.ts
+++ b/app/src/util/fetch.ts
@@ -48,7 +48,6 @@ export const fetchPost = (url: string, data?: any, cb?: (response: IWebSocketDat
if (url === "/api/system/exit" || url === "/api/system/setWorkspaceDir" || (
["/api/system/setUILayout"].includes(url) && data.exit // 内核中断,点关闭处理
)) {
- ipcRenderer.send(Constants.SIYUAN_CONFIG_CLOSETRAY);
ipcRenderer.send(Constants.SIYUAN_QUIT);
}
/// #endif
diff --git a/app/src/util/onGetConfig.ts b/app/src/util/onGetConfig.ts
index 8605a1003..97e6e13a5 100644
--- a/app/src/util/onGetConfig.ts
+++ b/app/src/util/onGetConfig.ts
@@ -126,14 +126,25 @@ export const onGetConfig = (isStart: boolean) => {
data: window.siyuan.config.keymap
}, () => {
/// #if !BROWSER
- ipcRenderer.send(Constants.SIYUAN_HOTKEY, hotKey2Electron(window.siyuan.config.keymap.general.toggleWin.custom));
+ 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_CONFIG_CLOSE, window.siyuan.config.appearance.closeButtonBehavior);
- ipcRenderer.send(Constants.SIYUAN_INIT, window.siyuan.languages);
- ipcRenderer.send(Constants.SIYUAN_HOTKEY, hotKey2Electron(window.siyuan.config.keymap.general.toggleWin.custom));
+ ipcRenderer.send(Constants.SIYUAN_INIT, {
+ languages: window.siyuan.languages["_trayMenu"],
+ workspaceDir: window.siyuan.config.system.workspaceDir,
+ id: getCurrentWindow().id,
+ });
+ ipcRenderer.send(Constants.SIYUAN_HOTKEY, {
+ languages: window.siyuan.languages["_trayMenu"],
+ id: getCurrentWindow().id,
+ hotkey: hotKey2Electron(window.siyuan.config.keymap.general.toggleWin.custom)
+ });
/// #endif
if (!window.siyuan.config.uiLayout || (window.siyuan.config.uiLayout && !window.siyuan.config.uiLayout.left)) {
window.siyuan.config.uiLayout = Constants.SIYUAN_EMPTY_LAYOUT;
@@ -332,7 +343,7 @@ const winOnClose = (currentWindow: Electron.BrowserWindow, close = false) => {
if (window.siyuan.config.appearance.closeButtonBehavior === 1 && !close) {
// 最小化
if ("windows" === window.siyuan.config.system.os) {
- ipcRenderer.send(Constants.SIYUAN_CONFIG_TRAY);
+ ipcRenderer.send(Constants.SIYUAN_CONFIG_TRAY, getCurrentWindow().id);
} else {
if (currentWindow.isFullScreen()) {
currentWindow.once("leave-full-screen", () => currentWindow.hide());
@@ -359,10 +370,16 @@ const initWindow = () => {
if (!/^siyuan:\/\/blocks\/\d{14}-\w{7}/.test(url)) {
return;
}
- openFileById({
- id: url.substr(16, 22),
- action: [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT],
- zoomIn: getSearch("focus", url) === "1"
+ 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) => {
diff --git a/app/stage/auth.html b/app/stage/auth.html
index dc685a1d4..65c82c249 100644
--- a/app/stage/auth.html
+++ b/app/stage/auth.html
@@ -432,8 +432,8 @@
const exitApp = () => {
try {
const {ipcRenderer} = require('electron')
- ipcRenderer.send('siyuan-config-closetray')
- ipcRenderer.send('siyuan-quit')
+ const {getCurrentWindow} = require('@electron/remote');
+ ipcRenderer.send('siyuan-quit', getCurrentWindow().id)
} catch (e) {
if ((window.webkit && window.webkit.messageHandlers) || window.JSAndroid) {
window.location.href = 'siyuan://api/system/exit'