|
@@ -5,6 +5,7 @@ import { homedir } from 'node:os'
|
|
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
|
import { DesktopServiceManager } from './service-manager.mjs'
|
|
import { DesktopServiceManager } from './service-manager.mjs'
|
|
|
import { DesktopRunnerManager } from './runner-manager.mjs'
|
|
import { DesktopRunnerManager } from './runner-manager.mjs'
|
|
|
|
|
+import { DesktopPreferences } from './preferences.mjs'
|
|
|
|
|
|
|
|
const desktopRoot = dirname(fileURLToPath(import.meta.url))
|
|
const desktopRoot = dirname(fileURLToPath(import.meta.url))
|
|
|
const fixtureHome = process.env.HERMES_YAOYAO_DESKTOP_TEST_HOME
|
|
const fixtureHome = process.env.HERMES_YAOYAO_DESKTOP_TEST_HOME
|
|
@@ -21,6 +22,8 @@ else {
|
|
|
const logFile = join(logRoot, 'server.log')
|
|
const logFile = join(logRoot, 'server.log')
|
|
|
const log = text => appendFileSync(logFile, `${new Date().toISOString()} ${text.trim()}\n`, { mode: 0o600 })
|
|
const log = text => appendFileSync(logFile, `${new Date().toISOString()} ${text.trim()}\n`, { mode: 0o600 })
|
|
|
const home = fixtureHome || process.env.HERMES_YAOYAO_HOME || join(homedir(), '.hermes-yaoyao')
|
|
const home = fixtureHome || process.env.HERMES_YAOYAO_HOME || join(homedir(), '.hermes-yaoyao')
|
|
|
|
|
+ const preferences = new DesktopPreferences(home)
|
|
|
|
|
+ let startHidden = false
|
|
|
const port = Number(process.env.HERMES_YAOYAO_DESKTOP_PORT || 15300)
|
|
const port = Number(process.env.HERMES_YAOYAO_DESKTOP_PORT || 15300)
|
|
|
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error('桌面服务端口无效')
|
|
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error('桌面服务端口无效')
|
|
|
|
|
|
|
@@ -79,7 +82,7 @@ else {
|
|
|
} else callback(['notifications', 'clipboard-sanitized-write', 'fullscreen'].includes(permission))
|
|
} else callback(['notifications', 'clipboard-sanitized-write', 'fullscreen'].includes(permission))
|
|
|
})
|
|
})
|
|
|
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', () => window.show())
|
|
|
|
|
|
|
+ window.once('ready-to-show', () => { if (!startHidden) window.show() })
|
|
|
if (manager?.state.phase === 'ready') void window.loadURL(manager.state.url)
|
|
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'))
|
|
|
}
|
|
}
|
|
@@ -110,6 +113,8 @@ else {
|
|
|
})()
|
|
})()
|
|
|
})
|
|
})
|
|
|
app.whenReady().then(async () => {
|
|
app.whenReady().then(async () => {
|
|
|
|
|
+ await preferences.load()
|
|
|
|
|
+ startHidden = preferences.value.backgroundAtLogin && app.getLoginItemSettings().wasOpenedAtLogin
|
|
|
const packageVersion = JSON.parse(readFileSync(join(root, 'release.json'), 'utf8')).webVersion
|
|
const packageVersion = JSON.parse(readFileSync(join(root, 'release.json'), 'utf8')).webVersion
|
|
|
manager = new DesktopServiceManager({ home, port, version: packageVersion, log, onState: stateChanged,
|
|
manager = new DesktopServiceManager({ home, port, version: packageVersion, log, onState: stateChanged,
|
|
|
fork: ({ home, port }) => {
|
|
fork: ({ home, port }) => {
|
|
@@ -139,7 +144,13 @@ else {
|
|
|
Menu.setApplicationMenu(Menu.buildFromTemplate([
|
|
Menu.setApplicationMenu(Menu.buildFromTemplate([
|
|
|
{ label: '夭夭', submenu: [{ role: 'about', label: '关于夭夭' }, { type: 'separator' },
|
|
{ label: '夭夭', submenu: [{ role: 'about', label: '关于夭夭' }, { type: 'separator' },
|
|
|
{ label: '显示窗口', click: show }, { label: '在浏览器中打开', click: () => { if (manager.state.url) safeExternal(manager.state.url) } },
|
|
{ label: '显示窗口', click: show }, { label: '在浏览器中打开', click: () => { if (manager.state.url) safeExternal(manager.state.url) } },
|
|
|
- { label: '查看服务日志', click: () => shell.showItemInFolder(logFile) }, { type: 'separator' },
|
|
|
|
|
|
|
+ { label: '查看服务日志', click: () => shell.showItemInFolder(logFile) },
|
|
|
|
|
+ { id:'desktop-login', label:'登录 macOS 时启动', type:'checkbox', enabled:app.isPackaged, checked:app.getLoginItemSettings().openAtLogin,
|
|
|
|
|
+ click:async item=>{try{app.setLoginItemSettings({openAtLogin:item.checked});item.checked=app.getLoginItemSettings().openAtLogin}catch(error){item.checked=app.getLoginItemSettings().openAtLogin;await dialog.showMessageBox(window,{type:'error',message:'无法修改登录启动设置',detail:error.message})}} },
|
|
|
|
|
+ { id:'desktop-background', label:'登录启动时仅驻留菜单栏', type:'checkbox', checked:preferences.value.backgroundAtLogin,
|
|
|
|
|
+ click:async item=>{try{await preferences.setBackgroundAtLogin(item.checked)}catch(error){item.checked=preferences.value.backgroundAtLogin;await dialog.showMessageBox(window,{type:'error',message:error.message})}} },
|
|
|
|
|
+ { id:'desktop-update-help', label:'App 更新与回退…', click:()=>dialog.showMessageBox(window,{type:'info',message:`夭夭 App ${packageVersion}`,detail:`当前服务:${manager.state.version||'尚未就绪'}${manager.state.external?'(已有外部服务)':'(App 托管)'}\n\n更新:先停止任务并退出 App,备份数据目录,保留旧 App,再用经过签名验证的新 App 替换。新 App 打开后核对服务版本及任务状态。\n\n回退:退出新 App,恢复旧 App;若版本包含数据迁移,同时恢复更新前的数据备份。App 不会自动替换已有外部服务。\n\n数据目录:${home}`,buttons:['知道了','打开数据目录']}).then(result=>{if(result.response===1)shell.showItemInFolder(home)}) },
|
|
|
|
|
+ { type: 'separator' },
|
|
|
{ role: 'hide', label: '隐藏夭夭' }, { role: 'hideOthers', label: '隐藏其他' }, { role: 'unhide', label: '显示全部' },
|
|
{ role: 'hide', label: '隐藏夭夭' }, { role: 'hideOthers', label: '隐藏其他' }, { role: 'unhide', label: '显示全部' },
|
|
|
{ type: 'separator' }, { role: 'quit', label: '退出夭夭' }] },
|
|
{ type: 'separator' }, { role: 'quit', label: '退出夭夭' }] },
|
|
|
{label:'执行节点',submenu:[{id:'runner-status',label:'未配置执行节点',enabled:false},{type:'separator'},
|
|
{label:'执行节点',submenu:[{id:'runner-status',label:'未配置执行节点',enabled:false},{type:'separator'},
|