Add library UI, spatial nav & game launcher

Introduce a full library feature: add new frontend modules (libraryBridge, libraryComponents, libraryController, libraryFilters, libraryModel) and refactor library view JS to use them; implement rich library CSS and UI templates in index.html (new hints, sidebar entries). Rework spatial navigation (src/core/nav.js) with a geometry-based picker, anchor behavior, sidebar/content regions and integration hook for external nebula navigation; add navigation refresh handling in main.js. Update controller/state glyphs to include a 'clear' button. On the Tauri backend, add a launch_library_game command and its result type, wire it into lib.rs commands, and adjust DB upsert to handle app_kind correctly and export list_games usage. Misc: various wiring to load/scan/launch library items and improved focus/keyboard behaviors.
This commit is contained in:
2026-05-16 20:45:54 +12:00
parent 9de7a338a4
commit a04ae7803b
14 changed files with 1944 additions and 581 deletions
+4 -1
View File
@@ -1,7 +1,9 @@
mod library;
mod storage;
use library::commands::{list_library_games, scan_library_command, update_library_game};
use library::commands::{
launch_library_game, list_library_games, scan_library_command, update_library_game,
};
use rusqlite::{params, Connection, OptionalExtension};
use serde::Serialize;
use std::time::{SystemTime, UNIX_EPOCH};
@@ -199,6 +201,7 @@ pub fn run() {
get_first_user,
create_user,
list_library_games,
launch_library_game,
scan_library_command,
update_library_game
])
+52 -1
View File
@@ -1,9 +1,18 @@
use super::db::{ensure_library_schema, update_customization};
use super::db::{ensure_library_schema, list_games, update_customization};
use super::models::{GameCustomizationRequest, LibraryGame, LibraryScanRequest, ScanSummary};
use super::{list_visible_games, scan_library};
use crate::storage::AppStorage;
use serde::Serialize;
use std::path::PathBuf;
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LaunchLibraryGameResult {
pub launched: bool,
pub action: String,
pub message: String,
}
#[tauri::command]
pub async fn scan_library_command(
request: LibraryScanRequest,
@@ -45,3 +54,45 @@ pub async fn update_library_game(
.await
.map_err(|err| format!("Library update task failed: {err}"))?
}
#[tauri::command]
pub async fn launch_library_game(
game_id: i64,
storage: tauri::State<'_, AppStorage>,
) -> Result<LaunchLibraryGameResult, String> {
let storage = storage.inner().clone();
tauri::async_runtime::spawn_blocking(move || {
let conn = storage.connect_library()?;
ensure_library_schema(&conn)?;
let game = list_games(&conn, true)?
.into_iter()
.find(|game| game.id == game_id)
.ok_or_else(|| "Library app was not found.".to_string())?;
let launch_target = game
.launch_command
.as_deref()
.or(game.executable_path.as_deref())
.unwrap_or(game.install_path.as_str());
// Future provider launchers plug in here:
// Steam: steam://run/<appid>, GOG/Epic URI handlers, emulator profiles,
// and native executable spawning with per-app environment overrides.
println!(
"[NebulaOS] launch placeholder: {} via {}",
game.user_title.as_deref().unwrap_or(&game.title),
launch_target
);
Ok(LaunchLibraryGameResult {
launched: true,
action: "placeholder".to_string(),
message: format!(
"Launch requested for {}.",
game.user_title.unwrap_or(game.title)
),
})
})
.await
.map_err(|err| format!("Library launch task failed: {err}"))?
}
+1 -1
View File
@@ -133,7 +133,7 @@ pub fn upsert_candidate(
executable_path,
launch_command,
description,
app_kind.as_str(),
app_kind,
steam_app_type,
genres_json,
steam_categories_json,