Add mode READMEs and move Bigscreen app

Add comprehensive READMEs for Bigscreen and Desktop modes, and relocate the Tauri frontend prototype into a new Bigscreen/ subdirectory. This moves package.json/package-lock, src, src-tauri, assets, views, styles and related files into Bigscreen/ to separate the controller-first shell from top-level docs. Update the root README.md to reframe the project as "NebulaOS", describe Bigscreen/Desktop modes, the vision, tech stack and development notes. This reorganizes the repo layout for clearer mode separation and documentation.
This commit is contained in:
Andrew Zambazos
2026-05-21 20:11:18 +12:00
parent b141c0a058
commit f0d2926872
87 changed files with 974 additions and 322 deletions
+309
View File
@@ -0,0 +1,309 @@
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,
};
};