From b0cc528ca2fbcf01b51aab185dcef31c826c24c3 Mon Sep 17 00:00:00 2001 From: Jacob Date: Mon, 17 Nov 2025 21:59:54 +0800 Subject: [PATCH 01/17] chore: OneClient DialogTrigger -> Overlay.Trigger Code Clean --- apps/oneclient/frontend/src/bindings.gen.ts | 10 +++++----- .../frontend/src/components/DeleteAccountButton.tsx | 5 ++--- .../oneclient/frontend/src/components/LaunchButton.tsx | 5 ++--- .../frontend/src/components/overlay/AccountPopup.tsx | 5 ++--- .../frontend/src/components/overlay/NoAccountPopup.tsx | 5 ++--- .../frontend/src/routes/app/account/skins.tsx | 9 ++++----- apps/oneclient/frontend/src/routes/app/accounts.tsx | 6 +++--- 7 files changed, 20 insertions(+), 25 deletions(-) diff --git a/apps/oneclient/frontend/src/bindings.gen.ts b/apps/oneclient/frontend/src/bindings.gen.ts index 850b2eb7..64e54c25 100644 --- a/apps/oneclient/frontend/src/bindings.gen.ts +++ b/apps/oneclient/frontend/src/bindings.gen.ts @@ -190,7 +190,7 @@ export type SettingProfileModel = { name: string; java_id: number | null; res: R 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 SettingsOsExtra = { enable_gamemode: boolean | null } +export type SettingsOsExtra = Record export type SkinVariant = "classic" | "slim" @@ -255,7 +255,7 @@ export type VersionType = */ "old_beta" -const ARGS_MAP = { 'core':'{"open":["input"],"fetchLoggedInProfile":["access_token"],"convertUsernameUUID":["username_uuid"],"getGlobalProfile":[],"getRunningProcessesByClusterId":["cluster_id"],"getClusterById":["id"],"readSettings":[],"getClusters":[],"updateClusterProfile":["name","profile"],"createCluster":["options"],"getGameVersions":[],"writeSettings":["setting"],"getLoadersForVersion":["mc_version"],"getUser":["uuid"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"changeCape":["access_token","cape_uuid"],"updateClusterById":["id","request"],"fetchMinecraftProfile":["uuid"],"getPackage":["provider","slug"],"getLogByName":["id","name"],"launchCluster":["id","uuid"],"getWorlds":["id"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"removeUser":["uuid"],"getMultiplePackages":["provider","slugs"],"installModpack":["modpack","cluster_id"],"getRunningProcesses":[],"getLogs":["id"],"searchPackages":["provider","query"],"getPackageBody":["provider","body"],"changeSkin":["access_token","skin_url","skin_variant"],"removeCape":["access_token"],"removeCluster":["id"],"isClusterRunning":["cluster_id"],"killProcess":["pid"],"getUsersFromAuthor":["provider","author"],"createSettingsProfile":["name"],"getDefaultUser":["fallback"],"getUsers":[],"getProfileOrDefault":["name"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"getScreenshots":["id"],"setDefaultUser":["uuid"],"openMsaLogin":[]}', 'oneclient':'{"openDevTools":[],"getClustersGroupedByMajor":[],"getBundlesFor":["cluster_id"]}', 'events':'{"ingress":["event"],"message":["event"],"process":["event"]}', 'folders':'{"fromCluster":["folder_name"],"openCluster":["folder_name"]}' } +const ARGS_MAP = { 'events':'{"ingress":["event"],"message":["event"],"process":["event"]}', 'oneclient':'{"getClustersGroupedByMajor":[],"getBundlesFor":["cluster_id"],"openDevTools":[]}', 'core':'{"getLoadersForVersion":["mc_version"],"getLogByName":["id","name"],"removeUser":["uuid"],"killProcess":["pid"],"getWorlds":["id"],"updateClusterProfile":["name","profile"],"setDefaultUser":["uuid"],"getPackage":["provider","slug"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"searchPackages":["provider","query"],"changeSkin":["access_token","skin_url","skin_variant"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"installModpack":["modpack","cluster_id"],"openMsaLogin":[],"getClusters":[],"getMultiplePackages":["provider","slugs"],"fetchLoggedInProfile":["access_token"],"createSettingsProfile":["name"],"getUser":["uuid"],"removeCape":["access_token"],"open":["input"],"writeSettings":["setting"],"getRunningProcesses":[],"getDefaultUser":["fallback"],"getUsersFromAuthor":["provider","author"],"createCluster":["options"],"updateClusterById":["id","request"],"getClusterById":["id"],"removeCluster":["id"],"getPackageBody":["provider","body"],"isClusterRunning":["cluster_id"],"getScreenshots":["id"],"getProfileOrDefault":["name"],"changeCape":["access_token","cape_uuid"],"fetchMinecraftProfile":["uuid"],"getRunningProcessesByClusterId":["cluster_id"],"convertUsernameUUID":["username_uuid"],"launchCluster":["id","uuid"],"getUsers":[],"readSettings":[],"getGlobalProfile":[],"getLogs":["id"],"getGameVersions":[]}', 'folders':'{"openCluster":["folder_name"],"fromCluster":["folder_name"]}' } export type Router = { 'core': { getClusters: () => Promise, getClusterById: (id: number) => Promise, removeCluster: (id: number) => Promise, @@ -300,12 +300,12 @@ changeCape: (accessToken: string, capeUuid: string) => Promise Promise, convertUsernameUUID: (usernameUuid: string) => Promise, open: (input: string) => Promise }, -'oneclient': { openDevTools: () => Promise, -getClustersGroupedByMajor: () => Promise>, -getBundlesFor: (clusterId: number) => Promise }, 'events': { ingress: (event: IngressPayload) => Promise, message: (event: MessagePayload) => Promise, process: (event: ProcessPayload) => Promise }, +'oneclient': { openDevTools: () => Promise, +getClustersGroupedByMajor: () => Promise>, +getBundlesFor: (clusterId: number) => Promise }, 'folders': { fromCluster: (folderName: string) => Promise, openCluster: (folderName: string) => Promise } }; diff --git a/apps/oneclient/frontend/src/components/DeleteAccountButton.tsx b/apps/oneclient/frontend/src/components/DeleteAccountButton.tsx index 7aace1e0..7c11ac30 100644 --- a/apps/oneclient/frontend/src/components/DeleteAccountButton.tsx +++ b/apps/oneclient/frontend/src/components/DeleteAccountButton.tsx @@ -1,12 +1,11 @@ import type { MinecraftCredentials } from '@/bindings.gen'; import { Button } from '@onelauncher/common/components'; import { Trash01Icon } from '@untitled-theme/icons-react'; -import { DialogTrigger } from 'react-aria-components'; import { Overlay, RemoveAccountModal } from './overlay'; export function DeleteAccountButton({ profile, onPress }: { profile: MinecraftCredentials; onPress: () => void }) { return ( - + @@ -14,6 +13,6 @@ export function DeleteAccountButton({ profile, onPress }: { profile: MinecraftCr - + ); } diff --git a/apps/oneclient/frontend/src/components/LaunchButton.tsx b/apps/oneclient/frontend/src/components/LaunchButton.tsx index 0129d552..430f7ff5 100644 --- a/apps/oneclient/frontend/src/components/LaunchButton.tsx +++ b/apps/oneclient/frontend/src/components/LaunchButton.tsx @@ -5,7 +5,6 @@ import { bindings } from '@/main'; import { useCommandSuspense } from '@onelauncher/common'; import { Button } from '@onelauncher/common/components'; import { useState } from 'react'; -import { DialogTrigger } from 'react-aria-components'; import { tv } from 'tailwind-variants'; import { NoAccountPopup, Overlay } from './overlay'; @@ -43,7 +42,7 @@ export function LaunchButton({ }; return ( - + @@ -17,7 +16,7 @@ export function NoAccountPopup() { - + ); } diff --git a/apps/oneclient/frontend/src/routes/app/account/skins.tsx b/apps/oneclient/frontend/src/routes/app/account/skins.tsx index 41bb2bd3..6438671d 100644 --- a/apps/oneclient/frontend/src/routes/app/account/skins.tsx +++ b/apps/oneclient/frontend/src/routes/app/account/skins.tsx @@ -12,7 +12,6 @@ import { save } from '@tauri-apps/plugin-dialog'; import { exists, mkdir, readTextFile, writeFile, writeTextFile } from '@tauri-apps/plugin-fs'; import { Download01Icon, PlusIcon, Trash01Icon } from '@untitled-theme/icons-react'; import { useEffect, useState } from 'react'; -import { DialogTrigger } from 'react-aria-components'; import { CrouchAnimation, FlyingAnimation, HitAnimation, IdleAnimation, WalkingAnimation } from 'skinview3d'; interface Skin { @@ -283,7 +282,7 @@ function SkinHistoryRow({ selected, animation, setSelectedSkin, skins, setSkins,
- + @@ -366,7 +365,7 @@ function RenderSkin({ skin, selected, animation, setSelectedSkin, setSkins, cape setSkins(prev => prev.filter(skinData => skinData.skin_url !== skin.skin_url))} /> - + )} @@ -174,6 +174,6 @@ function AddAccountButton({ - + ); } From c843ee2f143c6fcd78d9ccf9fa8f719c84c5f1ec Mon Sep 17 00:00:00 2001 From: Jacob Date: Tue, 18 Nov 2025 11:17:01 +0800 Subject: [PATCH 02/17] refactor: OneClient Stepper --- apps/oneclient/frontend/src/bindings.gen.ts | 2 +- .../frontend/src/components/Stepper.tsx | 2 +- apps/oneclient/frontend/src/styles/global.css | 17 ++++++++++++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/apps/oneclient/frontend/src/bindings.gen.ts b/apps/oneclient/frontend/src/bindings.gen.ts index 64e54c25..198b9a23 100644 --- a/apps/oneclient/frontend/src/bindings.gen.ts +++ b/apps/oneclient/frontend/src/bindings.gen.ts @@ -255,7 +255,7 @@ export type VersionType = */ "old_beta" -const ARGS_MAP = { 'events':'{"ingress":["event"],"message":["event"],"process":["event"]}', 'oneclient':'{"getClustersGroupedByMajor":[],"getBundlesFor":["cluster_id"],"openDevTools":[]}', 'core':'{"getLoadersForVersion":["mc_version"],"getLogByName":["id","name"],"removeUser":["uuid"],"killProcess":["pid"],"getWorlds":["id"],"updateClusterProfile":["name","profile"],"setDefaultUser":["uuid"],"getPackage":["provider","slug"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"searchPackages":["provider","query"],"changeSkin":["access_token","skin_url","skin_variant"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"installModpack":["modpack","cluster_id"],"openMsaLogin":[],"getClusters":[],"getMultiplePackages":["provider","slugs"],"fetchLoggedInProfile":["access_token"],"createSettingsProfile":["name"],"getUser":["uuid"],"removeCape":["access_token"],"open":["input"],"writeSettings":["setting"],"getRunningProcesses":[],"getDefaultUser":["fallback"],"getUsersFromAuthor":["provider","author"],"createCluster":["options"],"updateClusterById":["id","request"],"getClusterById":["id"],"removeCluster":["id"],"getPackageBody":["provider","body"],"isClusterRunning":["cluster_id"],"getScreenshots":["id"],"getProfileOrDefault":["name"],"changeCape":["access_token","cape_uuid"],"fetchMinecraftProfile":["uuid"],"getRunningProcessesByClusterId":["cluster_id"],"convertUsernameUUID":["username_uuid"],"launchCluster":["id","uuid"],"getUsers":[],"readSettings":[],"getGlobalProfile":[],"getLogs":["id"],"getGameVersions":[]}', 'folders':'{"openCluster":["folder_name"],"fromCluster":["folder_name"]}' } +const ARGS_MAP = { 'folders':'{"openCluster":["folder_name"],"fromCluster":["folder_name"]}', 'events':'{"message":["event"],"process":["event"],"ingress":["event"]}', 'core':'{"getRunningProcesses":[],"getUsers":[],"getUsersFromAuthor":["provider","author"],"changeSkin":["access_token","skin_url","skin_variant"],"searchPackages":["provider","query"],"getWorlds":["id"],"getDefaultUser":["fallback"],"readSettings":[],"openMsaLogin":[],"writeSettings":["setting"],"getMultiplePackages":["provider","slugs"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"createSettingsProfile":["name"],"convertUsernameUUID":["username_uuid"],"setDefaultUser":["uuid"],"fetchLoggedInProfile":["access_token"],"removeUser":["uuid"],"removeCape":["access_token"],"getPackageBody":["provider","body"],"open":["input"],"changeCape":["access_token","cape_uuid"],"launchCluster":["id","uuid"],"getScreenshots":["id"],"getUser":["uuid"],"getClusters":[],"updateClusterById":["id","request"],"getGameVersions":[],"getPackage":["provider","slug"],"getGlobalProfile":[],"fetchMinecraftProfile":["uuid"],"installModpack":["modpack","cluster_id"],"getClusterById":["id"],"getLogs":["id"],"killProcess":["pid"],"removeCluster":["id"],"getLogByName":["id","name"],"getRunningProcessesByClusterId":["cluster_id"],"getProfileOrDefault":["name"],"updateClusterProfile":["name","profile"],"isClusterRunning":["cluster_id"],"getLoadersForVersion":["mc_version"],"createCluster":["options"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"]}', 'oneclient':'{"openDevTools":[],"getClustersGroupedByMajor":[],"getBundlesFor":["cluster_id"]}' } export type Router = { 'core': { getClusters: () => Promise, getClusterById: (id: number) => Promise, removeCluster: (id: number) => Promise, diff --git a/apps/oneclient/frontend/src/components/Stepper.tsx b/apps/oneclient/frontend/src/components/Stepper.tsx index 68e04bab..05ddd090 100644 --- a/apps/oneclient/frontend/src/components/Stepper.tsx +++ b/apps/oneclient/frontend/src/components/Stepper.tsx @@ -11,7 +11,7 @@ export function Stepper({ steps, currentStepIndex }: VerticalStepperProps) {
{steps.map((step, index) => (
Date: Tue, 18 Nov 2025 14:49:30 +0800 Subject: [PATCH 03/17] refactor: OneClient AddAcccountModal to use Overlay.Buttons --- apps/oneclient/frontend/src/bindings.gen.ts | 22 ++++++------ .../components/overlay/AddAccountModal.tsx | 34 +++---------------- 2 files changed, 15 insertions(+), 41 deletions(-) diff --git a/apps/oneclient/frontend/src/bindings.gen.ts b/apps/oneclient/frontend/src/bindings.gen.ts index 198b9a23..86a75212 100644 --- a/apps/oneclient/frontend/src/bindings.gen.ts +++ b/apps/oneclient/frontend/src/bindings.gen.ts @@ -255,8 +255,16 @@ export type VersionType = */ "old_beta" -const ARGS_MAP = { 'folders':'{"openCluster":["folder_name"],"fromCluster":["folder_name"]}', 'events':'{"message":["event"],"process":["event"],"ingress":["event"]}', 'core':'{"getRunningProcesses":[],"getUsers":[],"getUsersFromAuthor":["provider","author"],"changeSkin":["access_token","skin_url","skin_variant"],"searchPackages":["provider","query"],"getWorlds":["id"],"getDefaultUser":["fallback"],"readSettings":[],"openMsaLogin":[],"writeSettings":["setting"],"getMultiplePackages":["provider","slugs"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"createSettingsProfile":["name"],"convertUsernameUUID":["username_uuid"],"setDefaultUser":["uuid"],"fetchLoggedInProfile":["access_token"],"removeUser":["uuid"],"removeCape":["access_token"],"getPackageBody":["provider","body"],"open":["input"],"changeCape":["access_token","cape_uuid"],"launchCluster":["id","uuid"],"getScreenshots":["id"],"getUser":["uuid"],"getClusters":[],"updateClusterById":["id","request"],"getGameVersions":[],"getPackage":["provider","slug"],"getGlobalProfile":[],"fetchMinecraftProfile":["uuid"],"installModpack":["modpack","cluster_id"],"getClusterById":["id"],"getLogs":["id"],"killProcess":["pid"],"removeCluster":["id"],"getLogByName":["id","name"],"getRunningProcessesByClusterId":["cluster_id"],"getProfileOrDefault":["name"],"updateClusterProfile":["name","profile"],"isClusterRunning":["cluster_id"],"getLoadersForVersion":["mc_version"],"createCluster":["options"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"]}', 'oneclient':'{"openDevTools":[],"getClustersGroupedByMajor":[],"getBundlesFor":["cluster_id"]}' } -export type Router = { 'core': { getClusters: () => Promise, +const ARGS_MAP = { 'events':'{"ingress":["event"],"message":["event"],"process":["event"]}', 'oneclient':'{"openDevTools":[],"getClustersGroupedByMajor":[],"getBundlesFor":["cluster_id"]}', 'folders':'{"fromCluster":["folder_name"],"openCluster":["folder_name"]}', 'core':'{"getGameVersions":[],"openMsaLogin":[],"createSettingsProfile":["name"],"getDefaultUser":["fallback"],"getProfileOrDefault":["name"],"removeCape":["access_token"],"fetchLoggedInProfile":["access_token"],"open":["input"],"getWorlds":["id"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"getMultiplePackages":["provider","slugs"],"changeSkin":["access_token","skin_url","skin_variant"],"removeCluster":["id"],"getGlobalProfile":[],"getUsersFromAuthor":["provider","author"],"getClusters":[],"getScreenshots":["id"],"getLogs":["id"],"getRunningProcesses":[],"getUser":["uuid"],"convertUsernameUUID":["username_uuid"],"searchPackages":["provider","query"],"getRunningProcessesByClusterId":["cluster_id"],"updateClusterProfile":["name","profile"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"installModpack":["modpack","cluster_id"],"updateClusterById":["id","request"],"getClusterById":["id"],"launchCluster":["id","uuid"],"killProcess":["pid"],"createCluster":["options"],"getUsers":[],"setDefaultUser":["uuid"],"removeUser":["uuid"],"readSettings":[],"getLogByName":["id","name"],"getPackage":["provider","slug"],"getPackageBody":["provider","body"],"writeSettings":["setting"],"getLoadersForVersion":["mc_version"],"changeCape":["access_token","cape_uuid"],"isClusterRunning":["cluster_id"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"fetchMinecraftProfile":["uuid"]}' } +export type Router = { 'oneclient': { openDevTools: () => Promise, +getClustersGroupedByMajor: () => Promise>, +getBundlesFor: (clusterId: number) => Promise }, +'folders': { fromCluster: (folderName: string) => Promise, +openCluster: (folderName: string) => Promise }, +'events': { ingress: (event: IngressPayload) => Promise, +message: (event: MessagePayload) => Promise, +process: (event: ProcessPayload) => Promise }, +'core': { getClusters: () => Promise, getClusterById: (id: number) => Promise, removeCluster: (id: number) => Promise, createCluster: (options: CreateCluster) => Promise, @@ -299,15 +307,7 @@ changeSkin: (accessToken: string, skinUrl: string, skinVariant: SkinVariant) => changeCape: (accessToken: string, capeUuid: string) => Promise, removeCape: (accessToken: string) => Promise, convertUsernameUUID: (usernameUuid: string) => Promise, -open: (input: string) => Promise }, -'events': { ingress: (event: IngressPayload) => Promise, -message: (event: MessagePayload) => Promise, -process: (event: ProcessPayload) => Promise }, -'oneclient': { openDevTools: () => Promise, -getClustersGroupedByMajor: () => Promise>, -getBundlesFor: (clusterId: number) => Promise }, -'folders': { fromCluster: (folderName: string) => Promise, -openCluster: (folderName: string) => Promise } }; +open: (input: string) => Promise } }; export type { InferCommandOutput } diff --git a/apps/oneclient/frontend/src/components/overlay/AddAccountModal.tsx b/apps/oneclient/frontend/src/components/overlay/AddAccountModal.tsx index 7f41197f..8a19bf48 100644 --- a/apps/oneclient/frontend/src/components/overlay/AddAccountModal.tsx +++ b/apps/oneclient/frontend/src/components/overlay/AddAccountModal.tsx @@ -2,7 +2,6 @@ import type { MinecraftCredentials } from '@/bindings.gen'; import { AccountAvatar } from '@/components/AccountAvatar'; import { bindings } from '@/main'; import { useCommandMut } from '@onelauncher/common'; -import { Button } from '@onelauncher/common/components'; import { useQueryClient } from '@tanstack/react-query'; import { Overlay } from './Overlay'; @@ -27,45 +26,20 @@ export function AddAccountModal() { return ( Add Account - -

- Lorem ipsum dolor sit amet consectetur, adipisicing elit. Fugiat aut unde, rem esse natus iusto impedit doloribus laborum laboriosam amet? Totam, commodi sed ducimus dicta praesentium sunt? A, soluta iusto. -

- +

Add a new account to OneClient

{profile ? ( <> - + ) - : ( - - )} + : }
); } -function AccountRow({ - profile, -}: { - profile: MinecraftCredentials; -}) { +function AccountRow({ profile }: { profile: MinecraftCredentials }) { return (
From 8034365e6be63dc1ca60e4a815fae76942cdc066 Mon Sep 17 00:00:00 2001 From: Jacob Date: Tue, 18 Nov 2025 14:50:33 +0800 Subject: [PATCH 04/17] refactor: OneClient AccountPopup --- .../src/components/overlay/AccountPopup.tsx | 77 +++++++------------ 1 file changed, 28 insertions(+), 49 deletions(-) diff --git a/apps/oneclient/frontend/src/components/overlay/AccountPopup.tsx b/apps/oneclient/frontend/src/components/overlay/AccountPopup.tsx index a4237bc6..5fe31aa8 100644 --- a/apps/oneclient/frontend/src/components/overlay/AccountPopup.tsx +++ b/apps/oneclient/frontend/src/components/overlay/AccountPopup.tsx @@ -39,47 +39,40 @@ export function AccountPopup() {
{defaultUser.data && ( -
- { }} - onDelete={() => deleteUser(defaultUser.data as MinecraftCredentials)} - user={defaultUser.data} - /> -
+ { }} + onDelete={() => deleteUser(defaultUser.data as MinecraftCredentials)} + user={defaultUser.data} + /> )} {users.data?.filter(user => user.id !== defaultUser.data?.id).map(user => ( -
- setDefaultUser(user)} - onDelete={() => deleteUser(user)} - user={user} - /> -
+ setDefaultUser(user)} + onDelete={() => deleteUser(user)} + user={user} + /> ))}
-
+ + - - + + + + - - - - -
-
- - - -
+ + +
@@ -88,23 +81,9 @@ export function AccountPopup() { export default AccountPopup; -function AccountEntry({ - onClick, - onDelete, - user, - loggedIn = false, -}: { - onClick: () => void; - onDelete: () => void; - user: MinecraftCredentials; - loggedIn?: boolean; -}) { +function AccountEntry({ onClick, onDelete, user, loggedIn = false }: { onClick: () => void; onDelete: () => void; user: MinecraftCredentials; loggedIn?: boolean }) { return ( - - + {isRunning ? : } ); diff --git a/apps/oneclient/frontend/src/components/overlay/KillMinecraft.tsx b/apps/oneclient/frontend/src/components/overlay/KillMinecraft.tsx new file mode 100644 index 00000000..d85f14c0 --- /dev/null +++ b/apps/oneclient/frontend/src/components/overlay/KillMinecraft.tsx @@ -0,0 +1,19 @@ +import { Overlay } from '@/components'; +import { bindings } from '@/main'; +import { useCommandSuspense } from '@onelauncher/common'; + +export function KillMinecraft({ setOpen }: { setOpen: React.Dispatch> }) { + const { data: foundAllProcess } = useCommandSuspense(['getRunningProcesses'], () => bindings.core.getRunningProcesses()); + const kill = () => { + foundAllProcess.forEach(process => bindings.core.killProcess(process.pid)); + setOpen(false); + }; + + return ( + + Minecraft is running +

Do you want to kill minecraft?

+ +
+ ); +} diff --git a/apps/oneclient/frontend/src/components/overlay/index.ts b/apps/oneclient/frontend/src/components/overlay/index.ts index 8032c167..3865c7a2 100644 --- a/apps/oneclient/frontend/src/components/overlay/index.ts +++ b/apps/oneclient/frontend/src/components/overlay/index.ts @@ -3,6 +3,7 @@ export * from './AddAccountModal'; export * from './BundleModListModal'; export * from './DebugInfo'; export * from './ImportSkinModal'; +export * from './KillMinecraft'; export * from './NoAccountPopup'; export * from './Overlay'; export * from './Popup'; From 03ef47bd3e175f330268c524b94a8d9eeb282a4c Mon Sep 17 00:00:00 2001 From: Jacob Date: Sat, 6 Dec 2025 20:13:51 +0800 Subject: [PATCH 08/17] fix(core): capabilities --- apps/oneclient/desktop/capabilities/default.json | 9 ++++++++- apps/oneclient/frontend/src/bindings.gen.ts | 16 ++++++++-------- .../desktop/capabilities/default.json | 9 ++++++++- packages/core/src/api/tauri/debug.rs | 14 +++++++------- 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/apps/oneclient/desktop/capabilities/default.json b/apps/oneclient/desktop/capabilities/default.json index 97bca373..79db1d31 100644 --- a/apps/oneclient/desktop/capabilities/default.json +++ b/apps/oneclient/desktop/capabilities/default.json @@ -38,6 +38,13 @@ "fs:allow-data-read-recursive", "fs:allow-data-write-recursive", - "os:default" + "os:allow-arch", + "os:deny-exe-extension", + "os:allow-family", + "os:deny-hostname", + "os:allow-locale", + "os:allow-os-type", + "os:allow-platform", + "os:allow-version" ] } diff --git a/apps/oneclient/frontend/src/bindings.gen.ts b/apps/oneclient/frontend/src/bindings.gen.ts index 4ae1f1b2..d81a202b 100644 --- a/apps/oneclient/frontend/src/bindings.gen.ts +++ b/apps/oneclient/frontend/src/bindings.gen.ts @@ -301,25 +301,25 @@ export type VersionType = */ "old_beta" -const ARGS_MAP = { 'oneclient':'{"getBundlesFor":["cluster_id"],"getVersions":[],"getClustersGroupedByMajor":[]}', 'events':'{"message":["event"],"process":["event"],"ingress":["event"]}', 'folders':'{"openCluster":["folder_name"],"fromCluster":["folder_name"]}', 'debug':'{"getType":[],"isInDev":[],"getFamily":[],"getArch":[],"getBuildTimestamp":[],"getPlatform":[],"openDevTools":[],"getLocale":[],"getVersion":[],"getCommitHash":[]}', 'core':'{"getScreenshots":["id"],"getClusterById":["id"],"launchCluster":["id","uuid"],"getGameVersions":[],"convertUsernameUUID":["username_uuid"],"changeSkin":["access_token","skin_url","skin_variant"],"createCluster":["options"],"getMultiplePackages":["provider","slugs"],"getLogByName":["id","name"],"fetchMinecraftProfile":["uuid"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"getClusters":[],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"getLoadersForVersion":["mc_version"],"getWorlds":["id"],"killProcess":["pid"],"searchPackages":["provider","query"],"getDefaultUser":["fallback"],"getRunningProcessesByClusterId":["cluster_id"],"updateClusterProfile":["name","profile"],"getPackageBody":["provider","body"],"getUsersFromAuthor":["provider","author"],"open":["input"],"updateClusterById":["id","request"],"removeCluster":["id"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"readSettings":[],"getGlobalProfile":[],"getUsers":[],"changeCape":["access_token","cape_uuid"],"removeCape":["access_token"],"getProfileOrDefault":["name"],"getUser":["uuid"],"writeSettings":["setting"],"getRunningProcesses":[],"createSettingsProfile":["name"],"setDefaultUser":["uuid"],"openMsaLogin":[],"getLogs":["id"],"getPackage":["provider","slug"],"installModpack":["modpack","cluster_id"],"fetchLoggedInProfile":["access_token"],"isClusterRunning":["cluster_id"],"removeUser":["uuid"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"]}' } -export type Router = { 'events': { ingress: (event: IngressPayload) => Promise, -message: (event: MessagePayload) => Promise, -process: (event: ProcessPayload) => Promise }, -'folders': { fromCluster: (folderName: string) => Promise, -openCluster: (folderName: string) => Promise }, -'debug': { openDevTools: () => Promise, +const ARGS_MAP = { 'events':'{"message":["event"],"ingress":["event"],"process":["event"]}', 'oneclient':'{"getVersions":[],"getBundlesFor":["cluster_id"],"getClustersGroupedByMajor":[]}', 'debug':'{"getArch":[],"getVersion":[],"getFamily":[],"getCommitHash":[],"getBuildTimestamp":[],"getLocale":[],"openDevTools":[],"isInDev":[],"getType":[],"getPlatform":[]}', 'core':'{"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"fetchLoggedInProfile":["access_token"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"removeCape":["access_token"],"getLogs":["id"],"getUsers":[],"getGameVersions":[],"readSettings":[],"getPackage":["provider","slug"],"fetchMinecraftProfile":["uuid"],"removeCluster":["id"],"writeSettings":["setting"],"updateClusterById":["id","request"],"getPackageBody":["provider","body"],"convertUsernameUUID":["username_uuid"],"open":["input"],"isClusterRunning":["cluster_id"],"getRunningProcessesByClusterId":["cluster_id"],"getScreenshots":["id"],"removeUser":["uuid"],"installModpack":["modpack","cluster_id"],"getUsersFromAuthor":["provider","author"],"changeCape":["access_token","cape_uuid"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"changeSkin":["access_token","skin_url","skin_variant"],"getClusters":[],"launchCluster":["id","uuid"],"createCluster":["options"],"getProfileOrDefault":["name"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"openMsaLogin":[],"getWorlds":["id"],"getLogByName":["id","name"],"getRunningProcesses":[],"setDefaultUser":["uuid"],"getGlobalProfile":[],"getClusterById":["id"],"getLoadersForVersion":["mc_version"],"createSettingsProfile":["name"],"searchPackages":["provider","query"],"killProcess":["pid"],"getDefaultUser":["fallback"],"updateClusterProfile":["name","profile"],"getUser":["uuid"],"getMultiplePackages":["provider","slugs"]}', 'folders':'{"fromCluster":["folder_name"],"openCluster":["folder_name"]}' } +export type Router = { 'debug': { openDevTools: () => Promise, isInDev: () => Promise, -getPlatform: () => Promise, getArch: () => Promise, getFamily: () => Promise, getLocale: () => Promise, getType: () => Promise, +getPlatform: () => Promise, getVersion: () => Promise, getCommitHash: () => Promise, getBuildTimestamp: () => Promise }, +'events': { ingress: (event: IngressPayload) => Promise, +message: (event: MessagePayload) => Promise, +process: (event: ProcessPayload) => Promise }, 'oneclient': { getClustersGroupedByMajor: () => Promise>, getBundlesFor: (clusterId: number) => Promise, getVersions: () => Promise }, +'folders': { fromCluster: (folderName: string) => Promise, +openCluster: (folderName: string) => Promise }, 'core': { getClusters: () => Promise, getClusterById: (id: number) => Promise, removeCluster: (id: number) => Promise, diff --git a/apps/onelauncher/desktop/capabilities/default.json b/apps/onelauncher/desktop/capabilities/default.json index 9c9bb6a1..232bc50b 100644 --- a/apps/onelauncher/desktop/capabilities/default.json +++ b/apps/onelauncher/desktop/capabilities/default.json @@ -46,6 +46,13 @@ "opener:allow-open-url", "opener:default", - "os:default" + "os:allow-arch", + "os:deny-exe-extension", + "os:allow-family", + "os:deny-hostname", + "os:allow-locale", + "os:allow-os-type", + "os:allow-platform", + "os:allow-version" ] } diff --git a/packages/core/src/api/tauri/debug.rs b/packages/core/src/api/tauri/debug.rs index d3faddda..b17f7179 100644 --- a/packages/core/src/api/tauri/debug.rs +++ b/packages/core/src/api/tauri/debug.rs @@ -11,9 +11,6 @@ pub trait TauriLauncherDebugApi { #[taurpc(alias = "isInDev")] async fn is_in_dev() -> bool; - #[taurpc(alias = "getPlatform")] - async fn get_platform() -> String; - #[taurpc(alias = "getArch")] async fn get_arch() -> String; @@ -26,6 +23,9 @@ pub trait TauriLauncherDebugApi { #[taurpc(alias = "getType")] async fn get_type() -> String; + #[taurpc(alias = "getPlatform")] + async fn get_platform() -> String; + #[taurpc(alias = "getVersion")] async fn get_version() -> String; @@ -49,10 +49,6 @@ impl TauriLauncherDebugApi for TauriLauncherDebugApiImpl { tauri::is_dev() } - async fn get_platform(self) -> String { - platform().to_string() - } - async fn get_arch(self) -> String { arch().to_string() } @@ -69,6 +65,10 @@ impl TauriLauncherDebugApi for TauriLauncherDebugApiImpl { type_().to_string() } + async fn get_platform(self) -> String { + platform().to_string() + } + async fn get_version(self) -> String { version().to_string() } From 00c9d383a27e2bbd44d6c884e71b867c0e8a10f5 Mon Sep 17 00:00:00 2001 From: Jacob Date: Sat, 6 Dec 2025 21:00:09 +0800 Subject: [PATCH 09/17] fix(core): debug get_commit_hash --- packages/core/src/api/tauri/debug.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/core/src/api/tauri/debug.rs b/packages/core/src/api/tauri/debug.rs index b17f7179..0ce58a7f 100644 --- a/packages/core/src/api/tauri/debug.rs +++ b/packages/core/src/api/tauri/debug.rs @@ -74,12 +74,8 @@ impl TauriLauncherDebugApi for TauriLauncherDebugApiImpl { } async fn get_commit_hash(self) -> LauncherResult { - if tauri::is_dev() { - let hash = std::env::var("GIT_HASH").map_err(anyhow::Error::from)?; - Ok(hash) - } else { - Ok("null".to_string()) - } + let hash = std::env::var("GIT_HASH").map_err(anyhow::Error::from)?; + Ok(hash) } async fn get_build_timestamp(self) -> LauncherResult { From 242434414752dd6e914c2f57d629133d6b54b7bc Mon Sep 17 00:00:00 2001 From: Jacob Date: Mon, 8 Dec 2025 11:46:14 +0800 Subject: [PATCH 10/17] fix(oneclient-NoAccountPopup): No longer opens a second popup when asking for a login --- .../components/overlay/AddAccountModal.tsx | 30 +++++++++++-------- .../src/components/overlay/NoAccountPopup.tsx | 14 ++------- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/apps/oneclient/frontend/src/components/overlay/AddAccountModal.tsx b/apps/oneclient/frontend/src/components/overlay/AddAccountModal.tsx index a1b0402b..e8cc62ff 100644 --- a/apps/oneclient/frontend/src/components/overlay/AddAccountModal.tsx +++ b/apps/oneclient/frontend/src/components/overlay/AddAccountModal.tsx @@ -5,6 +5,16 @@ import { useCommandMut } from '@onelauncher/common'; import { useQueryClient } from '@tanstack/react-query'; export function AddAccountModal() { + return ( + + Add Account +

Add a new account to OneClient

+ +
+ ); +} + +export function AddAccountModalButton() { const queryClient = useQueryClient(); const { data: profile, isPending, mutate: login } = useCommandMut(bindings.core.openMsaLogin, { @@ -23,18 +33,14 @@ export function AddAccountModal() { }; return ( - - Add Account -

Add a new account to OneClient

- {profile - ? ( - <> - - - - ) - : } -
+ profile + ? ( + <> + + + + ) + : ); } diff --git a/apps/oneclient/frontend/src/components/overlay/NoAccountPopup.tsx b/apps/oneclient/frontend/src/components/overlay/NoAccountPopup.tsx index 13f99dab..7bb814e9 100644 --- a/apps/oneclient/frontend/src/components/overlay/NoAccountPopup.tsx +++ b/apps/oneclient/frontend/src/components/overlay/NoAccountPopup.tsx @@ -1,21 +1,11 @@ -import { AddAccountModal, Overlay } from '@/components'; -import { Button } from '@onelauncher/common/components'; +import { AddAccountModalButton, Overlay } from '@/components'; export function NoAccountPopup() { return ( No Account

Please add an account before you start minecraft

- - - - - - - - +
); } From 426569e133c8aef22857a3acdaccfd1b921dbcb3 Mon Sep 17 00:00:00 2001 From: Jacob Date: Mon, 8 Dec 2025 11:56:30 +0800 Subject: [PATCH 11/17] feat(oneclient): Discord RPC closes: https://github.com/Polyfrost/OneLauncher/issues/391 --- apps/oneclient/desktop/src/constants.rs | 2 +- apps/oneclient/desktop/src/lib.rs | 2 +- apps/oneclient/frontend/src/bindings.gen.ts | 43 ++++++++-------- apps/oneclient/frontend/src/routes/__root.tsx | 51 ++++++++++++++++++- packages/core/src/api/tauri/commands.rs | 22 ++++++++ packages/core/src/store/settings.rs | 2 +- 6 files changed, 97 insertions(+), 25 deletions(-) diff --git a/apps/oneclient/desktop/src/constants.rs b/apps/oneclient/desktop/src/constants.rs index 8a278c20..75cec7c2 100644 --- a/apps/oneclient/desktop/src/constants.rs +++ b/apps/oneclient/desktop/src/constants.rs @@ -1,7 +1,7 @@ // === API === pub const CURSEFORGE_API_KEY: &'static str = "$2a$10$6utA1UNSmFPrE/Lh7b7ndeeGmiOkjKNY8kpFB0fsmE/d42ZAfFgCe"; -pub const DISCORD_CLIENT_ID: &'static str = "1274084193193955408"; +pub const DISCORD_CLIENT_ID: &'static str = "1426999264633946334"; // === Meta === // Cannot contain trailing slash diff --git a/apps/oneclient/desktop/src/lib.rs b/apps/oneclient/desktop/src/lib.rs index 3f329ac2..399747f1 100644 --- a/apps/oneclient/desktop/src/lib.rs +++ b/apps/oneclient/desktop/src/lib.rs @@ -33,7 +33,7 @@ async fn initialize_core() -> LauncherResult<()> { launcher_name: "OneClient".to_string(), launcher_version: env!("CARGO_PKG_VERSION").to_string(), launcher_website: "https://polyfrost.org/".to_string(), - discord_client_id: None, //Some(constants::DISCORD_CLIENT_ID.to_string()), // disabled for now + discord_client_id: Some(constants::DISCORD_CLIENT_ID.to_string()), fetch_attempts: 3, logger_filter: Some(format!( "{}={level},onelauncher_core={level}", diff --git a/apps/oneclient/frontend/src/bindings.gen.ts b/apps/oneclient/frontend/src/bindings.gen.ts index d81a202b..1f4fb23a 100644 --- a/apps/oneclient/frontend/src/bindings.gen.ts +++ b/apps/oneclient/frontend/src/bindings.gen.ts @@ -301,26 +301,8 @@ export type VersionType = */ "old_beta" -const ARGS_MAP = { 'events':'{"message":["event"],"ingress":["event"],"process":["event"]}', 'oneclient':'{"getVersions":[],"getBundlesFor":["cluster_id"],"getClustersGroupedByMajor":[]}', 'debug':'{"getArch":[],"getVersion":[],"getFamily":[],"getCommitHash":[],"getBuildTimestamp":[],"getLocale":[],"openDevTools":[],"isInDev":[],"getType":[],"getPlatform":[]}', 'core':'{"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"fetchLoggedInProfile":["access_token"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"removeCape":["access_token"],"getLogs":["id"],"getUsers":[],"getGameVersions":[],"readSettings":[],"getPackage":["provider","slug"],"fetchMinecraftProfile":["uuid"],"removeCluster":["id"],"writeSettings":["setting"],"updateClusterById":["id","request"],"getPackageBody":["provider","body"],"convertUsernameUUID":["username_uuid"],"open":["input"],"isClusterRunning":["cluster_id"],"getRunningProcessesByClusterId":["cluster_id"],"getScreenshots":["id"],"removeUser":["uuid"],"installModpack":["modpack","cluster_id"],"getUsersFromAuthor":["provider","author"],"changeCape":["access_token","cape_uuid"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"changeSkin":["access_token","skin_url","skin_variant"],"getClusters":[],"launchCluster":["id","uuid"],"createCluster":["options"],"getProfileOrDefault":["name"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"openMsaLogin":[],"getWorlds":["id"],"getLogByName":["id","name"],"getRunningProcesses":[],"setDefaultUser":["uuid"],"getGlobalProfile":[],"getClusterById":["id"],"getLoadersForVersion":["mc_version"],"createSettingsProfile":["name"],"searchPackages":["provider","query"],"killProcess":["pid"],"getDefaultUser":["fallback"],"updateClusterProfile":["name","profile"],"getUser":["uuid"],"getMultiplePackages":["provider","slugs"]}', 'folders':'{"fromCluster":["folder_name"],"openCluster":["folder_name"]}' } -export type Router = { 'debug': { openDevTools: () => Promise, -isInDev: () => Promise, -getArch: () => Promise, -getFamily: () => Promise, -getLocale: () => Promise, -getType: () => Promise, -getPlatform: () => Promise, -getVersion: () => Promise, -getCommitHash: () => Promise, -getBuildTimestamp: () => Promise }, -'events': { ingress: (event: IngressPayload) => Promise, -message: (event: MessagePayload) => Promise, -process: (event: ProcessPayload) => Promise }, -'oneclient': { getClustersGroupedByMajor: () => Promise>, -getBundlesFor: (clusterId: number) => Promise, -getVersions: () => Promise }, -'folders': { fromCluster: (folderName: string) => Promise, -openCluster: (folderName: string) => Promise }, -'core': { getClusters: () => Promise, +const ARGS_MAP = { 'folders':'{"openCluster":["folder_name"],"fromCluster":["folder_name"]}', 'oneclient':'{"getBundlesFor":["cluster_id"],"getClustersGroupedByMajor":[],"getVersions":[]}', 'core':'{"getGameVersions":[],"createCluster":["options"],"getWorlds":["id"],"openMsaLogin":[],"readSettings":[],"killProcess":["pid"],"getPackage":["provider","slug"],"removeCape":["access_token"],"launchCluster":["id","uuid"],"getLogs":["id"],"createSettingsProfile":["name"],"removeUser":["uuid"],"getPackageBody":["provider","body"],"installModpack":["modpack","cluster_id"],"removeCluster":["id"],"getGlobalProfile":[],"setDiscordRPCMessage":["message"],"getProfileOrDefault":["name"],"getClusterById":["id"],"setDefaultUser":["uuid"],"getScreenshots":["id"],"getLogByName":["id","name"],"getRunningProcessesByClusterId":["cluster_id"],"getUser":["uuid"],"getRunningProcesses":[],"getLoadersForVersion":["mc_version"],"searchPackages":["provider","query"],"getUsersFromAuthor":["provider","author"],"fetchMinecraftProfile":["uuid"],"fetchLoggedInProfile":["access_token"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"changeSkin":["access_token","skin_url","skin_variant"],"changeCape":["access_token","cape_uuid"],"convertUsernameUUID":["username_uuid"],"getDefaultUser":["fallback"],"isClusterRunning":["cluster_id"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"updateClusterProfile":["name","profile"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"writeSettings":["setting"],"open":["input"],"getMultiplePackages":["provider","slugs"],"updateClusterById":["id","request"],"getClusters":[],"getUsers":[],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"]}', 'events':'{"ingress":["event"],"message":["event"],"process":["event"]}', 'debug':'{"openDevTools":[],"getFamily":[],"getType":[],"getPlatform":[],"getBuildTimestamp":[],"getCommitHash":[],"getLocale":[],"getVersion":[],"isInDev":[],"getArch":[]}' } +export type Router = { 'core': { getClusters: () => Promise, getClusterById: (id: number) => Promise, removeCluster: (id: number) => Promise, createCluster: (options: CreateCluster) => Promise, @@ -364,7 +346,26 @@ changeSkin: (accessToken: string, skinUrl: string, skinVariant: SkinVariant) => changeCape: (accessToken: string, capeUuid: string) => Promise, removeCape: (accessToken: string) => Promise, convertUsernameUUID: (usernameUuid: string) => Promise, -open: (input: string) => Promise } }; +setDiscordRPCMessage: (message: string) => Promise, +open: (input: string) => Promise }, +'oneclient': { getClustersGroupedByMajor: () => Promise>, +getBundlesFor: (clusterId: number) => Promise, +getVersions: () => Promise }, +'folders': { fromCluster: (folderName: string) => Promise, +openCluster: (folderName: string) => Promise }, +'debug': { openDevTools: () => Promise, +isInDev: () => Promise, +getArch: () => Promise, +getFamily: () => Promise, +getLocale: () => Promise, +getType: () => Promise, +getPlatform: () => Promise, +getVersion: () => Promise, +getCommitHash: () => Promise, +getBuildTimestamp: () => Promise }, +'events': { ingress: (event: IngressPayload) => Promise, +message: (event: MessagePayload) => Promise, +process: (event: ProcessPayload) => Promise } }; export type { InferCommandOutput } diff --git a/apps/oneclient/frontend/src/routes/__root.tsx b/apps/oneclient/frontend/src/routes/__root.tsx index 9346c88b..7cdaf29f 100644 --- a/apps/oneclient/frontend/src/routes/__root.tsx +++ b/apps/oneclient/frontend/src/routes/__root.tsx @@ -2,10 +2,13 @@ import type { QueryClient } from '@tanstack/react-query'; import type { NavigateOptions, ToOptions } from '@tanstack/react-router'; import { Toasts } from '@/components'; import { useSettings } from '@/hooks/useSettings'; +import { bindings } from '@/main'; +import { useCommand } from '@onelauncher/common'; import { TanStackDevtools } from '@tanstack/react-devtools'; import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools'; -import { createRootRouteWithContext, Outlet, useRouter } from '@tanstack/react-router'; +import { createRootRouteWithContext, Outlet, useLocation, useRouter } from '@tanstack/react-router'; import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools'; +import { useEffect } from 'react'; import { RouterProvider } from 'react-aria-components'; interface AppRouterContext { @@ -23,9 +26,55 @@ export const Route = createRootRouteWithContext()({ component: RootRoute, }); +type URLPath = Exclude; +const ResolvedPathNames: Record = { + '.': 'UNKNOWN', + '..': 'UNKNOWN', + '/': 'Viewing Home', + '/app': 'Viewing Homepage', + '/app/account': 'Viewing Account', + '/app/account/skins': 'Viewing Skin Manager', + '/app/cluster': 'Viewing Clusters', + '/app/cluster/browser': 'Viewing {clusterName}\'s mods', + '/app/cluster/browser/package': 'Browsing {packageName}', + '/app/cluster/logs': 'Viewing {clusterName}\'s logs', + '/app/cluster/mods': 'Viewing {clusterName}\'s mods', + '/app/cluster/overview': 'Viewing {clusterName}', + '/app/cluster/process': 'Viewing {clusterName}', + '/app/cluster/settings': 'Viewing {clusterName}\'s settings', + '/app/settings': 'Viewing Settings', + '/app/settings/appearance': 'Viewing Settings', + '/app/settings/developer': 'Viewing Settings', + '/app/settings/minecraft': 'Viewing Settings', + '/app/accounts': 'Viewing Accounts', + '/app/clusters': 'Viewing Clusters', + '/onboarding': 'Preparing OneClient', + '/onboarding/account': 'Preparing OneClient', + '/onboarding/finished': 'Preparing OneClient', + '/onboarding/language': 'Preparing OneClient', + '/onboarding/preferences/version': 'Preparing OneClient', + '/onboarding/preferences/versionCategory': 'Preparing OneClient', + '/onboarding/preferences': 'Preparing OneClient', +}; + +// Credit - https://github.com/DuckySoLucky/hypixel-discord-chat-bridge/blob/d3ea84a26ebf094c8191d50b4954549e2dd4dc7f/src/contracts/helperFunctions.js#L216-L225 +function ReplaceVariables(template: string, variables: Record) { + return template.replace(/\{(\w+)\}/g, (match: any, name: string | number) => variables[name] ?? match); +} + +function useDiscordRPC() { + const location = useLocation(); + const { data: cluster } = useCommand(['getClusterById'], () => bindings.core.getClusterById(location.search.clusterId ?? -1)); + const { data: managedPackage } = useCommand(['getPackage'], () => bindings.core.getPackage(location.search.provider ?? 'Modrinth', location.search.packageId ?? '8pJYUDNi')); + useEffect(() => { + bindings.core.setDiscordRPCMessage(ReplaceVariables(ResolvedPathNames[location.pathname as URLPath], { clusterName: cluster?.name ?? 'UNKNOWN', packageName: managedPackage?.name ?? 'UNKNOWN' })); + }, [location.pathname, location.search.clusterId, cluster?.name, managedPackage?.name]); +} + function RootRoute() { const router = useRouter(); const { setting } = useSettings(); + useDiscordRPC(); return ( router.navigate({ to, ...options })} diff --git a/packages/core/src/api/tauri/commands.rs b/packages/core/src/api/tauri/commands.rs index e02c990e..719ae70a 100644 --- a/packages/core/src/api/tauri/commands.rs +++ b/packages/core/src/api/tauri/commands.rs @@ -248,6 +248,12 @@ pub trait TauriLauncherApi { ) -> LauncherResult; // endregion: minecraft + // MARK: API: discord RPC + // region: discord RPC + #[taurpc(alias = "setDiscordRPCMessage")] + async fn set_discord_rpc_message(message: String) -> LauncherResult<()>; + // endregion: discord RPC + // MARK: API: Other async fn open(input: String) -> LauncherResult<()>; } @@ -815,6 +821,22 @@ impl TauriLauncherApi for TauriLauncherApiImpl { } // endregion: minecraft + // MARK: Impl: discord RPC + // region: discord RPC + async fn set_discord_rpc_message(self, message: String) -> LauncherResult<()> { + let state = State::get().await?; + if let Some(discord) = &state.rpc { + if !state.settings.read().await.discord_enabled { + discord.clear_activity().await; + return Ok(()); + } + discord.set_message(&message, None).await; + } + + Ok(()) + } + // endregion: discord RPC + // MARK: Impl: Other // region: other async fn open(self, input: String) -> LauncherResult<()> { diff --git a/packages/core/src/store/settings.rs b/packages/core/src/store/settings.rs index c067e366..4ee4b53e 100644 --- a/packages/core/src/store/settings.rs +++ b/packages/core/src/store/settings.rs @@ -30,7 +30,7 @@ impl Default for Settings { Self { global_game_settings: setting_profiles::Model::default_global_profile(), allow_parallel_running_clusters: false, - discord_enabled: false, + discord_enabled: true, seen_onboarding: false, mod_list_use_grid: true, parallel_mod_downloading: true, From c25ff9e15325405505f35ba1b81e9f5b852504ac Mon Sep 17 00:00:00 2001 From: Jacob Date: Mon, 8 Dec 2025 12:02:00 +0800 Subject: [PATCH 12/17] chore(oneclient): formatting --- .../src/components/overlay/AddAccountModal.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/oneclient/frontend/src/components/overlay/AddAccountModal.tsx b/apps/oneclient/frontend/src/components/overlay/AddAccountModal.tsx index e8cc62ff..badab9fb 100644 --- a/apps/oneclient/frontend/src/components/overlay/AddAccountModal.tsx +++ b/apps/oneclient/frontend/src/components/overlay/AddAccountModal.tsx @@ -35,11 +35,11 @@ export function AddAccountModalButton() { return ( profile ? ( - <> - - - - ) + <> + + + + ) : ); } From c73d25da0621edddee83868fc01adfeeb0984e99 Mon Sep 17 00:00:00 2001 From: Jacob Date: Wed, 10 Dec 2025 15:57:17 +0800 Subject: [PATCH 13/17] fix(core): debug data not working with a built version Closes: https://github.com/Polyfrost/OneLauncher/issues/425 --- Cargo.lock | 122 +++++++++++++++++- Cargo.toml | 1 + apps/oneclient/desktop/build.rs | 17 --- apps/oneclient/frontend/src/bindings.gen.ts | 13 +- .../src/components/overlay/DebugInfo.tsx | 26 ++-- apps/onelauncher/desktop/build.rs | 17 --- packages/core/Cargo.toml | 4 + packages/core/build.rs | 5 + packages/core/src/api/tauri/debug.rs | 27 ++-- packages/core/src/lib.rs | 3 + 10 files changed, 172 insertions(+), 63 deletions(-) create mode 100644 packages/core/build.rs diff --git a/Cargo.lock b/Cargo.lock index 11d3abcc..bd59fa95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -971,6 +971,26 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "const_format" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -1275,7 +1295,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2041,6 +2061,19 @@ dependencies = [ "winapi", ] +[[package]] +name = "git2" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fda788993cc341f69012feba8bf45c0ba4f3291fcc08e214b4d5a7332d88aff" +dependencies = [ + "bitflags 2.6.0", + "libc", + "libgit2-sys", + "log", + "url", +] + [[package]] name = "glib" version = "0.18.5" @@ -2561,6 +2594,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "is_debug" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fe266d2e243c931d8190177f20bf7f24eed45e96f39e87dc49a27b32d12d407" + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -2742,6 +2781,18 @@ version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +[[package]] +name = "libgit2-sys" +version = "0.18.3+1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9b3acc4b91781bb0b3386669d325163746af5f6e4f73e6d2d630e09a35f3487" +dependencies = [ + "cc", + "libc", + "libz-sys", + "pkg-config", +] + [[package]] name = "libloading" version = "0.7.4" @@ -2800,6 +2851,18 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "libz-sys" +version = "1.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -3176,6 +3239,15 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + [[package]] name = "number_prefix" version = "0.4.0" @@ -3527,6 +3599,7 @@ dependencies = [ "serde_with", "sha1", "sha2", + "shadow-rs", "specta", "sysinfo", "tauri", @@ -5262,6 +5335,19 @@ dependencies = [ "digest", ] +[[package]] +name = "shadow-rs" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d18183cef626bce22836103349c7050d73db799be0171386b80947d157ae32" +dependencies = [ + "const_format", + "git2", + "is_debug", + "time", + "tzdb", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -6504,7 +6590,9 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa 1.0.11", + "libc", "num-conv", + "num_threads", "powerfmt", "serde", "time-core", @@ -6830,6 +6918,32 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "tz-rs" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14eff19b8dc1ace5bf7e4d920b2628ae3837f422ff42210cb1567cbf68b5accf" + +[[package]] +name = "tzdb" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0be2ea5956f295449f47c0b825c5e109022ff1a6a53bb4f77682a87c2341fbf5" +dependencies = [ + "iana-time-zone", + "tz-rs", + "tzdb_data", +] + +[[package]] +name = "tzdb_data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c4c81d75033770e40fbd3643ce7472a1a9fd301f90b7139038228daf8af03ec" +dependencies = [ + "tz-rs", +] + [[package]] name = "uds_windows" version = "1.1.0" @@ -6930,6 +7044,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "untrusted" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index fb99741e..ac04216b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -173,6 +173,7 @@ sysinfo = { version = "=0.34.2", features = [ "serde" ] } thiserror = { version = "2" } whoami = { version = "=1.6.0" } opener = { version = "=0.8.2" } +shadow-rs = { version = "1.4.0", default-features = false } # macro paste = { version = "1" } diff --git a/apps/oneclient/desktop/build.rs b/apps/oneclient/desktop/build.rs index 9f7658d7..106b7123 100644 --- a/apps/oneclient/desktop/build.rs +++ b/apps/oneclient/desktop/build.rs @@ -1,20 +1,3 @@ -use std::process::Command; -use std::time::{SystemTime, UNIX_EPOCH}; - fn main() { - let output = Command::new("git") - .args(&["rev-parse", "HEAD"]) - .output() - .unwrap(); - let git_hash = String::from_utf8(output.stdout).unwrap(); - println!("cargo:rustc-env=GIT_HASH={}", git_hash); - - let start = SystemTime::now(); - let timestamp = start - .duration_since(UNIX_EPOCH) - .expect("Time went backwards") - .as_secs(); - println!("cargo:rustc-env=BUILD_TIMESTAMP={}", timestamp); - tauri_build::build(); } diff --git a/apps/oneclient/frontend/src/bindings.gen.ts b/apps/oneclient/frontend/src/bindings.gen.ts index 118a2ed3..9ee20903 100644 --- a/apps/oneclient/frontend/src/bindings.gen.ts +++ b/apps/oneclient/frontend/src/bindings.gen.ts @@ -301,10 +301,12 @@ export type VersionType = */ "old_beta" -const ARGS_MAP = { 'debug':'{"isInDev":[],"openDevTools":[],"getVersion":[],"getFamily":[],"getPlatform":[],"getLocale":[],"getCommitHash":[],"getBuildTimestamp":[],"getType":[],"getArch":[]}', 'events':'{"message":["event"],"ingress":["event"],"process":["event"]}', 'oneclient':'{"getClustersGroupedByMajor":[],"getBundlesFor":["cluster_id"],"getVersions":[]}', 'core':'{"getLoadersForVersion":["mc_version"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"convertUsernameUUID":["username_uuid"],"getUsers":[],"setDefaultUser":["uuid"],"getUsersFromAuthor":["provider","author"],"open":["input"],"createCluster":["options"],"getClusters":[],"createSettingsProfile":["name"],"openMsaLogin":[],"removeUser":["uuid"],"getPackageBody":["provider","body"],"changeCape":["access_token","cape_uuid"],"getScreenshots":["id"],"getMultiplePackages":["provider","slugs"],"getLogs":["id"],"fetchLoggedInProfile":["access_token"],"getClusterById":["id"],"getDefaultUser":["fallback"],"writeSettings":["setting"],"removeCape":["access_token"],"setDiscordRPCMessage":["message"],"getWorlds":["id"],"getProfileOrDefault":["name"],"getGlobalProfile":[],"updateClusterProfile":["name","profile"],"readSettings":[],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"killProcess":["pid"],"getGameVersions":[],"isClusterRunning":["cluster_id"],"getRunningProcesses":[],"getLogByName":["id","name"],"launchCluster":["id","uuid"],"getPackage":["provider","slug"],"fetchMinecraftProfile":["uuid"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"removeCluster":["id"],"installModpack":["modpack","cluster_id"],"getUser":["uuid"],"getRunningProcessesByClusterId":["cluster_id"],"searchPackages":["provider","query"],"updateClusterById":["id","request"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"changeSkin":["access_token","skin_url","skin_variant"]}', 'folders':'{"openCluster":["folder_name"],"fromCluster":["folder_name"]}' } +const ARGS_MAP = { 'folders':'{"openCluster":["folder_name"],"fromCluster":["folder_name"]}', 'core':'{"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"changeSkin":["access_token","skin_url","skin_variant"],"getRunningProcessesByClusterId":["cluster_id"],"getWorlds":["id"],"getLogByName":["id","name"],"getUsers":[],"convertUsernameUUID":["username_uuid"],"setDiscordRPCMessage":["message"],"open":["input"],"getMultiplePackages":["provider","slugs"],"getProfileOrDefault":["name"],"createCluster":["options"],"killProcess":["pid"],"fetchMinecraftProfile":["uuid"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"updateClusterProfile":["name","profile"],"removeCluster":["id"],"getGlobalProfile":[],"createSettingsProfile":["name"],"removeUser":["uuid"],"getLoadersForVersion":["mc_version"],"getScreenshots":["id"],"getDefaultUser":["fallback"],"updateClusterById":["id","request"],"getClusters":[],"isClusterRunning":["cluster_id"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"getRunningProcesses":[],"getLogs":["id"],"launchCluster":["id","uuid"],"readSettings":[],"getPackage":["provider","slug"],"writeSettings":["setting"],"getPackageBody":["provider","body"],"getClusterById":["id"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"installModpack":["modpack","cluster_id"],"changeCape":["access_token","cape_uuid"],"removeCape":["access_token"],"getUser":["uuid"],"setDefaultUser":["uuid"],"openMsaLogin":[],"searchPackages":["provider","query"],"getGameVersions":[],"fetchLoggedInProfile":["access_token"],"getUsersFromAuthor":["provider","author"]}', 'oneclient':'{"getBundlesFor":["cluster_id"],"getVersions":[],"getClustersGroupedByMajor":[]}', 'events':'{"process":["event"],"ingress":["event"],"message":["event"]}', 'debug':'{"getLocale":[],"getOsVersion":[],"getBuildTimestamp":[],"openDevTools":[],"getPackageVersion":[],"getArch":[],"getGitCommitHash":[],"getType":[],"getFamily":[],"isInDev":[],"getPlatform":[]}' } export type Router = { 'oneclient': { getClustersGroupedByMajor: () => Promise>, getBundlesFor: (clusterId: number) => Promise, getVersions: () => Promise }, +'folders': { fromCluster: (folderName: string) => Promise, +openCluster: (folderName: string) => Promise }, 'debug': { openDevTools: () => Promise, isInDev: () => Promise, getArch: () => Promise, @@ -312,11 +314,10 @@ getFamily: () => Promise, getLocale: () => Promise, getType: () => Promise, getPlatform: () => Promise, -getVersion: () => Promise, -getCommitHash: () => Promise, -getBuildTimestamp: () => Promise }, -'folders': { fromCluster: (folderName: string) => Promise, -openCluster: (folderName: string) => Promise }, +getOsVersion: () => Promise, +getGitCommitHash: () => Promise, +getBuildTimestamp: () => Promise, +getPackageVersion: () => Promise }, 'events': { ingress: (event: IngressPayload) => Promise, message: (event: MessagePayload) => Promise, process: (event: ProcessPayload) => Promise }, diff --git a/apps/oneclient/frontend/src/components/overlay/DebugInfo.tsx b/apps/oneclient/frontend/src/components/overlay/DebugInfo.tsx index beeba96c..54a5882b 100644 --- a/apps/oneclient/frontend/src/components/overlay/DebugInfo.tsx +++ b/apps/oneclient/frontend/src/components/overlay/DebugInfo.tsx @@ -12,9 +12,10 @@ export interface DebugInfoData { family: string; locale: string; type: string; - version: string; + osVersion: string; commitHash: string; buildTimestamp: string; + buildVersion: string; } export function useDebugInfo(): DebugInfoArray { @@ -25,9 +26,10 @@ export function useDebugInfo(): DebugInfoArray { family: 'UNKNOWN', locale: 'UNKNOWN', type: 'UNKNOWN', - version: 'UNKNOWN', + osVersion: 'UNKNOWN', commitHash: 'UNKNOWN', buildTimestamp: 'UNKNOWN', + buildVersion: 'UNKNOWN', }); useEffect(() => { @@ -39,9 +41,10 @@ export function useDebugInfo(): DebugInfoArray { family, locale, type, - version, + osVersion, commitHash, buildTimestamp, + buildVersion, ] = await Promise.all([ bindings.debug.isInDev(), bindings.debug.getPlatform(), @@ -49,9 +52,10 @@ export function useDebugInfo(): DebugInfoArray { bindings.debug.getFamily(), bindings.debug.getLocale(), bindings.debug.getType(), - bindings.debug.getVersion(), - bindings.debug.getCommitHash(), + bindings.debug.getOsVersion(), + bindings.debug.getGitCommitHash(), bindings.debug.getBuildTimestamp(), + bindings.debug.getPackageVersion(), ]); setDevInfo({ @@ -61,9 +65,10 @@ export function useDebugInfo(): DebugInfoArray { family, locale: locale ?? 'UNKNOWN', type, - version, + osVersion, commitHash, - buildTimestamp, + buildTimestamp: new Date(buildTimestamp).getTime().toString(), + buildVersion, }); }; @@ -77,9 +82,10 @@ export function useDebugInfo(): DebugInfoArray { { title: 'Family', value: devInfo.family }, { title: 'Locale', value: devInfo.locale }, { title: 'Type', value: devInfo.type }, - { title: 'Version', value: devInfo.version }, + { title: 'Os Version', value: devInfo.osVersion }, { title: 'Commit Hash', value: devInfo.commitHash }, { title: 'Build Timestamp', value: devInfo.buildTimestamp }, + { title: 'Version', value: devInfo.buildVersion }, ]; } @@ -87,7 +93,7 @@ export function copyDebugInfo(debugInfo: DebugInfoArray) { const timestamp = Math.floor(new Date().getTime() / 1000); const lines = [`**Data exported at:** (\`${timestamp}\`)`, ...debugInfo.map((lineData) => { if (lineData.title === 'Build Timestamp') - return `**${lineData.title}:** (\`${lineData.value}\`)`; + return `**${lineData.title}:** (\`${Math.floor(Number(lineData.value) / 1000)}\`)`; return `**${lineData.title}:** \`${lineData.value}\``; })]; writeText(lines.join('\n')); @@ -111,7 +117,7 @@ export function RawDebugInfo({ debugInfo }: { debugInfo: DebugInfoArray }) { {debugInfo.map((lineData) => { let line = ''; if (lineData.title === 'Build Timestamp') - line = `${lineData.title}: ${new Date(Number(lineData.value) * 1000)}`; + line = `${lineData.title}: ${new Date(Number(lineData.value))}`; else line = `${lineData.title}: ${lineData.value}`; return

{line}

; diff --git a/apps/onelauncher/desktop/build.rs b/apps/onelauncher/desktop/build.rs index 9f7658d7..106b7123 100644 --- a/apps/onelauncher/desktop/build.rs +++ b/apps/onelauncher/desktop/build.rs @@ -1,20 +1,3 @@ -use std::process::Command; -use std::time::{SystemTime, UNIX_EPOCH}; - fn main() { - let output = Command::new("git") - .args(&["rev-parse", "HEAD"]) - .output() - .unwrap(); - let git_hash = String::from_utf8(output.stdout).unwrap(); - println!("cargo:rustc-env=GIT_HASH={}", git_hash); - - let start = SystemTime::now(); - let timestamp = start - .duration_since(UNIX_EPOCH) - .expect("Time went backwards") - .as_secs(); - println!("cargo:rustc-env=BUILD_TIMESTAMP={}", timestamp); - tauri_build::build(); } diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index 58477635..996ecd0b 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -32,6 +32,9 @@ gui = [] [lints] workspace = true +[build-dependencies] +shadow-rs = { workspace = true, features = ["default"] } + [dependencies] # core interpulse = { workspace = true } @@ -74,6 +77,7 @@ paste = { workspace = true } byteorder = { workspace = true } merge = { workspace = true } opener = { workspace = true } +shadow-rs = { workspace = true } # integration discord-rich-presence = { workspace = true } diff --git a/packages/core/build.rs b/packages/core/build.rs new file mode 100644 index 00000000..f16b4ee9 --- /dev/null +++ b/packages/core/build.rs @@ -0,0 +1,5 @@ +use shadow_rs::ShadowBuilder; + +fn main() { + ShadowBuilder::builder().build().unwrap(); +} diff --git a/packages/core/src/api/tauri/debug.rs b/packages/core/src/api/tauri/debug.rs index 0ce58a7f..5a50ad13 100644 --- a/packages/core/src/api/tauri/debug.rs +++ b/packages/core/src/api/tauri/debug.rs @@ -1,8 +1,6 @@ use tauri::Runtime; use tauri_plugin_os::{arch, family, locale, platform, type_, version}; -use crate::error::LauncherResult; - #[taurpc::procedures(path = "debug")] pub trait TauriLauncherDebugApi { #[taurpc(alias = "openDevTools")] @@ -26,14 +24,17 @@ pub trait TauriLauncherDebugApi { #[taurpc(alias = "getPlatform")] async fn get_platform() -> String; - #[taurpc(alias = "getVersion")] + #[taurpc(alias = "getOsVersion")] async fn get_version() -> String; - #[taurpc(alias = "getCommitHash")] - async fn get_commit_hash() -> LauncherResult; + #[taurpc(alias = "getGitCommitHash")] + async fn get_git_commit_hash() -> String; #[taurpc(alias = "getBuildTimestamp")] - async fn get_build_timestamp() -> LauncherResult; + async fn get_build_timestamp() -> String; + + #[taurpc(alias = "getPackageVersion")] + async fn get_package_version() -> String; } #[taurpc::ipc_type] @@ -73,13 +74,15 @@ impl TauriLauncherDebugApi for TauriLauncherDebugApiImpl { version().to_string() } - async fn get_commit_hash(self) -> LauncherResult { - let hash = std::env::var("GIT_HASH").map_err(anyhow::Error::from)?; - Ok(hash) + async fn get_git_commit_hash(self) -> String { + crate::build::COMMIT_HASH.to_string() + } + + async fn get_build_timestamp(self) -> String { + crate::build::BUILD_TIME.to_string() } - async fn get_build_timestamp(self) -> LauncherResult { - let timestamp = std::env::var("BUILD_TIMESTAMP").map_err(anyhow::Error::from)?; - Ok(timestamp) + async fn get_package_version(self) -> String { + crate::build::PKG_VERSION.to_string() } } diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index 9bdd2e23..6dbd55e4 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -1,6 +1,9 @@ #![feature(slice_as_array)] #![allow(clippy::struct_excessive_bools)] +shadow_rs::shadow!(build); +pub use build::*; + use api::proxy::LauncherProxy; use error::LauncherResult; pub use logger::start_logger; From 03f632137c601cc4e8b0e74222e4871f2409fe9a Mon Sep 17 00:00:00 2001 From: Jacob Date: Wed, 10 Dec 2025 19:36:32 +0800 Subject: [PATCH 14/17] fix(oneclient): build --- apps/oneclient/frontend/src/routes/app/cluster/logs.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/oneclient/frontend/src/routes/app/cluster/logs.tsx b/apps/oneclient/frontend/src/routes/app/cluster/logs.tsx index 3b9335d3..8f3d8a5f 100644 --- a/apps/oneclient/frontend/src/routes/app/cluster/logs.tsx +++ b/apps/oneclient/frontend/src/routes/app/cluster/logs.tsx @@ -16,7 +16,7 @@ function RouteComponent() { const { data: fileNames } = useCommandSuspense(['getLogs', cluster.id], () => bindings.core.getLogs(cluster.id)); - const [activeFileName, setActiveFileName] = useState(fileNames[0] || undefined); + const [activeFileName, setActiveFileName] = useState(fileNames[0] || null); return ( From f649d3a028fbb47421383a5857fdc8580e5289f1 Mon Sep 17 00:00:00 2001 From: Jacob Date: Fri, 12 Dec 2025 12:16:02 +0800 Subject: [PATCH 15/17] fix(oneclient): rpc not showing the cluster/package --- apps/oneclient/frontend/src/bindings.gen.ts | 38 +++++++++---------- apps/oneclient/frontend/src/routes/__root.tsx | 9 +++-- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/apps/oneclient/frontend/src/bindings.gen.ts b/apps/oneclient/frontend/src/bindings.gen.ts index 9ee20903..1d6e5aa8 100644 --- a/apps/oneclient/frontend/src/bindings.gen.ts +++ b/apps/oneclient/frontend/src/bindings.gen.ts @@ -301,26 +301,13 @@ export type VersionType = */ "old_beta" -const ARGS_MAP = { 'folders':'{"openCluster":["folder_name"],"fromCluster":["folder_name"]}', 'core':'{"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"changeSkin":["access_token","skin_url","skin_variant"],"getRunningProcessesByClusterId":["cluster_id"],"getWorlds":["id"],"getLogByName":["id","name"],"getUsers":[],"convertUsernameUUID":["username_uuid"],"setDiscordRPCMessage":["message"],"open":["input"],"getMultiplePackages":["provider","slugs"],"getProfileOrDefault":["name"],"createCluster":["options"],"killProcess":["pid"],"fetchMinecraftProfile":["uuid"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"updateClusterProfile":["name","profile"],"removeCluster":["id"],"getGlobalProfile":[],"createSettingsProfile":["name"],"removeUser":["uuid"],"getLoadersForVersion":["mc_version"],"getScreenshots":["id"],"getDefaultUser":["fallback"],"updateClusterById":["id","request"],"getClusters":[],"isClusterRunning":["cluster_id"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"getRunningProcesses":[],"getLogs":["id"],"launchCluster":["id","uuid"],"readSettings":[],"getPackage":["provider","slug"],"writeSettings":["setting"],"getPackageBody":["provider","body"],"getClusterById":["id"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"installModpack":["modpack","cluster_id"],"changeCape":["access_token","cape_uuid"],"removeCape":["access_token"],"getUser":["uuid"],"setDefaultUser":["uuid"],"openMsaLogin":[],"searchPackages":["provider","query"],"getGameVersions":[],"fetchLoggedInProfile":["access_token"],"getUsersFromAuthor":["provider","author"]}', 'oneclient':'{"getBundlesFor":["cluster_id"],"getVersions":[],"getClustersGroupedByMajor":[]}', 'events':'{"process":["event"],"ingress":["event"],"message":["event"]}', 'debug':'{"getLocale":[],"getOsVersion":[],"getBuildTimestamp":[],"openDevTools":[],"getPackageVersion":[],"getArch":[],"getGitCommitHash":[],"getType":[],"getFamily":[],"isInDev":[],"getPlatform":[]}' } -export type Router = { 'oneclient': { getClustersGroupedByMajor: () => Promise>, -getBundlesFor: (clusterId: number) => Promise, -getVersions: () => Promise }, -'folders': { fromCluster: (folderName: string) => Promise, -openCluster: (folderName: string) => Promise }, -'debug': { openDevTools: () => Promise, -isInDev: () => Promise, -getArch: () => Promise, -getFamily: () => Promise, -getLocale: () => Promise, -getType: () => Promise, -getPlatform: () => Promise, -getOsVersion: () => Promise, -getGitCommitHash: () => Promise, -getBuildTimestamp: () => Promise, -getPackageVersion: () => Promise }, -'events': { ingress: (event: IngressPayload) => Promise, +const ARGS_MAP = { 'events':'{"message":["event"],"process":["event"],"ingress":["event"]}', 'oneclient':'{"getVersions":[],"getBundlesFor":["cluster_id"],"getClustersGroupedByMajor":[]}', 'folders':'{"openCluster":["folder_name"],"fromCluster":["folder_name"]}', 'debug':'{"getBuildTimestamp":[],"getOsVersion":[],"getType":[],"getLocale":[],"getPlatform":[],"isInDev":[],"getFamily":[],"getPackageVersion":[],"getArch":[],"openDevTools":[],"getGitCommitHash":[]}', 'core':'{"killProcess":["pid"],"removeUser":["uuid"],"getWorlds":["id"],"getMultiplePackages":["provider","slugs"],"getPackageBody":["provider","body"],"setDiscordRPCMessage":["message"],"removeCluster":["id"],"getRunningProcessesByClusterId":["cluster_id"],"getLoadersForVersion":["mc_version"],"getRunningProcesses":[],"createSettingsProfile":["name"],"getPackage":["provider","slug"],"installModpack":["modpack","cluster_id"],"open":["input"],"getScreenshots":["id"],"setDefaultUser":["uuid"],"readSettings":[],"fetchLoggedInProfile":["access_token"],"convertUsernameUUID":["username_uuid"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"getProfileOrDefault":["name"],"writeSettings":["setting"],"getLogs":["id"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"updateClusterProfile":["name","profile"],"getLogByName":["id","name"],"getClusters":[],"getUsersFromAuthor":["provider","author"],"changeSkin":["access_token","skin_url","skin_variant"],"getGlobalProfile":[],"createCluster":["options"],"fetchMinecraftProfile":["uuid"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"changeCape":["access_token","cape_uuid"],"removeCape":["access_token"],"searchPackages":["provider","query"],"isClusterRunning":["cluster_id"],"getClusterById":["id"],"getDefaultUser":["fallback"],"getGameVersions":[],"openMsaLogin":[],"updateClusterById":["id","request"],"launchCluster":["id","uuid"],"getUsers":[],"getUser":["uuid"]}' } +export type Router = { 'events': { ingress: (event: IngressPayload) => Promise, message: (event: MessagePayload) => Promise, process: (event: ProcessPayload) => Promise }, +'oneclient': { getClustersGroupedByMajor: () => Promise>, +getBundlesFor: (clusterId: number) => Promise, +getVersions: () => Promise }, 'core': { getClusters: () => Promise, getClusterById: (id: number) => Promise, removeCluster: (id: number) => Promise, @@ -366,7 +353,20 @@ changeCape: (accessToken: string, capeUuid: string) => Promise Promise, convertUsernameUUID: (usernameUuid: string) => Promise, setDiscordRPCMessage: (message: string) => Promise, -open: (input: string) => Promise } }; +open: (input: string) => Promise }, +'folders': { fromCluster: (folderName: string) => Promise, +openCluster: (folderName: string) => Promise }, +'debug': { openDevTools: () => Promise, +isInDev: () => Promise, +getArch: () => Promise, +getFamily: () => Promise, +getLocale: () => Promise, +getType: () => Promise, +getPlatform: () => Promise, +getOsVersion: () => Promise, +getGitCommitHash: () => Promise, +getBuildTimestamp: () => Promise, +getPackageVersion: () => Promise } }; export type { InferCommandOutput } diff --git a/apps/oneclient/frontend/src/routes/__root.tsx b/apps/oneclient/frontend/src/routes/__root.tsx index 7cdaf29f..c21ca4b1 100644 --- a/apps/oneclient/frontend/src/routes/__root.tsx +++ b/apps/oneclient/frontend/src/routes/__root.tsx @@ -64,8 +64,11 @@ function ReplaceVariables(template: string, variables: Record) { function useDiscordRPC() { const location = useLocation(); - const { data: cluster } = useCommand(['getClusterById'], () => bindings.core.getClusterById(location.search.clusterId ?? -1)); - const { data: managedPackage } = useCommand(['getPackage'], () => bindings.core.getPackage(location.search.provider ?? 'Modrinth', location.search.packageId ?? '8pJYUDNi')); + const clusterId = location.search.clusterId ?? 0 + const provider = location.search.provider ?? 'Modrinth' + const packageId = location.search.packageId ?? '8pJYUDNi' + const { data: cluster } = useCommand(['getClusterById', clusterId], () => bindings.core.getClusterById(clusterId)); + const { data: managedPackage } = useCommand(['getPackage', provider, packageId], () => bindings.core.getPackage(provider, packageId)); useEffect(() => { bindings.core.setDiscordRPCMessage(ReplaceVariables(ResolvedPathNames[location.pathname as URLPath], { clusterName: cluster?.name ?? 'UNKNOWN', packageName: managedPackage?.name ?? 'UNKNOWN' })); }, [location.pathname, location.search.clusterId, cluster?.name, managedPackage?.name]); @@ -95,7 +98,7 @@ function DevTools() { return ( Date: Fri, 12 Dec 2025 19:55:51 +0800 Subject: [PATCH 16/17] feat(oneclient): Prompt user to install mods on clusters Closes: https://github.com/Polyfrost/OneLauncher/issues/422 --- apps/oneclient/frontend/src/bindings.gen.ts | 9 ++- .../frontend/src/components/LaunchButton.tsx | 73 +++++++++++++++---- .../frontend/src/hooks/useClusters.ts | 2 +- .../frontend/src/routes/app/cluster/route.tsx | 4 +- .../frontend/src/routes/app/clusters.tsx | 19 ++++- .../frontend/src/routes/app/index.tsx | 25 ++----- .../frontend/src/routes/app/route.tsx | 4 +- packages/core/src/api/tauri/commands.rs | 16 ++++ 8 files changed, 110 insertions(+), 42 deletions(-) diff --git a/apps/oneclient/frontend/src/bindings.gen.ts b/apps/oneclient/frontend/src/bindings.gen.ts index 1d6e5aa8..2ffe3630 100644 --- a/apps/oneclient/frontend/src/bindings.gen.ts +++ b/apps/oneclient/frontend/src/bindings.gen.ts @@ -301,7 +301,7 @@ export type VersionType = */ "old_beta" -const ARGS_MAP = { 'events':'{"message":["event"],"process":["event"],"ingress":["event"]}', 'oneclient':'{"getVersions":[],"getBundlesFor":["cluster_id"],"getClustersGroupedByMajor":[]}', 'folders':'{"openCluster":["folder_name"],"fromCluster":["folder_name"]}', 'debug':'{"getBuildTimestamp":[],"getOsVersion":[],"getType":[],"getLocale":[],"getPlatform":[],"isInDev":[],"getFamily":[],"getPackageVersion":[],"getArch":[],"openDevTools":[],"getGitCommitHash":[]}', 'core':'{"killProcess":["pid"],"removeUser":["uuid"],"getWorlds":["id"],"getMultiplePackages":["provider","slugs"],"getPackageBody":["provider","body"],"setDiscordRPCMessage":["message"],"removeCluster":["id"],"getRunningProcessesByClusterId":["cluster_id"],"getLoadersForVersion":["mc_version"],"getRunningProcesses":[],"createSettingsProfile":["name"],"getPackage":["provider","slug"],"installModpack":["modpack","cluster_id"],"open":["input"],"getScreenshots":["id"],"setDefaultUser":["uuid"],"readSettings":[],"fetchLoggedInProfile":["access_token"],"convertUsernameUUID":["username_uuid"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"getProfileOrDefault":["name"],"writeSettings":["setting"],"getLogs":["id"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"updateClusterProfile":["name","profile"],"getLogByName":["id","name"],"getClusters":[],"getUsersFromAuthor":["provider","author"],"changeSkin":["access_token","skin_url","skin_variant"],"getGlobalProfile":[],"createCluster":["options"],"fetchMinecraftProfile":["uuid"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"changeCape":["access_token","cape_uuid"],"removeCape":["access_token"],"searchPackages":["provider","query"],"isClusterRunning":["cluster_id"],"getClusterById":["id"],"getDefaultUser":["fallback"],"getGameVersions":[],"openMsaLogin":[],"updateClusterById":["id","request"],"launchCluster":["id","uuid"],"getUsers":[],"getUser":["uuid"]}' } +const ARGS_MAP = { 'debug':'{"getType":[],"getGitCommitHash":[],"getBuildTimestamp":[],"getPlatform":[],"getOsVersion":[],"getArch":[],"getPackageVersion":[],"openDevTools":[],"getFamily":[],"isInDev":[],"getLocale":[]}', 'oneclient':'{"getClustersGroupedByMajor":[],"getVersions":[],"getBundlesFor":["cluster_id"]}', 'events':'{"message":["event"],"process":["event"],"ingress":["event"]}', 'core':'{"getScreenshots":["id"],"getPackageBody":["provider","body"],"convertUsernameUUID":["username_uuid"],"writeSettings":["setting"],"createCluster":["options"],"createSettingsProfile":["name"],"getMultiplePackages":["provider","slugs"],"updateClusterProfile":["name","profile"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"openMsaLogin":[],"getClusters":[],"installModpack":["modpack","cluster_id"],"fetchMinecraftProfile":["uuid"],"killProcess":["pid"],"getUsers":[],"searchPackages":["provider","query"],"readSettings":[],"getLoadersForVersion":["mc_version"],"getRunningProcessesByClusterId":["cluster_id"],"getProfileOrDefault":["name"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"getUser":["uuid"],"getRunningProcesses":[],"fetchLoggedInProfile":["access_token"],"getLogs":["id"],"isClusterRunning":["cluster_id"],"getWorlds":["id"],"removeCape":["access_token"],"updateClusterById":["id","request"],"getGameVersions":[],"launchCluster":["id","uuid"],"removeCluster":["id"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"getLinkedPackages":["cluster_id"],"getClusterById":["id"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"removeUser":["uuid"],"changeSkin":["access_token","skin_url","skin_variant"],"getUsersFromAuthor":["provider","author"],"getGlobalProfile":[],"changeCape":["access_token","cape_uuid"],"getLogByName":["id","name"],"getDefaultUser":["fallback"],"setDiscordRPCMessage":["message"],"getPackage":["provider","slug"],"open":["input"],"setDefaultUser":["uuid"]}', 'folders':'{"openCluster":["folder_name"],"fromCluster":["folder_name"]}' } export type Router = { 'events': { ingress: (event: IngressPayload) => Promise, message: (event: MessagePayload) => Promise, process: (event: ProcessPayload) => Promise }, @@ -344,6 +344,7 @@ getPackageVersions: (provider: Provider, slug: string, mcVersion: string | null, downloadPackage: (provider: Provider, packageId: string, versionId: string, clusterId: number, skipCompatibility: boolean | null) => Promise, downloadExternalPackage: (package: ExternalPackage, clusterId: number, force: boolean | null, skipCompatibility: boolean | null) => Promise, getUsersFromAuthor: (provider: Provider, author: PackageAuthor) => Promise, +getLinkedPackages: (clusterId: number) => Promise, installModpack: (modpack: ModpackArchive, clusterId: number) => Promise, fetchMinecraftProfile: (uuid: string) => Promise, fetchLoggedInProfile: (accessToken: string) => Promise, @@ -354,8 +355,6 @@ removeCape: (accessToken: string) => Promise, convertUsernameUUID: (usernameUuid: string) => Promise, setDiscordRPCMessage: (message: string) => Promise, open: (input: string) => Promise }, -'folders': { fromCluster: (folderName: string) => Promise, -openCluster: (folderName: string) => Promise }, 'debug': { openDevTools: () => Promise, isInDev: () => Promise, getArch: () => Promise, @@ -366,7 +365,9 @@ getPlatform: () => Promise, getOsVersion: () => Promise, getGitCommitHash: () => Promise, getBuildTimestamp: () => Promise, -getPackageVersion: () => Promise } }; +getPackageVersion: () => Promise }, +'folders': { fromCluster: (folderName: string) => Promise, +openCluster: (folderName: string) => Promise } }; export type { InferCommandOutput } diff --git a/apps/oneclient/frontend/src/components/LaunchButton.tsx b/apps/oneclient/frontend/src/components/LaunchButton.tsx index a5c30261..e064d23b 100644 --- a/apps/oneclient/frontend/src/components/LaunchButton.tsx +++ b/apps/oneclient/frontend/src/components/LaunchButton.tsx @@ -1,3 +1,4 @@ +import type { ClusterModel } from '@/bindings.gen'; import type { ButtonProps } from '@onelauncher/common/components'; import { KillMinecraft, NoAccountPopup, Overlay } from '@/components'; import { useIsRunning } from '@/hooks/useClusters'; @@ -5,11 +6,12 @@ import { useLaunchCluster } from '@/hooks/useLaunchCluster'; import { bindings } from '@/main'; import { useCommandSuspense } from '@onelauncher/common'; import { Button } from '@onelauncher/common/components'; +import { useNavigate } from '@tanstack/react-router'; import { useState } from 'react'; import { tv } from 'tailwind-variants'; export type LaunchButtonProps = Omit & { - clusterId: number | undefined | null; + cluster: ClusterModel; }; const launchButtonVariants = tv({ @@ -20,29 +22,70 @@ const launchButtonVariants = tv({ }, }); +function PromptForOnboarding({ launch, setSkipPackagesCheck, cluster }: { launch: () => void; setSkipPackagesCheck: React.Dispatch>; cluster: ClusterModel }) { + const navigate = useNavigate(); + const onClickYes = () => { + localStorage.setItem('selectedClusters', JSON.stringify([{ mc_version: cluster.mc_version, mc_loader: cluster.mc_loader }])); + navigate({ to: `/onboarding/preferences/versionCategory` }); + }; + + const onClickLaunch = () => { + setSkipPackagesCheck(true); + launch(); + }; + + return ( + + Not Setup +
+

It seems like you haven't setup this version

+

Do you want to setup this version?

+
+ +
+ ); +} + export function LaunchButton({ - clusterId, + cluster, isDisabled, className, ...rest }: LaunchButtonProps) { const { data: currentAccount } = useCommandSuspense(['getDefaultUser'], () => bindings.core.getDefaultUser(true)); - const launchCluster = useLaunchCluster(clusterId); - const isRunning = useIsRunning(clusterId); - const [open, setOpen] = useState(false); + const { data: installedPackages } = useCommandSuspense(['getLinkedPackages', cluster.id], () => bindings.core.getLinkedPackages(cluster.id)); + const launchCluster = useLaunchCluster(cluster.id); + const isRunning = useIsRunning(cluster.id); + + const [reason, setReason] = useState<'account' | 'packages' | 'kill' | null>(null); + const [skipPackagesCheck, setSkipPackagesCheck] = useState(false); const launch = () => { - if (currentAccount === null || isRunning) { - setOpen(true); - } - else { - launchCluster(); - setOpen(false); - } + if (isRunning) + return setReason('kill'); + if (!currentAccount) + return setReason('account'); + if (installedPackages.length === 0 && !skipPackagesCheck) + return setReason('packages'); + + launchCluster(); + setSkipPackagesCheck(false); + setReason(null); }; return ( - + !open && setReason(null)}>
@@ -153,7 +153,7 @@ function HeaderSmall() {
diff --git a/apps/oneclient/frontend/src/routes/app/clusters.tsx b/apps/oneclient/frontend/src/routes/app/clusters.tsx index dac87215..ae132e16 100644 --- a/apps/oneclient/frontend/src/routes/app/clusters.tsx +++ b/apps/oneclient/frontend/src/routes/app/clusters.tsx @@ -103,6 +103,21 @@ function RouteComponent() { }); }, [cluster, navigate]); + if (!cluster) + return ( + } + headerSmall={} + > +
+ +

Something wen't wrong while getting a cluster

+

Please reload and if this does not work please contact support

+
+
+
+ ); + return ( } @@ -137,7 +152,7 @@ function RouteComponent() {
-

Version {cluster?.mc_version}

+

Version {cluster.mc_version}

{versionInfo.longDescription}

{minorVersions.length > 1 && ( @@ -194,7 +209,7 @@ function RouteComponent() { )}
- +