export const LIBRARY_CATEGORIES = [ { id: "games", label: "Games" }, { id: "software", label: "Software" }, { id: "unified", label: "Unified Library" }, ]; export const GENRE_FILTERS = [ "All Genres", "Action", "RPG", "Strategy", "Indie", "Latest Installed", "Recently Played", ]; export const PLATFORM_FILTERS = ["all", "steam", "gog", "epic", "native", "emulated", "other"]; export const SORT_OPTIONS = [ { id: "recentlyPlayed", label: "Recently played" }, { id: "recentlyInstalled", label: "Recently installed" }, { id: "alphabetical", label: "Alphabetical" }, { id: "playtime", label: "Playtime" }, { id: "platform", label: "Platform" }, ]; export const SOURCE_LABELS = { steam: "Steam", gog: "GOG", epic: "Epic", native: "Native", emulated: "Emulated", other: "Other", }; export const TYPE_LABELS = { game: "Game", software: "Software", tool: "Tool", launcher: "Launcher", }; export const PLAY_STATE_LABELS = { all: "All play states", played: "Played", unplayed: "Unplayed", }; export const createDefaultLibraryQuery = () => ({ category: "games", genre: "All Genres", platform: "all", installedOnly: false, playState: "all", coOpOnly: false, achievementsOnly: false, sortBy: "recentlyPlayed", includeHidden: false, }); const sourceFromBackend = (source) => { const normalized = String(source ?? "").toLowerCase(); if (normalized === "local") return "native"; if (["steam", "gog", "epic", "native", "emulated"].includes(normalized)) return normalized; return "other"; }; const typeFromBackend = (kind) => { const normalized = String(kind ?? "").toLowerCase(); if (["game", "software", "tool", "launcher"].includes(normalized)) return normalized; return "game"; }; export const initialsForTitle = (title) => String(title ?? "Nebula") .split(/\s+/) .filter(Boolean) .slice(0, 2) .map((part) => part[0]?.toUpperCase()) .join("") || "OS"; export const formatPlaytime = (minutes = 0) => { if (!minutes) return "Not played"; if (minutes < 60) return `${minutes}m`; const hours = Math.floor(minutes / 60); const remainder = minutes % 60; return remainder ? `${hours}h ${remainder}m` : `${hours}h`; }; export const formatShortDate = (value) => { if (!value) return "Never"; const date = new Date(value); if (Number.isNaN(date.getTime())) return "Never"; return date.toLocaleDateString([], { month: "short", day: "numeric" }); }; export const normalizeLibraryItem = (raw, index = 0, convertFileSrc = null) => { const title = raw?.userTitle || raw?.title || "Unknown App"; const source = sourceFromBackend(raw?.platformSource ?? raw?.source); const type = typeFromBackend(raw?.appKind ?? raw?.type); const convert = (path) => (path && convertFileSrc ? convertFileSrc(path) : path || null); const convertArray = (paths) => (Array.isArray(paths) ? paths.map(convert).filter(Boolean) : []); const installed = raw?.installed ?? Boolean(raw?.installPath || raw?.install_path); return { id: String(raw?.id ?? `backend-${index}`), backendId: raw?.id ?? null, title, type, source, genre: Array.isArray(raw?.genre) ? raw.genre : Array.isArray(raw?.genres) ? raw.genres : type === "game" ? ["Action"] : ["Utilities"], installed, installPath: raw?.installPath ?? raw?.install_path ?? null, executablePath: raw?.executablePath ?? raw?.executable_path ?? null, coverImage: convert(raw?.coverImage ?? raw?.cover_image), bannerImage: convert(raw?.bannerImage ?? raw?.heroImage ?? raw?.hero_image), iconImage: convert(raw?.iconImage ?? raw?.icon_image), screenshots: convertArray(raw?.screenshots ?? []), description: raw?.description || "Scanned from your local library. Metadata can be enriched later by Steam, GOG, Epic, emulator, and local metadata providers.", longDescription: raw?.longDescription ?? null, lastPlayed: raw?.lastPlayed ?? null, installedAt: raw?.installedAt ?? raw?.createdAt ?? null, playtimeMinutes: Number(raw?.playtimeMinutes ?? 0), supportsController: Boolean(raw?.supportsController ?? type === "game"), steamDeckVerified: Boolean(raw?.steamDeckVerified ?? source === "steam"), achievementsSupported: Boolean(raw?.achievementsSupported ?? false), achievementsUnlocked: raw?.achievementsUnlocked ?? null, achievementsTotal: raw?.achievementsTotal ?? null, hidden: Boolean(raw?.hidden ?? raw?.userHidden ?? false), multiplayer: Boolean(raw?.multiplayer ?? false), coOp: Boolean(raw?.coOp ?? false), accent: raw?.accent ?? ["#4fd8ff", "#9d4fe0", "#1f7aff", "#39ffd2"][index % 4], }; }; export const createMockLibraryItems = () => [ { id: "mock-starfall", title: "Starfall Protocol", type: "game", source: "steam", genre: ["Action", "RPG"], installed: true, installPath: "C:/Games/Starfall Protocol", executablePath: "C:/Games/Starfall Protocol/starfall.exe", description: "Pilot a relic fighter through collapsing gates in a neon campaign built for controller play.", longDescription: "Experience a fast-paced action-RPG where you pilot experimental fighter craft through a collapsing interdimensional gateway. Built from the ground up with controller support, Starfall Protocol features real-time combat, intricate level design, and a gripping sci-fi narrative. Perfect for couch co-op sessions or solo playthroughs.", lastPlayed: "2026-05-15T21:15:00Z", installedAt: "2026-05-01T08:30:00Z", playtimeMinutes: 1874, supportsController: true, steamDeckVerified: true, achievementsSupported: true, achievementsUnlocked: 28, achievementsTotal: 54, multiplayer: true, coOp: true, screenshots: [ "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='960' height='540'%3E%3Crect fill='%234fd8ff' width='960' height='540'/%3E%3Ctext x='480' y='270' font-size='24' fill='white' text-anchor='middle' dominant-baseline='middle'%3EStarfall Protocol - Screenshot 1%3C/text%3E%3C/svg%3E", "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='960' height='540'%3E%3Crect fill='%239d4fe0' width='960' height='540'/%3E%3Ctext x='480' y='270' font-size='24' fill='white' text-anchor='middle' dominant-baseline='middle'%3EStarfall Protocol - Screenshot 2%3C/text%3E%3C/svg%3E", "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='960' height='540'%3E%3Crect fill='%231f7aff' width='960' height='540'/%3E%3Ctext x='480' y='270' font-size='24' fill='white' text-anchor='middle' dominant-baseline='middle'%3EStarfall Protocol - Screenshot 3%3C/text%3E%3C/svg%3E", ], accent: "#4fd8ff", }, { id: "mock-iron-vault", title: "Iron Vault Tactics", type: "game", source: "gog", genre: ["Strategy", "Indie"], installed: true, installPath: "D:/Games/Iron Vault Tactics", executablePath: "D:/Games/Iron Vault Tactics/ivt.exe", description: "A turn-based tactics sandbox with long-form campaigns, mod support, and couch co-op skirmishes.", longDescription: "Command your squad through dynamic turn-based tactical battles in Iron Vault Tactics. Features mod support, deep unit customization, engaging campaign narratives, and intense couch co-op multiplayer. Each decision matters in this tactical masterpiece.", lastPlayed: "2026-05-10T11:00:00Z", installedAt: "2026-04-18T18:00:00Z", playtimeMinutes: 942, supportsController: true, achievementsSupported: false, multiplayer: true, coOp: true, screenshots: [ "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='960' height='540'%3E%3Crect fill='%2339ffd2' width='960' height='540'/%3E%3Ctext x='480' y='270' font-size='24' fill='white' text-anchor='middle' dominant-baseline='middle'%3EIron Vault Tactics - Screenshot 1%3C/text%3E%3C/svg%3E", "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='960' height='540'%3E%3Crect fill='%234fd8ff' width='960' height='540'/%3E%3Ctext x='480' y='270' font-size='24' fill='white' text-anchor='middle' dominant-baseline='middle'%3EIron Vault Tactics - Screenshot 2%3C/text%3E%3C/svg%3E", "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='960' height='540'%3E%3Crect fill='%239d4fe0' width='960' height='540'/%3E%3Ctext x='480' y='270' font-size='24' fill='white' text-anchor='middle' dominant-baseline='middle'%3EIron Vault Tactics - Screenshot 3%3C/text%3E%3C/svg%3E", ], accent: "#39ffd2", }, { id: "mock-nebula-paint", title: "Nebula Paint Studio", type: "software", source: "native", genre: ["Creative", "Utilities"], installed: true, installPath: "C:/Program Files/Nebula Paint", executablePath: "C:/Program Files/Nebula Paint/paint.exe", description: "A TV-friendly concept art tool for quick capture, markup, and launcher artwork editing.", longDescription: "Nebula Paint Studio is a professional-grade digital painting application optimized for controller input and TV display. Create stunning concept art, edit game launcher artwork, and collaborate seamlessly with frame-perfect precision.", lastPlayed: "2026-05-11T06:20:00Z", installedAt: "2026-03-12T09:00:00Z", playtimeMinutes: 223, supportsController: true, achievementsSupported: false, multiplayer: false, coOp: false, screenshots: [ "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='960' height='540'%3E%3Crect fill='%239d4fe0' width='960' height='540'/%3E%3Ctext x='480' y='270' font-size='24' fill='white' text-anchor='middle' dominant-baseline='middle'%3ENebula Paint Studio - Screenshot 1%3C/text%3E%3C/svg%3E", "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='960' height='540'%3E%3Crect fill='%234fd8ff' width='960' height='540'/%3E%3Ctext x='480' y='270' font-size='24' fill='white' text-anchor='middle' dominant-baseline='middle'%3ENebula Paint Studio - Screenshot 2%3C/text%3E%3C/svg%3E", ], accent: "#9d4fe0", }, { id: "mock-ember", title: "Emberline", type: "game", source: "epic", genre: ["Action", "Indie"], installed: false, installPath: null, executablePath: null, description: "Wishlist entry from a linked store. Install support will be routed through the Epic integration later.", longDescription: "Emberline is an indie action game with stunning visuals and engaging gameplay. Add it to your library to stay updated on new releases, sales, and updates.", lastPlayed: null, installedAt: null, playtimeMinutes: 0, supportsController: true, steamDeckVerified: false, achievementsSupported: true, achievementsUnlocked: 0, achievementsTotal: 32, multiplayer: false, coOp: false, screenshots: [ "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='960' height='540'%3E%3Crect fill='%231f7aff' width='960' height='540'/%3E%3Ctext x='480' y='270' font-size='24' fill='white' text-anchor='middle' dominant-baseline='middle'%3EEmberline - Screenshot 1%3C/text%3E%3C/svg%3E", "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='960' height='540'%3E%3Crect fill='%239d4fe0' width='960' height='540'/%3E%3Ctext x='480' y='270' font-size='24' fill='white' text-anchor='middle' dominant-baseline='middle'%3EEmberline - Screenshot 2%3C/text%3E%3C/svg%3E", ], accent: "#1f7aff", }, { id: "mock-retro-core", title: "RetroCore Station", type: "launcher", source: "emulated", genre: ["Launcher", "Retro"], installed: true, installPath: "D:/Emulation/RetroCore", executablePath: "D:/Emulation/RetroCore/retrocore.exe", description: "A controller-native emulator hub prepared for future ROM library scanning and save sync.", longDescription: "RetroCore Station is your gateway to classic gaming. Play thousands of retro games with full controller support, save synchronization, and achievement tracking across multiple emulation systems.", lastPlayed: "2026-05-14T04:10:00Z", installedAt: "2026-05-03T15:10:00Z", playtimeMinutes: 517, supportsController: true, achievementsSupported: true, achievementsUnlocked: 88, achievementsTotal: 210, multiplayer: true, coOp: true, screenshots: [ "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='960' height='540'%3E%3Crect fill='%23ffb84f' width='960' height='540'/%3E%3Ctext x='480' y='270' font-size='24' fill='white' text-anchor='middle' dominant-baseline='middle'%3ERetroCore Station - Screenshot 1%3C/text%3E%3C/svg%3E", "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='960' height='540'%3E%3Crect fill='%234fd8ff' width='960' height='540'/%3E%3Ctext x='480' y='270' font-size='24' fill='white' text-anchor='middle' dominant-baseline='middle'%3ERetroCore Station - Screenshot 2%3C/text%3E%3C/svg%3E", "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='960' height='540'%3E%3Crect fill='%239d4fe0' width='960' height='540'/%3E%3Ctext x='480' y='270' font-size='24' fill='white' text-anchor='middle' dominant-baseline='middle'%3ERetroCore Station - Screenshot 3%3C/text%3E%3C/svg%3E", ], accent: "#ffb84f", }, { id: "mock-orbit-tools", title: "Orbit Mod Tools", type: "tool", source: "other", genre: ["Utilities", "Game Development"], installed: false, installPath: null, executablePath: null, description: "Tooling placeholder for future mod SDK detection, dependency checks, and per-game utilities.", longDescription: "Orbit Mod Tools provides a comprehensive suite of utilities for game modding, including SDK management, dependency resolution, and per-game optimization tools. Essential for serious mod creators.", lastPlayed: null, installedAt: null, playtimeMinutes: 0, supportsController: false, achievementsSupported: false, multiplayer: false, coOp: false, screenshots: [], accent: "#ff6b9a", }, ].map((item, index) => normalizeLibraryItem(item, index)); export const summarizeLibrary = (items) => { const visible = items.filter((item) => !item.hidden); return { total: visible.length, games: visible.filter((item) => item.type === "game").length, verified: visible.filter((item) => item.steamDeckVerified).length, hidden: items.filter((item) => item.hidden).length, installed: visible.filter((item) => item.installed).length, }; };