// Pithos desktop: an Electron window over the same control server the web // console runs. The server binds a random loopback port and the window signs // in with the one-time secret, so nothing else on the machine can drive it. const { app, BrowserWindow, shell, Menu, dialog } = require('electron'); const path = require('node:path'); const { pathToFileURL } = require('node:url'); let pithos; let win; if (!app.requestSingleInstanceLock()) { app.quit(); } else { app.on('second-instance', () => { if (win) { if (win.isMinimized()) win.restore(); win.focus(); } }); app.whenReady().then(start).catch((err) => { dialog.showErrorBox('Pithos failed to start', String(err?.stack || err)); app.quit(); }); } async function start() { const { createPithos } = await import(pathToFileURL(path.join(__dirname, '..', 'core', 'server.js')).href); pithos = await createPithos({ host: '127.0.0.1', port: 0, hostName: 'desktop', configFile: process.env.S3D_CONFIG_FILE || undefined, // The pinned s3d release installs here, outside the (read-only) app bundle. binDir: path.join(app.getPath('userData'), 'bin'), openExternal: (url) => shell.openExternal(url), }); Menu.setApplicationMenu(process.platform === 'darwin' ? Menu.buildFromTemplate([ { role: 'appMenu' }, { role: 'editMenu' }, { role: 'viewMenu' }, { role: 'windowMenu' }, ]) : null); win = new BrowserWindow({ width: 1180, height: 800, minWidth: 380, minHeight: 500, title: 'Pithos', backgroundColor: '#0f1214', autoHideMenuBar: true, webPreferences: { contextIsolation: true, nodeIntegration: false, sandbox: true }, }); const origin = new URL(pithos.url).origin; // Keep the window on the console; anything else goes to the system browser. win.webContents.on('will-navigate', (e, url) => { if (new URL(url).origin !== origin) { e.preventDefault(); if (url.startsWith('https://')) shell.openExternal(url); } }); win.webContents.setWindowOpenHandler(({ url }) => { if (url.startsWith('https://')) shell.openExternal(url); return { action: 'deny' }; }); win.loadURL(pithos.authUrl); win.on('closed', () => { win = null; }); } let quitting = false; app.on('before-quit', (e) => { if (quitting || !pithos) return; // Stop s3d cleanly so its upload buffer and database are flushed to disk. e.preventDefault(); quitting = true; pithos.close().finally(() => app.quit()); }); app.on('window-all-closed', () => app.quit());