import { app, BrowserWindow, ipcMain, dialog, shell, globalShortcut } from 'electron' import { join } from 'path' import { detectSteamGames } from './steam' import { detectEpicGames } from './epic' import { CustomGame, AppEntry, readCustomGames, writeCustomGames, addCustomGame, removeCustomGame, updateCustomGame, readApps, addApp, removeApp, updateApp, } from './config' import { launchSteamGame, launchEpicGame, launchExe } from './launcher' import { getFavorites, toggleFavorite, getRecent, addRecent } from './favorites' let mainWindow: BrowserWindow | null = null function createWindow(): void { mainWindow = new BrowserWindow({ width: 1280, height: 800, minWidth: 960, minHeight: 600, backgroundColor: '#0f0f0f', title: 'Club Launcher', frame: true, autoHideMenuBar: true, webPreferences: { preload: join(__dirname, '../preload/preload.js'), contextIsolation: true, nodeIntegration: false, webSecurity: false, }, }) if (process.env['ELECTRON_RENDERER_URL']) { mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL']) mainWindow.webContents.openDevTools({ mode: 'detach' }) } else { mainWindow.loadFile(join(__dirname, '../renderer/index.html')) } mainWindow.on('closed', () => { mainWindow = null }) } // ── IPC ────────────────────────────────────────────────────────────────── ipcMain.handle('games:get-all', () => { const steamGames = detectSteamGames() const epicGames = detectEpicGames() const customGames = readCustomGames() const apps = readApps() return [...steamGames, ...epicGames, ...customGames, ...apps] }) ipcMain.handle('games:launch', (_event, id: string) => { if (id.startsWith('steam_')) { launchSteamGame(id.replace('steam_', '')) return { ok: true, recent: addRecent(id) } } if (id.startsWith('epic_')) { launchEpicGame(id.replace('epic_', '')) return { ok: true, recent: addRecent(id) } } const all = [...readCustomGames(), ...readApps()] const game = all.find((g) => g.id === id) if (!game) return { ok: false, error: 'Not found' } try { launchExe(game.exe, game.args) return { ok: true, recent: addRecent(id) } } catch (e) { return { ok: false, error: String(e) } } }) // Custom games ipcMain.handle('admin:add-game', (_e, d: Omit) => addCustomGame(d)) ipcMain.handle('admin:remove-game', (_e, id: string) => { removeCustomGame(id); return { ok: true } }) ipcMain.handle('admin:update-game', (_e, g: CustomGame) => { updateCustomGame(g); return { ok: true } }) // App entries ipcMain.handle('apps:add', (_e, d: Omit) => addApp(d)) ipcMain.handle('apps:remove', (_e, id: string) => { removeApp(id); return { ok: true } }) ipcMain.handle('apps:update', (_e, a: AppEntry) => { updateApp(a); return { ok: true } }) // Dialogs ipcMain.handle('dialog:pick-exe', async () => { const r = await dialog.showOpenDialog({ title: 'Выберите исполняемый файл', filters: [{ name: 'Executable', extensions: ['exe', 'bat', 'cmd', 'lnk'] }], properties: ['openFile'], }) return r.canceled ? null : r.filePaths[0] }) ipcMain.handle('dialog:pick-image', async () => { const r = await dialog.showOpenDialog({ title: 'Выберите обложку', filters: [{ name: 'Image', extensions: ['png', 'jpg', 'jpeg', 'webp', 'gif', 'ico'] }], properties: ['openFile'], }) return r.canceled ? null : r.filePaths[0] }) // Favorites / recent ipcMain.handle('favorites:get', () => getFavorites()) ipcMain.handle('favorites:toggle', (_e, id: string) => toggleFavorite(id)) ipcMain.handle('recent:get', () => getRecent()) // ── App lifecycle ──────────────────────────────────────────────────────── app.whenReady().then(() => { createWindow() globalShortcut.register('CommandOrControl+Alt+A', () => { mainWindow?.webContents.send('admin:open') }) app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow() }) }) app.on('window-all-closed', () => { globalShortcut.unregisterAll() if (process.platform !== 'darwin') app.quit() })