diff --git a/Cargo.lock b/Cargo.lock index 2d05a4b6..6727a4ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3374,6 +3374,7 @@ dependencies = [ "tauri-plugin-single-instance", "tauri-plugin-updater", "tauri-plugin-window-state", + "taurpc", "thiserror 2.0.12", "tokio", "tracing", diff --git a/apps/oneclient/desktop/Cargo.toml b/apps/oneclient/desktop/Cargo.toml index b54a2104..0f0e8f14 100644 --- a/apps/oneclient/desktop/Cargo.toml +++ b/apps/oneclient/desktop/Cargo.toml @@ -39,6 +39,7 @@ tauri-plugin-dialog = { workspace = true } tauri-plugin-deep-link = { workspace = true } # code gen +taurpc = { workspace = true } specta = { workspace = true } specta-typescript = { workspace = true } strum = { workspace = true } diff --git a/apps/oneclient/desktop/src/api/commands.rs b/apps/oneclient/desktop/src/api/commands.rs index 85ed4cc7..436ecb42 100644 --- a/apps/oneclient/desktop/src/api/commands.rs +++ b/apps/oneclient/desktop/src/api/commands.rs @@ -1,30 +1,27 @@ -// pub mod cluster; -// pub mod users; -// pub mod processor; -// pub mod package; -// pub mod other; -// pub mod settings; +use onelauncher_core::error::{DaoError, LauncherResult}; +use tauri::Runtime; -// #[macro_export(local_inner_macros)] -// macro_rules! collect_commands { -// () => {{ -// use $crate::api::commands::*; -// tauri_specta::collect_commands![ -// users::get_users, -// users::get_user, -// users::remove_user, -// users::get_default_user, -// users::set_default_user, -// users::begin_ms_flow, +#[taurpc::procedures(path = "oneclient", export_to = "../frontend/src/bindings.gen.ts")] +pub trait OneClientApi { + async fn return_error() -> LauncherResult<()>; -// cluster::create_cluster, -// cluster::get_clusters, -// cluster::get_cluster_by_id, + async fn open_dev_tools(webview_window: tauri::WebviewWindow); +} -// settings::get_global_profile, -// settings::get_profile_or_default, +#[taurpc::ipc_type] +pub struct OneClientApiImpl; -// other::open_dev_tools, -// ] -// }}; -// } +#[taurpc::resolvers] +impl OneClientApi for OneClientApiImpl { + + async fn return_error(self) -> LauncherResult<()> { + let err = DaoError::NotFound.into(); + Err(err) + } + + async fn open_dev_tools(self, webview_window: tauri::WebviewWindow) { + #[cfg(feature = "devtools")] + webview_window.open_devtools(); + } + +} diff --git a/apps/oneclient/desktop/src/api/commands/cluster.rs b/apps/oneclient/desktop/src/api/commands/cluster.rs deleted file mode 100644 index 1bb53684..00000000 --- a/apps/oneclient/desktop/src/api/commands/cluster.rs +++ /dev/null @@ -1,40 +0,0 @@ -use onelauncher_core::{api::{cluster, setting_profiles}, entity::{clusters::Model, icon::Icon, loader::GameLoader}, migration::sea_orm}; -use serde::{Deserialize, Serialize}; -use specta::Type; -use crate::api::error::SerializableResult; - -#[derive(Serialize, Deserialize, Type)] -pub struct CreateCluster { - name: String, - mc_version: String, - mc_loader: GameLoader, - mc_loader_version: Option, - icon_url: Option -} - -#[specta::specta] -#[tauri::command] -pub async fn create_cluster(options: CreateCluster) -> SerializableResult { - let thing = cluster::create_cluster(&options.name, &options.mc_version, options.mc_loader, options.mc_loader_version.as_deref(), options.icon_url).await?; - - if setting_profiles::dao::get_profile_by_name(&options.name).await?.is_none() { - setting_profiles::create_profile(&options.name, async |mut profile| { - profile.mem_max = sea_orm::ActiveValue::Set(Some(2048)); - Ok(profile) - }).await?; - } - - Ok(thing) -} - -#[specta::specta] -#[tauri::command] -pub async fn get_clusters() -> SerializableResult> { - Ok(cluster::dao::get_all_clusters().await?) -} - -#[specta::specta] -#[tauri::command] -pub async fn get_cluster_by_id(id: i64) -> SerializableResult> { - Ok(cluster::dao::get_cluster_by_id(id).await?) -} \ No newline at end of file diff --git a/apps/oneclient/desktop/src/api/commands/other.rs b/apps/oneclient/desktop/src/api/commands/other.rs deleted file mode 100644 index 8d3014b3..00000000 --- a/apps/oneclient/desktop/src/api/commands/other.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[onelauncher_core::command] -#[allow(unused_parens)] -pub fn open_dev_tools(webview: tauri::WebviewWindow) { - #[cfg(feature = "devtools")] - webview.open_devtools(); -} \ No newline at end of file diff --git a/apps/oneclient/desktop/src/api/commands/package.rs b/apps/oneclient/desktop/src/api/commands/package.rs deleted file mode 100644 index 165a4b23..00000000 --- a/apps/oneclient/desktop/src/api/commands/package.rs +++ /dev/null @@ -1,234 +0,0 @@ -// use std::path::PathBuf; - -// use onelauncher::cluster::content::package; -// use onelauncher::data::{Loader, ManagedPackage, ManagedUser, ManagedVersion, PackageType}; -// use onelauncher::package::content::Providers; -// use onelauncher::package::import::ImportType; -// use onelauncher::store::{ -// Author, ClusterPath, Package, PackageBody, PackagePath, ProviderSearchResults, -// }; -// use onelauncher::utils::pagination::Pagination; -// use uuid::Uuid; - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_launcher_instances( -// launcher: ImportType, -// path: Option, -// ) -> Result<(String, Vec), String> { -// let (dir, instances) = onelauncher::api::package::import::get_launcher_instances(launcher, path).await?; -// Ok(( -// dir.to_str().ok_or("could not convert pathbuf to string")?.to_string(), -// instances -// )) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn import_instances( -// launcher: ImportType, -// base_path: PathBuf, -// instances: Vec, -// ) -> Result<(), String> { -// onelauncher::api::package::import::import_instances(launcher, base_path, instances).await?; -// Ok(()) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_provider_package( -// provider: Providers, -// project_id: String, -// ) -> Result { -// Ok(provider.get(&project_id).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_provider_packages( -// provider: Providers, -// project_ids: Vec, -// ) -> Result, String> { -// Ok(provider.get_multiple(&project_ids).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_all_provider_package_versions( -// provider: Providers, -// project_id: String, -// game_versions: Option>, -// loaders: Option>, -// page: Option, -// page_size: Option, -// ) -> Result<(Vec, Pagination), String> { -// Ok(provider -// .get_all_versions(&project_id, game_versions, loaders, page, page_size) -// .await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_provider_package_versions( -// provider: Providers, -// versions: Vec, -// ) -> Result, String> { -// Ok(provider.get_versions(versions).await?) -// } - -// #[derive(specta::Type, serde::Deserialize, serde::Serialize)] -// pub struct ProviderSearchQuery { -// query: Option, -// limit: Option, -// offset: Option, -// game_versions: Option>, -// categories: Option>, -// loaders: Option>, -// package_types: Option>, -// open_source: Option, -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn search_provider_packages( -// provider: Providers, -// query: ProviderSearchQuery, -// ) -> Result { -// Ok(provider -// .search( -// query.query, -// query.limit, -// query.offset, -// query.game_versions, -// query.categories, -// query.loaders, -// query.package_types, -// query.open_source, -// ) -// .await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_provider_authors( -// provider: Providers, -// author: Author, -// ) -> Result, String> { -// Ok(provider.get_authors(&author).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_package_body(provider: Providers, body: PackageBody) -> Result { -// Ok(provider.get_package_body(&body).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn download_provider_package( -// provider: Providers, -// package_id: String, -// cluster_id: Uuid, -// game_version: Option, -// loader: Option, -// package_version: Option, -// ) -> Result<(), String> { -// let mut cluster = onelauncher::cluster::get_by_uuid(cluster_id) -// .await? -// .ok_or("cluster not found")?; - -// let mgd_pkg = provider.get(&package_id).await?; - -// let (pkg_path, pkg) = package::download_package( -// &mgd_pkg, -// &mut cluster, -// game_version, -// loader, -// package_version, -// ) -// .await?; - -// package::add_package( -// &cluster.cluster_path(), -// pkg_path, -// pkg, -// ) -// .await?; - -// Ok(()) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_cluster_package( -// cluster_path: ClusterPath, -// package_path: PackagePath, -// package_type: PackageType, -// ) -> Result { -// Ok(package::get_package(&cluster_path, &package_path, package_type).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_cluster_packages( -// cluster_path: ClusterPath, -// package_type: PackageType, -// ) -> Result, String> { -// Ok(package::get_packages(&cluster_path, package_type).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn add_cluster_package( -// cluster_path: ClusterPath, -// file_name: String, -// package_type: PackageType, -// pkg: Package, -// ) -> Result<(), String> { -// let package_path = PackagePath::new(package_type, file_name)?; -// package::add_package(&cluster_path, package_path, pkg).await?; -// Ok(()) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn remove_cluster_package( -// cluster_path: ClusterPath, -// file_name: String, -// package_type: PackageType, -// ) -> Result<(), String> { -// let package_path = PackagePath::new(package_type, file_name)?; -// package::remove_package(&cluster_path, &package_path).await?; -// Ok(()) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn set_cluster_package_enabled( -// cluster_path: ClusterPath, -// file_name: String, -// package_type: PackageType, -// enabled: bool, -// ) -> Result<(), String> { -// let package_path = PackagePath::new(package_type, file_name)?; -// package::set_package_enabled(&cluster_path, &package_path, enabled).await?; -// Ok(()) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn sync_cluster_packages(cluster_path: ClusterPath) -> Result<(), String> { -// package::sync_packages(&cluster_path).await?; -// Ok(()) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn sync_cluster_packages_by_type( -// cluster_path: ClusterPath, -// package_type: PackageType, -// clear: Option, -// ) -> Result<(), String> { -// package::sync_packages_by_type(&cluster_path, package_type, clear).await?; -// Ok(()) -// } diff --git a/apps/oneclient/desktop/src/api/commands/processor.rs b/apps/oneclient/desktop/src/api/commands/processor.rs deleted file mode 100644 index 84a18027..00000000 --- a/apps/oneclient/desktop/src/api/commands/processor.rs +++ /dev/null @@ -1,61 +0,0 @@ -// use chrono::{DateTime, Utc}; -// use onelauncher::processor::{self, DetailedProcess}; -// use onelauncher::store::{Cluster, ClusterPath}; -// use uuid::Uuid; - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_running_clusters() -> Result, String> { -// Ok(processor::get_running_clusters().await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn is_cluster_running(uuid: Uuid) -> Result { -// Ok(processor::is_cluster_running(uuid).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_processes_by_path(path: ClusterPath) -> Result, String> { -// Ok(processor::get_uuids_by_cluster_path(path).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_processes_detailed_by_path( -// path: ClusterPath, -// ) -> Result, String> { -// Ok(processor::get_processes_detailed_by_path(path).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_process_detailed_by_id(uuid: Uuid) -> Result { -// Ok(processor::get_process_detailed_by_id(uuid).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_user_by_process(uuid: Uuid) -> Result, String> { -// Ok(processor::get_user_by_process(uuid).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_process_started_at(uuid: Uuid) -> Result, String> { -// Ok(processor::get_process_started_at(uuid).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_pid_by_uuid(uuid: Uuid) -> Result { -// Ok(processor::get_pid_by_uuid(uuid).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn kill_process(uuid: Uuid) -> Result<(), String> { -// processor::kill_by_uuid(uuid).await?; -// Ok(()) -// } diff --git a/apps/oneclient/desktop/src/api/commands/settings.rs b/apps/oneclient/desktop/src/api/commands/settings.rs deleted file mode 100644 index 3173c553..00000000 --- a/apps/oneclient/desktop/src/api/commands/settings.rs +++ /dev/null @@ -1,18 +0,0 @@ -use onelauncher_core::entity::setting_profiles::Model as SettingsModel; -use onelauncher_core::api::setting_profiles; - -use crate::api::error::SerializableResult; - -#[specta::specta] -#[tauri::command] -pub async fn get_global_profile() -> SerializableResult { - Ok(setting_profiles::get_global_profile().await) -} - -#[specta::specta] -#[tauri::command] -pub async fn get_profile_or_default( - name: Option, -) -> SerializableResult { - Ok(setting_profiles::dao::get_profile_or_default(name.as_ref()).await?) -} \ No newline at end of file diff --git a/apps/oneclient/desktop/src/api/commands/users.rs b/apps/oneclient/desktop/src/api/commands/users.rs deleted file mode 100644 index 6c4d4a04..00000000 --- a/apps/oneclient/desktop/src/api/commands/users.rs +++ /dev/null @@ -1,112 +0,0 @@ -use onelauncher_core::{api::credentials, error::LauncherResult, store::{credentials::{MinecraftCredentials, MinecraftLogin}, Core}}; -use tauri::{AppHandle, Manager}; -use uuid::Uuid; - -use crate::api::error::SerializableResult; - -#[specta::specta] -#[tauri::command] -pub async fn get_users() -> SerializableResult> { - Ok(credentials::users().await?) -} - -#[specta::specta] -#[tauri::command] -pub async fn get_user(uuid: Uuid) -> SerializableResult { - Ok(credentials::get_user(uuid).await?) -} - -#[specta::specta] -#[tauri::command] -pub async fn remove_user(uuid: Uuid) -> SerializableResult<()> { - Ok(credentials::remove_user(uuid).await?) -} - -#[specta::specta] -#[tauri::command] -pub async fn get_default_user( - fallback: Option, -) -> SerializableResult> { - let uuid = credentials::get_default_user().await?; - - if fallback.is_some_and(|fallback| fallback) && uuid.is_none() { - return Ok(credentials::users().await?.first().cloned()); - } - - match uuid { - Some(uuid) => Ok(Some(credentials::get_user(uuid).await?)), - None => Ok(None), - } -} - -#[specta::specta] -#[tauri::command] -pub async fn set_default_user(uuid: Option) -> SerializableResult<()> { - credentials::set_default_user(uuid).await?; - Ok(()) -} - -#[specta::specta] -#[tauri::command] -pub async fn begin_ms_flow(handle: tauri::AppHandle) -> SerializableResult> { - let flow = credentials::begin().await?; - let result = spawn_webview(handle, flow).await?; - - Ok(result) -} - -async fn spawn_webview( - handle: AppHandle, - flow: MinecraftLogin, -) -> LauncherResult> { - let now = chrono::Utc::now(); - - if let Some(win) = handle.get_webview_window("login") { - win.close()?; - } - - let win = tauri::WebviewWindowBuilder::new( - &handle, - "login", - tauri::WebviewUrl::External( - flow.redirect_uri - .parse() - .map_err(|_| anyhow::anyhow!("failed to parse auth redirect url"))?, - ), - ) - .title(format!("Login to {}", Core::get().launcher_name)) - .center() - .focused(true) - .build()?; - - win.request_user_attention(Some(tauri::UserAttentionType::Critical))?; - - while (chrono::Utc::now() - now) < chrono::Duration::minutes(10) { - if win.title().is_err() { - return Ok(None); - } - - if win - .url()? - .as_str() - .starts_with("https://login.live.com/oauth20_desktop.srf") - { - if let Some((_, code)) = win - .url()? - .query_pairs() - .find(|x| x.0 == "code") - { - win.close()?; - let value = credentials::finish(&code.clone(), flow).await?; - - return Ok(Some(value)); - } - } - - tokio::time::sleep(std::time::Duration::from_millis(50)).await; - } - - win.close()?; - - Ok(None) -} diff --git a/apps/oneclient/desktop/src/api/error.rs b/apps/oneclient/desktop/src/api/error.rs deleted file mode 100644 index 3185836a..00000000 --- a/apps/oneclient/desktop/src/api/error.rs +++ /dev/null @@ -1,61 +0,0 @@ -use onelauncher_core::error::LauncherError; -use serde::{Serialize, Serializer, ser::SerializeStruct}; -use specta::Type; - -#[derive(thiserror::Error, Debug, Type)] -#[serde(tag = "kind", content = "data")] -pub enum SerializableError { - #[error(transparent)] - CoreError(#[serde(skip)] #[from] onelauncher_core::error::LauncherError), -} - -pub type SerializableResult = std::result::Result; - -macro_rules! impl_serialize { - ($($variant:ident),* $(,)?) => { - impl Serialize for SerializableError { - fn serialize(&self, serializer: S) -> std::result::Result - where - S: Serializer, - { - match self { - SerializableError::CoreError(error) => { - display_tracing_error(error); - - let mut state = serializer.serialize_struct("CoreError", 2)?; - state.serialize_field("field_name", "CoreError")?; - state.serialize_field("message", &error.to_string())?; - state.end() - } - $( - SerializableError::$variant(message) => { - let mut state = serializer.serialize_struct(stringify!($variant), 2)?; - state.serialize_field("field_name", stringify!($variant))?; - state.serialize_field("message", &message.to_string())?; - state.end() - }, - )* - } - } - } - }; -} - -impl_serialize! {} - -pub fn display_tracing_error(err: &LauncherError) { - match get_span_trace(err) { - Some(span_trace) => { - tracing::error!(error = %err, span_trace = %span_trace); - } - None => { - tracing::error!(error = %err); - } - } -} - -pub fn get_span_trace<'a>( - error: &'a (dyn std::error::Error + 'static), -) -> Option<&'a tracing_error::SpanTrace> { - error.source().and_then(tracing_error::ExtractSpanTrace::span_trace) -} diff --git a/apps/oneclient/desktop/src/api/events.rs b/apps/oneclient/desktop/src/api/events.rs deleted file mode 100644 index c347136f..00000000 --- a/apps/oneclient/desktop/src/api/events.rs +++ /dev/null @@ -1,8 +0,0 @@ -// #[macro_export] -// macro_rules! collect_events { -// () => {{ -// tauri_specta::collect_events! { -// onelauncher_core::api::proxy::event::LauncherEvent -// } -// }}; -// } \ No newline at end of file diff --git a/apps/oneclient/desktop/src/api/mod.rs b/apps/oneclient/desktop/src/api/mod.rs index 4a937850..5bfa754f 100644 --- a/apps/oneclient/desktop/src/api/mod.rs +++ b/apps/oneclient/desktop/src/api/mod.rs @@ -1,4 +1,2 @@ pub mod commands; -pub mod error; -pub mod events; pub mod statics; diff --git a/apps/oneclient/desktop/src/error.rs b/apps/oneclient/desktop/src/error.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/oneclient/desktop/src/ext/mod.rs b/apps/oneclient/desktop/src/ext/mod.rs index 864c0b2a..8810da49 100644 --- a/apps/oneclient/desktop/src/ext/mod.rs +++ b/apps/oneclient/desktop/src/ext/mod.rs @@ -1,2 +1,2 @@ pub mod specta; -pub mod updater; +// pub mod updater; diff --git a/apps/oneclient/desktop/src/ext/specta.rs b/apps/oneclient/desktop/src/ext/specta.rs index 951373d5..624414b6 100644 --- a/apps/oneclient/desktop/src/ext/specta.rs +++ b/apps/oneclient/desktop/src/ext/specta.rs @@ -3,15 +3,21 @@ use std::path::Path; use std::process::Command; pub fn formatter(file: &Path) -> io::Result<()> { - Command::new("pnpm") + let result = Command::new("pnpm") .arg("eslint") .arg("--flag") .arg("unstable_ts_config") .arg("--fix") .arg(file) - .output() - .map(|_| ()) - .map_err(io::Error::other) + .output(); + + match result { + Ok(_) => Ok(()), + Err(e) => { + eprintln!("error running formatter on {}: {}", file.display(), e); + Ok(()) + } + } } const _: specta_typescript::FormatterFn = formatter; diff --git a/apps/oneclient/desktop/src/ext/updater.rs b/apps/oneclient/desktop/src/ext/updater.rs index eafc3c98..82551d15 100644 --- a/apps/oneclient/desktop/src/ext/updater.rs +++ b/apps/oneclient/desktop/src/ext/updater.rs @@ -1,117 +1,117 @@ -use onelauncher_core::store::Core; -use tauri::plugin::TauriPlugin; -use tauri::{Emitter, Runtime}; -use tauri_plugin_updater::{Update as TauriPluginUpdate, UpdaterExt}; -use tokio::sync::Mutex; +// use onelauncher_core::store::Core; +// use tauri::plugin::TauriPlugin; +// use tauri::{Emitter, Runtime}; +// use tauri_plugin_updater::{Update as TauriPluginUpdate, UpdaterExt}; +// use tokio::sync::Mutex; -#[derive(Debug, Clone, specta::Type, serde::Serialize)] -pub struct Update { - pub version: String, -} +// #[derive(Debug, Clone, specta::Type, serde::Serialize)] +// pub struct Update { +// pub version: String, +// } -impl Update { - fn new(update: &TauriPluginUpdate) -> Self { - Self { - version: update.version.clone(), - } - } -} +// impl Update { +// fn new(update: &TauriPluginUpdate) -> Self { +// Self { +// version: update.version.clone(), +// } +// } +// } -#[derive(Default)] -pub struct State { - install_lock: Mutex<()>, -} +// #[derive(Default)] +// pub struct State { +// install_lock: Mutex<()>, +// } -async fn get_update(app: tauri::AppHandle) -> Result, String> { - app.updater_builder() - .header("X-OneClient-Version", "stable") - .map_err(|e| e.to_string())? - .build() - .map_err(|e| e.to_string())? - .check() - .await - .map_err(|e| e.to_string()) -} +// async fn get_update(app: tauri::AppHandle) -> Result, String> { +// app.updater_builder() +// .header("X-OneClient-Version", "stable") +// .map_err(|e| e.to_string())? +// .build() +// .map_err(|e| e.to_string())? +// .check() +// .await +// .map_err(|e| e.to_string()) +// } -#[derive(Debug, Clone, serde::Serialize, specta::Type, tauri_specta::Event)] -#[serde(rename_all = "camelCase", tag = "status")] -pub enum UpdateEvent { - Loading, - Error(String), - UpdateAvailable { update: Update }, - NoUpdateAvailable, - Installing, -} +// #[derive(Debug, Clone, serde::Serialize, specta::Type, tauri_specta::Event)] +// #[serde(rename_all = "camelCase", tag = "status")] +// pub enum UpdateEvent { +// Loading, +// Error(String), +// UpdateAvailable { update: Update }, +// NoUpdateAvailable, +// Installing, +// } -#[tauri::command] -#[specta::specta] -pub async fn check_for_update(app: tauri::AppHandle) -> Result, String> { - app.emit("updater", UpdateEvent::Loading).ok(); +// #[tauri::command] +// #[specta::specta] +// pub async fn check_for_update(app: tauri::AppHandle) -> Result, String> { +// app.emit("updater", UpdateEvent::Loading).ok(); - let update = match get_update(app.clone()).await { - Ok(update) => update, - Err(e) => { - app.emit("updater", UpdateEvent::Error(e.clone())).ok(); - return Err(e); - } - }; +// let update = match get_update(app.clone()).await { +// Ok(update) => update, +// Err(e) => { +// app.emit("updater", UpdateEvent::Error(e.clone())).ok(); +// return Err(e); +// } +// }; - let update = update.map(|u| Update::new(&u)); +// let update = update.map(|u| Update::new(&u)); - app.emit( - "updater", - update.clone().map_or(UpdateEvent::NoUpdateAvailable, |u| { - UpdateEvent::UpdateAvailable { update: u } - }), - ) - .ok(); +// app.emit( +// "updater", +// update.clone().map_or(UpdateEvent::NoUpdateAvailable, |u| { +// UpdateEvent::UpdateAvailable { update: u } +// }), +// ) +// .ok(); - Ok(update) -} +// Ok(update) +// } -#[tauri::command] -#[specta::specta] -pub async fn install_update( - app: tauri::AppHandle, - state: tauri::State<'_, State>, -) -> Result<(), String> { - let Ok(lock) = state.install_lock.try_lock() else { - return Err("Update already installing".into()); - }; +// #[tauri::command] +// #[specta::specta] +// pub async fn install_update( +// app: tauri::AppHandle, +// state: tauri::State<'_, State>, +// ) -> Result<(), String> { +// let Ok(lock) = state.install_lock.try_lock() else { +// return Err("Update already installing".into()); +// }; - app.emit("updater", UpdateEvent::Installing).ok(); +// app.emit("updater", UpdateEvent::Installing).ok(); - get_update(app.clone()) - .await? - .ok_or_else(|| "No update required".to_string())? - .download_and_install(|_, _| {}, || {}) - .await - .map_err(|e| e.to_string())?; +// get_update(app.clone()) +// .await? +// .ok_or_else(|| "No update required".to_string())? +// .download_and_install(|_, _| {}, || {}) +// .await +// .map_err(|e| e.to_string())?; - drop(lock); +// drop(lock); - Ok(()) -} +// Ok(()) +// } -#[must_use] -pub fn plugin() -> TauriPlugin { - tauri::plugin::Builder::new("onelauncher-updater") - .on_page_load(|window, _| { - #[cfg(target_os = "linux")] - let updater_available = false; +// #[must_use] +// pub fn plugin() -> TauriPlugin { +// tauri::plugin::Builder::new("onelauncher-updater") +// .on_page_load(|window, _| { +// #[cfg(target_os = "linux")] +// let updater_available = false; - #[cfg(not(target_os = "linux"))] - let updater_available = true; +// #[cfg(not(target_os = "linux"))] +// let updater_available = true; - if updater_available { - window - .eval("window.__LAUNCHER_UPDATER__ = true;") - .expect("Failed to inject updater JS"); - } - }) - .js_init_script(format!( - r#"window.__ONECLIENT_VERSION__ = "{}";"#, - Core::get().launcher_version, - )) - .build() -} +// if updater_available { +// window +// .eval("window.__LAUNCHER_UPDATER__ = true;") +// .expect("Failed to inject updater JS"); +// } +// }) +// .js_init_script(format!( +// r#"window.__ONECLIENT_VERSION__ = "{}";"#, +// Core::get().launcher_version, +// )) +// .build() +// } diff --git a/apps/oneclient/desktop/src/lib.rs b/apps/oneclient/desktop/src/lib.rs index 41ed8fbf..d46bfc5c 100644 --- a/apps/oneclient/desktop/src/lib.rs +++ b/apps/oneclient/desktop/src/lib.rs @@ -1,13 +1,16 @@ use onelauncher_core::api::proxy::ProxyTauri; +use onelauncher_core::api::tauri::{TauriLauncherApi, TauriLauncherEventApi}; use onelauncher_core::error::LauncherResult; use onelauncher_core::store::proxy::ProxyState; use onelauncher_core::store::semaphore::SemaphoreStore; use onelauncher_core::store::{Core, CoreOptions, Dirs, State}; use tauri::{Emitter, Manager}; +use crate::api::commands::OneClientApi; + pub mod api; pub mod constants; -// pub mod ext; +pub mod ext; #[derive(Clone, serde::Serialize)] pub struct SingleInstancePayload { @@ -38,19 +41,15 @@ async fn initialize_core() -> LauncherResult<()> { #[tracing::instrument(skip_all)] async fn initialize_tauri(builder: tauri::Builder) -> LauncherResult { - // let prebuild = tauri_specta::Builder::::new() - // .commands(collect_commands!()) - // .events(collect_events!()); - - // #[cfg(debug_assertions)] - // prebuild - // .export( - // specta_typescript::Typescript::default() - // .bigint(specta_typescript::BigIntExportBehavior::BigInt) - // .formatter(ext::specta::formatter), - // "../frontend/src/bindings.gen.ts", - // ) - // .expect("failed to export debug bindings!"); + let router = taurpc::Router::new() + .export_config( + specta_typescript::Typescript::default() + .bigint(specta_typescript::BigIntExportBehavior::BigInt) + .formatter(ext::specta::formatter), + ) + .merge(api::commands::OneClientApiImpl.into_handler()) + .merge(onelauncher_core::api::tauri::TauriLauncherApiImpl.into_handler()) + .merge(onelauncher_core::api::tauri::TauriLauncherEventApiImpl.into_handler()); let builder = builder .plugin(tauri_plugin_single_instance::init(|app, argv, cwd| { @@ -60,15 +59,11 @@ async fn initialize_tauri(builder: tauri::Builder) -> LauncherResult })) .plugin(tauri_plugin_updater::Builder::new().build()) .plugin(tauri_plugin_clipboard_manager::init()) - // .plugin(ext::updater::plugin()) - // .manage(ext::updater::State::default()) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_deep_link::init()) - // .plugin(api::init()) .menu(tauri::menu::Menu::new) - // .invoke_handler(prebuild.invoke_handler()) + .invoke_handler(router.into_handler()) .setup(move |app| { - // prebuild.mount_events(app.handle()); setup_window(app.handle()).expect("failed to setup main window"); Ok(()) }); diff --git a/apps/oneclient/frontend/package.json b/apps/oneclient/frontend/package.json index f6b6548d..9dd8fcad 100644 --- a/apps/oneclient/frontend/package.json +++ b/apps/oneclient/frontend/package.json @@ -14,6 +14,7 @@ "dependencies": { "@fontsource/poppins": "^5.2.6", "@formatjs/intl-durationformat": "^0.7.4", + "@onelauncher/common": "workspace:*", "@tailwindcss/vite": "^4.0.6", "@tanstack/match-sorter-utils": "^8.19.4", "@tanstack/react-query": "^5.66.5", @@ -37,7 +38,8 @@ "tailwind-variants": "^1.0.0", "tailwindcss": "^4.0.6", "tailwindcss-animated": "^2.0.0", - "tailwindcss-react-aria-components": "^2.0.0" + "tailwindcss-react-aria-components": "^2.0.0", + "taurpc": "^1.8.1" }, "devDependencies": { "@tanstack/eslint-config": "^0.1.0", diff --git a/apps/oneclient/frontend/src/bindings.gen.ts b/apps/oneclient/frontend/src/bindings.gen.ts index c408de2e..6b4f2cd0 100644 --- a/apps/oneclient/frontend/src/bindings.gen.ts +++ b/apps/oneclient/frontend/src/bindings.gen.ts @@ -1,152 +1,65 @@ -// This file was generated by [tauri-specta](https://github.com/oscartbeaumont/tauri-specta). Do not edit this file manually. +// This file has been generated by Specta. DO NOT EDIT. -/** user-defined commands */ +import type { InferCommandOutput } from 'taurpc'; +import { createTauRPCProxy as createProxy } from 'taurpc'; -/** tauri-specta globals */ +type TAURI_CHANNEL = (response: T) => void; -import type { WebviewWindow as __WebviewWindow__ } from '@tauri-apps/api/webviewWindow'; -import { - invoke as TAURI_INVOKE, -} from '@tauri-apps/api/core'; -import * as TAURI_API_EVENT from '@tauri-apps/api/event'; - -export const commands = { - async getUsers(): Promise, SerializableError>> { - try { - return { status: 'ok', data: await TAURI_INVOKE('get_users') }; - } - catch (e) { - if (e instanceof Error) - throw e; - else return { status: 'error', error: e as any }; - } - }, - async getUser(uuid: string): Promise> { - try { - return { status: 'ok', data: await TAURI_INVOKE('get_user', { uuid }) }; - } - catch (e) { - if (e instanceof Error) - throw e; - else return { status: 'error', error: e as any }; - } - }, - async removeUser(uuid: string): Promise> { - try { - return { status: 'ok', data: await TAURI_INVOKE('remove_user', { uuid }) }; - } - catch (e) { - if (e instanceof Error) - throw e; - else return { status: 'error', error: e as any }; - } - }, - async getDefaultUser(fallback: boolean | null): Promise> { - try { - return { status: 'ok', data: await TAURI_INVOKE('get_default_user', { fallback }) }; - } - catch (e) { - if (e instanceof Error) - throw e; - else return { status: 'error', error: e as any }; - } - }, - async setDefaultUser(uuid: string | null): Promise> { - try { - return { status: 'ok', data: await TAURI_INVOKE('set_default_user', { uuid }) }; - } - catch (e) { - if (e instanceof Error) - throw e; - else return { status: 'error', error: e as any }; - } - }, - async beginMsFlow(): Promise> { - try { - return { status: 'ok', data: await TAURI_INVOKE('begin_ms_flow') }; - } - catch (e) { - if (e instanceof Error) - throw e; - else return { status: 'error', error: e as any }; - } - }, - async createCluster(options: CreateCluster): Promise> { - try { - return { status: 'ok', data: await TAURI_INVOKE('create_cluster', { options }) }; - } - catch (e) { - if (e instanceof Error) - throw e; - else return { status: 'error', error: e as any }; - } - }, - async getClusters(): Promise, SerializableError>> { - try { - return { status: 'ok', data: await TAURI_INVOKE('get_clusters') }; - } - catch (e) { - if (e instanceof Error) - throw e; - else return { status: 'error', error: e as any }; - } - }, - async getClusterById(id: bigint): Promise> { - try { - return { status: 'ok', data: await TAURI_INVOKE('get_cluster_by_id', { id }) }; - } - catch (e) { - if (e instanceof Error) - throw e; - else return { status: 'error', error: e as any }; - } - }, - async getGlobalProfile(): Promise> { - try { - return { status: 'ok', data: await TAURI_INVOKE('get_global_profile') }; - } - catch (e) { - if (e instanceof Error) - throw e; - else return { status: 'error', error: e as any }; - } - }, - async getProfileOrDefault(name: string | null): Promise> { - try { - return { status: 'ok', data: await TAURI_INVOKE('get_profile_or_default', { name }) }; - } - catch (e) { - if (e instanceof Error) - throw e; - else return { status: 'error', error: e as any }; - } - }, - async openDevTools(): Promise { - await TAURI_INVOKE('open_dev_tools'); - }, -}; - -/** user-defined events */ - -export const events = __makeEvents__<{ - launcherEvent: LauncherEvent; -}>({ - launcherEvent: 'launcher-event', -}); - -/** user-defined constants */ - -/** user-defined types */ +export type ClusterError = { InvalidVersion: string } | 'MissingJavaVersion' | 'ClusterDownloading' | 'ClusterAlreadyRunning'; export type ClusterStage = 'notready' | 'downloading' | 'repairing' | 'ready'; -export interface CreateCluster { name: string; mc_version: string; mc_loader: GameLoader; mc_loader_version: string | null; icon_url: Icon | null } + +export interface CreateCluster { name: string; mc_version: string; mc_loader: GameLoader; mc_loader_version: string | null; icon: Icon | null } + +export type CryptoError = { InvalidHash: { algorithm: HashAlgorithm; expected: string; actual: string } } | 'InvalidAlgorithm'; + +export type DaoError = 'NotFound' | 'AlreadyExists' | { InvalidValue: [string, string] }; + +export type DirectoryError = 'BaseDir'; + +export type DiscordError = 'MissingClientId' | 'ConnectError'; + export type GameLoader = 'vanilla' | 'forge' | 'neoforge' | 'quilt' | 'fabric' | 'legacyfabric'; + +export type HashAlgorithm = 'Sha1' | 'Sha256' | 'Md5'; + +/** + * A wrapper around generic and unhelpful [`std::io::Error`] messages. + */ +export type IOError = { InvalidAbsolutePath: string } | { IOErrorWrapper: { path: string } } | 'IOError' | 'DeserializeError' | 'AsyncZipError'; + export type Icon = string; + +export type IncompatiblePackageType = 'McVersion' | 'Loader'; + +export type IngressError = 'NotFound'; + export interface IngressPayload { id: string; message: string; ingress_type: IngressType; percent: number | null; total: number } + export type IngressType = { Download: { file_name: string } } | 'JavaPrepare' | 'JavaCheck' | 'JavaLocate' | 'MinecraftDownload' | { PrepareCluster: { cluster_name: string } }; + +export type JavaError = { ParseVersion: [string] } | 'Execute' | 'MissingJava'; + +export type LauncherError = { type: 'DirError'; data: DirectoryError } | { type: 'IOError'; data: IOError } | { type: 'IngressError'; data: IngressError } | { type: 'JavaError'; data: JavaError } | { type: 'CryptoError'; data: CryptoError } | { type: 'DiscordError'; data: DiscordError } | { type: 'MetadataError'; data: MetadataError } | { type: 'ClusterError'; data: ClusterError } | { type: 'MinecraftAuthError'; data: MinecraftAuthError } | { type: 'ProcessError'; data: ProcessError } | { type: 'PackageError'; data: PackageError } | { type: 'DaoError'; data: DaoError } | { type: 'SerdeError' } | { type: 'AnyhowError' } | { type: 'DbError' } | { type: 'ReqwestError' } | { type: 'InterpulseError' } | { type: 'RegexError' } | { type: 'SemaphoreError' } | { type: 'UrlError' } | { type: 'TauriError' }; + export type LauncherEvent = { Ingress: IngressPayload } | { Message: MessagePayload } | { Process: ProcessPayload }; + export type MessageLevel = 'Info' | 'Warn' | 'Error'; + export interface MessagePayload { level: MessageLevel; message: string } + +export type MetadataError = 'FetchError' | { NotModdedManifest: GameLoader } | { NotVanillaManifest: GameLoader } | 'ParseError' | 'NoMatchingLoader' | 'NoMatchingVersion'; + +/** + * Wrapper around all `Error`s that can occur during the Microsoft authentication process. + */ +export type MinecraftAuthError = 'PublicKeyReading' | 'PKCS8Error' | { SerializeError: { step: MinecraftAuthStep } } | { DeserializeError: { step: MinecraftAuthStep; raw: string } } | { RequestError: { step: MinecraftAuthStep } } | { SigningError: { step: MinecraftAuthStep } } | 'HashError' | 'SessionIdError'; + +/** + * An ordered list of all MSA authentication steps. + */ +export type MinecraftAuthStep = 'DeviceToken' | 'SisuAuthenicate' | 'OAuthToken' | 'RefreshOAuthToken' | 'SisuAuthorize' | 'XstsAuthorize' | 'MinecraftToken' | 'MinecraftEntitlements' | 'MinecraftProfile'; + /** * A structure of all needed Minecraft credentials for logging in and account management. */ @@ -173,61 +86,29 @@ export interface MinecraftCredentials { expires: string; } + export interface Model { id: bigint; folder_name: string; stage: ClusterStage; created_at: string; group_id: bigint | null; name: string; mc_version: string; mc_loader: GameLoader; mc_loader_version: string | null; last_played: string | null; overall_played: bigint | null; icon_url: Icon | null; setting_profile_name: string | null; linked_pack_id: string | null; linked_pack_version: string | null } + +export type PackageError = 'NoPrimaryFile' | 'IsModPack' | { Incompatible: IncompatiblePackageType }; + export interface Process { pid: number; started_at: string; cluster_id: bigint; post_hook: string | null; account_id: string } + +export type ProcessError = { HookUnsuccessful: number } | 'NoPid'; + export type ProcessPayload = { Starting: { command: string } } | { Started: { process: Process } } | { Stopped: { pid: number; exit_code: number } } | { Output: { pid: number; output: string } }; + export interface Resolution { width: number; height: number } -export interface SerializableError { kind: 'CoreError' } + export interface SettingProfileModel { name: string; java_id: bigint | null; res: Resolution | null; force_fullscreen: boolean | null; mem_max: number | null; launch_args: string | null; launch_env: string | null; hook_pre: string | null; hook_wrapper: string | null; hook_post: string | null; os_extra: SettingsOsExtra | null } + export interface SettingsOsExtra { enable_gamemode: boolean | null } -interface __EventObj__ { - listen: ( - cb: TAURI_API_EVENT.EventCallback, - ) => ReturnType>; - once: ( - cb: TAURI_API_EVENT.EventCallback, - ) => ReturnType>; - emit: null extends T - ? (payload?: T) => ReturnType - : (payload: T) => ReturnType; +const ARGS_MAP = { 'core': '{"get_cluster":["id"],"remove_cluster":["id"],"get_profile_or_default":["name"],"get_clusters":[],"remove_user":["uuid"],"get_global_profile":[],"get_users":[],"get_default_user":["fallback"],"create_cluster":["options"],"open_msa_login":[],"get_user":["uuid"],"set_default_user":["uuid"]}', 'oneclient': '{"open_dev_tools":[],"return_error":[]}', '': '{"send_event":["event"]}' }; +export interface Router { + 'core': { get_clusters: () => Promise>; get_cluster: (id: bigint) => Promise; remove_cluster: (id: bigint) => Promise; create_cluster: (options: CreateCluster) => Promise; get_profile_or_default: (name: string | null) => Promise; get_global_profile: () => Promise; get_users: () => Promise>; get_user: (uuid: string) => Promise; remove_user: (uuid: string) => Promise; get_default_user: (fallback: boolean | null) => Promise; set_default_user: (uuid: string | null) => Promise; open_msa_login: () => Promise }; + 'oneclient': { return_error: () => Promise; open_dev_tools: () => Promise }; + '': { send_event: (event: LauncherEvent) => Promise }; } -export type Result = - | { status: 'ok'; data: T } - | { status: 'error'; error: E }; - -function __makeEvents__>( - mappings: Record, -) { - return new Proxy( - {} as unknown as { - [K in keyof T]: __EventObj__ & { - (handle: __WebviewWindow__): __EventObj__; - }; - }, - { - get: (_, event) => { - const name = mappings[event as keyof T]; - - return new Proxy((() => {}) as any, { - apply: (_, __, [window]: [__WebviewWindow__]) => ({ - listen: (arg: any) => window.listen(name, arg), - once: (arg: any) => window.once(name, arg), - emit: (arg: any) => window.emit(name, arg), - }), - get: (_, command: keyof __EventObj__) => { - switch (command) { - case 'listen': - return (arg: any) => TAURI_API_EVENT.listen(name, arg); - case 'once': - return (arg: any) => TAURI_API_EVENT.once(name, arg); - case 'emit': - return (arg: any) => TAURI_API_EVENT.emit(name, arg); - } - }, - }); - }, - }, - ); -} +export type { InferCommandOutput }; +export const createTauRPCProxy = () => createProxy(ARGS_MAP); diff --git a/apps/oneclient/frontend/src/components/Header.tsx b/apps/oneclient/frontend/src/components/Header.tsx deleted file mode 100644 index 287272b3..00000000 --- a/apps/oneclient/frontend/src/components/Header.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { Link } from '@tanstack/react-router'; - -export default function Header() { - return ( -
- -
- ); -} diff --git a/apps/oneclient/frontend/src/fonts.ts b/apps/oneclient/frontend/src/fonts.ts new file mode 100644 index 00000000..bc81d9d9 --- /dev/null +++ b/apps/oneclient/frontend/src/fonts.ts @@ -0,0 +1,14 @@ +import '@fontsource/poppins/300.css'; +import '@fontsource/poppins/300-italic.css'; +import '@fontsource/poppins/400.css'; +import '@fontsource/poppins/400-italic.css'; +import '@fontsource/poppins/500.css'; +import '@fontsource/poppins/500-italic.css'; +import '@fontsource/poppins/600.css'; +import '@fontsource/poppins/600-italic.css'; +import '@fontsource/poppins/700.css'; +import '@fontsource/poppins/700-italic.css'; +import '@fontsource/poppins/800.css'; +import '@fontsource/poppins/800-italic.css'; +import '@fontsource/poppins/900.css'; +import '@fontsource/poppins/900-italic.css'; diff --git a/apps/oneclient/frontend/src/integrations/tanstack-query/layout.tsx b/apps/oneclient/frontend/src/integrations/tanstack-query/layout.tsx deleted file mode 100644 index 3fef7cba..00000000 --- a/apps/oneclient/frontend/src/integrations/tanstack-query/layout.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { ReactQueryDevtools } from '@tanstack/react-query-devtools' - -export default function LayoutAddition() { - return -} diff --git a/apps/oneclient/frontend/src/integrations/tanstack-query/root-provider.tsx b/apps/oneclient/frontend/src/integrations/tanstack-query/root-provider.tsx deleted file mode 100644 index 00e048a1..00000000 --- a/apps/oneclient/frontend/src/integrations/tanstack-query/root-provider.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { QueryClient, QueryClientProvider } from '@tanstack/react-query' - -const queryClient = new QueryClient() - -export function getContext() { - return { - queryClient, - } -} - -export function Provider({ children }: { children: React.ReactNode }) { - return ( - {children} - ) -} diff --git a/apps/oneclient/frontend/src/logo.svg b/apps/oneclient/frontend/src/logo.svg deleted file mode 100644 index d6c2da20..00000000 --- a/apps/oneclient/frontend/src/logo.svg +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/apps/oneclient/frontend/src/main.tsx b/apps/oneclient/frontend/src/main.tsx index 988dba6d..b9cf6e98 100644 --- a/apps/oneclient/frontend/src/main.tsx +++ b/apps/oneclient/frontend/src/main.tsx @@ -1,47 +1,62 @@ -import { StrictMode } from 'react' -import ReactDOM from 'react-dom/client' -import { RouterProvider, createRouter } from '@tanstack/react-router' +import { isDev, registerDevExperience, registerNativeExperience } from '@onelauncher/common'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { createRouter, RouterProvider } from '@tanstack/react-router'; +import { StrictMode } from 'react'; +import ReactDOM from 'react-dom/client'; +import { createTauRPCProxy } from './bindings.gen'; +import { routeTree } from './routeTree.gen'; -import * as TanstackQuery from './integrations/tanstack-query/root-provider' +import './fonts'; +import 'overlayscrollbars/overlayscrollbars.css'; +import './styles/global.css'; -// Import the generated route tree -import { routeTree } from './routeTree.gen' +// Tauri bindings +export const bindings = createTauRPCProxy(); -import './styles.css' +// Tanstack Query Client +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: false, + enabled: false, + }, + }, +}); -// Create a new router instance +// Tanstack Router const router = createRouter({ - routeTree, - context: { - ...TanstackQuery.getContext(), - }, - defaultPreload: 'intent', - scrollRestoration: true, - defaultStructuralSharing: true, - defaultPreloadStaleTime: 0, -}) + routeTree, + context: { + queryClient, + }, + defaultPreload: 'intent', + scrollRestoration: true, + defaultStructuralSharing: true, + defaultPreloadStaleTime: 0, + defaultNotFoundComponent: () =>
Not Found
, +}); -// Register the router instance for type safety declare module '@tanstack/react-router' { - interface Register { - router: typeof router - } + interface Register { + router: typeof router; + } } +// Register UX changes +if (isDev) + registerDevExperience(); + +registerNativeExperience(); + // Render the app -const rootElement = document.getElementById('app') +const rootElement = document.getElementById('app'); if (rootElement && !rootElement.innerHTML) { - const root = ReactDOM.createRoot(rootElement) - root.render( - - - - - , - ) + const root = ReactDOM.createRoot(rootElement); + root.render( + + + + + , + ); } - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals() diff --git a/apps/oneclient/frontend/src/routes/__root.tsx b/apps/oneclient/frontend/src/routes/__root.tsx index 774c7fce..27616b3d 100644 --- a/apps/oneclient/frontend/src/routes/__root.tsx +++ b/apps/oneclient/frontend/src/routes/__root.tsx @@ -1,25 +1,20 @@ -import { Outlet, createRootRouteWithContext } from '@tanstack/react-router' -import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' - -import Header from '../components/Header' - -import TanstackQueryLayout from '../integrations/tanstack-query/layout' - -import type { QueryClient } from '@tanstack/react-query' +import type { QueryClient } from '@tanstack/react-query'; +import { createRootRouteWithContext, Outlet } from '@tanstack/react-router'; +import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'; interface MyRouterContext { - queryClient: QueryClient + queryClient: QueryClient; } export const Route = createRootRouteWithContext()({ - component: () => ( - <> -
+ component: RootLayout, +}); - - - - - - ), -}) +function RootLayout() { + return ( + <> + + + + ); +} diff --git a/apps/oneclient/frontend/src/routes/index.tsx b/apps/oneclient/frontend/src/routes/index.tsx index b285335d..90519972 100644 --- a/apps/oneclient/frontend/src/routes/index.tsx +++ b/apps/oneclient/frontend/src/routes/index.tsx @@ -1,39 +1,15 @@ -import { createFileRoute } from '@tanstack/react-router' -import logo from '../logo.svg' +import { createFileRoute } from '@tanstack/react-router'; +import { Button } from "@onelauncher/common/components"; export const Route = createFileRoute('/')({ - component: App, -}) + component: App, +}); function App() { - return ( -
-
- logo -

- Edit src/routes/index.tsx and save to reload. -

- - Learn React - - - Learn TanStack - -
-
- ) + return ( +
+

test

+ +
+ ); } diff --git a/apps/oneclient/frontend/src/styles/global.css b/apps/oneclient/frontend/src/styles/global.css new file mode 100644 index 00000000..f0ebce68 --- /dev/null +++ b/apps/oneclient/frontend/src/styles/global.css @@ -0,0 +1,108 @@ +@import "tailwindcss"; +@plugin "tailwindcss-react-aria-components"; +@plugin "tailwindcss-animated"; +@source "../../node_modules/@onelauncher/common"; + +@theme { + /* foreground colors */ + --color-fg-primary: #d5dbff; + --color-fg-primary-hover: #dae0ff; + --color-fg-primary-pressed: #e1e5ff; + --color-fg-primary-disabled: #cdd1eb; + + --color-fg-secondary: #78818d; + --color-fg-secondary-hover: #5f6874; + --color-fg-secondary-pressed: #828d9b; + --color-fg-secondary-disabled: #646d79; + + --color-link: #61afef; + --color-link-hover: #5f87ff; + --color-link-pressed: #72afff; + --color-link-disabled: #7597b3; + + /* background colors */ + --color-brand: #2b4bff; + --color-brand-hover: #2843dd; + --color-brand-pressed: #3957ff; + --color-brand-disabled: #2543eb; + + --color-component-bg: #1a2228; + --color-component-bg-hover: #1d242b; + --color-component-bg-pressed: #222c35; + --color-component-bg-disabled: #10181f; + + --color-page: #11171c; + --color-page-elevated: #151c22; + --color-window-frame: #151b20; + + /* general colors */ + --color-danger: #ff4444; + --color-danger-hover: #d63434; + --color-danger-pressed: #ff5656; + --color-danger-disabled: #eb3030; + + --color-success: #239a60; + --color-success-hover: #1a8752; + --color-success-pressed: #2cac6e; + --color-success-disabled: #0f864c; + + --color-code-info: #61afef; + --color-code-warn: #e5c07b; + --color-code-error: #be5046; + --color-code-debug: #2b4bff; + --color-code-trace: #fdfdfd; + + /* border */ + --color-component-border: rgba(255, 255, 255, 0.05); + --color-component-border-hover: rgba(255, 255, 255, 0.10); + --color-component-border-pressed: rgba(255, 255, 255, 0.15); + + /* overlay */ + --color-ghost-overlay: rgba(255, 255, 255, 0.05); + --color-ghost-overlay-hover: rgba(255, 255, 255, 0.10); + + --color-scrollbar-bg: rgba(255, 255, 255, 0.05); + --color-scrollbar-bg-hover: rgba(255, 255, 255, 0.10); + --color-scrollbar-bg-pressed: rgba(255, 255, 255, 0.15); + + /* other */ + --font-sans: "Poppins", sans-serif; + --text-xs: 0.688rem; + --text-sm: 0.75rem; + --text-base: 0.875rem; + --text-lg: 1rem; + --text-2lg: 1.125rem; + --text-xl: 1.25rem; + --text-xxl: 1.5rem; + --text-2xl: 2rem; + --text-3xl: 2.5rem; + --text-4xl: 3rem; +} + +* { + user-select: none !important; + -webkit-user-select: none !important; +} + +body { + @apply bg-page text-fg-primary text-base; +} + +.os-scrollbar { + --os-size: 10px; + --os-track-border-radius: var(--border-radius-lg); + --os-handle-bg: var(--color-scrollbar-bg); + --os-handle-bg-hover: var(--color-scrollbar-bg-hover); + --os-handle-bg-active: var(--color-scrollbar-bg-pressed); + --os-handle-perpendicular-size-hover: 140%; + --os-handle-perpendicular-size-active: 140%; + --os-handle-interactive-area-offset: 4px; +} + +.os-scrollbar-offset-right { + --os-padding-axis: 10; +} + +.os-hide-horizontal-scrollbar > .os-scrollbar-horizontal { + display: none; +} diff --git a/apps/oneclient/frontend/src/utils/theming.ts b/apps/oneclient/frontend/src/utils/theming.ts new file mode 100644 index 00000000..56a08beb --- /dev/null +++ b/apps/oneclient/frontend/src/utils/theming.ts @@ -0,0 +1,26 @@ +export const DEFAULT_THEME = 'OneLauncher-Dark'; +export const THEMES = { + OneLauncher: { + variants: [ + { + name: 'Dark', + type: 'dark', + }, + { + name: 'Light', + type: 'light', + }, + ], + }, +} as const satisfies Record; + +export interface Theme { + variants: Array; +} + +export type ThemeTypes = 'dark' | 'light'; + +export interface ThemeVariant { + type: ThemeTypes; + name: string; +}; diff --git a/apps/onelauncher/frontend/package.json b/apps/onelauncher/frontend/package.json index 52857545..6f234a87 100644 --- a/apps/onelauncher/frontend/package.json +++ b/apps/onelauncher/frontend/package.json @@ -13,6 +13,7 @@ "dependencies": { "@fontsource/poppins": "^5.2.6", "@formatjs/intl-durationformat": "^0.7.4", + "@onelauncher/common": "workspace:*", "@tailwindcss/vite": "^4.0.6", "@tanstack/match-sorter-utils": "^8.19.4", "@tanstack/react-query": "^5.66.5", diff --git a/apps/onelauncher/frontend/src/bindings.gen.ts b/apps/onelauncher/frontend/src/bindings.gen.ts index 4b96eeea..9b6eccee 100644 --- a/apps/onelauncher/frontend/src/bindings.gen.ts +++ b/apps/onelauncher/frontend/src/bindings.gen.ts @@ -103,11 +103,11 @@ export interface SettingProfileModel { name: string; java_id: bigint | null; res export interface SettingsOsExtra { enable_gamemode: boolean | null } -const ARGS_MAP = { 'onelauncher': '{"open_dev_tools":[],"return_error":[]}', '': '{"send_event":["event"]}', 'core': '{"get_clusters":[],"get_user":["uuid"],"remove_user":["uuid"],"create_cluster":["options"],"get_profile_or_default":["name"],"get_default_user":["fallback"],"set_default_user":["uuid"],"get_global_profile":[],"open_msa_login":[],"get_users":[],"get_cluster":["id"],"remove_cluster":["id"]}' }; +const ARGS_MAP = { '': '{"send_event":["event"]}', 'onelauncher': '{"return_error":[],"open_dev_tools":[]}', 'core': '{"get_user":["uuid"],"get_default_user":["fallback"],"set_default_user":["uuid"],"get_global_profile":[],"get_clusters":[],"remove_cluster":["id"],"get_profile_or_default":["name"],"get_users":[],"remove_user":["uuid"],"open_msa_login":[],"create_cluster":["options"],"get_cluster":["id"]}' }; export interface Router { 'onelauncher': { return_error: () => Promise; open_dev_tools: () => Promise }; - '': { send_event: (event: LauncherEvent) => Promise }; 'core': { get_clusters: () => Promise>; get_cluster: (id: bigint) => Promise; remove_cluster: (id: bigint) => Promise; create_cluster: (options: CreateCluster) => Promise; get_profile_or_default: (name: string | null) => Promise; get_global_profile: () => Promise; get_users: () => Promise>; get_user: (uuid: string) => Promise; remove_user: (uuid: string) => Promise; get_default_user: (fallback: boolean | null) => Promise; set_default_user: (uuid: string | null) => Promise; open_msa_login: () => Promise }; + '': { send_event: (event: LauncherEvent) => Promise }; } export type { InferCommandOutput }; diff --git a/apps/onelauncher/frontend/src/components/Navbar.tsx b/apps/onelauncher/frontend/src/components/Navbar.tsx index e2d848e4..d37dc4ca 100644 --- a/apps/onelauncher/frontend/src/components/Navbar.tsx +++ b/apps/onelauncher/frontend/src/components/Navbar.tsx @@ -1,11 +1,10 @@ import type { ToOptions } from '@tanstack/react-router'; -import useCommand from '@/hooks/useCommand'; +import {useCommand} from '@onelauncher/common'; import { bindings } from '@/main'; import { Link } from '@tanstack/react-router'; import { Bell01Icon, Cloud01Icon, TerminalBrowserIcon } from '@untitled-theme/icons-react'; import { MenuTrigger } from 'react-aria-components'; -import Button from './base/Button'; -import Menu from './base/Menu'; +import { Button, Menu } from '@onelauncher/common/components'; import PlayerHead from './content/PlayerHead'; import AccountPopup from './overlay/popups/AccountPopup'; diff --git a/apps/onelauncher/frontend/src/components/ScrollableContainer.tsx b/apps/onelauncher/frontend/src/components/ScrollableContainer.tsx index 20cda4b3..e21f49f7 100644 --- a/apps/onelauncher/frontend/src/components/ScrollableContainer.tsx +++ b/apps/onelauncher/frontend/src/components/ScrollableContainer.tsx @@ -4,16 +4,16 @@ interface ScrollableContainerProps { children: React.ReactNode; } -function ScrollableContainer(props: ScrollableContainerProps) { +function ScrollableContainer({ children }: ScrollableContainerProps) { return ( -
+
- {props.children} + {children}
); } -export default ScrollableContainer; \ No newline at end of file +export default ScrollableContainer; diff --git a/apps/onelauncher/frontend/src/components/WindowFrame.tsx b/apps/onelauncher/frontend/src/components/WindowFrame.tsx index 6a84e795..8ef44e8b 100644 --- a/apps/onelauncher/frontend/src/components/WindowFrame.tsx +++ b/apps/onelauncher/frontend/src/components/WindowFrame.tsx @@ -1,10 +1,10 @@ import type { ComponentType, JSX } from 'react'; import type { PressEvent } from 'react-aria-components'; +import { Button } from '@onelauncher/common/components'; import { Window } from '@tauri-apps/api/window'; import { ChevronLeftIcon, Maximize02Icon, Minimize01Icon, MinusIcon, XCloseIcon } from '@untitled-theme/icons-react'; import { useEffect, useState } from 'react'; import { twMerge } from 'tailwind-merge'; -import Button from './base/Button'; function WindowFrame() { const [isMaximized, setIsMaximized] = useState(false); diff --git a/apps/onelauncher/frontend/src/components/base/Dropdown.tsx b/apps/onelauncher/frontend/src/components/base/Dropdown.tsx deleted file mode 100644 index f0e57f7d..00000000 --- a/apps/onelauncher/frontend/src/components/base/Dropdown.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import type { ClassNameString } from '@/types/global'; -import type { - SelectProps as AriaComboBoxProps, - ListBoxItemProps as AriaListBoxItemProps -} from 'react-aria-components'; -import { - Select as AriaComboBox, - Label as AriaLabel, - ListBox as AriaListBox, - ListBoxItem as AriaListBoxItem, - Popover as AriaPopover, - SelectValue -} from 'react-aria-components'; -import { tv } from 'tailwind-variants'; -import Button from './Button'; - -interface ComboBoxWrapperProps extends Omit, 'className'>, ClassNameString { - label?: string; -} - -const dropdownStyles = tv({ - base: [ - "flex flex-col border border-component-bg-hover w-fit rounded-lg focus:outline-none" - ] -}) - -function Dropdown({ - className, - label, - children, - ...rest -}: ComboBoxWrapperProps) { - return ( - - {label && {label}} -
- - -
- - - {children} - - -
- ); -} - -export default Dropdown; - -Dropdown.Item = ({ - className, - ...rest -}: AriaListBoxItemProps & ClassNameString) => ( - -); \ No newline at end of file diff --git a/apps/onelauncher/frontend/src/components/base/Show.tsx b/apps/onelauncher/frontend/src/components/base/Show.tsx deleted file mode 100644 index 6b6f0745..00000000 --- a/apps/onelauncher/frontend/src/components/base/Show.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React, { type JSX } from 'react'; - -interface ShowProps { - /** - * Condition to evaluate. - * If truthy, `children` are rendered. - * If T is a value (not just boolean), you might want to use it in children, - * but this basic version doesn't automatically pass it like SolidJS does for type narrowing. - */ - when: T | boolean | undefined | null; - /** - * Content to render if `when` is falsy. - */ - fallback?: React.ReactNode; - /** - * Content to render if `when` is truthy. - */ - children: React.ReactNode | ((item: NonNullable) => React.ReactNode); -} - -const ShowComponent = ({ when, fallback = null, children }: ShowProps): JSX.Element | null => { - if (Boolean(when)) { - if (typeof children === 'function') { - // Ensure 'when' is not null or undefined before calling the function. - // This mimics Solid's behavior where children can be a function that receives the truthy 'when' value. - return <>{children(when as NonNullable)}; - } - return <>{children}; - } - return <>{fallback}; -}; - -export const Show = React.memo(ShowComponent) as (props: ShowProps) => JSX.Element | null; \ No newline at end of file diff --git a/apps/onelauncher/frontend/src/components/base/Switch.tsx b/apps/onelauncher/frontend/src/components/base/Switch.tsx deleted file mode 100644 index b0a87e7c..00000000 --- a/apps/onelauncher/frontend/src/components/base/Switch.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import type React from 'react'; -import type { RefAttributes } from 'react'; -import type { SwitchProps as AriaSwitchProps } from 'react-aria-components'; -import { Switch as AriaSwitch } from 'react-aria-components'; -import { tv } from 'tailwind-variants'; - -export const switchVariants = tv({ - base: [ - 'group inline-flex h-6 w-11 items-center rounded-full transition-colors', - 'bg-component-bg-pressed data-[selected]:bg-blue-600', - 'focus:outline-none', - 'disabled:cursor-not-allowed disabled:opacity-50', - ], -}); - -export const switchThumbVariants = tv({ - base: [ - 'pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow ring-0 transition-transform', - 'translate-x-1 group-data-[selected]:translate-x-6', - ], -}); - -export type SwitchProps = AriaSwitchProps & RefAttributes & { - className?: string; - children?: React.ReactNode; -}; - -function Switch({ - className, - children, - ...props -}: SwitchProps) { - return ( - - - {children && {children}} - - ); -} - -export default Switch; \ No newline at end of file diff --git a/apps/onelauncher/frontend/src/components/base/Tooltip.tsx b/apps/onelauncher/frontend/src/components/base/Tooltip.tsx deleted file mode 100644 index f2b3dfd7..00000000 --- a/apps/onelauncher/frontend/src/components/base/Tooltip.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import type React from 'react'; -import type { RefAttributes } from 'react'; -import type { TooltipTriggerComponentProps as AriaTooltipTriggerProps } from 'react-aria-components'; -import { - TooltipTrigger as AriaTooltipTrigger, - Tooltip as AriaTooltip, - OverlayArrow -} from 'react-aria-components'; -import { tv } from 'tailwind-variants'; - -export const tooltipVariants = tv({ - base: [ - 'px-3 py-2 text-sm font-medium text-white bg-gray-900 rounded-lg shadow-lg', - 'border border-gray-700 max-w-xs z-50', - 'data-[entering]:animate-in data-[entering]:fade-in data-[entering]:zoom-in-95', - 'data-[exiting]:animate-out data-[exiting]:fade-out data-[exiting]:zoom-out-95', - ], -}); - -export const tooltipArrowVariants = tv({ - base: [ - 'fill-gray-900 stroke-gray-700 stroke-1', - ], -}); - -export type TooltipProps = AriaTooltipTriggerProps & RefAttributes & { - text: string; - children: React.ReactElement; - placement?: 'top' | 'bottom' | 'left' | 'right'; - className?: string; - delay?: number; - closeDelay?: number; -}; - -function Tooltip({ - text, - children, - placement = 'top', - className, - delay = 700, - closeDelay = 0, - ...props -}: TooltipProps) { - return ( - - {children} - - - - - - - {text} - - - ); -} - -export default Tooltip; \ No newline at end of file diff --git a/apps/onelauncher/frontend/src/components/overlay/popups/AccountPopup.tsx b/apps/onelauncher/frontend/src/components/overlay/popups/AccountPopup.tsx index 37d64c6d..7af01da0 100644 --- a/apps/onelauncher/frontend/src/components/overlay/popups/AccountPopup.tsx +++ b/apps/onelauncher/frontend/src/components/overlay/popups/AccountPopup.tsx @@ -1,7 +1,6 @@ -import Button from '@/components/base/Button'; -import Menu from '@/components/base/Menu'; import PlayerHead from '@/components/content/PlayerHead'; -import useCommand from '@/hooks/useCommand'; +import {useCommand} from '@onelauncher/common'; +import { Button, Menu } from '@onelauncher/common/components'; import { bindings } from '@/main'; import { PlusIcon, Settings01Icon } from '@untitled-theme/icons-react'; import { Separator } from 'react-aria-components'; diff --git a/apps/onelauncher/frontend/src/hooks/useCommand.ts b/apps/onelauncher/frontend/src/hooks/useCommand.ts deleted file mode 100644 index 3095ccf7..00000000 --- a/apps/onelauncher/frontend/src/hooks/useCommand.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { bindings } from '@/main'; -import type { UndefinedInitialDataOptions } from '@tanstack/react-query'; -import { useQuery } from '@tanstack/react-query'; - -type CacheKey = (keyof typeof bindings.core) | (keyof typeof bindings.onelauncher); - -function useCommand(cacheKey: CacheKey | (string & {}), command: () => Promise, options?: Omit, 'queryKey' | 'queryFn'>) { - return useQuery({ - queryKey: [cacheKey], - queryFn: async () => { - try { - const result = await command(); - return result; - } - catch (e) { - console.error(e); - if (e instanceof Error) - return Promise.reject(e); - else - return Promise.reject(new Error('Unknown error')); - } - }, - enabled: true, - ...options, - }); -} - -export default useCommand; diff --git a/apps/onelauncher/frontend/src/logo.svg b/apps/onelauncher/frontend/src/logo.svg deleted file mode 100644 index d6c2da20..00000000 --- a/apps/onelauncher/frontend/src/logo.svg +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/apps/onelauncher/frontend/src/main.tsx b/apps/onelauncher/frontend/src/main.tsx index 1be1a51c..b9cf6e98 100644 --- a/apps/onelauncher/frontend/src/main.tsx +++ b/apps/onelauncher/frontend/src/main.tsx @@ -1,14 +1,12 @@ +import { isDev, registerDevExperience, registerNativeExperience } from '@onelauncher/common'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; - import { createRouter, RouterProvider } from '@tanstack/react-router'; import { StrictMode } from 'react'; import ReactDOM from 'react-dom/client'; import { createTauRPCProxy } from './bindings.gen'; - import { routeTree } from './routeTree.gen'; + import './fonts'; -import './utils/nativeExperience'; -import './utils/devExperience'; import 'overlayscrollbars/overlayscrollbars.css'; import './styles/global.css'; @@ -44,6 +42,12 @@ declare module '@tanstack/react-router' { } } +// Register UX changes +if (isDev) + registerDevExperience(); + +registerNativeExperience(); + // Render the app const rootElement = document.getElementById('app'); if (rootElement && !rootElement.innerHTML) { diff --git a/apps/onelauncher/frontend/src/routes/__root.tsx b/apps/onelauncher/frontend/src/routes/__root.tsx index 47d6806a..f1d572ca 100644 --- a/apps/onelauncher/frontend/src/routes/__root.tsx +++ b/apps/onelauncher/frontend/src/routes/__root.tsx @@ -1,7 +1,7 @@ import type { QueryClient } from '@tanstack/react-query'; import type { NavigateOptions, ToOptions } from '@tanstack/react-router'; -import { AnimatedOutletProvider } from '@/components/AnimatedOutlet'; import WindowFrame from '@/components/WindowFrame'; +import { AnimatedOutletProvider } from '@onelauncher/common/components'; import { createRootRouteWithContext, Outlet, useRouter } from '@tanstack/react-router'; import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'; import { RouterProvider } from 'react-aria-components'; diff --git a/apps/onelauncher/frontend/src/routes/app/browser/index.tsx b/apps/onelauncher/frontend/src/routes/app/browser/index.tsx index c812a9ae..d5a45078 100644 --- a/apps/onelauncher/frontend/src/routes/app/browser/index.tsx +++ b/apps/onelauncher/frontend/src/routes/app/browser/index.tsx @@ -1,6 +1,4 @@ -import Button from '@/components/base/Button'; -import Dropdown from '@/components/base/Dropdown'; -import { TextField } from '@/components/base/TextField'; +import { Button, TextField } from '@onelauncher/common/components'; import Modal from '@/components/overlay/Modal'; import { createFileRoute } from '@tanstack/react-router'; diff --git a/apps/onelauncher/frontend/src/routes/app/cluster/index.tsx b/apps/onelauncher/frontend/src/routes/app/cluster/index.tsx index 00042c6d..efe0e46e 100644 --- a/apps/onelauncher/frontend/src/routes/app/cluster/index.tsx +++ b/apps/onelauncher/frontend/src/routes/app/cluster/index.tsx @@ -1,201 +1,199 @@ -import useCommand from '@/hooks/useCommand' -import { bindings } from '@/main' -import { createFileRoute } from '@tanstack/react-router' -import Sidebar from '../settings/route' -import Button from '@/components/base/Button' -import { Edit02Icon, FolderIcon, ImagePlusIcon, Share07Icon, Tool02Icon, Trash01Icon } from '@untitled-theme/icons-react' -import LoaderIcon from '@/components/launcher/LoaderIcon' -import { Show } from '@/components/base/Show' -import { TextField } from '@/components/base/TextField' -import type { Model } from '@/bindings.gen' -import ScrollableContainer from '@/components/ScrollableContainer' -import { useState, type Dispatch, type SetStateAction } from 'react' -import SettingsRow from '@/components/SettingsRow' -import ToggleButton from '@/components/base/ToggleButton' +import type { Model } from '@/bindings.gen'; +import type { Dispatch, SetStateAction } from 'react'; +import LoaderIcon from '@/components/launcher/LoaderIcon'; +import ScrollableContainer from '@/components/ScrollableContainer'; +import SettingsRow from '@/components/SettingsRow'; +import useCommand from '@/hooks/useCommand'; +import { bindings } from '@/main'; +import { Button, Show, TextField } from '@onelauncher/common/components'; +import { createFileRoute } from '@tanstack/react-router'; +import { Edit02Icon, FolderIcon, ImagePlusIcon, Share07Icon, Tool02Icon, Trash01Icon } from '@untitled-theme/icons-react'; +import { useState } from 'react'; +import Sidebar from '../settings/route'; export const Route = createFileRoute('/app/cluster/')({ - component: RouteComponent -}) + component: RouteComponent, +}); function RouteComponent() { - const { id } = Route.useSearch() - const [newCover, setNewCover] = useState("") - const [newName, setNewName] = useState("") + const { id } = Route.useSearch(); + const [newCover, setNewCover] = useState(''); + const [newName, setNewName] = useState(''); - // dumbass fix ik - const cluster = useCommand("getClusterById", () => bindings.commands.getClusterById(Number(id.toString()) as unknown as bigint)) + // dumbass fix ik + const cluster = useCommand('getClusterById', () => bindings.commands.getClusterById(Number(id.toString()) as unknown as bigint)); - return ( - -

Overview

- -
- + return ( + +

Overview

+ +
+ - Folders and Files - - )} - description={"burada path var işte yersen"} - disabled={false} - icon={} - title="Cluster Folder" - /> + Folders and Files + + )} + description="burada path var işte yersen" + disabled={false} + icon={} + title="Cluster Folder" + /> - Cluster Actions - Edit - )} - description="Edit the cluster name and cover image." - icon={} - title="Edit Cluster" - /> - - )} - description="Delete this cluster and all its data." - disabled={false} - icon={} - title="Delete Cluster" - /> - - )} - description="Verifies whether all assets, libraries and natives were properly installed." - icon={} - title="Verify Cluster" - /> -
-
-
- ) + Cluster Actions + Edit + )} + description="Edit the cluster name and cover image." + icon={} + title="Edit Cluster" + /> + + )} + description="Delete this cluster and all its data." + disabled={false} + icon={} + title="Delete Cluster" + /> + + )} + description="Verifies whether all assets, libraries and natives were properly installed." + icon={} + title="Verify Cluster" + /> +
+
+
+ ); } interface BannerProps { - cluster: Model | null | undefined; - editMode: boolean; - newName: string; - setNewName: Dispatch>; - newCover: string; - setNewCover: Dispatch>; - refetch: () => void; + cluster: Model | null | undefined; + editMode: boolean; + newName: string; + setNewName: Dispatch>; + newCover: string; + setNewCover: Dispatch>; + refetch: () => void; } function Banner(props: BannerProps) { - // async function launchFilePicker() { - // const selected = await dialog.open({ - // multiple: false, - // directory: false, - // filters: [{ - // name: 'Image', - // extensions: ['png', 'jpg', 'jpeg', 'webp'], - // }], - // }); + // async function launchFilePicker() { + // const selected = await dialog.open({ + // multiple: false, + // directory: false, + // filters: [{ + // name: 'Image', + // extensions: ['png', 'jpg', 'jpeg', 'webp'], + // }], + // }); - // if (selected === null) - // return; + // if (selected === null) + // return; - // props.setNewCover(selected); - // } + // props.setNewCover(selected); + // } - function updateName(name: string) { - if (name.length > 30 || name.length <= 0) - return; + function updateName(name: string) { + if (name.length > 30 || name.length <= 0) + return; - props.setNewName(name); - } + props.setNewName(name); + } - return ( -
-
- -
- -
-
+ return ( +
+
+ +
+ +
+
- {/* */} - -
+ {/* */} + +
-
-
- {props.cluster?.name} - } - when={props.editMode} - > - updateName(e.target.value)} - placeholder={props.cluster?.name} - /> - -
+
+
+ {props.cluster?.name} + } + when={props.editMode} + > + updateName(e.target.value)} + placeholder={props.cluster?.name} + /> + +
-
-
- - - {props.cluster?.mc_version} - {props.cluster?.mc_loader || 'unknown'} - {props.cluster?.mc_loader_version && {props.cluster.mc_loader_version}} - - - Played for - {' '} - {props.cluster?.overall_played || 0} - . - -
+
+
+ + + {props.cluster?.mc_version} + {props.cluster?.mc_loader || 'unknown'} + {props.cluster?.mc_loader_version && {props.cluster.mc_loader_version}} + + + Played for + {' '} + {props.cluster?.overall_played || 0} + . + +
-
-
-
+
+
-
-
- ); +
+
+ ); } diff --git a/apps/onelauncher/frontend/src/routes/app/index.tsx b/apps/onelauncher/frontend/src/routes/app/index.tsx index 0e7d483b..2f18576c 100644 --- a/apps/onelauncher/frontend/src/routes/app/index.tsx +++ b/apps/onelauncher/frontend/src/routes/app/index.tsx @@ -2,11 +2,10 @@ import type { Model } from '@/bindings.gen'; import DefaultBanner from '@/assets/images/default_banner.png'; import HeaderImage from '@/assets/images/default_banner.png'; import DefaultInstancePhoto from '@/assets/images/default_instance_cover.jpg'; -import Button from '@/components/base/Button'; -import { TextField } from '@/components/base/TextField'; import Modal from '@/components/overlay/Modal'; -import useCommand from '@/hooks/useCommand'; import { bindings } from '@/main'; +import { useCommand } from '@onelauncher/common'; +import { Button, TextField } from '@onelauncher/common/components'; import { createFileRoute, Link, useNavigate } from '@tanstack/react-router'; import { Server01Icon } from '@untitled-theme/icons-react'; import { OverlayScrollbarsComponent } from 'overlayscrollbars-react'; @@ -21,10 +20,6 @@ and i am very angry rn so i will not be fixing it rn */ function RouteComponent() { const result = useCommand('getClusters', bindings.core.get_clusters); - const test = useCommand('ajgiagwo', bindings.onelauncher.return_error, { - enabled: true, - retry: false, - }); return (
diff --git a/apps/onelauncher/frontend/src/routes/app/route.tsx b/apps/onelauncher/frontend/src/routes/app/route.tsx index 615af06d..e18f1696 100644 --- a/apps/onelauncher/frontend/src/routes/app/route.tsx +++ b/apps/onelauncher/frontend/src/routes/app/route.tsx @@ -1,5 +1,5 @@ -import { AnimatedOutlet } from '@/components/AnimatedOutlet'; import Navbar from '@/components/Navbar'; +import { AnimatedOutlet } from '@onelauncher/common/components'; import { createFileRoute } from '@tanstack/react-router'; import { OverlayScrollbarsComponent } from 'overlayscrollbars-react'; diff --git a/apps/onelauncher/frontend/src/routes/app/settings/accounts.tsx b/apps/onelauncher/frontend/src/routes/app/settings/accounts.tsx index 8db5d86f..5cb86bcf 100644 --- a/apps/onelauncher/frontend/src/routes/app/settings/accounts.tsx +++ b/apps/onelauncher/frontend/src/routes/app/settings/accounts.tsx @@ -1,133 +1,140 @@ -import { createFileRoute } from '@tanstack/react-router' -import Sidebar from './route' -import ScrollableContainer from '@/components/ScrollableContainer' -import Button from '@/components/base/Button' -import { InfoCircleIcon, Trash01Icon, UserPlus02Icon } from '@untitled-theme/icons-react' -import PlayerHead from '@/components/content/PlayerHead' -import { useState } from 'react' -import { Show } from '@/components/base/Show' -import Tooltip from '@/components/base/Tooltip' -import useCommand from '@/hooks/useCommand' -import { bindings } from '@/main' +import PlayerHead from '@/components/content/PlayerHead'; +import ScrollableContainer from '@/components/ScrollableContainer'; +import useCommand from '@/hooks/useCommand'; +import { bindings } from '@/main'; +import { Button, Show, Tooltip } from '@onelauncher/common/components'; +import { createFileRoute } from '@tanstack/react-router'; +import { InfoCircleIcon, Trash01Icon, UserPlus02Icon } from '@untitled-theme/icons-react'; +import { useState } from 'react'; +import Sidebar from './route'; export const Route = createFileRoute('/app/settings/accounts')({ - component: RouteComponent, -}) + component: RouteComponent, +}); interface Account { - id: string; - username: string; + id: string; + username: string; } function RouteComponent() { - const { data: usersData, isLoading: usersLoading } = useCommand("getUsers", bindings.commands.getUsers) - const { data: defaultUserData, isLoading: defaultUserLoading } = useCommand("getDefaultUser", () => bindings.commands.getDefaultUser(false)) + const { data: usersData, isLoading: usersLoading } = useCommand('getUsers', bindings.commands.getUsers); + const { data: defaultUserData, isLoading: defaultUserLoading } = useCommand('getDefaultUser', () => bindings.commands.getDefaultUser(false)); - if (usersLoading || defaultUserLoading) { - return

Accounts

Loading accounts...

; - } + if (usersLoading || defaultUserLoading) + return ( + +

Accounts

+

Loading accounts...

+
+ ); - return ( - -

Accounts

- -
- 0} fallback={

No accounts found.

}> - {usersData?.map((account) => ( - - ))} -
-
-
+ return ( + +

Accounts

+ +
+ No accounts found.

} when={usersData && usersData.length > 0}> + {usersData?.map(account => ( + + ))} +
+
+
-
- -
-
- ) +
+ +
+
+ ); } interface AccountRowProps { - account: Account; - isCurrent?: boolean; + account: Account; + isCurrent?: boolean; }; function AccountRow({ account, isCurrent }: AccountRowProps) { - const [errored, setErrored] = useState(false); + const [errored, setErrored] = useState(false); - const setDefaultUserCommand = useCommand( - "setDefaultUser", - () => bindings.commands.setDefaultUser(account.id), - { - enabled: false, - subscribed: false, - } - ); + const setDefaultUserCommand = useCommand( + 'setDefaultUser', + () => bindings.commands.setDefaultUser(account.id), + { + enabled: false, + subscribed: false, + }, + ); - const removeUserCommand = useCommand( - "removeUser", - () => bindings.commands.removeUser(account.id), - { - enabled: false, - subscribed: false, - } - ); + const removeUserCommand = useCommand( + 'removeUser', + () => bindings.commands.removeUser(account.id), + { + enabled: false, + subscribed: false, + }, + ); - const handleSetDefault = () => { - setDefaultUserCommand.refetch(); - }; + const handleSetDefault = () => { + setDefaultUserCommand.refetch(); + }; - const handleDelete = (e: React.MouseEvent) => { - e.stopPropagation(); - removeUserCommand.refetch(); - }; + const handleDelete = (e: React.MouseEvent) => { + e.stopPropagation(); + removeUserCommand.refetch(); + }; - return ( -
{ if (e.key === 'Enter' || e.key === ' ') handleSetDefault(); }} - > -
- setErrored(true)} - uuid={account.id} - /> -
+ return ( +
{ + if (e.key === 'Enter' || e.key === ' ') + handleSetDefault(); + }} + role="button" + tabIndex={0} + > +
+ setErrored(true)} + uuid={account.id} + /> +
-
-
-

{account.username}

- - - - - -
-

{account.id}

-
+
+
+

{account.username}

+ + + + + +
+

{account.id}

+
-
- -
-
- ); -} \ No newline at end of file +
+ +
+
+ ); +} diff --git a/apps/onelauncher/frontend/src/routes/app/settings/appearance.tsx b/apps/onelauncher/frontend/src/routes/app/settings/appearance.tsx index 362fcea3..e3ad63ed 100644 --- a/apps/onelauncher/frontend/src/routes/app/settings/appearance.tsx +++ b/apps/onelauncher/frontend/src/routes/app/settings/appearance.tsx @@ -1,27 +1,26 @@ -import { createFileRoute } from '@tanstack/react-router' -import Sidebar from './route' -import ScrollableContainer from '@/components/ScrollableContainer' -import { THEMES } from '@/utils/theming' -import SettingsRow from '@/components/SettingsRow' -import { Monitor01Icon, PackageIcon, Speedometer04Icon } from '@untitled-theme/icons-react' -import ToggleButton from '@/components/base/ToggleButton' -import Switch from '@/components/base/Switch' +import ScrollableContainer from '@/components/ScrollableContainer'; +import SettingsRow from '@/components/SettingsRow'; +import { THEMES } from '@/utils/theming'; +import { Switch } from '@onelauncher/common/components'; +import { createFileRoute } from '@tanstack/react-router'; +import { Monitor01Icon, PackageIcon, Speedometer04Icon } from '@untitled-theme/icons-react'; +import Sidebar from './route'; export const Route = createFileRoute('/app/settings/appearance')({ - component: RouteComponent, -}) + component: RouteComponent, +}); function RouteComponent() { - return ( - - -
-

Appearance

+ return ( + + +
+

Appearance

-
- -
- {/* +
+ +
+ {/* {theme => ( {variant => ( @@ -34,82 +33,184 @@ function RouteComponent() { )} */} - {Object.keys(THEMES).map((theme) => { - return THEMES[theme as keyof typeof THEMES].variants.map((variant, i: number) => { - return ( - { }} key={i} /> - ) - }) - })} -
-
+ {Object.keys(THEMES).map((theme) => { + return THEMES[theme as keyof typeof THEMES].variants.map((variant) => { + return ( + { }} themeSelector={`${theme}-${variant.name}`} /> + ); + }); + })} +
+
- } - title="Package List Style" - > -

i hope i'll fix this

-
+ } + title="Package List Style" + > +

i hope i'll fix this

+
- } - title="Custom Window Frame" - > - - + } + title="Custom Window Frame" + > + + - } - title="Disable Animations" - > - - -
-
-
- ) + } + title="Disable Animations" + > + + +
+
+
+ ); } interface ThemeCardProps { - themeSelector: string; - setTheme: (merged: string) => void; + themeSelector: string; + setTheme: (merged: string) => void; }; function ThemeCard(props: ThemeCardProps) { - return ( -
props.setTheme(props.themeSelector)}> - - - - - - - - + return ( +
props.setTheme(props.themeSelector)}> + + + + + + + + -
- ); +
+ ); } function PrimaryThemeCard() { - return ( -
- - - - - - - - - - - - - -
- ); -} \ No newline at end of file + return ( +
+ + + + + + + + + + + + + +
+ ); +} diff --git a/apps/onelauncher/frontend/src/routes/app/settings/index.tsx b/apps/onelauncher/frontend/src/routes/app/settings/index.tsx index 1579c9ca..3d30952d 100644 --- a/apps/onelauncher/frontend/src/routes/app/settings/index.tsx +++ b/apps/onelauncher/frontend/src/routes/app/settings/index.tsx @@ -1,42 +1,41 @@ -import { createFileRoute } from '@tanstack/react-router' -import Sidebar from './route' -import useCommand from '@/hooks/useCommand' -import { bindings } from '@/main' -import SettingsRow from '@/components/SettingsRow' -import DiscordIcon from "@/assets/logos/discord.svg" -import Switch from '@/components/base/Switch' -import Button from '@/components/base/Button' -import { FolderIcon, LinkExternal01Icon, XIcon } from '@untitled-theme/icons-react' +import DiscordIcon from '@/assets/logos/discord.svg'; +import SettingsRow from '@/components/SettingsRow'; +import { bindings } from '@/main'; +import { useCommand } from '@onelauncher/common'; +import { Button, Switch } from '@onelauncher/common/components'; +import { createFileRoute } from '@tanstack/react-router'; +import { FolderIcon, LinkExternal01Icon, XIcon } from '@untitled-theme/icons-react'; +import Sidebar from './route'; export const Route = createFileRoute('/app/settings/')({ - component: RouteComponent, -}) + component: RouteComponent, +}); function RouteComponent() { - const result = useCommand("getGlobalProfile", bindings.commands.getGlobalProfile) + const result = useCommand('getGlobalProfile', bindings.commands.getGlobalProfile); - return ( - -
-

General Settings

+ return ( + +
+

General Settings

- } - title="Discord RPC" - > - - + } + title="Discord RPC" + > + + - } - title="Hide Close Dialog" - > - - + } + title="Hide Close Dialog" + > + + - {/* } title="Error Analytics" @@ -47,17 +46,19 @@ function RouteComponent() { /> */} - Folders and Files - } - title="Launcher Folder" - > - - -
-
- ) + Folders and Files + } + title="Launcher Folder" + > + + +
+
+ ); } diff --git a/apps/onelauncher/frontend/src/routes/app/settings/minecraft.tsx b/apps/onelauncher/frontend/src/routes/app/settings/minecraft.tsx index b6bbff8a..5ee5bef8 100644 --- a/apps/onelauncher/frontend/src/routes/app/settings/minecraft.tsx +++ b/apps/onelauncher/frontend/src/routes/app/settings/minecraft.tsx @@ -1,142 +1,143 @@ -import { createFileRoute } from '@tanstack/react-router' -import Sidebar from './route' -import SettingsRow from '@/components/SettingsRow' -import { Database01Icon, EyeIcon, FilePlus02Icon, FileX02Icon, LayoutTopIcon, Maximize01Icon, ParagraphWrapIcon, XIcon } from '@untitled-theme/icons-react' -import Button from '@/components/base/Button' -import useCommand from '@/hooks/useCommand' -import { bindings } from '@/main' -import { TextField } from '@/components/base/TextField' -import ToggleButton from '@/components/base/ToggleButton' -import ScrollableContainer from '@/components/ScrollableContainer' -import Switch from '@/components/base/Switch' +import ScrollableContainer from '@/components/ScrollableContainer'; +import SettingsRow from '@/components/SettingsRow'; +import { Switch, TextField, ToggleButton } from '@onelauncher/common/components'; +import { createFileRoute } from '@tanstack/react-router'; +import { Database01Icon, EyeIcon, FilePlus02Icon, FileX02Icon, LayoutTopIcon, Maximize01Icon, ParagraphWrapIcon, XIcon } from '@untitled-theme/icons-react'; +import Sidebar from './route'; export const Route = createFileRoute('/app/settings/minecraft')({ - component: RouteComponent, -}) + component: RouteComponent, +}); function RouteComponent() { - return ( - - -
-

Minecraft Settings

+ return ( + + +
+

Minecraft Settings

- + - {/* */} + {/* */} - -
-
-
- ) + +
+
+
+ ); } export function GameSettings() { - return <> - Game + return ( + <> + Game - } - title="Force Fullscreen" - > - - + } + title="Force Fullscreen" + > + + - } - title="Resolution" - > -
- - - -
-
+ } + title="Resolution" + > +
+ + + +
+
- } - title="Memory" - > -
-
- Min: - -
+ } + title="Memory" + > +
+
+ Min: + +
-
- Max: - -
-
-
- +
+ Max: + +
+
+
+ + ); } -function LauncherSettings() { - return <> - Launcher - } - title="Hide On Launch" - > - - - -} +// function LauncherSettings() { +// return ( +// <> +// Launcher +// } +// title="Hide On Launch" +// > +// +// +// +// ); +// } export function ProcessSettings() { - return <> - Process + return ( + <> + Process - } - title="Pre-Launch Command" - > - - + } + title="Pre-Launch Command" + > + + - } - title="Wrapper Command" - > - - + } + title="Wrapper Command" + > + + - } - title="Post-Exit Command" - > - - - -} \ No newline at end of file + } + title="Post-Exit Command" + > + + + + ); +} diff --git a/apps/onelauncher/frontend/src/routes/app/settings/route.tsx b/apps/onelauncher/frontend/src/routes/app/settings/route.tsx index 6f67d813..3db5530e 100644 --- a/apps/onelauncher/frontend/src/routes/app/settings/route.tsx +++ b/apps/onelauncher/frontend/src/routes/app/settings/route.tsx @@ -1,122 +1,122 @@ -import { createFileRoute, Outlet, useNavigate, useRouterState } from '@tanstack/react-router' +import type { JSX } from 'react'; +import { createFileRoute, Outlet, useNavigate, useRouterState } from '@tanstack/react-router'; import { Brush01Icon, CodeSnippet02Icon, MessageTextSquare01Icon, RefreshCcw02Icon, Rocket02Icon, Sliders04Icon, Users01Icon } from '@untitled-theme/icons-react'; -import { useEffect, type JSX } from 'react'; +import { useEffect } from 'react'; export const Route = createFileRoute('/app/settings')({ - component: RouteComponent, -}) + component: RouteComponent, +}); function RouteComponent() { - return ( -
-
- , 'General', '/'], - [, 'Appearance', '/appearance'], - // [, 'APIs', '/apis'], - // [, 'Language', '/language'], - ], - 'Game Settings': [ - [, 'Minecraft settings', '/minecraft'], - [, 'Accounts', '/accounts'], - ], - 'About': [ - [, 'Changelog', '/changelog'], - [, 'Feedback', '/feedback'], - [, 'Developer Options', '/developer'], - ], - }} - /> - {/* */} -
+ return ( +
+
+ , 'General', '/'], + [, 'Appearance', '/appearance'], + // [, 'APIs', '/apis'], + // [, 'Language', '/language'], + ], + 'Game Settings': [ + [, 'Minecraft settings', '/minecraft'], + [, 'Accounts', '/accounts'], + ], + 'About': [ + [, 'Changelog', '/changelog'], + [, 'Feedback', '/feedback'], + [, 'Developer Options', '/developer'], + ], + }} + /> + {/* */} +
-
- -
-
- ) +
+ +
+
+ ); } -type SidebarProps = { - base: string; - links: Record; -}; +interface SidebarProps { + base: string; + links: Record>; +} function Sidebar(props: SidebarProps) { - const navigate = useNavigate(); - const routerState = useRouterState(); + const navigate = useNavigate(); + const routerState = useRouterState(); - const location = routerState.location.pathname + const location = routerState.location.pathname; - useEffect(() => { - if (props.base.endsWith('/')) - throw new Error('Base should not end with a slash'); - }); + useEffect(() => { + if (props.base.endsWith('/')) + throw new Error('Base should not end with a slash'); + }); - function goto(href: string, params?: URLSearchParams) { - const currParams = new URLSearchParams(location); - if (params) - for (const [key, value] of params) - currParams.set(key, value); + function goto(href: string, params?: URLSearchParams) { + const currParams = new URLSearchParams(location); + if (params) + for (const [key, value] of params) + currParams.set(key, value); - const url = `${props.base}${href}`; - navigate({ to: url }) - } + const url = `${props.base}${href}`; + navigate({ to: url }); + } - function isActive(link: string, _params: URLSearchParams | undefined) { - return location === `${props.base}${link}` || `${location}/` === `${props.base}${link}`; - } + function isActive(link: string, _params: URLSearchParams | undefined) { + return location === `${props.base}${link}` || `${location}/` === `${props.base}${link}`; + } - return ( -
- {Object.keys(props.links).map((section, i) => ( -
-
-

{section.toUpperCase()}

-
- {props.links[section].map((link, i) => { - if (!link) - return; + return ( +
+ {Object.keys(props.links).map((section, i) => ( +
+
+

{section.toUpperCase()}

+
+ {props.links[section].map((link, i) => { + if (!link) + return; - return ( - goto(link[2], link[3])} - > - - {link[0]} - {link[1]} - - - ) - })} -
-
-
- ) - )} -
- ); + return ( + goto(link[2], link[3])} + > + + {link[0]} + {link[1]} + + + ); + })} +
+
+
+ ), + )} +
+ ); } interface SidebarPageProps { - className?: string; - children: JSX.Element | JSX.Element[]; + className?: string; + children: JSX.Element | Array; } Sidebar.Page = function (props: SidebarPageProps) { - return ( -
- {props.children} -
- ); + return ( +
+ {props.children} +
+ ); }; export default Sidebar; - diff --git a/apps/onelauncher/frontend/src/routes/onboarding/import.tsx b/apps/onelauncher/frontend/src/routes/onboarding/import.tsx index 0c9a2795..f9881570 100644 --- a/apps/onelauncher/frontend/src/routes/onboarding/import.tsx +++ b/apps/onelauncher/frontend/src/routes/onboarding/import.tsx @@ -1,37 +1,42 @@ -import { createFileRoute } from '@tanstack/react-router' -import { OnboardingStep } from './route' -import Illustration from '../../assets/illustrations/onboarding/import_from_others.svg'; -import { LAUNCHER_IMPORT_TYPES } from '@/utils'; +import Illustration from '@/assets/illustrations/onboarding/import_from_others.svg'; import LauncherIcon from '@/components/content/LauncherIcon'; +import { LAUNCHER_IMPORT_TYPES } from '@/utils'; +import { createFileRoute } from '@tanstack/react-router'; +import { OnboardingStep } from './route'; export const Route = createFileRoute('/onboarding/import')({ - component: RouteComponent, -}) + component: RouteComponent, +}); function RouteComponent() { - return ( - <> - } paragraph="Import your profiles from other launchers." - title="Import"> -
-
- {LAUNCHER_IMPORT_TYPES.map((x) => ( - - ))} -
- - Want to contribute a launcher import? Click - {' '} - here - . - -
-
- - ) + return ( + <> + } + paragraph="Import your profiles from other launchers." + title="Import" + > +
+
+ {LAUNCHER_IMPORT_TYPES.map(x => ( + + ))} +
+ + Want to contribute a launcher import? Click + {' '} + here + . + +
+
+ + ); } diff --git a/apps/onelauncher/frontend/src/routes/onboarding/language.tsx b/apps/onelauncher/frontend/src/routes/onboarding/language.tsx index 0abef23f..267057aa 100644 --- a/apps/onelauncher/frontend/src/routes/onboarding/language.tsx +++ b/apps/onelauncher/frontend/src/routes/onboarding/language.tsx @@ -1,52 +1,54 @@ -import { createFileRoute } from '@tanstack/react-router' -import { OnboardingStep } from './route' -import Illustration from '../../assets/illustrations/onboarding/language.svg'; +import Illustration from '@/assets/illustrations/onboarding/language.svg'; +import { createFileRoute } from '@tanstack/react-router'; +import { OnboardingStep } from './route'; export const Route = createFileRoute('/onboarding/language')({ - component: RouteComponent, -}) + component: RouteComponent, +}); const languages = [ - { - lang: "English", - percentage: 100 - }, -] + { + lang: 'English', + percentage: 100, + }, +]; function RouteComponent() { - return ( - <> - } - paragraph="Choose your preferred language." - title="Language"> -
-
-
-
- -
-
-
+ return ( + <> + } + paragraph="Choose your preferred language." + title="Language" + > +
+
+
+
+ +
+
+
-
-

Help translate OneLauncher

-
-
-
- - ) +
+

Help translate OneLauncher

+
+
+
+ + ); } diff --git a/apps/onelauncher/frontend/src/routes/onboarding/login.tsx b/apps/onelauncher/frontend/src/routes/onboarding/login.tsx index b23bb8df..54604a14 100644 --- a/apps/onelauncher/frontend/src/routes/onboarding/login.tsx +++ b/apps/onelauncher/frontend/src/routes/onboarding/login.tsx @@ -1,66 +1,65 @@ -import { type MinecraftCredentials } from '@/bindings.gen' -import Button from '@/components/base/Button' -import { Show } from '@/components/base/Show' -import PlayerHead from '@/components/content/PlayerHead' -import useCommand from '@/hooks/useCommand' -import { bindings } from '@/main' -import { createFileRoute } from '@tanstack/react-router' -import { useState } from 'react' +import type { MinecraftCredentials } from '@/bindings.gen'; +import PlayerHead from '@/components/content/PlayerHead'; +import { bindings } from '@/main'; +import { useCommand } from '@onelauncher/common'; +import { Button, Show } from '@onelauncher/common/components'; +import { createFileRoute } from '@tanstack/react-router'; +import { useState } from 'react'; export const Route = createFileRoute('/onboarding/login')({ - component: RouteComponent, -}) + component: RouteComponent, +}); function RouteComponent() { - const [profile, setProfile] = useState() - const [errorMessage, setErrorMessage] = useState('') - const result = useCommand("beginMsFlow", bindings.commands.beginMsFlow, { - enabled: false, - subscribed: false - }) + const [profile, setProfile] = useState(); + const [errorMessage, setErrorMessage] = useState(''); + const result = useCommand('beginMsFlow', bindings.commands.beginMsFlow, { + enabled: false, + subscribed: false, + }); - function beginMsAuthFlow() { - result.refetch() + function beginMsAuthFlow() { + result.refetch(); - if (result.isError) { - setErrorMessage(result.error.message) - return; - } + if (result.isError) { + setErrorMessage(result.error.message); + return; + } - if (!result.data) { - setErrorMessage('No account was found. Please try again.'); - return; - } + if (!result.data) { + setErrorMessage('No account was found. Please try again.'); + return; + } - setProfile(result.data); - } + setProfile(result.data); + } - return ( - <> - {/* trust me i'll fix ui issues later */} -
-

Login

+ return ( + <> + {/* trust me i'll fix ui issues later */} +
+

Login

-

Before you continue, we require you to own a copy of Minecraft: Java Edition.

+

Before you continue, we require you to own a copy of Minecraft: Java Edition.

- -
- -
-

{profile?.username}

-

{profile?.id}

-
-
-
+ +
+ +
+

{profile?.username}

+

{profile?.id}

+
+
+
-
- - ) +
+ + ); } diff --git a/apps/onelauncher/frontend/src/routes/onboarding/route.tsx b/apps/onelauncher/frontend/src/routes/onboarding/route.tsx index 698975a0..3152ba5c 100644 --- a/apps/onelauncher/frontend/src/routes/onboarding/route.tsx +++ b/apps/onelauncher/frontend/src/routes/onboarding/route.tsx @@ -1,140 +1,142 @@ -import Button from '@/components/base/Button' -import { createFileRoute, Outlet, useNavigate, useRouterState } from '@tanstack/react-router' -import { createContext, useState, type JSX } from 'react'; +import type { JSX } from 'react'; +import { Button } from '@onelauncher/common/components'; +import { createFileRoute, Outlet, useNavigate, useRouterState } from '@tanstack/react-router'; export const Route = createFileRoute('/onboarding')({ - component: RouteComponent, -}) + component: RouteComponent, +}); const steps = [ - '/onboarding', // done - '/onboarding/language', // done (only ui) - '/onboarding/login', // wip - '/onboarding/import', // wip + '/onboarding', // done + '/onboarding/language', // done (only ui) + '/onboarding/login', // wip + '/onboarding/import', // wip - '/onboarding/summary', // wip - '/onboarding/complete', // wip + '/onboarding/summary', // wip + '/onboarding/complete', // wip ] as const; -type Language = { - lang: string, - percentage: number +interface Language { + lang: string; + percentage: number; } interface ImportInstancesType { basePath: string; - instances: string[]; + instances: Array; } interface OnboardingContextType { - setLanguage: (language: Language) => void; - language: () => Language; + setLanguage: (language: Language) => void; + language: () => Language; - setImportInstances: (type: string, basePath: string, instances: string[]) => void; - importInstances: (type: string) => ImportInstancesType | undefined; + setImportInstances: (type: string, basePath: string, instances: Array) => void; + importInstances: (type: string) => ImportInstancesType | undefined; - setForwardButtonEnabled: (enabled: boolean) => void; + setForwardButtonEnabled: (enabled: boolean) => void; - getTasks: () => string[]; + getTasks: () => Array; } function RouteComponent() { - const navigate = useNavigate() - const routerState = useRouterState() + const navigate = useNavigate(); + const routerState = useRouterState(); - const currentPath = routerState.location.pathname - const currentStepIndex = steps.findIndex(path => currentPath === path || currentPath.startsWith(path)) + const currentPath = routerState.location.pathname; + const currentStepIndex = steps.findIndex(path => currentPath === path || currentPath.startsWith(path)); - const progressPercentage = - currentStepIndex >= 0 && steps.length > 1 - ? (currentStepIndex / (steps.length - 1)) * 100 - : 0; + const progressPercentage = currentStepIndex >= 0 && steps.length > 1 + ? (currentStepIndex / (steps.length - 1)) * 100 + : 0; - const handleBack = () => { - console.log("Geri için mevcut yol:", currentPath); - console.log("Geri için mevcut adım indeksi:", currentStepIndex); + const handleBack = () => { + console.log('Geri için mevcut yol:', currentPath); + console.log('Geri için mevcut adım indeksi:', currentStepIndex); - if (currentStepIndex > 0) { - const previousStep = steps[currentStepIndex - 1]; - console.log("Geri gidiliyor:", previousStep); - navigate({ to: previousStep as any }); - } else { - console.log("Zaten ilk adımda veya adım bulunamadı, geri gidilemez."); - } - }; + if (currentStepIndex > 0) { + const previousStep = steps[currentStepIndex - 1]; + console.log('Geri gidiliyor:', previousStep); + navigate({ to: previousStep as any }); + } + else { + console.log('Zaten ilk adımda veya adım bulunamadı, geri gidilemez.'); + } + }; - const handleNext = () => { - console.log("İleri için mevcut yol:", currentPath); - console.log("İleri için mevcut adım indeksi:", currentStepIndex); + const handleNext = () => { + console.log('İleri için mevcut yol:', currentPath); + console.log('İleri için mevcut adım indeksi:', currentStepIndex); - if (currentStepIndex === -1) { - navigate({ to: steps[0] as any }); - return; - } + if (currentStepIndex === -1) { + navigate({ to: steps[0] as any }); + return; + } - if (currentStepIndex < steps.length - 1) { - const nextStep = steps[currentStepIndex + 1]; - console.log("İleri gidiliyor:", nextStep); - navigate({ to: nextStep as any }); - } else if (currentStepIndex === steps.length - 1) { - console.log("Onboarding tamamlandı, /app adresine gidiliyor"); - navigate({ to: '/app' as any }); - } else { - console.log("Bir sonraki adım belirlenemedi."); - } - }; + if (currentStepIndex < steps.length - 1) { + const nextStep = steps[currentStepIndex + 1]; + console.log('İleri gidiliyor:', nextStep); + navigate({ to: nextStep as any }); + } + else if (currentStepIndex === steps.length - 1) { + console.log('Onboarding tamamlandı, /app adresine gidiliyor'); + navigate({ to: '/app' as any }); + } + else { + console.log('Bir sonraki adım belirlenemedi.'); + } + }; - return ( - // remind me 2 hours! i'll fix this - // update: it's fixed! -
-
-
-
+ return ( + // remind me 2 hours! i'll fix this + // update: it's fixed! +
+
+
+
-
- -
+
+ +
-
-
- - -
-
-
- ) +
+
+ + +
+
+
+ ); } export interface OnboardingStepProps { - title: string; - paragraph: string; - illustration: JSX.Element; - children: JSX.Element; + title: string; + paragraph: string; + illustration: JSX.Element; + children: JSX.Element; } export function OnboardingStep(props: OnboardingStepProps) { - return ( -
-
- {props.illustration} -
+ return ( +
+
+ {props.illustration} +
-
-
-

{props.title}

-

{props.paragraph}

-
+
+
+

{props.title}

+

{props.paragraph}

+
-
- {props.children} -
-
-
- ); -} \ No newline at end of file +
+ {props.children} +
+
+
+ ); +} diff --git a/apps/onelauncher/frontend/src/routes/onboarding/summary.tsx b/apps/onelauncher/frontend/src/routes/onboarding/summary.tsx index 40063d1d..1206badd 100644 --- a/apps/onelauncher/frontend/src/routes/onboarding/summary.tsx +++ b/apps/onelauncher/frontend/src/routes/onboarding/summary.tsx @@ -1,9 +1,9 @@ -import { createFileRoute } from '@tanstack/react-router' +import { createFileRoute } from '@tanstack/react-router'; export const Route = createFileRoute('/onboarding/summary')({ - component: RouteComponent, -}) + component: RouteComponent, +}); function RouteComponent() { - return
Hello "/onboarding/summary"!
+ return
Hello "/onboarding/summary"!
; } diff --git a/apps/onelauncher/frontend/src/styles/global.css b/apps/onelauncher/frontend/src/styles/global.css index c6e975e6..f0ebce68 100644 --- a/apps/onelauncher/frontend/src/styles/global.css +++ b/apps/onelauncher/frontend/src/styles/global.css @@ -1,6 +1,7 @@ @import "tailwindcss"; @plugin "tailwindcss-react-aria-components"; @plugin "tailwindcss-animated"; +@source "../../node_modules/@onelauncher/common"; @theme { /* foreground colors */ diff --git a/apps/onelauncher/frontend/src/utils/devExperience.ts b/apps/onelauncher/frontend/src/utils/devExperience.ts deleted file mode 100644 index 35d5e48c..00000000 --- a/apps/onelauncher/frontend/src/utils/devExperience.ts +++ /dev/null @@ -1,11 +0,0 @@ -(() => { - if (location.hostname !== 'localhost') - return; - - window.addEventListener('keypress', (e) => { - if (e.getModifierState('Control') && e.key === 'r') { - e.preventDefault(); - window.location.reload(); // tauri disables this i think - } - }); -})(); diff --git a/apps/onelauncher/frontend/src/utils/index.ts b/apps/onelauncher/frontend/src/utils/index.ts index 11b8e068..ea104d2c 100644 --- a/apps/onelauncher/frontend/src/utils/index.ts +++ b/apps/onelauncher/frontend/src/utils/index.ts @@ -1,4 +1,4 @@ -export const LAUNCHER_IMPORT_TYPES: string[] = [ +export const LAUNCHER_IMPORT_TYPES: Array = [ 'PrismLauncher', 'Curseforge', // 'Modrinth', @@ -8,4 +8,4 @@ export const LAUNCHER_IMPORT_TYPES: string[] = [ 'MultiMC', // 'TLauncher', // 'Technic' -] as const; \ No newline at end of file +] as const; diff --git a/apps/onelauncher/frontend/src/utils/nativeExperience.ts b/apps/onelauncher/frontend/src/utils/nativeExperience.ts deleted file mode 100644 index a18b7202..00000000 --- a/apps/onelauncher/frontend/src/utils/nativeExperience.ts +++ /dev/null @@ -1,3 +0,0 @@ -document.addEventListener('contextmenu', (e) => { - e.preventDefault(); -}); diff --git a/apps/onelauncher/frontend/src/utils/theming.ts b/apps/onelauncher/frontend/src/utils/theming.ts index d9fb26bb..56a08beb 100644 --- a/apps/onelauncher/frontend/src/utils/theming.ts +++ b/apps/onelauncher/frontend/src/utils/theming.ts @@ -15,7 +15,7 @@ export const THEMES = { } as const satisfies Record; export interface Theme { - variants: ThemeVariant[]; + variants: Array; } export type ThemeTypes = 'dark' | 'light'; @@ -23,4 +23,4 @@ export type ThemeTypes = 'dark' | 'light'; export interface ThemeVariant { type: ThemeTypes; name: string; -}; \ No newline at end of file +}; diff --git a/package.json b/package.json index 66f08a6d..fd274981 100644 --- a/package.json +++ b/package.json @@ -24,15 +24,17 @@ "prep": "pnpm scripts prep", "typecheck": "pnpm -r typecheck", "bootstrap": "cargo clean && ./packages/scripts/setup.sh && pnpm install && pnpm prep && pnpm codegen", + + "core": "pnpm --filter @onelauncher/core -- ", "migrate": "sea-orm-cli migrate -d ./packages/migration", "entity:gen": "sea-orm-cli generate entity -o packages/entity/src/entities --with-serde both --model-extra-attributes 'onelauncher_macro::specta' -t", "onelauncher:frontend": "pnpm --filter @onelauncher/frontend -- ", "onelauncher:desktop": "pnpm --filter @onelauncher/desktop -- ", "oneclient:frontend": "pnpm --filter @oneclient/frontend -- ", "oneclient:desktop": "pnpm --filter @oneclient/desktop -- ", - "client": "pnpm --filter @onelauncher/client -- ", - "core": "pnpm --filter @onelauncher/core -- ", + "web_common": "pnpm --filter @onelauncher/common -- ", "scripts": "pnpm --filter @onelauncher/scripts -- ", + "format": "pnpm format:es && pnpm format:rs", "format:es": "pnpm lint:es --fix", "format:rs": "cargo +nightly fmt && cargo clippy --fix", diff --git a/packages/web_common/.gitignore b/packages/web_common/.gitignore new file mode 100644 index 00000000..37bf8330 --- /dev/null +++ b/packages/web_common/.gitignore @@ -0,0 +1,3 @@ +lib/ +dist/ +out/ diff --git a/packages/web_common/README.md b/packages/web_common/README.md new file mode 100644 index 00000000..70cbf979 --- /dev/null +++ b/packages/web_common/README.md @@ -0,0 +1,6 @@ +### Registering TailwindCSS +In your main tailwind css config file, use the following to register the components as a source: +(relative to the config file) +```css +@source "../../node_modules/@onelauncher/common"; +``` diff --git a/packages/web_common/package.json b/packages/web_common/package.json new file mode 100644 index 00000000..01c54673 --- /dev/null +++ b/packages/web_common/package.json @@ -0,0 +1,42 @@ +{ + "name": "@onelauncher/common", + "type": "module", + "version": "1.0.0", + "description": "Common web utilities for this monorepo", + "authors": "Polyfrost", + "exports": { + ".": { + "types": "./lib/index.d.ts", + "import": "./lib/index.js" + }, + "./components": { + "types": "./lib/components.d.ts", + "import": "./lib/components.js" + } + }, + "main": "./lib/index.js", + "typings": "./lib/index.d.ts", + "scripts": { + "build": "rollup -c" + }, + "peerDependencies": { + "@tanstack/react-query": "^5.66.5", + "@tanstack/react-router": "^1.114.3", + "motion": "^12.10.5", + "react": "^19.0.0", + "react-aria-components": "^1.8.0", + "tailwind-merge": "^3.2.0", + "tailwind-variants": "^1.0.0", + "tailwindcss": "^4.0.6", + "tailwindcss-animated": "^2.0.0", + "tailwindcss-react-aria-components": "^2.0.0" + }, + "devDependencies": { + "@types/react": "^19.0.8", + "esbuild": "^0.25.5", + "rollup": "^4.41.1", + "rollup-plugin-dts": "^6.2.1", + "rollup-plugin-esbuild": "^6.2.1", + "typescript": "^5.8.3" + } +} diff --git a/packages/web_common/rollup.config.mjs b/packages/web_common/rollup.config.mjs new file mode 100644 index 00000000..ac27b05c --- /dev/null +++ b/packages/web_common/rollup.config.mjs @@ -0,0 +1,82 @@ +import process from 'node:process'; +import dts from 'rollup-plugin-dts'; +import esbuild from 'rollup-plugin-esbuild'; + +/** + * @param {string} id + * @returns {boolean} true if the id is an external module + */ +const isExternal + = process.platform === 'win32' + ? (/** @type {string} */ id) => !/^(?:[a-z]:\\|[.\\])/i.test(id) + : (/** @type {string} */ id) => !/^[./]/.test(id); + +/** + * @param {string} input + * @param {import('rollup').RollupOptions} config + * @returns {import('rollup').RollupOptions} bundle options + */ +function bundle(input, config) { + return { + ...config, + input, + external: isExternal, + }; +} + +export default [ + // Output for NodeJS + bundle('./src/index.ts', { + plugins: [esbuild({ target: 'es2020' })], + output: { + file: `./lib/index.js`, + format: 'esm', + sourcemap: false, + compact: false, + }, + }), + + // Output for Typescript's .d.ts + bundle('./src/index.ts', { + plugins: [dts()], + output: { + file: `./lib/index.d.ts`, + format: 'esm', + }, + }), + + // Output for React components + bundle('./src/components/index.ts', { + plugins: [esbuild({ target: 'es2020' })], + output: { + file: `./lib/components.js`, + format: 'esm', + sourcemap: false, + compact: false, + }, + jsx: 'react-jsx', + }), + + // Output for React components .d.ts + bundle('./src/components/index.ts', { + plugins: [dts()], + output: { + file: `./lib/components.d.ts`, + format: 'esm', + }, + jsx: 'react-jsx', + }), + + // Output for browser + // bundle({ + // plugins: [esbuild({ target: 'es2020', minify: true })], + // output: { + // file: `./dist/${name}-v${version}.js`, + // format: 'esm', + // name: camelCaseName, + // sourcemap: true, + // compact: true, + // }, + // jsx: 'react-jsx', + // }), +]; diff --git a/apps/onelauncher/frontend/src/components/AnimatedOutlet.tsx b/packages/web_common/src/components/AnimatedOutlet.tsx similarity index 100% rename from apps/onelauncher/frontend/src/components/AnimatedOutlet.tsx rename to packages/web_common/src/components/AnimatedOutlet.tsx diff --git a/apps/onelauncher/frontend/src/components/base/Button.tsx b/packages/web_common/src/components/Button.tsx similarity index 91% rename from apps/onelauncher/frontend/src/components/base/Button.tsx rename to packages/web_common/src/components/Button.tsx index 3dc34d8e..394f843f 100644 --- a/apps/onelauncher/frontend/src/components/base/Button.tsx +++ b/packages/web_common/src/components/Button.tsx @@ -35,16 +35,16 @@ export const buttonVariants = tv({ }, }); -export type ButtonVariants = VariantProps; +export type ButtonVariantsProps = VariantProps; export type ButtonProps = AriaButtonProps & RefAttributes & { - color?: ButtonVariants['color']; - size?: ButtonVariants['size']; + color?: ButtonVariantsProps['color']; + size?: ButtonVariantsProps['size']; className?: string; isDisabled?: boolean; }; -function Button({ +export function Button({ color = 'primary', size = 'normal', className, @@ -58,5 +58,3 @@ function Button({ /> ); } - -export default Button; diff --git a/apps/onelauncher/frontend/src/components/base/ContextMenu.tsx b/packages/web_common/src/components/ContextMenu.tsx similarity index 93% rename from apps/onelauncher/frontend/src/components/base/ContextMenu.tsx rename to packages/web_common/src/components/ContextMenu.tsx index 02b388c9..5f832b1a 100644 --- a/apps/onelauncher/frontend/src/components/base/ContextMenu.tsx +++ b/packages/web_common/src/components/ContextMenu.tsx @@ -4,14 +4,14 @@ import { useEffect, useRef, useState } from 'react'; import { Menu, MenuItem, Popover } from 'react-aria-components'; import { twMerge } from 'tailwind-merge'; -interface ContextMenuProps extends MenuProps { +export interface ContextMenuProps extends MenuProps { className?: string; isOpen: boolean; setOpen: (open: boolean) => void; triggerRef: RefObject; } -function ContextMenu({ +export function ContextMenu({ className, isOpen, setOpen, @@ -87,6 +87,4 @@ function ContextMenu({ ); } -export default ContextMenu; - ContextMenu.Item = ({ className, ...rest }: MenuItemProps) => ; diff --git a/packages/web_common/src/components/Dropdown.tsx b/packages/web_common/src/components/Dropdown.tsx new file mode 100644 index 00000000..c6095d05 --- /dev/null +++ b/packages/web_common/src/components/Dropdown.tsx @@ -0,0 +1,65 @@ +import type { + SelectProps as AriaComboBoxProps, + ListBoxItemProps as AriaListBoxItemProps, +} from 'react-aria-components'; +import type { VariantProps } from 'tailwind-variants'; +import type { ClassNameString } from '../types/global'; +import { + Select as AriaComboBox, + Label as AriaLabel, + ListBox as AriaListBox, + ListBoxItem as AriaListBoxItem, + Popover as AriaPopover, + SelectValue, +} from 'react-aria-components'; +import { tv } from 'tailwind-variants'; +import { Button } from '.'; + +export interface ComboBoxWrapperProps extends Omit, 'className'>, ClassNameString { + label?: string; +} + +const dropdownVariants = tv({ + base: [ + 'flex flex-col border border-component-bg-hover w-fit rounded-lg focus:outline-none', + ], +}); + +export type DropdownVariantsProps = VariantProps; + +export function Dropdown({ + className, + label, + children, + ...rest +}: ComboBoxWrapperProps) { + return ( + + {label && {label}} +
+ + +
+ + + {children} + + +
+ ); +} + +export default Dropdown; + +Dropdown.Item = ({ + className, + ...rest +}: AriaListBoxItemProps & ClassNameString) => ( + +); diff --git a/apps/onelauncher/frontend/src/components/base/Menu.tsx b/packages/web_common/src/components/Menu.tsx similarity index 88% rename from apps/onelauncher/frontend/src/components/base/Menu.tsx rename to packages/web_common/src/components/Menu.tsx index 9925a302..157101db 100644 --- a/apps/onelauncher/frontend/src/components/base/Menu.tsx +++ b/packages/web_common/src/components/Menu.tsx @@ -1,9 +1,9 @@ -import type { ClassNameString } from '@/types/global'; import type { MenuItemProps as AriaMenuItemProps, MenuProps as AriaMenuProps, PopoverProps as AriaPopoverProps } from 'react-aria-components'; +import type { ClassNameString } from '../types/global'; import { Menu as AriaMenu, MenuItem as AriaMenuItem, Popover as AriaPopover } from 'react-aria-components'; import { twMerge } from 'tailwind-merge'; -function Menu({ +export function Menu({ className, ...rest }: AriaMenuProps & ClassNameString) { @@ -18,8 +18,6 @@ function Menu({ ); } -export default Menu; - Menu.Popover = ({ className, ...rest diff --git a/packages/web_common/src/components/Show.tsx b/packages/web_common/src/components/Show.tsx new file mode 100644 index 00000000..4837a43d --- /dev/null +++ b/packages/web_common/src/components/Show.tsx @@ -0,0 +1,34 @@ +import type { JSX } from 'react'; +import React from 'react'; + +export interface ShowProps { + /** + * Condition to evaluate. + * If truthy, `children` are rendered. + * If T is a value (not just boolean), you might want to use it in children, + * but this basic version doesn't automatically pass it like SolidJS does for type narrowing. + */ + when: T | boolean | undefined | null; + /** + * Content to render if `when` is falsy. + */ + fallback?: React.ReactNode; + /** + * Content to render if `when` is truthy. + */ + children: React.ReactNode | ((item: NonNullable) => React.ReactNode); +} + +export function ShowComponent({ when, fallback = null, children }: ShowProps): JSX.Element | null { + if (when) { + if (typeof children === 'function') + // Ensure 'when' is not null or undefined before calling the function. + // This mimics Solid's behavior where children can be a function that receives the truthy 'when' value. + return <>{children(when as NonNullable)}; + + return <>{children}; + } + return <>{fallback}; +} + +export const Show = React.memo(ShowComponent) as (props: ShowProps) => JSX.Element | null; diff --git a/packages/web_common/src/components/Switch.tsx b/packages/web_common/src/components/Switch.tsx new file mode 100644 index 00000000..ccd0c7d2 --- /dev/null +++ b/packages/web_common/src/components/Switch.tsx @@ -0,0 +1,43 @@ +import type React from 'react'; +import type { RefAttributes } from 'react'; +import type { SwitchProps as AriaSwitchProps } from 'react-aria-components'; +import type { VariantProps } from 'tailwind-variants'; +import { Switch as AriaSwitch } from 'react-aria-components'; +import { tv } from 'tailwind-variants'; + +export const switchVariants = tv({ + base: [ + 'group inline-flex h-6 w-11 items-center rounded-full transition-colors', + 'bg-component-bg-pressed data-[selected]:bg-blue-600', + 'focus:outline-none', + 'disabled:cursor-not-allowed disabled:opacity-50', + ], +}); + +export const switchThumbVariants = tv({ + base: [ + 'pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow ring-0 transition-transform', + 'translate-x-1 group-data-[selected]:translate-x-6', + ], +}); + +export type SwitchVariantsProps = VariantProps; +export type SwitchThumbVariantsProps = VariantProps; + +export type SwitchProps = AriaSwitchProps & RefAttributes & { + className?: string; + children?: React.ReactNode; +}; + +export function Switch({ + className, + children, + ...props +}: SwitchProps) { + return ( + + + {children && {children}} + + ); +} diff --git a/apps/onelauncher/frontend/src/components/base/TextField.tsx b/packages/web_common/src/components/TextField.tsx similarity index 85% rename from apps/onelauncher/frontend/src/components/base/TextField.tsx rename to packages/web_common/src/components/TextField.tsx index f245a318..dcf6be14 100644 --- a/apps/onelauncher/frontend/src/components/base/TextField.tsx +++ b/packages/web_common/src/components/TextField.tsx @@ -1,7 +1,7 @@ -import { Input } from 'react-aria-components'; import type { RefAttributes } from 'react'; import type { InputProps as AriaInputProps } from 'react-aria-components'; import type { VariantProps } from 'tailwind-variants'; +import { Input } from 'react-aria-components'; import { tv } from 'tailwind-variants'; export const textFieldVariants = tv({ @@ -16,18 +16,18 @@ export const textFieldVariants = tv({ 'text-sm text-fg-primary font-medium', 'bg-transparent outline-none appearance-none p-0 m-0 box-border w-full font-medium', '[&::selection]:bg-brand/30', - 'placeholder:text-fg-secondary/40 placeholder:font-normal' + 'placeholder:text-fg-secondary/40 placeholder:font-normal', ], variants: { invalid: { true: { - base: 'border-danger' - } - } - } -}) + base: 'border-danger', + }, + }, + }, +}); -export type TextFieldVariants = VariantProps; +export type TextFieldVariantsProps = VariantProps; export type TextFieldProps = AriaInputProps & RefAttributes & { className?: string; @@ -41,5 +41,5 @@ export function TextField({ <> - ) -} \ No newline at end of file + ); +} diff --git a/apps/onelauncher/frontend/src/components/base/ToggleButton.tsx b/packages/web_common/src/components/ToggleButton.tsx similarity index 76% rename from apps/onelauncher/frontend/src/components/base/ToggleButton.tsx rename to packages/web_common/src/components/ToggleButton.tsx index 661fa861..baedee9d 100644 --- a/apps/onelauncher/frontend/src/components/base/ToggleButton.tsx +++ b/packages/web_common/src/components/ToggleButton.tsx @@ -1,16 +1,16 @@ import type { RefAttributes } from 'react'; import type { ToggleButtonProps as AriaToggleButtonProps } from 'react-aria-components'; -import type { ButtonVariants } from './Button'; +import type { ButtonVariantsProps } from './Button'; import { ToggleButton as AriaToggleButton } from 'react-aria-components'; import { buttonVariants } from './Button'; export type ToggleButtonProps = AriaToggleButtonProps & RefAttributes & { - color?: ButtonVariants['color']; - size?: ButtonVariants['size']; + color?: ButtonVariantsProps['color']; + size?: ButtonVariantsProps['size']; className?: string; }; -function ToggleButton({ +export function ToggleButton({ color = 'ghost', size = 'normal', className, @@ -20,5 +20,3 @@ function ToggleButton({ ); } - -export default ToggleButton; diff --git a/packages/web_common/src/components/Tooltip.tsx b/packages/web_common/src/components/Tooltip.tsx new file mode 100644 index 00000000..d67ff23e --- /dev/null +++ b/packages/web_common/src/components/Tooltip.tsx @@ -0,0 +1,66 @@ +import type React from 'react'; +import type { RefAttributes } from 'react'; +import type { TooltipTriggerComponentProps as AriaTooltipTriggerProps } from 'react-aria-components'; +import type { VariantProps } from 'tailwind-variants'; +import { + Tooltip as AriaTooltip, + TooltipTrigger as AriaTooltipTrigger, + OverlayArrow, +} from 'react-aria-components'; +import { tv } from 'tailwind-variants'; + +export const tooltipVariants = tv({ + base: [ + 'px-3 py-2 text-sm font-medium text-white bg-gray-900 rounded-lg shadow-lg', + 'border border-gray-700 max-w-xs z-50', + 'data-[entering]:animate-in data-[entering]:fade-in data-[entering]:zoom-in-95', + 'data-[exiting]:animate-out data-[exiting]:fade-out data-[exiting]:zoom-out-95', + ], +}); + +export const tooltipArrowVariants = tv({ + base: [ + 'fill-gray-900 stroke-gray-700 stroke-1', + ], +}); + +export type TooltipVariantProps = VariantProps; +export type TooltipArrowVariantProps = VariantProps; + +export type TooltipProps = AriaTooltipTriggerProps & RefAttributes & { + text: string; + children: React.ReactElement; + placement?: 'top' | 'bottom' | 'left' | 'right'; + className?: string; + delay?: number; + closeDelay?: number; +}; + +export function Tooltip({ + text, + children, + placement = 'top', + className, + delay = 700, + closeDelay = 0, + ...props +}: TooltipProps) { + return ( + + {children} + + + + + + + {text} + + + ); +} diff --git a/packages/web_common/src/components/index.ts b/packages/web_common/src/components/index.ts new file mode 100644 index 00000000..dfd448b4 --- /dev/null +++ b/packages/web_common/src/components/index.ts @@ -0,0 +1,10 @@ +export * from './AnimatedOutlet'; +export * from './Button'; +export * from './ContextMenu'; +export * from './Dropdown'; +export * from './Menu'; +export * from './Show'; +export * from './Switch'; +export * from './TextField'; +export * from './ToggleButton'; +export * from './Tooltip'; diff --git a/packages/web_common/src/hooks/index.ts b/packages/web_common/src/hooks/index.ts new file mode 100644 index 00000000..62c47efe --- /dev/null +++ b/packages/web_common/src/hooks/index.ts @@ -0,0 +1 @@ +export * from './useCommand'; diff --git a/packages/web_common/src/hooks/useCommand.ts b/packages/web_common/src/hooks/useCommand.ts new file mode 100644 index 00000000..01c5d0a7 --- /dev/null +++ b/packages/web_common/src/hooks/useCommand.ts @@ -0,0 +1,28 @@ +import type { UndefinedInitialDataOptions, UseQueryResult } from '@tanstack/react-query'; +import { useQuery } from '@tanstack/react-query'; + +type CacheKey = string; + +export function useCommand(cacheKey: CacheKey | (string & {}), command: () => Promise, options?: Omit, 'queryKey' | 'queryFn'>): UseQueryResult { + return useQuery({ + queryKey: [cacheKey], + queryFn: async () => { + try { + const result = await command(); + return result; + } + // TODO: (lynith) handle errors better (requires modifications to TauRPC) + catch (e) { + console.error(e); + if (e instanceof Error) + return Promise.reject(e); + else + return Promise.reject(new Error('Unknown error')); + } + }, + enabled: true, + ...options, + }); +} + +// export useCommand; diff --git a/packages/web_common/src/index.ts b/packages/web_common/src/index.ts new file mode 100644 index 00000000..fd70c425 --- /dev/null +++ b/packages/web_common/src/index.ts @@ -0,0 +1,2 @@ +export * from './hooks'; +export * from './utils'; diff --git a/packages/web_common/src/types/global.d.ts b/packages/web_common/src/types/global.d.ts new file mode 100644 index 00000000..d5d4d0a1 --- /dev/null +++ b/packages/web_common/src/types/global.d.ts @@ -0,0 +1,3 @@ +export interface ClassNameString { + className?: string; +} diff --git a/packages/web_common/src/utils/dev.ts b/packages/web_common/src/utils/dev.ts new file mode 100644 index 00000000..678515b1 --- /dev/null +++ b/packages/web_common/src/utils/dev.ts @@ -0,0 +1,17 @@ +/* eslint-disable node/prefer-global/process -- we dont need imports this way */ +const env = (import.meta as unknown as { env: { DEV: boolean; MODE: string } }).env; + +export const isDev = env.DEV + || env.MODE === 'development' + || (typeof window !== 'undefined' && (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1')) + || (typeof globalThis.process !== 'undefined' && globalThis.process.env.NODE_ENV === 'development'); + +export function registerDevExperience() { + // Enable CTRL+R to reload the page in dev mode + window.addEventListener('keypress', (e) => { + if (e.getModifierState('Control') && e.key === 'r') { + e.preventDefault(); + window.location.reload(); // tauri disables this + } + }); +} diff --git a/packages/web_common/src/utils/index.ts b/packages/web_common/src/utils/index.ts new file mode 100644 index 00000000..67b87f0c --- /dev/null +++ b/packages/web_common/src/utils/index.ts @@ -0,0 +1,2 @@ +export * from './dev'; +export * from './nativeExperience'; diff --git a/packages/web_common/src/utils/nativeExperience.ts b/packages/web_common/src/utils/nativeExperience.ts new file mode 100644 index 00000000..abe9fac2 --- /dev/null +++ b/packages/web_common/src/utils/nativeExperience.ts @@ -0,0 +1,6 @@ +export function registerNativeExperience() { + // disable browser right-click context menu + document.addEventListener('contextmenu', (e) => { + e.preventDefault(); + }); +} diff --git a/packages/web_common/tsconfig.json b/packages/web_common/tsconfig.json new file mode 100644 index 00000000..71c838a6 --- /dev/null +++ b/packages/web_common/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "esnext", + "jsx": "react-jsx", + "module": "esnext", + "moduleResolution": "bundler", + "allowJs": true, + "checkJs": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src/**/*", "rollup.config.mjs"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dfef8c1f..f2a6a1e7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -91,6 +91,9 @@ importers: '@formatjs/intl-durationformat': specifier: ^0.7.4 version: 0.7.4 + '@onelauncher/common': + specifier: workspace:* + version: link:../../../packages/web_common '@tailwindcss/vite': specifier: ^4.0.6 version: 4.1.5(vite@6.3.4(@types/node@22.7.9)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.80.3)(tsx@4.19.4)(yaml@2.6.0)) @@ -163,6 +166,9 @@ importers: tailwindcss-react-aria-components: specifier: ^2.0.0 version: 2.0.0(tailwindcss@4.1.5) + taurpc: + specifier: ^1.8.1 + version: 1.8.1 devDependencies: '@tanstack/eslint-config': specifier: ^0.1.0 @@ -201,6 +207,9 @@ importers: '@formatjs/intl-durationformat': specifier: ^0.7.4 version: 0.7.4 + '@onelauncher/common': + specifier: workspace:* + version: link:../../../packages/web_common '@tailwindcss/vite': specifier: ^4.0.6 version: 4.1.5(vite@6.3.4(@types/node@22.7.9)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.80.3)(tsx@4.19.4)(yaml@2.6.0)) @@ -336,6 +345,58 @@ importers: packages/utils: {} + packages/web_common: + dependencies: + '@tanstack/react-query': + specifier: ^5.66.5 + version: 5.75.0(react@19.1.0) + '@tanstack/react-router': + specifier: ^1.114.3 + version: 1.119.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + motion: + specifier: ^12.10.5 + version: 12.10.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: + specifier: ^19.0.0 + version: 19.1.0 + react-aria-components: + specifier: ^1.8.0 + version: 1.8.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + tailwind-merge: + specifier: ^3.2.0 + version: 3.2.0 + tailwind-variants: + specifier: ^1.0.0 + version: 1.0.0(tailwindcss@4.1.5) + tailwindcss: + specifier: ^4.0.6 + version: 4.1.5 + tailwindcss-animated: + specifier: ^2.0.0 + version: 2.0.0(tailwindcss@4.1.5) + tailwindcss-react-aria-components: + specifier: ^2.0.0 + version: 2.0.0(tailwindcss@4.1.5) + devDependencies: + '@types/react': + specifier: ^19.0.8 + version: 19.1.2 + esbuild: + specifier: ^0.25.5 + version: 0.25.5 + rollup: + specifier: ^4.41.1 + version: 4.41.1 + rollup-plugin-dts: + specifier: ^6.2.1 + version: 6.2.1(rollup@4.41.1)(typescript@5.8.3) + rollup-plugin-esbuild: + specifier: ^6.2.1 + version: 6.2.1(esbuild@0.25.5)(rollup@4.41.1) + typescript: + specifier: ^5.8.3 + version: 5.8.3 + packages: '@actions/artifact@2.1.11': @@ -551,6 +612,12 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.25.5': + resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.21.5': resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} engines: {node: '>=12'} @@ -563,6 +630,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.25.5': + resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.21.5': resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} engines: {node: '>=12'} @@ -575,6 +648,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.25.5': + resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.21.5': resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} engines: {node: '>=12'} @@ -587,6 +666,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.25.5': + resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.21.5': resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} engines: {node: '>=12'} @@ -599,6 +684,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.25.5': + resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.21.5': resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} engines: {node: '>=12'} @@ -611,6 +702,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.25.5': + resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.21.5': resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} engines: {node: '>=12'} @@ -623,6 +720,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.25.5': + resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.21.5': resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} engines: {node: '>=12'} @@ -635,6 +738,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.25.5': + resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.21.5': resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} engines: {node: '>=12'} @@ -647,6 +756,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.25.5': + resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.21.5': resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} engines: {node: '>=12'} @@ -659,6 +774,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.25.5': + resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.21.5': resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} engines: {node: '>=12'} @@ -671,6 +792,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.25.5': + resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.21.5': resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} engines: {node: '>=12'} @@ -683,6 +810,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.25.5': + resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.21.5': resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} engines: {node: '>=12'} @@ -695,6 +828,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.25.5': + resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.21.5': resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} engines: {node: '>=12'} @@ -707,6 +846,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.25.5': + resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.21.5': resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} engines: {node: '>=12'} @@ -719,6 +864,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.25.5': + resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.21.5': resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} engines: {node: '>=12'} @@ -731,6 +882,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.25.5': + resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.21.5': resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} engines: {node: '>=12'} @@ -743,12 +900,24 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.25.5': + resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-arm64@0.25.3': resolution: {integrity: sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-arm64@0.25.5': + resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.21.5': resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} @@ -761,12 +930,24 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.25.5': + resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.25.3': resolution: {integrity: sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.25.5': + resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.21.5': resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} engines: {node: '>=12'} @@ -779,6 +960,12 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.25.5': + resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/sunos-x64@0.21.5': resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} engines: {node: '>=12'} @@ -791,6 +978,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.25.5': + resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.21.5': resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} engines: {node: '>=12'} @@ -803,6 +996,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.25.5': + resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.21.5': resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} engines: {node: '>=12'} @@ -815,6 +1014,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.25.5': + resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.21.5': resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} engines: {node: '>=12'} @@ -827,6 +1032,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.25.5': + resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-plugin-eslint-comments@4.5.0': resolution: {integrity: sha512-MAhuTKlr4y/CE3WYX26raZjy+I/kS2PLKSzvfmDCGrBLTFHOYwqROZdr4XwPgXwX3K9rjzMr4pSmUWGnzsUyMg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1861,101 +2072,201 @@ packages: cpu: [arm] os: [android] + '@rollup/rollup-android-arm-eabi@4.41.1': + resolution: {integrity: sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==} + cpu: [arm] + os: [android] + '@rollup/rollup-android-arm64@4.40.1': resolution: {integrity: sha512-PPkxTOisoNC6TpnDKatjKkjRMsdaWIhyuMkA4UsBXT9WEZY4uHezBTjs6Vl4PbqQQeu6oION1w2voYZv9yquCw==} cpu: [arm64] os: [android] + '@rollup/rollup-android-arm64@4.41.1': + resolution: {integrity: sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==} + cpu: [arm64] + os: [android] + '@rollup/rollup-darwin-arm64@4.40.1': resolution: {integrity: sha512-VWXGISWFY18v/0JyNUy4A46KCFCb9NVsH+1100XP31lud+TzlezBbz24CYzbnA4x6w4hx+NYCXDfnvDVO6lcAA==} cpu: [arm64] os: [darwin] + '@rollup/rollup-darwin-arm64@4.41.1': + resolution: {integrity: sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==} + cpu: [arm64] + os: [darwin] + '@rollup/rollup-darwin-x64@4.40.1': resolution: {integrity: sha512-nIwkXafAI1/QCS7pxSpv/ZtFW6TXcNUEHAIA9EIyw5OzxJZQ1YDrX+CL6JAIQgZ33CInl1R6mHet9Y/UZTg2Bw==} cpu: [x64] os: [darwin] + '@rollup/rollup-darwin-x64@4.41.1': + resolution: {integrity: sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==} + cpu: [x64] + os: [darwin] + '@rollup/rollup-freebsd-arm64@4.40.1': resolution: {integrity: sha512-BdrLJ2mHTrIYdaS2I99mriyJfGGenSaP+UwGi1kB9BLOCu9SR8ZpbkmmalKIALnRw24kM7qCN0IOm6L0S44iWw==} cpu: [arm64] os: [freebsd] + '@rollup/rollup-freebsd-arm64@4.41.1': + resolution: {integrity: sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==} + cpu: [arm64] + os: [freebsd] + '@rollup/rollup-freebsd-x64@4.40.1': resolution: {integrity: sha512-VXeo/puqvCG8JBPNZXZf5Dqq7BzElNJzHRRw3vjBE27WujdzuOPecDPc/+1DcdcTptNBep3861jNq0mYkT8Z6Q==} cpu: [x64] os: [freebsd] + '@rollup/rollup-freebsd-x64@4.41.1': + resolution: {integrity: sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==} + cpu: [x64] + os: [freebsd] + '@rollup/rollup-linux-arm-gnueabihf@4.40.1': resolution: {integrity: sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-gnueabihf@4.41.1': + resolution: {integrity: sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.40.1': resolution: {integrity: sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.41.1': + resolution: {integrity: sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.40.1': resolution: {integrity: sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.41.1': + resolution: {integrity: sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-arm64-musl@4.40.1': resolution: {integrity: sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-musl@4.41.1': + resolution: {integrity: sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-loongarch64-gnu@4.40.1': resolution: {integrity: sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ==} cpu: [loong64] os: [linux] + '@rollup/rollup-linux-loongarch64-gnu@4.41.1': + resolution: {integrity: sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==} + cpu: [loong64] + os: [linux] + '@rollup/rollup-linux-powerpc64le-gnu@4.40.1': resolution: {integrity: sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg==} cpu: [ppc64] os: [linux] + '@rollup/rollup-linux-powerpc64le-gnu@4.41.1': + resolution: {integrity: sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==} + cpu: [ppc64] + os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.40.1': resolution: {integrity: sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.41.1': + resolution: {integrity: sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-riscv64-musl@4.40.1': resolution: {integrity: sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-musl@4.41.1': + resolution: {integrity: sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.40.1': resolution: {integrity: sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg==} cpu: [s390x] os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.41.1': + resolution: {integrity: sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==} + cpu: [s390x] + os: [linux] + '@rollup/rollup-linux-x64-gnu@4.40.1': resolution: {integrity: sha512-XiK5z70PEFEFqcNj3/zRSz/qX4bp4QIraTy9QjwJAb/Z8GM7kVUsD0Uk8maIPeTyPCP03ChdI+VVmJriKYbRHQ==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-gnu@4.41.1': + resolution: {integrity: sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==} + cpu: [x64] + os: [linux] + '@rollup/rollup-linux-x64-musl@4.40.1': resolution: {integrity: sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-musl@4.41.1': + resolution: {integrity: sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==} + cpu: [x64] + os: [linux] + '@rollup/rollup-win32-arm64-msvc@4.40.1': resolution: {integrity: sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg==} cpu: [arm64] os: [win32] + '@rollup/rollup-win32-arm64-msvc@4.41.1': + resolution: {integrity: sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==} + cpu: [arm64] + os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.40.1': resolution: {integrity: sha512-DfcogW8N7Zg7llVEfpqWMZcaErKfsj9VvmfSyRjCyo4BI3wPEfrzTtJkZG6gKP/Z92wFm6rz2aDO7/JfiR/whA==} cpu: [ia32] os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.41.1': + resolution: {integrity: sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==} + cpu: [ia32] + os: [win32] + '@rollup/rollup-win32-x64-msvc@4.40.1': resolution: {integrity: sha512-ECyOuDeH3C1I8jH2MK1RtBJW+YPMvSfT0a5NN0nHfQYnDSJ6tUiZH3gzwVP5/Kfh/+Tt7tpWVF9LXNTnhTJ3kA==} cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-msvc@4.41.1': + resolution: {integrity: sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==} + cpu: [x64] + os: [win32] + '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} @@ -2935,6 +3246,9 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + esbuild@0.21.5: resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} @@ -2945,6 +3259,11 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.25.5: + resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -4257,11 +4576,30 @@ packages: engines: {node: 20 || >=22} hasBin: true + rollup-plugin-dts@6.2.1: + resolution: {integrity: sha512-sR3CxYUl7i2CHa0O7bA45mCrgADyAQ0tVtGSqi3yvH28M+eg1+g5d7kQ9hLvEz5dorK3XVsH5L2jwHLQf72DzA==} + engines: {node: '>=16'} + peerDependencies: + rollup: ^3.29.4 || ^4 + typescript: ^4.5 || ^5.0 + + rollup-plugin-esbuild@6.2.1: + resolution: {integrity: sha512-jTNOMGoMRhs0JuueJrJqbW8tOwxumaWYq+V5i+PD+8ecSCVkuX27tGW7BXqDgoULQ55rO7IdNxPcnsWtshz3AA==} + engines: {node: '>=14.18.0'} + peerDependencies: + esbuild: '>=0.18.0' + rollup: ^1.20.0 || ^2.0.0 || ^3.0.0 || ^4.0.0 + rollup@4.40.1: resolution: {integrity: sha512-C5VvvgCCyfyotVITIAv+4efVytl5F7wt+/I2i9q9GZcEXW9BP52YYOXC58igUi+LFZVHukErIIqQSWwv/M3WRw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rollup@4.41.1: + resolution: {integrity: sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + rrweb-cssom@0.7.1: resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==} @@ -4644,6 +4982,10 @@ packages: universal-user-agent@6.0.1: resolution: {integrity: sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==} + unplugin-utils@0.2.4: + resolution: {integrity: sha512-8U/MtpkPkkk3Atewj1+RcKIjb5WBimZ/WSLhhR3w6SsIj8XJuKTacSP8g+2JhfSGw0Cb125Y+2zA/IzJZDVbhA==} + engines: {node: '>=18.12.0'} + unplugin@2.3.2: resolution: {integrity: sha512-3n7YA46rROb3zSj8fFxtxC/PqoyvYQ0llwz9wtUPUutr9ig09C8gGo5CWCwHrUzlqC1LLR43kxp5vEIyH1ac1w==} engines: {node: '>=18.12.0'} @@ -5240,144 +5582,219 @@ snapshots: '@esbuild/aix-ppc64@0.25.3': optional: true + '@esbuild/aix-ppc64@0.25.5': + optional: true + '@esbuild/android-arm64@0.21.5': optional: true '@esbuild/android-arm64@0.25.3': optional: true + '@esbuild/android-arm64@0.25.5': + optional: true + '@esbuild/android-arm@0.21.5': optional: true '@esbuild/android-arm@0.25.3': optional: true + '@esbuild/android-arm@0.25.5': + optional: true + '@esbuild/android-x64@0.21.5': optional: true '@esbuild/android-x64@0.25.3': optional: true + '@esbuild/android-x64@0.25.5': + optional: true + '@esbuild/darwin-arm64@0.21.5': optional: true '@esbuild/darwin-arm64@0.25.3': optional: true + '@esbuild/darwin-arm64@0.25.5': + optional: true + '@esbuild/darwin-x64@0.21.5': optional: true '@esbuild/darwin-x64@0.25.3': optional: true + '@esbuild/darwin-x64@0.25.5': + optional: true + '@esbuild/freebsd-arm64@0.21.5': optional: true '@esbuild/freebsd-arm64@0.25.3': optional: true + '@esbuild/freebsd-arm64@0.25.5': + optional: true + '@esbuild/freebsd-x64@0.21.5': optional: true '@esbuild/freebsd-x64@0.25.3': optional: true + '@esbuild/freebsd-x64@0.25.5': + optional: true + '@esbuild/linux-arm64@0.21.5': optional: true '@esbuild/linux-arm64@0.25.3': optional: true + '@esbuild/linux-arm64@0.25.5': + optional: true + '@esbuild/linux-arm@0.21.5': optional: true '@esbuild/linux-arm@0.25.3': optional: true + '@esbuild/linux-arm@0.25.5': + optional: true + '@esbuild/linux-ia32@0.21.5': optional: true '@esbuild/linux-ia32@0.25.3': optional: true + '@esbuild/linux-ia32@0.25.5': + optional: true + '@esbuild/linux-loong64@0.21.5': optional: true '@esbuild/linux-loong64@0.25.3': optional: true + '@esbuild/linux-loong64@0.25.5': + optional: true + '@esbuild/linux-mips64el@0.21.5': optional: true '@esbuild/linux-mips64el@0.25.3': optional: true + '@esbuild/linux-mips64el@0.25.5': + optional: true + '@esbuild/linux-ppc64@0.21.5': optional: true '@esbuild/linux-ppc64@0.25.3': optional: true + '@esbuild/linux-ppc64@0.25.5': + optional: true + '@esbuild/linux-riscv64@0.21.5': optional: true '@esbuild/linux-riscv64@0.25.3': optional: true + '@esbuild/linux-riscv64@0.25.5': + optional: true + '@esbuild/linux-s390x@0.21.5': optional: true '@esbuild/linux-s390x@0.25.3': optional: true + '@esbuild/linux-s390x@0.25.5': + optional: true + '@esbuild/linux-x64@0.21.5': optional: true '@esbuild/linux-x64@0.25.3': optional: true + '@esbuild/linux-x64@0.25.5': + optional: true + '@esbuild/netbsd-arm64@0.25.3': optional: true + '@esbuild/netbsd-arm64@0.25.5': + optional: true + '@esbuild/netbsd-x64@0.21.5': optional: true '@esbuild/netbsd-x64@0.25.3': optional: true + '@esbuild/netbsd-x64@0.25.5': + optional: true + '@esbuild/openbsd-arm64@0.25.3': optional: true + '@esbuild/openbsd-arm64@0.25.5': + optional: true + '@esbuild/openbsd-x64@0.21.5': optional: true '@esbuild/openbsd-x64@0.25.3': optional: true + '@esbuild/openbsd-x64@0.25.5': + optional: true + '@esbuild/sunos-x64@0.21.5': optional: true '@esbuild/sunos-x64@0.25.3': optional: true + '@esbuild/sunos-x64@0.25.5': + optional: true + '@esbuild/win32-arm64@0.21.5': optional: true '@esbuild/win32-arm64@0.25.3': optional: true + '@esbuild/win32-arm64@0.25.5': + optional: true + '@esbuild/win32-ia32@0.21.5': optional: true '@esbuild/win32-ia32@0.25.3': optional: true + '@esbuild/win32-ia32@0.25.5': + optional: true + '@esbuild/win32-x64@0.21.5': optional: true '@esbuild/win32-x64@0.25.3': optional: true + '@esbuild/win32-x64@0.25.5': + optional: true + '@eslint-community/eslint-plugin-eslint-comments@4.5.0(eslint@9.25.1(jiti@2.4.2))': dependencies: escape-string-regexp: 4.0.0 @@ -6939,63 +7356,123 @@ snapshots: '@rollup/rollup-android-arm-eabi@4.40.1': optional: true + '@rollup/rollup-android-arm-eabi@4.41.1': + optional: true + '@rollup/rollup-android-arm64@4.40.1': optional: true + '@rollup/rollup-android-arm64@4.41.1': + optional: true + '@rollup/rollup-darwin-arm64@4.40.1': optional: true + '@rollup/rollup-darwin-arm64@4.41.1': + optional: true + '@rollup/rollup-darwin-x64@4.40.1': optional: true + '@rollup/rollup-darwin-x64@4.41.1': + optional: true + '@rollup/rollup-freebsd-arm64@4.40.1': optional: true + '@rollup/rollup-freebsd-arm64@4.41.1': + optional: true + '@rollup/rollup-freebsd-x64@4.40.1': optional: true + '@rollup/rollup-freebsd-x64@4.41.1': + optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.40.1': optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.41.1': + optional: true + '@rollup/rollup-linux-arm-musleabihf@4.40.1': optional: true + '@rollup/rollup-linux-arm-musleabihf@4.41.1': + optional: true + '@rollup/rollup-linux-arm64-gnu@4.40.1': optional: true + '@rollup/rollup-linux-arm64-gnu@4.41.1': + optional: true + '@rollup/rollup-linux-arm64-musl@4.40.1': optional: true + '@rollup/rollup-linux-arm64-musl@4.41.1': + optional: true + '@rollup/rollup-linux-loongarch64-gnu@4.40.1': optional: true + '@rollup/rollup-linux-loongarch64-gnu@4.41.1': + optional: true + '@rollup/rollup-linux-powerpc64le-gnu@4.40.1': optional: true + '@rollup/rollup-linux-powerpc64le-gnu@4.41.1': + optional: true + '@rollup/rollup-linux-riscv64-gnu@4.40.1': optional: true + '@rollup/rollup-linux-riscv64-gnu@4.41.1': + optional: true + '@rollup/rollup-linux-riscv64-musl@4.40.1': optional: true + '@rollup/rollup-linux-riscv64-musl@4.41.1': + optional: true + '@rollup/rollup-linux-s390x-gnu@4.40.1': optional: true + '@rollup/rollup-linux-s390x-gnu@4.41.1': + optional: true + '@rollup/rollup-linux-x64-gnu@4.40.1': optional: true + '@rollup/rollup-linux-x64-gnu@4.41.1': + optional: true + '@rollup/rollup-linux-x64-musl@4.40.1': optional: true + '@rollup/rollup-linux-x64-musl@4.41.1': + optional: true + '@rollup/rollup-win32-arm64-msvc@4.40.1': optional: true + '@rollup/rollup-win32-arm64-msvc@4.41.1': + optional: true + '@rollup/rollup-win32-ia32-msvc@4.40.1': optional: true + '@rollup/rollup-win32-ia32-msvc@4.41.1': + optional: true + '@rollup/rollup-win32-x64-msvc@4.40.1': optional: true + '@rollup/rollup-win32-x64-msvc@4.41.1': + optional: true + '@sec-ant/readable-stream@0.4.1': {} '@sindresorhus/merge-streams@4.0.0': {} @@ -7975,6 +8452,8 @@ snapshots: dependencies: is-arrayish: 0.2.1 + es-module-lexer@1.7.0: {} + esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 @@ -8030,6 +8509,34 @@ snapshots: '@esbuild/win32-ia32': 0.25.3 '@esbuild/win32-x64': 0.25.3 + esbuild@0.25.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.5 + '@esbuild/android-arm': 0.25.5 + '@esbuild/android-arm64': 0.25.5 + '@esbuild/android-x64': 0.25.5 + '@esbuild/darwin-arm64': 0.25.5 + '@esbuild/darwin-x64': 0.25.5 + '@esbuild/freebsd-arm64': 0.25.5 + '@esbuild/freebsd-x64': 0.25.5 + '@esbuild/linux-arm': 0.25.5 + '@esbuild/linux-arm64': 0.25.5 + '@esbuild/linux-ia32': 0.25.5 + '@esbuild/linux-loong64': 0.25.5 + '@esbuild/linux-mips64el': 0.25.5 + '@esbuild/linux-ppc64': 0.25.5 + '@esbuild/linux-riscv64': 0.25.5 + '@esbuild/linux-s390x': 0.25.5 + '@esbuild/linux-x64': 0.25.5 + '@esbuild/netbsd-arm64': 0.25.5 + '@esbuild/netbsd-x64': 0.25.5 + '@esbuild/openbsd-arm64': 0.25.5 + '@esbuild/openbsd-x64': 0.25.5 + '@esbuild/sunos-x64': 0.25.5 + '@esbuild/win32-arm64': 0.25.5 + '@esbuild/win32-ia32': 0.25.5 + '@esbuild/win32-x64': 0.25.5 + escalade@3.2.0: {} escape-string-regexp@1.0.5: {} @@ -9712,6 +10219,25 @@ snapshots: glob: 11.0.0 package-json-from-dist: 1.0.1 + rollup-plugin-dts@6.2.1(rollup@4.41.1)(typescript@5.8.3): + dependencies: + magic-string: 0.30.17 + rollup: 4.41.1 + typescript: 5.8.3 + optionalDependencies: + '@babel/code-frame': 7.27.1 + + rollup-plugin-esbuild@6.2.1(esbuild@0.25.5)(rollup@4.41.1): + dependencies: + debug: 4.4.0 + es-module-lexer: 1.7.0 + esbuild: 0.25.5 + get-tsconfig: 4.10.0 + rollup: 4.41.1 + unplugin-utils: 0.2.4 + transitivePeerDependencies: + - supports-color + rollup@4.40.1: dependencies: '@types/estree': 1.0.7 @@ -9738,6 +10264,32 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.40.1 fsevents: 2.3.3 + rollup@4.41.1: + dependencies: + '@types/estree': 1.0.7 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.41.1 + '@rollup/rollup-android-arm64': 4.41.1 + '@rollup/rollup-darwin-arm64': 4.41.1 + '@rollup/rollup-darwin-x64': 4.41.1 + '@rollup/rollup-freebsd-arm64': 4.41.1 + '@rollup/rollup-freebsd-x64': 4.41.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.41.1 + '@rollup/rollup-linux-arm-musleabihf': 4.41.1 + '@rollup/rollup-linux-arm64-gnu': 4.41.1 + '@rollup/rollup-linux-arm64-musl': 4.41.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.41.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.41.1 + '@rollup/rollup-linux-riscv64-gnu': 4.41.1 + '@rollup/rollup-linux-riscv64-musl': 4.41.1 + '@rollup/rollup-linux-s390x-gnu': 4.41.1 + '@rollup/rollup-linux-x64-gnu': 4.41.1 + '@rollup/rollup-linux-x64-musl': 4.41.1 + '@rollup/rollup-win32-arm64-msvc': 4.41.1 + '@rollup/rollup-win32-ia32-msvc': 4.41.1 + '@rollup/rollup-win32-x64-msvc': 4.41.1 + fsevents: 2.3.3 + rrweb-cssom@0.7.1: optional: true @@ -10034,7 +10586,7 @@ snapshots: tsx@4.19.4: dependencies: - esbuild: 0.25.3 + esbuild: 0.25.5 get-tsconfig: 4.10.0 optionalDependencies: fsevents: 2.3.3 @@ -10111,6 +10663,11 @@ snapshots: universal-user-agent@6.0.1: {} + unplugin-utils@0.2.4: + dependencies: + pathe: 2.0.3 + picomatch: 4.0.2 + unplugin@2.3.2: dependencies: acorn: 8.14.1 @@ -10204,7 +10761,7 @@ snapshots: dependencies: esbuild: 0.21.5 postcss: 8.5.3 - rollup: 4.40.1 + rollup: 4.41.1 optionalDependencies: '@types/node': 22.7.9 fsevents: 2.3.3