132 lines
4.0 KiB
TypeScript
132 lines
4.0 KiB
TypeScript
import { app, BrowserWindow, ipcMain, dialog, shell, globalShortcut } from 'electron'
|
|
import { join } from 'path'
|
|
import { v4 as uuidv4 } from 'uuid'
|
|
import { detectSteamGames, SteamGame } from './steam'
|
|
import { readCustomGames, writeCustomGames, CustomGame } from './config'
|
|
import { launchSteamGame, launchExe } from './launcher'
|
|
|
|
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, // allow loading steam CDN images and local file:// images
|
|
},
|
|
})
|
|
|
|
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 Handlers ────────────────────────────────────────────────────────────
|
|
|
|
ipcMain.handle('games:get-all', async () => {
|
|
const [steamGames, customGames] = await Promise.all([
|
|
detectSteamGames(),
|
|
Promise.resolve(readCustomGames()),
|
|
])
|
|
return [...steamGames, ...customGames]
|
|
})
|
|
|
|
ipcMain.handle('games:launch', (_event, id: string) => {
|
|
if (id.startsWith('steam_')) {
|
|
const appid = id.replace('steam_', '')
|
|
launchSteamGame(appid)
|
|
return { ok: true }
|
|
}
|
|
|
|
const customs = readCustomGames()
|
|
const game = customs.find((g) => g.id === id)
|
|
if (!game) return { ok: false, error: 'Game not found' }
|
|
|
|
try {
|
|
launchExe(game.exe, game.args)
|
|
return { ok: true }
|
|
} catch (e) {
|
|
return { ok: false, error: String(e) }
|
|
}
|
|
})
|
|
|
|
ipcMain.handle('admin:add-game', (_event, game: Omit<CustomGame, 'id' | 'source'>) => {
|
|
const customs = readCustomGames()
|
|
const newGame: CustomGame = {
|
|
...game,
|
|
id: uuidv4(),
|
|
source: 'custom',
|
|
args: game.args ?? [],
|
|
image: game.image ?? '',
|
|
category: game.category ?? 'Other',
|
|
}
|
|
writeCustomGames([...customs, newGame])
|
|
return newGame
|
|
})
|
|
|
|
ipcMain.handle('admin:remove-game', (_event, id: string) => {
|
|
const customs = readCustomGames()
|
|
writeCustomGames(customs.filter((g) => g.id !== id))
|
|
return { ok: true }
|
|
})
|
|
|
|
ipcMain.handle('admin:update-game', (_event, updated: CustomGame) => {
|
|
const customs = readCustomGames()
|
|
writeCustomGames(customs.map((g) => (g.id === updated.id ? { ...g, ...updated } : g)))
|
|
return { ok: true }
|
|
})
|
|
|
|
ipcMain.handle('dialog:pick-exe', async () => {
|
|
const result = await dialog.showOpenDialog({
|
|
title: 'Выберите исполняемый файл',
|
|
filters: [{ name: 'Executable', extensions: ['exe', 'bat', 'cmd'] }],
|
|
properties: ['openFile'],
|
|
})
|
|
return result.canceled ? null : result.filePaths[0]
|
|
})
|
|
|
|
ipcMain.handle('dialog:pick-image', async () => {
|
|
const result = await dialog.showOpenDialog({
|
|
title: 'Выберите обложку',
|
|
filters: [{ name: 'Image', extensions: ['png', 'jpg', 'jpeg', 'webp', 'gif'] }],
|
|
properties: ['openFile'],
|
|
})
|
|
return result.canceled ? null : result.filePaths[0]
|
|
})
|
|
|
|
// ── App lifecycle ────────────────────────────────────────────────────────────
|
|
|
|
app.whenReady().then(() => {
|
|
createWindow()
|
|
|
|
// Ctrl+Alt+A → open admin panel
|
|
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()
|
|
})
|