|
@@ -23,7 +23,13 @@ if (!app.requestSingleInstanceLock()) { app.quit() }
|
|
|
else {
|
|
else {
|
|
|
let window, tray, manager, runnerManager, quitting = false, closing = false, timer
|
|
let window, tray, manager, runnerManager, quitting = false, closing = false, timer
|
|
|
let updateWindow, updater, environmentHost, hostManager
|
|
let updateWindow, updater, environmentHost, hostManager
|
|
|
- const closeComputerViewer = installComputerViewer({ owner: () => window, origin: () => manager?.state.url,
|
|
|
|
|
|
|
+ let remoteMode = false, remoteURL = ''
|
|
|
|
|
+ const serviceURL = () => remoteMode ? remoteURL : manager?.state.url
|
|
|
|
|
+ const serviceOrigins = () => {
|
|
|
|
|
+ try { return remoteMode && remoteURL ? [new URL(remoteURL).origin] : manager?.state.url ? [new URL(manager.state.url).origin] : [] }
|
|
|
|
|
+ catch { return [] }
|
|
|
|
|
+ }
|
|
|
|
|
+ const closeComputerViewer = installComputerViewer({ owner: () => window, origin: () => serviceURL(),
|
|
|
preload: join(desktopRoot, 'preload.cjs'), quitting: () => quitting })
|
|
preload: join(desktopRoot, 'preload.cjs'), quitting: () => quitting })
|
|
|
app.on('will-quit', closeComputerViewer)
|
|
app.on('will-quit', closeComputerViewer)
|
|
|
const updateURL = pathToFileURL(join(desktopRoot, 'update.html')).href
|
|
const updateURL = pathToFileURL(join(desktopRoot, 'update.html')).href
|
|
@@ -101,15 +107,9 @@ else {
|
|
|
|
|
|
|
|
function show() {
|
|
function show() {
|
|
|
if (closing || quitting) return
|
|
if (closing || quitting) return
|
|
|
- if (preferences.value.hostOnly) {
|
|
|
|
|
- if (window && !window.isDestroyed()) { window.show(); window.focus(); return }
|
|
|
|
|
- void dialog.showMessageBox({ type: 'info', title: '夭夭', message: '仅桌面主机模式',
|
|
|
|
|
- detail: '这台 Mac 正在作为远程桌面主机运行,本机 Web 服务未启动。如需使用本机服务,请打开菜单「桌面主机」,取消勾选「仅作为桌面主机运行」。' })
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
if (!window || window.isDestroyed()) createWindow()
|
|
if (!window || window.isDestroyed()) createWindow()
|
|
|
window.show(); window.focus()
|
|
window.show(); window.focus()
|
|
|
- if (manager) void manager.reconnect().then(() => stateChanged(manager.state)).catch(error => log(error.message))
|
|
|
|
|
|
|
+ if (!remoteMode && manager) void manager.reconnect().then(() => stateChanged(manager.state)).catch(error => log(error.message))
|
|
|
}
|
|
}
|
|
|
function safeExternal(url) {
|
|
function safeExternal(url) {
|
|
|
try { const parsed = new URL(url); if (['https:', 'http:', 'mailto:'].includes(parsed.protocol)) void shell.openExternal(url) }
|
|
try { const parsed = new URL(url); if (['https:', 'http:', 'mailto:'].includes(parsed.protocol)) void shell.openExternal(url) }
|
|
@@ -122,20 +122,21 @@ else {
|
|
|
webSecurity: true, preload: join(desktopRoot, 'preload.cjs') } })
|
|
webSecurity: true, preload: join(desktopRoot, 'preload.cjs') } })
|
|
|
window.webContents.setWindowOpenHandler(({ url }) => {
|
|
window.webContents.setWindowOpenHandler(({ url }) => {
|
|
|
try {
|
|
try {
|
|
|
- if (manager?.state.url && new URL(url).origin === manager.state.url) void window.loadURL(url)
|
|
|
|
|
|
|
+ if (serviceOrigins().includes(new URL(url).origin)) void window.loadURL(url)
|
|
|
else safeExternal(url)
|
|
else safeExternal(url)
|
|
|
} catch { /* Invalid navigation is denied. */ }
|
|
} catch { /* Invalid navigation is denied. */ }
|
|
|
return { action: 'deny' }
|
|
return { action: 'deny' }
|
|
|
})
|
|
})
|
|
|
const guardNavigation = (event, url) => {
|
|
const guardNavigation = (event, url) => {
|
|
|
- if (url === bootURL || (manager?.state.url && new URL(url).origin === manager.state.url)) return
|
|
|
|
|
|
|
+ try { if (url === bootURL || serviceOrigins().includes(new URL(url).origin)) return }
|
|
|
|
|
+ catch { /* Invalid navigation is denied. */ }
|
|
|
event.preventDefault(); safeExternal(url)
|
|
event.preventDefault(); safeExternal(url)
|
|
|
}
|
|
}
|
|
|
window.webContents.on('will-navigate', guardNavigation)
|
|
window.webContents.on('will-navigate', guardNavigation)
|
|
|
window.webContents.on('will-redirect', guardNavigation)
|
|
window.webContents.on('will-redirect', guardNavigation)
|
|
|
window.webContents.on('will-attach-webview', event => event.preventDefault())
|
|
window.webContents.on('will-attach-webview', event => event.preventDefault())
|
|
|
const trustedService = (contents, url) => {
|
|
const trustedService = (contents, url) => {
|
|
|
- try { return contents === window?.webContents && !!manager?.state.url && new URL(url).origin === manager.state.url }
|
|
|
|
|
|
|
+ try { return contents === window?.webContents && serviceOrigins().includes(new URL(url).origin) }
|
|
|
catch { return false }
|
|
catch { return false }
|
|
|
}
|
|
}
|
|
|
window.webContents.session.setPermissionCheckHandler((contents, permission, origin) =>
|
|
window.webContents.session.setPermissionCheckHandler((contents, permission, origin) =>
|
|
@@ -150,14 +151,15 @@ else {
|
|
|
})
|
|
})
|
|
|
window.on('close', event => { if (!quitting) { event.preventDefault(); window.hide() } })
|
|
window.on('close', event => { if (!quitting) { event.preventDefault(); window.hide() } })
|
|
|
window.once('ready-to-show', () => { if (!startHidden) window.show() })
|
|
window.once('ready-to-show', () => { if (!startHidden) window.show() })
|
|
|
- if (manager?.state.phase === 'ready') void window.loadURL(manager.state.url)
|
|
|
|
|
|
|
+ if (remoteMode && remoteURL) void window.loadURL(remoteURL)
|
|
|
|
|
+ else if (manager?.state.phase === 'ready') void window.loadURL(manager.state.url)
|
|
|
else void window.loadFile(join(desktopRoot, 'boot.html'))
|
|
else void window.loadFile(join(desktopRoot, 'boot.html'))
|
|
|
}
|
|
}
|
|
|
function stateChanged(state) {
|
|
function stateChanged(state) {
|
|
|
log(`${state.phase}: ${state.message}`)
|
|
log(`${state.phase}: ${state.message}`)
|
|
|
// Quit owns the window from this point on. A detached connection is not a
|
|
// Quit owns the window from this point on. A detached connection is not a
|
|
|
// stopped server and must never navigate the closing window to boot.html.
|
|
// stopped server and must never navigate the closing window to boot.html.
|
|
|
- if (closing || quitting) return
|
|
|
|
|
|
|
+ if (closing || quitting || remoteMode) return
|
|
|
const serviceStatus=Menu.getApplicationMenu()?.getMenuItemById('desktop-service-status')
|
|
const serviceStatus=Menu.getApplicationMenu()?.getMenuItemById('desktop-service-status')
|
|
|
if(serviceStatus)serviceStatus.label=state.phase==='ready'?'后台服务运行中':state.message
|
|
if(serviceStatus)serviceStatus.label=state.phase==='ready'?'后台服务运行中':state.message
|
|
|
const localVmMenu=Menu.getApplicationMenu()?.getMenuItemById('desktop-local-vm')
|
|
const localVmMenu=Menu.getApplicationMenu()?.getMenuItemById('desktop-local-vm')
|
|
@@ -201,9 +203,11 @@ else {
|
|
|
if(choice.response===1){quitting=true;setImmediate(()=>app.quit())}
|
|
if(choice.response===1){quitting=true;setImmediate(()=>app.quit())}
|
|
|
else {
|
|
else {
|
|
|
closing=false
|
|
closing=false
|
|
|
- await manager?.reconnect().catch(error=>log(error.message))
|
|
|
|
|
- await hostManager?.start().catch(error=>log(error.message))
|
|
|
|
|
- await runnerManager?.start().catch(error=>log(error.message))
|
|
|
|
|
|
|
+ if(remoteMode)await hostManager?.start().catch(error=>log(error.message))
|
|
|
|
|
+ else{
|
|
|
|
|
+ await manager?.reconnect().catch(error=>log(error.message))
|
|
|
|
|
+ await runnerManager?.start().catch(error=>log(error.message))
|
|
|
|
|
+ }
|
|
|
stateChanged(manager.state)
|
|
stateChanged(manager.state)
|
|
|
timer=setInterval(()=>{void manager.check()},10000);timer.unref()
|
|
timer=setInterval(()=>{void manager.check()},10000);timer.unref()
|
|
|
show()
|
|
show()
|
|
@@ -262,29 +266,67 @@ else {
|
|
|
decrypt:bytes=>credentials.decrypt(bytes),
|
|
decrypt:bytes=>credentials.decrypt(bytes),
|
|
|
onState:message=>{log(`桌面主机: ${message}`);const item=Menu.getApplicationMenu()?.getMenuItemById('desktop-host-status');if(item)item.label=message}})
|
|
onState:message=>{log(`桌面主机: ${message}`);const item=Menu.getApplicationMenu()?.getMenuItemById('desktop-host-status');if(item)item.label=message}})
|
|
|
async function hostAction(action){try{await action()}catch(error){await dialog.showMessageBox(window,{type:'error',message:'桌面主机操作未完成',detail:error.message})}}
|
|
async function hostAction(action){try{await action()}catch(error){await dialog.showMessageBox(window,{type:'error',message:'桌面主机操作未完成',detail:error.message})}}
|
|
|
|
|
+ const appDialog = options => window && !window.isDestroyed() ? dialog.showMessageBox(window, options) : dialog.showMessageBox(options)
|
|
|
async function importDesktopHost(){
|
|
async function importDesktopHost(){
|
|
|
- const selection=await dialog.showOpenDialog(window,{title:'导入桌面主机配置',properties:['openFile'],filters:[{name:'桌面主机配置',extensions:['json']}]})
|
|
|
|
|
|
|
+ const selection=window&&!window.isDestroyed()
|
|
|
|
|
+ ?await dialog.showOpenDialog(window,{title:'导入桌面主机配置',properties:['openFile'],filters:[{name:'桌面主机配置',extensions:['json']}]})
|
|
|
|
|
+ :await dialog.showOpenDialog({title:'导入桌面主机配置',properties:['openFile'],filters:[{name:'桌面主机配置',extensions:['json']}]})
|
|
|
if(!selection.canceled&&selection.filePaths[0]&&!closing)await hostAction(()=>hostManager.importFile(selection.filePaths[0]))
|
|
if(!selection.canceled&&selection.filePaths[0]&&!closing)await hostAction(()=>hostManager.importFile(selection.filePaths[0]))
|
|
|
}
|
|
}
|
|
|
- async function toggleHostOnly(item){
|
|
|
|
|
- const enabled=item.checked
|
|
|
|
|
|
|
+ async function enterRemoteMode(){
|
|
|
|
|
+ let config=await hostManager.read().catch(error=>{log(error.message);return undefined})
|
|
|
|
|
+ if(!config){await importDesktopHost();config=await hostManager.read().catch(()=>undefined)}
|
|
|
|
|
+ if(!config)return false
|
|
|
|
|
+ remoteMode=true;remoteURL=config.serverURL
|
|
|
|
|
+ await preferences.setStartupChoice('remote').catch(error=>log(error.message))
|
|
|
|
|
+ await hostManager.start().catch(error=>{hostManager.publish(error.message)})
|
|
|
|
|
+ const status=Menu.getApplicationMenu()?.getMenuItemById('desktop-service-status')
|
|
|
|
|
+ if(status)status.label=`已登录远程 · ${new URL(remoteURL).host}`
|
|
|
|
|
+ tray?.setToolTip(`夭夭 · 远程 ${new URL(remoteURL).host}`)
|
|
|
|
|
+ syncModeMenu()
|
|
|
|
|
+ log(`远程模式:${remoteURL}`)
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+ function syncModeMenu(){
|
|
|
|
|
+ const menu=Menu.getApplicationMenu()
|
|
|
|
|
+ const useRemote=menu?.getMenuItemById('desktop-host-use-remote'),useLocal=menu?.getMenuItemById('desktop-host-use-local'),ask=menu?.getMenuItemById('desktop-host-ask')
|
|
|
|
|
+ if(useRemote)useRemote.enabled=!remoteMode
|
|
|
|
|
+ if(useLocal)useLocal.enabled=remoteMode
|
|
|
|
|
+ if(ask)ask.checked=preferences.value.startupChoice==='ask'
|
|
|
|
|
+ }
|
|
|
|
|
+ async function switchMode(mode){
|
|
|
|
|
+ if(closing||quitting)return
|
|
|
try{
|
|
try{
|
|
|
- await preferences.setHostOnly(enabled)
|
|
|
|
|
- if(enabled){
|
|
|
|
|
- await environmentHost?.close().catch(error=>log(error.message))
|
|
|
|
|
- await runnerManager?.stop().catch(error=>log(error.message))
|
|
|
|
|
- await manager?.stop().catch(error=>log(error.message))
|
|
|
|
|
- if(window&&!window.isDestroyed())window.hide()
|
|
|
|
|
- await dialog.showMessageBox({type:'info',title:'夭夭',message:'已切换到仅桌面主机模式',detail:'本机 Web 服务已停止,这台 Mac 将只通过「桌面主机」连接远程夭夭服务器。需要本机服务时,请取消勾选。'})
|
|
|
|
|
|
|
+ if(mode==='remote'){
|
|
|
|
|
+ if(await enterRemoteMode()){
|
|
|
|
|
+ await environmentHost?.close().catch(error=>log(error.message))
|
|
|
|
|
+ await runnerManager?.stop().catch(error=>log(error.message))
|
|
|
|
|
+ await manager?.stop().catch(error=>log(error.message))
|
|
|
|
|
+ if(window&&!window.isDestroyed())void window.loadURL(remoteURL)
|
|
|
|
|
+ await appDialog({type:'info',message:'已登录远程服务器',detail:`窗口已切换到 ${remoteURL}。本机 Web 服务已停止;这台 Mac 的桌面可在远程服务器的电脑面板中授权使用。`})
|
|
|
|
|
+ }
|
|
|
}else{
|
|
}else{
|
|
|
|
|
+ remoteMode=false;remoteURL=''
|
|
|
|
|
+ await hostManager?.stop().catch(error=>log(error.message))
|
|
|
|
|
+ await preferences.setStartupChoice('local').catch(error=>log(error.message))
|
|
|
|
|
+ if(window&&!window.isDestroyed())void window.loadFile(join(desktopRoot,'boot.html'))
|
|
|
await manager?.start().catch(error=>log(error.message))
|
|
await manager?.start().catch(error=>log(error.message))
|
|
|
if(!closing)environmentHost?.start()
|
|
if(!closing)environmentHost?.start()
|
|
|
- await dialog.showMessageBox({type:'info',title:'夭夭',message:'已恢复本机 Web 服务',detail:'本机夭夭服务正在启动,稍后点按「显示窗口」即可使用。'})
|
|
|
|
|
|
|
+ await appDialog({type:'info',message:'已进入本地服务',detail:'本机夭夭服务正在启动,就绪后窗口会自动打开。'})
|
|
|
}
|
|
}
|
|
|
- }catch(error){
|
|
|
|
|
- item.checked=preferences.value.hostOnly
|
|
|
|
|
- await dialog.showMessageBox(window,{type:'error',message:'无法切换仅桌面主机模式',detail:error.message})
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ syncModeMenu()
|
|
|
|
|
+ }catch(error){await appDialog({type:'error',message:'切换使用方式未完成',detail:error.message})}
|
|
|
|
|
+ }
|
|
|
|
|
+ async function chooseStartupMode(){
|
|
|
|
|
+ if(fixtureHome)return 'local'
|
|
|
|
|
+ const stored=preferences.value.startupChoice
|
|
|
|
|
+ if(stored==='local'||stored==='remote')return stored
|
|
|
|
|
+ const choice=await dialog.showMessageBox({type:'question',title:'夭夭',message:'这台 Mac 如何使用夭夭?',
|
|
|
|
|
+ detail:'「登录远程」连接另一台电脑上的夭夭服务器:本机不运行 Web 服务,窗口直接打开远程夭夭,本机桌面也可供远程服务器的机器人操作(需要服务器下发的桌面主机配置文件)。「进入本地」使用这台 Mac 自己的夭夭服务。',
|
|
|
|
|
+ buttons:['登录远程…','进入本地'],defaultId:1,cancelId:1,checkboxLabel:'记住选择,之后启动不再询问',checkboxChecked:false,noLink:true})
|
|
|
|
|
+ const mode=choice.response===0?'remote':'local'
|
|
|
|
|
+ if(choice.checkboxChecked)await preferences.setStartupChoice(mode).catch(error=>log(error.message))
|
|
|
|
|
+ return mode
|
|
|
}
|
|
}
|
|
|
async function importRunner(){
|
|
async function importRunner(){
|
|
|
const selection=await dialog.showOpenDialog(window,{title:'导入执行节点配置',properties:['openFile'],filters:[{name:'Runner 配置',extensions:['json']}]})
|
|
const selection=await dialog.showOpenDialog(window,{title:'导入执行节点配置',properties:['openFile'],filters:[{name:'Runner 配置',extensions:['json']}]})
|
|
@@ -293,7 +335,7 @@ else {
|
|
|
Menu.setApplicationMenu(Menu.buildFromTemplate([
|
|
Menu.setApplicationMenu(Menu.buildFromTemplate([
|
|
|
{ label: '夭夭', submenu: [{ role: 'about', label: '关于夭夭' }, { type: 'separator' },
|
|
{ label: '夭夭', submenu: [{ role: 'about', label: '关于夭夭' }, { type: 'separator' },
|
|
|
{ id:'desktop-service-status',label:'正在连接后台服务…',enabled:false },
|
|
{ id:'desktop-service-status',label:'正在连接后台服务…',enabled:false },
|
|
|
- { label: '显示窗口', click: show }, { label: '在浏览器中打开', click: () => { if (manager.state.url) safeExternal(manager.state.url) } },
|
|
|
|
|
|
|
+ { label: '显示窗口', click: show }, { label: '在浏览器中打开', click: () => { const url = serviceURL(); if (url) safeExternal(url) } },
|
|
|
{ id:'desktop-local-vm',label:'本地虚拟机设置…',enabled:false,click:()=>{if(manager.state.phase==='ready'){show();void window.webContents.executeJavaScript("window.dispatchEvent(new CustomEvent('yaoyao:local-vm-settings'))")}} },
|
|
{ id:'desktop-local-vm',label:'本地虚拟机设置…',enabled:false,click:()=>{if(manager.state.phase==='ready'){show();void window.webContents.executeJavaScript("window.dispatchEvent(new CustomEvent('yaoyao:local-vm-settings'))")}} },
|
|
|
{ label: '查看服务日志', click: () => shell.showItemInFolder(logFile) },
|
|
{ label: '查看服务日志', click: () => shell.showItemInFolder(logFile) },
|
|
|
{ id:'desktop-sync-notice', label:'Web 同步待完成:服务忙碌', enabled:false, visible:false },
|
|
{ id:'desktop-sync-notice', label:'Web 同步待完成:服务忙碌', enabled:false, visible:false },
|
|
@@ -318,11 +360,13 @@ else {
|
|
|
{id:'runner-reconnect',label:'重新连接',click:()=>runnerAction(()=>runnerManager.start())},
|
|
{id:'runner-reconnect',label:'重新连接',click:()=>runnerAction(()=>runnerManager.start())},
|
|
|
{id:'runner-forget',label:'断开并忘记配置',click:()=>runnerAction(()=>runnerManager.forget())}]},
|
|
{id:'runner-forget',label:'断开并忘记配置',click:()=>runnerAction(()=>runnerManager.forget())}]},
|
|
|
{label:'桌面主机',submenu:[{id:'desktop-host-status',label:'未配置桌面主机',enabled:false},{type:'separator'},
|
|
{label:'桌面主机',submenu:[{id:'desktop-host-status',label:'未配置桌面主机',enabled:false},{type:'separator'},
|
|
|
|
|
+ {id:'desktop-host-use-remote',label:'登录远程服务器…',enabled:!remoteMode,click:()=>void switchMode('remote')},
|
|
|
|
|
+ {id:'desktop-host-use-local',label:'进入本地服务',enabled:remoteMode,click:()=>void switchMode('local')},{type:'separator'},
|
|
|
{id:'desktop-host-import',label:'导入主机配置…',click:importDesktopHost},
|
|
{id:'desktop-host-import',label:'导入主机配置…',click:importDesktopHost},
|
|
|
{id:'desktop-host-reconnect',label:'重新连接',click:()=>hostAction(()=>hostManager.start())},
|
|
{id:'desktop-host-reconnect',label:'重新连接',click:()=>hostAction(()=>hostManager.start())},
|
|
|
{id:'desktop-host-forget',label:'断开并忘记配置',click:()=>hostAction(()=>hostManager.forget())},{type:'separator'},
|
|
{id:'desktop-host-forget',label:'断开并忘记配置',click:()=>hostAction(()=>hostManager.forget())},{type:'separator'},
|
|
|
- {id:'desktop-host-only',label:'仅作为桌面主机运行',type:'checkbox',checked:preferences.value.hostOnly,
|
|
|
|
|
- toolTip:'不启动本机 Web 服务,只连接远程夭夭服务器',click:toggleHostOnly}]},
|
|
|
|
|
|
|
+ {id:'desktop-host-ask',label:'启动时询问使用方式',type:'checkbox',checked:preferences.value.startupChoice==='ask',
|
|
|
|
|
+ click:item=>{void preferences.setStartupChoice(item.checked?'ask':'local').catch(async error=>{item.checked=preferences.value.startupChoice==='ask';await appDialog({type:'error',message:'无法保存启动偏好',detail:error.message})})}}]},
|
|
|
{ role: 'editMenu', label: '编辑' }, { role: 'viewMenu', label: '显示' }, { role: 'windowMenu', label: '窗口' },
|
|
{ role: 'editMenu', label: '编辑' }, { role: 'viewMenu', label: '显示' }, { role: 'windowMenu', label: '窗口' },
|
|
|
]))
|
|
]))
|
|
|
const icon = nativeImage.createFromPath(join(desktopRoot, 'icon.png')).resize({ width: 20, height: 20 })
|
|
const icon = nativeImage.createFromPath(join(desktopRoot, 'icon.png')).resize({ width: 20, height: 20 })
|
|
@@ -331,16 +375,15 @@ else {
|
|
|
tray.setToolTip('夭夭')
|
|
tray.setToolTip('夭夭')
|
|
|
tray.setContextMenu(Menu.buildFromTemplate([{ label: '打开夭夭', click: show }, { label: '查看日志', click: () => shell.showItemInFolder(logFile) }, { type: 'separator' }, { label: '退出夭夭(后台继续运行)', click: () => requestQuit() }, { label: '停止后台服务并退出', click: () => requestQuit(true) }]))
|
|
tray.setContextMenu(Menu.buildFromTemplate([{ label: '打开夭夭', click: show }, { label: '查看日志', click: () => shell.showItemInFolder(logFile) }, { type: 'separator' }, { label: '退出夭夭(后台继续运行)', click: () => requestQuit() }, { label: '停止后台服务并退出', click: () => requestQuit(true) }]))
|
|
|
tray.on('click', show)
|
|
tray.on('click', show)
|
|
|
- if(!preferences.value.hostOnly)createWindow()
|
|
|
|
|
|
|
+ const startupRemote=(await chooseStartupMode())==='remote'&&await enterRemoteMode()
|
|
|
|
|
+ createWindow()
|
|
|
powerMonitor.on('resume', () => { if(!closing&&!quitting)void manager.check() })
|
|
powerMonitor.on('resume', () => { if(!closing&&!quitting)void manager.check() })
|
|
|
timer = setInterval(() => { void manager.check() }, 10_000); timer.unref()
|
|
timer = setInterval(() => { void manager.check() }, 10_000); timer.unref()
|
|
|
- if(preferences.value.hostOnly){
|
|
|
|
|
- log('仅桌面主机模式:不启动本机 Web 服务')
|
|
|
|
|
- }else{
|
|
|
|
|
|
|
+ if(!startupRemote){
|
|
|
|
|
+ syncModeMenu()
|
|
|
await manager.start().catch(error => log(error.message))
|
|
await manager.start().catch(error => log(error.message))
|
|
|
if(!closing)environmentHost.start()
|
|
if(!closing)environmentHost.start()
|
|
|
}
|
|
}
|
|
|
- if(!closing)hostManager.start().catch(error=>{hostManager.publish(error.message)})
|
|
|
|
|
- if(!closing&&!preferences.value.hostOnly&&manager.state.phase==='ready')await runnerManager.start().catch(error=>{runnerManager.publish(error.message)})
|
|
|
|
|
|
|
+ if(!closing&&!startupRemote&&manager.state.phase==='ready')await runnerManager.start().catch(error=>{runnerManager.publish(error.message)})
|
|
|
}).catch(error => { log(error.stack || error.message); quitting = true; app.quit() })
|
|
}).catch(error => { log(error.stack || error.message); quitting = true; app.quit() })
|
|
|
}
|
|
}
|