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>
65 lines
2.3 KiB
TypeScript
65 lines
2.3 KiB
TypeScript
import { useState, useEffect, useCallback } from 'react'
|
|
import type { AppEntry, CustomGame, Game } from '../types'
|
|
|
|
interface ScannedExe { name: string; exe: string }
|
|
interface AppSettings { steamPath: string | null; gameFolders: string[] }
|
|
|
|
declare global {
|
|
interface Window {
|
|
launcher: {
|
|
getGames: () => Promise<Game[]>
|
|
launchGame: (id: string) => Promise<{ ok: boolean; error?: string; recent?: string[] }>
|
|
// Custom games
|
|
addCustomGame: (g: Omit<CustomGame, 'id' | 'source'>) => Promise<CustomGame>
|
|
removeGame: (id: string) => Promise<{ ok: boolean }>
|
|
updateGame: (g: CustomGame) => Promise<{ ok: boolean }>
|
|
// App entries
|
|
addApp: (a: Omit<AppEntry, 'id' | 'source'>) => Promise<AppEntry>
|
|
removeApp: (id: string) => Promise<{ ok: boolean }>
|
|
updateApp: (a: AppEntry) => Promise<{ ok: boolean }>
|
|
// Dialogs
|
|
pickExe: () => Promise<string | null>
|
|
pickImage: () => Promise<string | null>
|
|
pickFolder: (title?: string) => Promise<string | null>
|
|
// Settings
|
|
getSettings: () => Promise<AppSettings & { resolvedSteamPath: string | null }>
|
|
setSteamPath: (path: string | null) => Promise<AppSettings>
|
|
addGameFolder: (folder: string) => Promise<AppSettings>
|
|
removeGameFolder: (folder: string) => Promise<AppSettings>
|
|
// Icon extraction
|
|
extractIcon: (exePath: string) => Promise<string>
|
|
// Scanner
|
|
scanFolder: (folder: string) => Promise<ScannedExe[]>
|
|
importScanned: (exes: ScannedExe[]) => Promise<CustomGame[]>
|
|
// Favorites / recent
|
|
getFavorites: () => Promise<string[]>
|
|
toggleFavorite: (id: string) => Promise<string[]>
|
|
getRecent: () => Promise<string[]>
|
|
// Events
|
|
onAdminOpen: (cb: () => void) => () => void
|
|
}
|
|
}
|
|
}
|
|
|
|
export function useGames() {
|
|
const [games, setGames] = useState<Game[]>([])
|
|
const [loading, setLoading] = useState(true)
|
|
const [error, setError] = useState<string | null>(null)
|
|
|
|
const load = useCallback(async () => {
|
|
setLoading(true)
|
|
setError(null)
|
|
try {
|
|
setGames(await window.launcher.getGames())
|
|
} catch (e) {
|
|
setError(String(e))
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}, [])
|
|
|
|
useEffect(() => { load() }, [load])
|
|
|
|
return { games, loading, error, reload: load }
|
|
}
|