Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
68f717f
feat: oneclient mods page
Kathund Oct 10, 2025
1dc3fe6
fix: formatting
Kathund Oct 10, 2025
13e5d5e
fix: mod icons
Kathund Oct 14, 2025
1ea5eff
fix: oneclient mod tags
Kathund Oct 15, 2025
26eb023
feat: oneclient mod tag link icon
Kathund Oct 15, 2025
7889b8e
fix: oneclient mods tags link to browser
Kathund Oct 16, 2025
c41d114
feat: oneclient onboarding mods list
Kathund Oct 16, 2025
879d190
feat: oneclient onboarding bundles tags
Kathund Oct 17, 2025
f65d3b1
feat: oneclient versions backend
Kathund Oct 17, 2025
13f6160
Merge branch 'oneclient/main' into feat/mods-page
Kathund Oct 18, 2025
a851a95
Merge branch 'oneclient/main' into feat/mods-page
Kathund Oct 22, 2025
07b75a8
refactor: oneclient oneboarding bundles
Kathund Oct 23, 2025
981fb61
fix: formatting
Kathund Oct 23, 2025
9d1ca6a
fix: formatting v2
Kathund Oct 23, 2025
cbdc0e9
feat: oneclient onboarding bundles downloading
Kathund Oct 24, 2025
b3161af
feat: oneclient oneboarding bundles downloading mods on next button
Kathund Oct 28, 2025
a1633db
fix: oneclient onboarding bundles going to next page
Kathund Oct 28, 2025
bb58914
feat: oneclient onboarding bundle categories
Kathund Nov 4, 2025
1ac87c5
feat: oneclient onboarding bundles Mod list
Kathund Nov 5, 2025
1e4fd86
refactor: oneclient bundles compoents
Kathund Nov 7, 2025
d7b8b21
format: oneclient
Kathund Nov 7, 2025
8794851
feat: oneclient onboarding bundles mod list model
Kathund Nov 8, 2025
5ce991b
fix: BundleModListModal background Color
Kathund Nov 8, 2025
50cfae2
feat: OneClient Mods List Grid Layout
Kathund Nov 11, 2025
a01b4e9
Merge branch 'oneclient/main' into feat/mods-page
Kathund Nov 11, 2025
9a87c9f
feat: OneClient Mods List Vertical Grid
Kathund Nov 13, 2025
5bbe03c
Merge branch 'oneclient/main' into feat/mods-page
Kathund Nov 14, 2025
1165833
fix: OneClient Bindings
Kathund Nov 14, 2025
4e7ab0a
feat: OneClient Onboarding BundleModList truncated authors
Kathund Nov 14, 2025
49c1213
refactor: OneClient Onboarding BundleModList data
Kathund Nov 14, 2025
f43b15e
refactor/cleanup: OneClient Onboarding
Kathund Nov 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions apps/oneclient/desktop/src/api/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use onelauncher_core::error::LauncherResult;
use tauri::Runtime;

use crate::oneclient::bundles::BundlesManager;
use crate::oneclient::clusters::{OnlineClusterManifest, get_data_storage_versions};

#[taurpc::procedures(path = "oneclient", export_to = "../frontend/src/bindings.gen.ts")]
pub trait OneClientApi {
Expand All @@ -18,6 +19,9 @@ pub trait OneClientApi {

#[taurpc(alias = "getBundlesFor")]
async fn get_bundles_for(cluster_id: ClusterId) -> LauncherResult<Vec<ModpackArchive>>;

#[taurpc(alias = "getVersions")]
async fn get_versions() -> LauncherResult<OnlineClusterManifest>;
}

#[taurpc::ipc_type]
Expand Down Expand Up @@ -72,4 +76,8 @@ impl OneClientApi for OneClientApiImpl {

Ok(bundles)
}

async fn get_versions(self) -> LauncherResult<OnlineClusterManifest> {
get_data_storage_versions().await
}
}
49 changes: 39 additions & 10 deletions apps/oneclient/desktop/src/oneclient/clusters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use onelauncher_core::error::LauncherResult;
use onelauncher_core::send_error;
use onelauncher_core::utils::http::fetch_json;
use reqwest::Method;
use serde::Deserialize;
use serde::{Deserialize, Serialize};

///
/// e.g.
Expand All @@ -15,44 +15,73 @@ use serde::Deserialize;
/// "clusters": [
/// {
/// "major_version": 21,
/// "name": "Tricky Trials",
/// "art": "/versions/art/Tricky_Trials.png",
/// "entries": [
/// {
/// "minor_version": 5,
/// "loader": "fabric"
/// "loader": "fabric",
/// "tags": ["PvP", "Survival"]
/// },
/// {
/// "minor_version": 5,
/// "loader": "forge"
/// "loader": "forge",
/// "tags": ["PvP", "Survival"]
/// }
/// ]
/// },
/// {
/// "major_version": 20,
/// "name": "Trails & Tales",
/// "art": "/versions/art/Trails_Tales.png",
/// "entries": [
/// {
/// "minor_version": 5,
/// "loader": "fabric"
/// "loader": "fabric",
/// "tags": ["PvP", "Survival"]
/// }
/// ]
/// }
/// ]
/// }
/// ```
#[derive(Deserialize)]
struct OnlineClusterManifest {
#[derive(specta::Type, Deserialize, Serialize)]
pub struct OnlineClusterManifest {
clusters: Vec<OnlineCluster>,
}

#[derive(Deserialize)]
struct OnlineCluster {
#[derive(specta::Type, Deserialize, Serialize)]
pub struct OnlineCluster {
major_version: u8,
name: String,
art: String,
entries: Vec<OnlineClusterEntry>,
}

#[derive(Deserialize)]
struct OnlineClusterEntry {
#[derive(specta::Type, Deserialize, Serialize)]
pub struct OnlineClusterEntry {
minor_version: u8,
loader: GameLoader,
tags: Vec<String>,
}

pub async fn get_data_storage_versions() -> LauncherResult<OnlineClusterManifest> {
let manifest = match fetch_json::<OnlineClusterManifest>(
Method::GET,
&format!("{}/versions/versions.json", crate::constants::META_URL_BASE),
None,
None,
)
.await
{
Ok(m) => m,
Err(e) => {
send_error!("failed to fetch clusters manifest: {}", e);
return Err(e);
}
};

Ok(manifest)
}

pub async fn init_clusters() -> LauncherResult<()> {
Expand Down
4 changes: 4 additions & 0 deletions apps/oneclient/frontend/src/assets/misc/missingLogo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
62 changes: 54 additions & 8 deletions apps/oneclient/frontend/src/bindings.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,50 @@ export type MojangSkin = { id: string; state: string; url: string; variant: Skin

export type MowojangProfile = { id: string; username: string }

export type OnlineCluster = { major_version: number; name: string; art: string; entries: OnlineClusterEntry[] }

export type OnlineClusterEntry = { minor_version: number; loader: GameLoader; tags: string[] }

/**
* e.g.
* ```json
* {
* "clusters": [
* {
* "major_version": 21,
* "name": "Tricky Trials",
* "art": "/versions/art/Tricky_Trials.png",
* "entries": [
* {
* "minor_version": 5,
* "loader": "fabric",
* "tags": ["PvP", "Survival"]
* },
* {
* "minor_version": 5,
* "loader": "forge",
* "tags": ["PvP", "Survival"]
* }
* ]
* },
* {
* "major_version": 20,
* "name": "Trails & Tales",
* "art": "/versions/art/Trails_Tales.png",
* "entries": [
* {
* "minor_version": 5,
* "loader": "fabric",
* "tags": ["PvP", "Survival"]
* }
* ]
* }
* ]
* }
* ```
*/
export type OnlineClusterManifest = { clusters: OnlineCluster[] }

export type PackageAuthor = { Team: { team_id: string; org_id: string | null } } | { Users: ManagedUser[] }

export type PackageCategories = { Mod: PackageModCategory[] } | { ResourcePack: PackageResourcePackCategory[] } | { Shader: PackageShaderCategory[] } | { DataPack: PackageModCategory[] } | { ModPack: PackageModPackCategory[] }
Expand Down Expand Up @@ -188,7 +232,7 @@ gallery: string[] }

export type SettingProfileModel = { name: string; java_id: number | 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 type Settings = { global_game_settings: SettingProfileModel; allow_parallel_running_clusters: boolean; enable_gamemode: boolean; discord_enabled: boolean; seen_onboarding: boolean; max_concurrent_requests: number; settings_version: number; native_window_frame: boolean; show_tanstack_dev_tools: boolean }
export type Settings = { global_game_settings: SettingProfileModel; allow_parallel_running_clusters: boolean; enable_gamemode: boolean; discord_enabled: boolean; seen_onboarding: boolean; mod_list_use_grid: boolean; max_concurrent_requests: number; settings_version: number; native_window_frame: boolean; show_tanstack_dev_tools: boolean }

export type SettingsOsExtra = Record<string, never>

Expand Down Expand Up @@ -255,8 +299,14 @@ export type VersionType =
*/
"old_beta"

const ARGS_MAP = { 'oneclient':'{"getClustersGroupedByMajor":[],"openDevTools":[],"getBundlesFor":["cluster_id"]}', 'core':'{"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"getRunningProcesses":[],"getClusters":[],"updateClusterById":["id","request"],"getGameVersions":[],"getLoadersForVersion":["mc_version"],"getUsers":[],"setDefaultUser":["uuid"],"changeSkin":["access_token","skin_url","skin_variant"],"getWorlds":["id"],"fetchLoggedInProfile":["access_token"],"createCluster":["options"],"getScreenshots":["id"],"searchPackages":["provider","query"],"installModpack":["modpack","cluster_id"],"changeCape":["access_token","cape_uuid"],"getLogs":["id"],"readSettings":[],"getRunningProcessesByClusterId":["cluster_id"],"getUsersFromAuthor":["provider","author"],"createSettingsProfile":["name"],"getPackageBody":["provider","body"],"convertUsernameUUID":["username_uuid"],"getClusterById":["id"],"getUser":["uuid"],"launchCluster":["id","uuid"],"getLogByName":["id","name"],"openMsaLogin":[],"getPackage":["provider","slug"],"getDefaultUser":["fallback"],"updateClusterProfile":["name","profile"],"open":["input"],"getGlobalProfile":[],"isClusterRunning":["cluster_id"],"removeCluster":["id"],"killProcess":["pid"],"writeSettings":["setting"],"removeUser":["uuid"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"getMultiplePackages":["provider","slugs"],"fetchMinecraftProfile":["uuid"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"removeCape":["access_token"],"getProfileOrDefault":["name"]}', 'events':'{"message":["event"],"ingress":["event"],"process":["event"]}', 'folders':'{"fromCluster":["folder_name"],"openCluster":["folder_name"]}' }
export type Router = { 'core': { getClusters: () => Promise<ClusterModel[]>,
const ARGS_MAP = { 'oneclient':'{"getVersions":[],"openDevTools":[],"getBundlesFor":["cluster_id"],"getClustersGroupedByMajor":[]}', 'core':'{"getRunningProcessesByClusterId":["cluster_id"],"getScreenshots":["id"],"getProfileOrDefault":["name"],"searchPackages":["provider","query"],"changeSkin":["access_token","skin_url","skin_variant"],"launchCluster":["id","uuid"],"getLoadersForVersion":["mc_version"],"fetchMinecraftProfile":["uuid"],"removeCape":["access_token"],"updateClusterById":["id","request"],"createSettingsProfile":["name"],"changeCape":["access_token","cape_uuid"],"getGlobalProfile":[],"getMultiplePackages":["provider","slugs"],"getUsers":[],"removeUser":["uuid"],"getRunningProcesses":[],"readSettings":[],"isClusterRunning":["cluster_id"],"getLogByName":["id","name"],"getClusterById":["id"],"killProcess":["pid"],"writeSettings":["setting"],"getMods":["id"],"getLogs":["id"],"getPackageBody":["provider","body"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"createCluster":["options"],"getUsersFromAuthor":["provider","author"],"getPackage":["provider","slug"],"installModpack":["modpack","cluster_id"],"convertUsernameUUID":["username_uuid"],"getClusters":[],"removeCluster":["id"],"fetchLoggedInProfile":["access_token"],"setDefaultUser":["uuid"],"getWorlds":["id"],"getGameVersions":[],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"open":["input"],"getUser":["uuid"],"getDefaultUser":["fallback"],"updateClusterProfile":["name","profile"],"openMsaLogin":[]}', 'folders':'{"fromCluster":["folder_name"],"openCluster":["folder_name"]}', 'events':'{"ingress":["event"],"message":["event"],"process":["event"]}' }
export type Router = { 'folders': { fromCluster: (folderName: string) => Promise<string>,
openCluster: (folderName: string) => Promise<null> },
'oneclient': { openDevTools: () => Promise<void>,
getClustersGroupedByMajor: () => Promise<Partial<{ [key in number]: ClusterModel[] }>>,
getBundlesFor: (clusterId: number) => Promise<ModpackArchive[]>,
getVersions: () => Promise<OnlineClusterManifest> },
'core': { getClusters: () => Promise<ClusterModel[]>,
getClusterById: (id: number) => Promise<ClusterModel | null>,
removeCluster: (id: number) => Promise<null>,
createCluster: (options: CreateCluster) => Promise<ClusterModel>,
Expand All @@ -266,6 +316,7 @@ getScreenshots: (id: number) => Promise<string[]>,
getWorlds: (id: number) => Promise<string[]>,
getLogs: (id: number) => Promise<string[]>,
getLogByName: (id: number, name: string) => Promise<string | null>,
getMods: (id: number) => Promise<string[]>,
getRunningProcesses: () => Promise<Process[]>,
getRunningProcessesByClusterId: (clusterId: number) => Promise<Process[]>,
isClusterRunning: (clusterId: number) => Promise<boolean>,
Expand Down Expand Up @@ -300,11 +351,6 @@ changeCape: (accessToken: string, capeUuid: string) => Promise<MojangFullPlayerP
removeCape: (accessToken: string) => Promise<MojangFullPlayerProfile>,
convertUsernameUUID: (usernameUuid: string) => Promise<MowojangProfile>,
open: (input: string) => Promise<null> },
'oneclient': { openDevTools: () => Promise<void>,
getClustersGroupedByMajor: () => Promise<Partial<{ [key in number]: ClusterModel[] }>>,
getBundlesFor: (clusterId: number) => Promise<ModpackArchive[]> },
'folders': { fromCluster: (folderName: string) => Promise<string>,
openCluster: (folderName: string) => Promise<null> },
'events': { ingress: (event: IngressPayload) => Promise<void>,
message: (event: MessagePayload) => Promise<void>,
process: (event: ProcessPayload) => Promise<void> } };
Expand Down
26 changes: 26 additions & 0 deletions apps/oneclient/frontend/src/components/Bundle/Bundle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { ClusterModel, ModpackArchive } from '@/bindings.gen';
import { useSettings } from '@/hooks/useSettings';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
import { twMerge } from 'tailwind-merge';
import { ModCard, useModCardContext } from '.';

interface BundleProps {
bundleData: ModpackArchive;
cluster: ClusterModel;
}

export function Bundle({ bundleData, cluster }: BundleProps) {
const { useVerticalGridLayout } = useModCardContext();
const { setting } = useSettings();
const useGridLayout = setting('mod_list_use_grid');

return (
<OverlayScrollbarsComponent>
<div className={twMerge('grid gap-2', useGridLayout ? 'grid-cols-3' : 'grid-cols-1', useGridLayout && useVerticalGridLayout ? 'max-h-128' : 'max-h-112')}>
{bundleData.manifest.files.map((file, index) => (
<ModCard cluster={cluster} file={file} key={index} />

Check warning on line 21 in apps/oneclient/frontend/src/components/Bundle/Bundle.tsx

View workflow job for this annotation

GitHub Actions / ES Checks

Do not use item index in the array as its key
))}
</div>
</OverlayScrollbarsComponent>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { ClusterModel, ManagedPackage, ManagedVersion } from '@/bindings.gen';
import { useSettings } from '@/hooks/useSettings';
import { bindings } from '@/main';
import { useCommandMut } from '@onelauncher/common';
import { Button } from '@onelauncher/common/components';
import { Download01Icon } from '@untitled-theme/icons-react';
import { twMerge } from 'tailwind-merge';

export function DownloadModButton({ pkg, version, cluster }: { pkg: ManagedPackage; version: ManagedVersion; cluster: ClusterModel }) {
const download = useCommandMut(() => bindings.core.downloadPackage(pkg.provider, pkg.id, version.version_id, cluster.id, true));

const handleDownload = () => {
(async () => {
await download.mutateAsync();
})();
};

const { setting } = useSettings();
const useGridLayout = setting('mod_list_use_grid');

return (
<Button
className={twMerge('flex flex-col items-center justify-center', useGridLayout ? 'w-full' : '')}
color="primary"
onClick={handleDownload}
size={useGridLayout ? 'large' : 'iconLarge'}
>
<Download01Icon />
</Button>
);
}
Loading
Loading