const { app, BrowserWindow, Menu, ipcMain, Notification, shell } = require('electron') const fs = require('fs') const path = require('path') const isDev = !!process.env.FABRIC_DESKTOP_URL const DEFAULT_DEV_URL = 'http://localhost:5173' const DEFAULT_PROD_ENTRY = path.join(__dirname, 'offline.html') function configPath() { return path.join(app.getPath('userData'), 'fabric-desktop.config.json') } function readConfig() { try { const raw = fs.readFileSync(configPath(), 'utf8') return JSON.parse(raw) } catch { return { centerApiBase: 'http://localhost:7001/api', guildApiBase: 'http://localhost:7002/api', guildSocketBase: 'http://localhost:7002/realtime', apiKey: '', } } } function writeConfig(next) { fs.mkdirSync(path.dirname(configPath()), { recursive: true }) fs.writeFileSync(configPath(), JSON.stringify(next, null, 2), 'utf8') return next } function createMenu() { const template = [ { label: 'Fabric', submenu: [ { role: 'reload', accelerator: 'CmdOrCtrl+R' }, { role: 'toggleDevTools', accelerator: 'CmdOrCtrl+Shift+I' }, { type: 'separator' }, { role: 'quit', accelerator: 'CmdOrCtrl+Q' }, ], }, { label: 'Edit', submenu: [{ role: 'undo' }, { role: 'redo' }, { type: 'separator' }, { role: 'copy' }, { role: 'paste' }], }, ] Menu.setApplicationMenu(Menu.buildFromTemplate(template)) } function createWindow() { const win = new BrowserWindow({ width: 1280, height: 840, minWidth: 1024, minHeight: 700, title: 'Fabric Desktop', autoHideMenuBar: false, webPreferences: { contextIsolation: true, nodeIntegration: false, sandbox: true, preload: path.join(__dirname, 'preload.js'), }, }) win.webContents.setWindowOpenHandler(() => ({ action: 'deny' })) win.webContents.on('will-navigate', (event, url) => { const allowedDev = isDev && url.startsWith((process.env.FABRIC_DESKTOP_URL || DEFAULT_DEV_URL)) const allowedFile = url.startsWith('file://') if (!allowedDev && !allowedFile) { event.preventDefault() shell.openExternal(url) } }) const devUrl = process.env.FABRIC_DESKTOP_URL || DEFAULT_DEV_URL if (isDev) { win.loadURL(devUrl) } else { win.loadFile(DEFAULT_PROD_ENTRY) } } app.whenReady().then(() => { createMenu() createWindow() ipcMain.handle('fabric:config:get', () => readConfig()) ipcMain.handle('fabric:config:set', (_evt, next) => writeConfig(next || {})) ipcMain.handle('fabric:notify', (_evt, payload) => { const title = payload?.title || 'Fabric' const body = payload?.body || '' if (Notification.isSupported()) { new Notification({ title, body }).show() return { ok: true } } return { ok: false } }) app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow() }) }) app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit() })