import { app, BrowserWindow, Menu, shell, ipcMain } from 'electron' import { join } from 'node:path' import { startWebUiServer, stopWebUiServer, getToken } from './webui-server' import { hermesBinExists, hermesBin } from './paths' import { initAutoUpdater } from './updater' const PORT = Number(process.env.HERMES_DESKTOP_PORT) || 8748 let mainWindow: BrowserWindow | null = null let serverUrl: string | null = null function createWindow() { mainWindow = new BrowserWindow({ width: 1280, height: 820, minWidth: 960, minHeight: 600, title: 'Hermes Studio', backgroundColor: '#1a1a1a', autoHideMenuBar: true, webPreferences: { preload: join(__dirname, '..', 'preload', 'index.js'), contextIsolation: true, nodeIntegration: false, sandbox: false, }, }) // External links → system browser mainWindow.webContents.setWindowOpenHandler(({ url }) => { if (url.startsWith('http://127.0.0.1') || url.startsWith('http://localhost')) { return { action: 'allow' } } shell.openExternal(url).catch(() => undefined) return { action: 'deny' } }) // If the Web UI server is already up (re-opening window after close on // macOS), go straight to it. Otherwise show a loading splash; bootstrap() // will swap in the real URL once the server is ready. if (serverUrl) { mainWindow.loadURL(serverUrl) } else { mainWindow.loadURL(splashHtml()) } } function splashHtml(): string { const html = `
${msg}
`,
))
}
}
}
ipcMain.handle('hermes-desktop:get-token', () => getToken())
const gotLock = app.requestSingleInstanceLock()
if (!gotLock) {
app.quit()
} else {
app.on('second-instance', () => {
if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore()
mainWindow.focus()
}
})
app.whenReady().then(() => {
// Drop the default File/Edit/View/Window menu on Windows/Linux. The web
// UI provides its own in-page controls, so the native menu bar is just
// visual clutter. macOS keeps a menu (system requirement) but Electron's
// default is fine there.
if (process.platform !== 'darwin') Menu.setApplicationMenu(null)
createWindow()
bootstrap()
initAutoUpdater()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
} else if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore()
mainWindow.show()
mainWindow.focus()
}
})
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
app.on('before-quit', async (e) => {
e.preventDefault()
await stopWebUiServer().catch(() => undefined)
app.exit(0)
})
}