9a4c4fa943
- electron/main.ts: extractExeIcon() via app.getFileIcon(), saves PNG to
userData/icons/{md5}.png; IPC icon:extract; scanner:import now auto-extracts
- electron/preload.ts: expose extractIcon()
- AdminPanel: Scan button next to image field — extracts icon from current exe
(shows animated pulse while loading, error if no exe selected or extraction fails)
- GameCard: isExtractedIcon() detects icons/ path; renders centered 64x64 icon
on gradient background instead of stretched cover
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
54 lines
2.6 KiB
TypeScript
54 lines
2.6 KiB
TypeScript
import { contextBridge, ipcRenderer } from 'electron'
|
|
import type { CustomGame, AppEntry } from './config'
|
|
import type { AppSettings } from './settings'
|
|
import type { ScannedExe } from './scanner'
|
|
|
|
const launcher = {
|
|
// Games
|
|
getGames: () => ipcRenderer.invoke('games:get-all'),
|
|
launchGame: (id: string) =>
|
|
ipcRenderer.invoke('games:launch', id) as Promise<{ ok: boolean; error?: string; recent?: string[] }>,
|
|
|
|
// Custom games (admin)
|
|
addCustomGame: (g: Omit<CustomGame, 'id' | 'source'>) => ipcRenderer.invoke('admin:add-game', g),
|
|
removeGame: (id: string) => ipcRenderer.invoke('admin:remove-game', id),
|
|
updateGame: (g: CustomGame) => ipcRenderer.invoke('admin:update-game', g),
|
|
|
|
// App entries (admin)
|
|
addApp: (a: Omit<AppEntry, 'id' | 'source'>) => ipcRenderer.invoke('apps:add', a),
|
|
removeApp: (id: string) => ipcRenderer.invoke('apps:remove', id),
|
|
updateApp: (a: AppEntry) => ipcRenderer.invoke('apps:update', a),
|
|
|
|
// Dialogs
|
|
pickExe: () => ipcRenderer.invoke('dialog:pick-exe') as Promise<string | null>,
|
|
pickImage: () => ipcRenderer.invoke('dialog:pick-image') as Promise<string | null>,
|
|
pickFolder: (title?: string) => ipcRenderer.invoke('dialog:pick-folder', title) as Promise<string | null>,
|
|
|
|
// Settings
|
|
getSettings: () => ipcRenderer.invoke('settings:get') as Promise<AppSettings & { resolvedSteamPath: string | null }>,
|
|
setSteamPath: (path: string | null) => ipcRenderer.invoke('settings:set-steam-path', path) as Promise<AppSettings>,
|
|
addGameFolder: (folder: string) => ipcRenderer.invoke('settings:add-folder', folder) as Promise<AppSettings>,
|
|
removeGameFolder: (folder: string) => ipcRenderer.invoke('settings:remove-folder', folder) as Promise<AppSettings>,
|
|
|
|
// Icon extraction
|
|
extractIcon: (exePath: string) => ipcRenderer.invoke('icon:extract', exePath) as Promise<string>,
|
|
|
|
// Scanner
|
|
scanFolder: (folder: string) => ipcRenderer.invoke('scanner:scan', folder) as Promise<ScannedExe[]>,
|
|
importScanned: (exes: ScannedExe[]) => ipcRenderer.invoke('scanner:import', exes) as Promise<CustomGame[]>,
|
|
|
|
// Favorites / recent
|
|
getFavorites: () => ipcRenderer.invoke('favorites:get') as Promise<string[]>,
|
|
toggleFavorite: (id: string) => ipcRenderer.invoke('favorites:toggle', id) as Promise<string[]>,
|
|
getRecent: () => ipcRenderer.invoke('recent:get') as Promise<string[]>,
|
|
|
|
// Events
|
|
onAdminOpen: (cb: () => void) => {
|
|
ipcRenderer.on('admin:open', cb)
|
|
return () => ipcRenderer.removeListener('admin:open', cb)
|
|
},
|
|
}
|
|
|
|
contextBridge.exposeInMainWorld('launcher', launcher)
|
|
export type LauncherAPI = typeof launcher
|