feat: debug page (#493)

* fix: discordRPC route sorting

It wasn't alphabetical and it was annoying me

* feat: debug page

* move stuff into hooks where they should be

* fix: building
This commit is contained in:
Jacob 2026-03-06 14:25:03 +08:00 • committed by GitHub
parent 963e7044f2
commit adf9f4bd63
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 293 additions and 139 deletions

View file

@ -29,7 +29,7 @@ export type DaoError = { type: "NotFound"; data: string } | { type: "AlreadyExis
export type DbVec = T[]
export type DebugInfoData = { inDev: boolean; platform: string; arch: string; family: string; locale: string; osType: string; osVersion: string; osDistro: string; commitHash: string; buildTimestamp: string; buildVersion: string }
export type DebugInfoData = { inDev: boolean; platform: string; arch: string; family: string; locale: string; osType: string; osVersion: string; osDistro: string; commitHash: string; buildTimestamp: string; buildVersion: string; hasDevSettings: boolean }
export type DebugInfoParsedLine = { title: string; value: string }
@ -250,7 +250,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; mod_list_use_grid: boolean; max_concurrent_requests: number; settings_version: number; native_window_frame: boolean; log_debug_info: boolean; show_tanstack_dev_tools: boolean }
export type SettingsOsExtra = Record<string, never>
export type SettingsOsExtra = { enable_gamemode: boolean | null }
export type SkinVariant = "classic" | "slim"
@ -317,10 +317,20 @@ export type VersionType =
*/
"old_beta"
const ARGS_MAP = { 'debug':'{"openDevTools":[],"getLocale":[],"getFullDebugInfoParsedString":[],"getArch":[],"getPlatform":[],"getOsVersion":[],"getOsDistro":[],"isInDev":[],"getFullDebugInfoParsed":[],"getFamily":[],"getGitCommitHash":[],"getType":[],"getPackageVersion":[],"getFullDebugInfo":[],"getBuildTimestamp":[]}', 'events':'{"message":["event"],"process":["event"],"ingress":["event"]}', 'folders':'{"fromCluster":["folder_name"],"openCluster":["folder_name"]}', 'core':'{"launchCluster":["id","uuid","search_for_java"],"getRunningProcessesByClusterId":["cluster_id"],"getDefaultUser":["fallback"],"refreshAccounts":[],"getPackage":["provider","slug"],"searchPackages":["provider","query"],"getProcessLogTail":["id","max_lines"],"getMultiplePackages":["provider","slugs"],"getUsersFromAuthor":["provider","author"],"togglePackage":["cluster_id","package_hash"],"fetchLoggedInProfile":["access_token"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"changeCape":["access_token","cape_uuid"],"fetchMinecraftProfile":["uuid"],"setClusterStage":["id","stage"],"convertUsernameUUID":["username_uuid"],"getGlobalProfile":[],"writeSettings":["setting"],"getScreenshots":["id"],"removeUser":["uuid"],"setDiscordRPCMessage":["message"],"isClusterRunning":["cluster_id"],"updateClusterProfile":["name","profile"],"getGameVersions":[],"installModpack":["modpack","cluster_id"],"getRunningProcesses":[],"syncCluster":["cluster_id"],"changeSkin":["access_token","skin_url","skin_variant"],"removeCape":["access_token"],"getLoadersForVersion":["mc_version"],"open":["input"],"getLinkedPackages":["cluster_id"],"getWorlds":["id"],"getLogs":["id"],"createCluster":["options"],"getLogByName":["id","name"],"getClusters":[],"getProfileOrDefault":["name"],"getUser":["uuid"],"createSettingsProfile":["name"],"openMsaLogin":[],"readSettings":[],"getPackageBody":["provider","body"],"removeCluster":["id"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"getClusterById":["id"],"updateClusterById":["id","request"],"killProcess":["pid"],"setDefaultUser":["uuid"],"removePackage":["cluster_id","package_hash"],"getUsers":[],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"refreshAccount":["uuid"]}', 'oneclient':'{"cacheArt":["path"],"getClustersGroupedByMajor":[],"refreshArt":["path"],"installUpdate":[],"extractBundleOverrides":["bundle_path","cluster_id"],"checkForUpdate":[],"updateBundlePackages":["cluster_id"],"getBundlesFor":["cluster_id"],"isBundleSyncing":[],"getVersions":[],"downloadPackageFromBundle":["package","cluster_id","bundle_name","skip_compatibility"]}' }
export type Router = { 'events': { ingress: (event: IngressPayload) => Promise<void>,
message: (event: MessagePayload) => Promise<void>,
process: (event: ProcessPayload) => Promise<void> },
const ARGS_MAP = { 'debug':'{"getFullDebugInfoParsed":[],"getLocale":[],"getType":[],"hasDevSettings":[],"isInDev":[],"getFamily":[],"getPackageVersion":[],"getOsVersion":[],"getGitCommitHash":[],"getPlatform":[],"getBuildTimestamp":[],"getOsDistro":[],"getArch":[],"getFullDebugInfo":[],"getFullDebugInfoParsedString":[],"openDevTools":[]}', 'folders':'{"fromCluster":["folder_name"],"openCluster":["folder_name"]}', 'events':'{"process":["event"],"message":["event"],"ingress":["event"]}', 'core':'{"searchPackages":["provider","query"],"getLinkedPackages":["cluster_id"],"getProcessLogTail":["id","max_lines"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"updateClusterById":["id","request"],"getLogs":["id"],"getGameVersions":[],"refreshAccounts":[],"syncCluster":["cluster_id"],"removeCape":["access_token"],"getClusterById":["id"],"openMsaLogin":[],"refreshAccount":["uuid"],"convertUsernameUUID":["username_uuid"],"getRunningProcessesByClusterId":["cluster_id"],"setDiscordRPCMessage":["message"],"open":["input"],"getClusters":[],"getProfileOrDefault":["name"],"getLogByName":["id","name"],"getPackage":["provider","slug"],"getUsers":[],"createSettingsProfile":["name"],"removeCluster":["id"],"getPackageBody":["provider","body"],"killProcess":["pid"],"changeSkin":["access_token","skin_url","skin_variant"],"getGlobalProfile":[],"getUser":["uuid"],"removeUser":["uuid"],"getDefaultUser":["fallback"],"getMultiplePackages":["provider","slugs"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"changeCape":["access_token","cape_uuid"],"launchCluster":["id","uuid","search_for_java"],"createCluster":["options"],"updateClusterProfile":["name","profile"],"getWorlds":["id"],"getScreenshots":["id"],"getLoadersForVersion":["mc_version"],"getUsersFromAuthor":["provider","author"],"isClusterRunning":["cluster_id"],"setClusterStage":["id","stage"],"setDefaultUser":["uuid"],"getRunningProcesses":[],"writeSettings":["setting"],"readSettings":[],"removePackage":["cluster_id","package_hash"],"fetchMinecraftProfile":["uuid"],"fetchLoggedInProfile":["access_token"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"togglePackage":["cluster_id","package_hash"],"installModpack":["modpack","cluster_id"]}', 'oneclient':'{"getVersions":[],"extractBundleOverrides":["bundle_path","cluster_id"],"downloadPackageFromBundle":["package","cluster_id","bundle_name","skip_compatibility"],"getBundlesFor":["cluster_id"],"isBundleSyncing":[],"installUpdate":[],"cacheArt":["path"],"refreshArt":["path"],"checkForUpdate":[],"getClustersGroupedByMajor":[],"updateBundlePackages":["cluster_id"]}' }
export type Router = { 'folders': { fromCluster: (folderName: string) => Promise<string>,
openCluster: (folderName: string) => Promise<null> },
'oneclient': { getClustersGroupedByMajor: () => Promise<Partial<{ [key in number]: ClusterModel[] }>>,
getBundlesFor: (clusterId: number) => Promise<ModpackArchive[]>,
getVersions: () => Promise<OnlineClusterManifest>,
extractBundleOverrides: (bundlePath: string, clusterId: number) => Promise<null>,
checkForUpdate: () => Promise<Update | null>,
installUpdate: () => Promise<null>,
downloadPackageFromBundle: (package: ModpackFileKind, clusterId: number, bundleName: string, skipCompatibility: boolean | null) => Promise<PackageModel>,
updateBundlePackages: (clusterId: number) => Promise<ApplyBundleUpdatesResult>,
isBundleSyncing: () => Promise<boolean>,
cacheArt: (path: string) => Promise<string>,
refreshArt: (path: string) => Promise<null> },
'debug': { openDevTools: () => Promise<void>,
isInDev: () => Promise<boolean>,
getArch: () => Promise<string>,
@ -335,9 +345,11 @@ getBuildTimestamp: () => Promise<string>,
getPackageVersion: () => Promise<string>,
getFullDebugInfo: () => Promise<DebugInfoData>,
getFullDebugInfoParsed: () => Promise<DebugInfoParsedLine[]>,
getFullDebugInfoParsedString: () => Promise<string> },
'folders': { fromCluster: (folderName: string) => Promise<string>,
openCluster: (folderName: string) => Promise<null> },
getFullDebugInfoParsedString: () => Promise<string>,
hasDevSettings: () => Promise<boolean> },
'events': { ingress: (event: IngressPayload) => Promise<void>,
message: (event: MessagePayload) => Promise<void>,
process: (event: ProcessPayload) => Promise<void> },
'core': { getClusters: () => Promise<ClusterModel[]>,
getClusterById: (id: number) => Promise<ClusterModel | null>,
removeCluster: (id: number) => Promise<null>,
@ -391,18 +403,7 @@ changeCape: (accessToken: string, capeUuid: string) => Promise<MojangFullPlayerP
removeCape: (accessToken: string) => Promise<MojangFullPlayerProfile>,
convertUsernameUUID: (usernameUuid: string) => Promise<MowojangProfile>,
setDiscordRPCMessage: (message: string) => Promise<null>,
open: (input: string) => Promise<null> },
'oneclient': { getClustersGroupedByMajor: () => Promise<Partial<{ [key in number]: ClusterModel[] }>>,
getBundlesFor: (clusterId: number) => Promise<ModpackArchive[]>,
getVersions: () => Promise<OnlineClusterManifest>,
extractBundleOverrides: (bundlePath: string, clusterId: number) => Promise<null>,
checkForUpdate: () => Promise<Update | null>,
installUpdate: () => Promise<null>,
downloadPackageFromBundle: (package: ModpackFileKind, clusterId: number, bundleName: string, skipCompatibility: boolean | null) => Promise<PackageModel>,
updateBundlePackages: (clusterId: number) => Promise<ApplyBundleUpdatesResult>,
isBundleSyncing: () => Promise<boolean>,
cacheArt: (path: string) => Promise<string>,
refreshArt: (path: string) => Promise<null> } };
open: (input: string) => Promise<null> } };
export type { InferCommandOutput }

View file

@ -1,5 +1,5 @@
import { copyDebugInfo } from '@/hooks/useDebugInfo';
import { bindings } from '@/main';
import { copyDebugInfo } from '@/utils/debugInfo';
import { useCommand } from '@onelauncher/common';
import { Button } from '@onelauncher/common/components';

View file

@ -2,6 +2,7 @@ import type { ButtonProps } from '@onelauncher/common/components';
import type { LinkProps, RegisteredRouter } from '@tanstack/react-router';
import LauncherLogo from '@/assets/logos/oneclient.svg?react';
import { AccountAvatar, AccountPopup, Overlay, SuperSecretDevOptions } from '@/components';
import { useSettings } from '@/hooks/useSettings';
import { bindings } from '@/main';
import { useCommand } from '@onelauncher/common';
import { Button, Popup } from '@onelauncher/common/components';
@ -12,6 +13,7 @@ import { twMerge } from 'tailwind-merge';
export function Navbar() {
const { data: currentAccount } = useCommand(['getDefaultUser'], () => bindings.core.getDefaultUser(true));
const { setting } = useSettings();
const navigate = useNavigate();
const onMinimize = () => Window.getCurrent().minimize();
@ -36,6 +38,7 @@ export function Navbar() {
<NavbarLink to=".">Home</NavbarLink>
<NavbarLink to="./clusters">Versions</NavbarLink>
<NavbarLink to="./accounts">Accounts</NavbarLink>
{setting('show_tanstack_dev_tools') && <NavbarLink to="./debug">Debug</NavbarLink>}
</div>
<div className="flex flex-1 items-center justify-end gap-6 pointer-events-none">

View file

@ -1,6 +1,6 @@
import type { DebugInfoArray } from '@/utils/debugInfo';
import type { DebugInfoArray } from '@/hooks/useDebugInfo';
import { Overlay } from '@/components';
import { copyDebugInfo, useDebugInfo } from '@/utils/debugInfo';
import { copyDebugInfo, useDebugInfo } from '@/hooks/useDebugInfo';
import { Button } from '@onelauncher/common/components';
export function DebugInfo() {

View file

@ -1,96 +1,42 @@
import { MinecraftAuthErrorModal, minecraftAuthErrors, Overlay, RawDebugInfo, SettingsRow, SettingsSwitch } from '@/components';
import { Overlay, SettingsRow, SettingsSwitch } from '@/components';
import { copyDebugInfo } from '@/hooks/useDebugInfo';
import { useSettings } from '@/hooks/useSettings';
import { bindings } from '@/main';
import { useDebugInfo } from '@/utils/debugInfo';
import { Button } from '@onelauncher/common/components';
import { Link } from '@tanstack/react-router';
import { dataDir, join } from '@tauri-apps/api/path';
import { Code02Icon } from '@untitled-theme/icons-react';
import { useEffect, useState } from 'react';
export function SuperSecretDevOptions() {
const { createSetting } = useSettings();
const debugInfo = useDebugInfo();
const [previewError, setPreviewError] = useState<string | null>(null);
const [launcherDir, setLauncherDir] = useState('');
useEffect(() => {
(async () => {
setLauncherDir(await join(await dataDir(), 'OneClient'));
})();
}, []);
const openLauncherDir = () => bindings.core.open(launcherDir);
const logRunningProcesses = () => {
(async () => {
const running = await bindings.core.getRunningProcesses();
// eslint-disable-next-line no-console -- Designed to log
console.log(running);
})();
};
return (
<Overlay.Dialog>
<Overlay.Title>Super Secret Dev Options</Overlay.Title>
<div>
<SettingsRow description="WARNING! This requires a restart to apply. Logs out debug info" icon={<Code02Icon />} title="Log Debug Info">
<SettingsSwitch setting={createSetting('log_debug_info')} />
</SettingsRow>
<SettingsRow description="Enable The Tanstack Dev Tools" icon={<Code02Icon />} title="Tanstack Dev Tools">
<SettingsRow description="Enable The Tanstack Dev Tools and shows debug page" title="Show Dev stuff">
<SettingsSwitch setting={createSetting('show_tanstack_dev_tools')} />
</SettingsRow>
<div className="grid grid-cols-3 gap-3 justify-items-center">
<Button onPress={bindings.debug.openDevTools} size="normal">Dev Tools</Button>
<SettingsRow description="WARNING! This requires a restart to apply. Logs out debug info" title="Log Debug Info">
<SettingsSwitch setting={createSetting('log_debug_info')} />
</SettingsRow>
<div className="grid grid-cols-4 gap-3 justify-items-center">
<Button onPress={bindings.debug.openDevTools} size="normal">Open Dev Tools</Button>
<Button onPress={copyDebugInfo}>Copy Debug Info</Button>
<Link to="/app/debug">
<Button size="normal">Debug Page</Button>
</Link>
<Link to="/onboarding/finished">
<Button size="normal">Skip Onboarding</Button>
</Link>
<Button onClick={openLauncherDir} size="normal">Open Launcher Data</Button>
<Button onClick={logRunningProcesses} size="normal">Console log processes</Button>
</div>
<div className="mt-3 border-t border-component-border pt-3">
<p className="text-fg-secondary text-xs mb-2">Auth Error Preview</p>
<div className="flex flex-wrap gap-2">
{minecraftAuthErrors.map(err => (
<Button
key={err.errorCode}
onClick={() => setPreviewError(`Minecraft authentication error: ${err.errorCode} during MSA step XstsAuthorize`)}
size="normal"
>
{err.errorCode}
</Button>
))}
<Button
onClick={() => setPreviewError('Some unknown auth error occurred')}
size="normal"
>
Unknown
</Button>
</div>
</div>
</div>
<RawDebugInfo debugInfo={debugInfo} />
{previewError && (
<Overlay
isDismissable
isOpen
onOpenChange={(open) => {
if (!open)
setPreviewError(null);
}}
>
<MinecraftAuthErrorModal error={previewError} />
</Overlay>
)}
</Overlay.Dialog>
);
}

View file

@ -15,27 +15,28 @@ export const ResolvedPathNames: Record<URLPath, string> = {
'/app/cluster': 'Viewing Versions',
'/app/cluster/browser': 'Viewing {clusterName}\'s mods',
'/app/cluster/browser/package': 'Browsing {packageName}',
'/app/cluster/datapacks': 'Viewing {clusterName}\'s data packs',
'/app/cluster/logs': 'Viewing {clusterName}\'s logs',
'/app/cluster/mods': 'Viewing {clusterName}\'s mods',
'/app/cluster/resource-packs': 'Viewing {clusterName}\'s resource packs',
'/app/cluster/shaders': 'Viewing {clusterName}\'s shaders',
'/app/cluster/datapacks': 'Viewing {clusterName}\'s data packs',
'/app/cluster/process': 'Viewing {clusterName}',
'/app/cluster/resource-packs': 'Viewing {clusterName}\'s resource packs',
'/app/cluster/settings': 'Viewing {clusterName}\'s settings',
'/app/cluster/shaders': 'Viewing {clusterName}\'s shaders',
'/app/settings': 'Viewing Settings',
'/app/settings/appearance': 'Viewing Settings',
'/app/settings/changelog': 'Viewing Settings',
'/app/settings/developer': 'Viewing Settings',
'/app/settings/minecraft': 'Viewing Settings',
'/app/settings/changelog': 'Viewing Settings',
'/app/accounts': 'Viewing Accounts',
'/app/clusters': 'Viewing Versions',
'/app/debug': 'Viewing Debug',
'/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',
'/onboarding/account': 'Preparing OneClient',
'/onboarding/finished': 'Preparing OneClient',
'/onboarding/language': 'Preparing OneClient',
};
// Credit - https://github.com/DuckySoLucky/hypixel-discord-chat-bridge/blob/d3ea84a26ebf094c8191d50b4954549e2dd4dc7f/src/contracts/helperFunctions.js#L216-L225

View file

@ -1,10 +1,10 @@
import type { QueryClient } from '@tanstack/react-query';
import type { NavigateOptions, ToOptions } from '@tanstack/react-router';
import { Toasts } from '@/components';
import { useDebugKeybind } from '@/hooks/useDebugInfo';
import { useDiscordRPC } from '@/hooks/useDiscordRPC';
import { useSettings } from '@/hooks/useSettings';
import { useDebugKeybind } from '@/utils/debugInfo';
import { useDiscordRPC } from '@/utils/discordRPC';
import { useAutoUpdater } from '@/utils/updater';
import { useAutoUpdater } from '@/hooks/useUpdater';
import { TanStackDevtools } from '@tanstack/react-devtools';
import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools';
import { createRootRouteWithContext, Outlet, useRouter } from '@tanstack/react-router';

View file

@ -0,0 +1,218 @@
import type { ToastData } from '@/utils/toast';
import { MinecraftAuthErrorModal, minecraftAuthErrors, Overlay, RawDebugInfo, SettingsRow, SettingsSwitch, SheetPage } from '@/components';
import { useDebugInfo } from '@/hooks/useDebugInfo';
import { useSettings } from '@/hooks/useSettings';
import { bindings } from '@/main';
import { toast, ToastTypes } from '@/utils/toast';
import { Button, Dropdown, TextField } from '@onelauncher/common/components';
import { createFileRoute } from '@tanstack/react-router';
import { dataDir, join } from '@tauri-apps/api/path';
import { useEffect, useState } from 'react';
export const Route = createFileRoute('/app/debug')({
component: RouteComponent,
});
function RouteComponent() {
return (
<SheetPage
headerLarge={<HeaderLarge />}
headerSmall={<HeaderSmall />}
>
<SheetPage.Content>
<div className="flex flex-col gap-4">
<AuthError />
<div className="w-full h-1 my-2 rounded bg-component-border"></div>
<Toasts />
<div className="w-full h-1 my-2 rounded bg-component-border"></div>
<Settings />
<div className="w-full h-1 my-2 rounded bg-component-border"></div>
<Other />
<div className="w-full h-1 my-2 rounded bg-component-border"></div>
<Info />
</div>
</SheetPage.Content>
</SheetPage>
);
}
function AuthError() {
const [errorInput, setErrorInput] = useState<string>('Some unknown auth error occurred');
const [previewError, setPreviewError] = useState<string | null>(null);
return (
<>
<div className="flex flex-col gap-4">
<h1 className="text-3xl font-semibold">Auth Error Preview</h1>
<div className="flex flex-row gap-4 items-center">
<p>Custom Input:</p>
<TextField className="flex-1" onChange={e => setErrorInput(e.target.value)} value={errorInput} />
</div>
<div className="flex flex-row flex-wrap gap-4">
{minecraftAuthErrors.map(err => (
<Button
key={err.errorCode}
onClick={() => setPreviewError(`Minecraft authentication error: ${err.errorCode} during MSA step XstsAuthorize`)}
size="normal"
>
{err.errorCode}
</Button>
))}
<Button onClick={() => setPreviewError(errorInput)} size="normal">Custom</Button>
</div>
</div>
{previewError && (
<Overlay
isDismissable
isOpen
onOpenChange={(open) => {
if (!open)
setPreviewError(null);
}}
>
<MinecraftAuthErrorModal error={previewError} />
</Overlay>
)}
</>
);
}
// Credit - https://github.com/DuckySoLucky/hypixel-discord-chat-bridge/blob/52887f12fce3bc9ebe91befaf394f289abd234a1/src/contracts/helperFunctions.js#L260-L278
function TitleCase(str: string): string {
if (!str)
return '';
if (typeof str !== 'string')
return '';
return str
.toLowerCase()
.replaceAll('_', ' ')
.split(' ')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
}
function Toasts() {
const [title, setTitle] = useState<string>('Title');
const [message, setMessage] = useState<string | undefined>('Message');
const [type, setType] = useState<ToastData['type']>('info');
return (
<div className="flex flex-col gap-4">
<h1 className="text-3xl font-semibold">Toasts</h1>
<div className="flex flex-row gap-4 items-center">
<p>Title:</p>
<TextField className="flex-1" onChange={e => setTitle(e.target.value)} value={title} />
</div>
<div className="flex flex-row gap-4 items-center">
<p>Message:</p>
<TextField className="flex-1" onChange={e => setMessage(e.target.value)} value={message} />
</div>
<div className="flex flex-row gap-4 items-center">
<p>Toast Type:</p>
<Dropdown onSelectionChange={key => setType(key as ToastData['type'])} selectedKey={type}>
{ToastTypes.map(type => <Dropdown.Item id={type} key={type}>{TitleCase(type)}</Dropdown.Item>)}
</Dropdown>
</div>
<div className="flex flex-row gap-4">
<Button onPress={() => toast({ type, title, message })} size="normal">Send Toast</Button>
</div>
</div>
);
}
function Settings() {
const { createSetting } = useSettings();
return (
<div className="flex flex-col gap-4">
<h1 className="text-3xl font-semibold">Settings</h1>
<div className="flex flex-row gap-4 flex-wrap">
<SettingsRow description="WARNING! This requires a restart to apply. Logs out debug info" title="Log Debug Info">
<SettingsSwitch setting={createSetting('log_debug_info')} />
</SettingsRow>
<SettingsRow description="Enable The Tanstack Dev Tools and shows debug page" title="Show Dev stuff">
<SettingsSwitch setting={createSetting('show_tanstack_dev_tools')} />
</SettingsRow>
<SettingsRow description="Seen onboarding" title="Seen Onboarding">
<SettingsSwitch setting={createSetting('seen_onboarding')} />
</SettingsRow>
<SettingsRow description="Use Grid On Mods List" title="Use Grid On Mods List">
<SettingsSwitch setting={createSetting('mod_list_use_grid')} />
</SettingsRow>
</div>
</div>
);
}
function Other() {
const [launcherDir, setLauncherDir] = useState('');
useEffect(() => {
(async () => {
setLauncherDir(await join(await dataDir(), 'OneClient'));
})();
}, []);
const openLauncherDir = () => bindings.core.open(launcherDir);
const logRunningProcesses = () => {
(async () => {
const running = await bindings.core.getRunningProcesses();
// eslint-disable-next-line no-console -- Designed to log
console.log(running);
})();
};
return (
<div className="flex flex-col gap-4">
<h1 className="text-3xl font-semibold">Other</h1>
<div className="flex flex-row gap-4">
<Button onPress={bindings.debug.openDevTools} size="normal">Open Dev Tools</Button>
<Button onClick={openLauncherDir} size="normal">Open Launcher Data</Button>
<Button onClick={logRunningProcesses} size="normal">Log Running Processes to frontend</Button>
</div>
</div>
);
}
function Info() {
const debugInfo = useDebugInfo();
return (
<div className="flex flex-col gap-4">
<h1 className="text-3xl font-semibold">Info</h1>
<div className="flex flex-row gap-4">
<RawDebugInfo debugInfo={debugInfo} />
</div>
</div>
);
}
function HeaderLarge() {
return (
<div className="flex flex-row justify-between items-end gap-16">
<div className="flex-1 flex flex-col">
<h1 className="text-3xl font-semibold">Debug</h1>
<p>The end user won't really be looking at this page. Design skills go out the window</p>
</div>
</div>
);
}
function HeaderSmall() {
return (
<div className="flex flex-row justify-between items-center h-full">
<h1 className="text-2lg h-full font-medium">Debug</h1>
</div>
);
}

View file

@ -1,10 +1,8 @@
import { DebugInfo, Overlay, SettingsRow, SettingsSwitch } from '@/components';
import { useSettings } from '@/hooks/useSettings';
import { bindings } from '@/main';
import { Sidebar } from '@/routes/app/settings/route';
import { Button } from '@onelauncher/common/components';
import { createFileRoute, Link } from '@tanstack/react-router';
import { Code02Icon } from '@untitled-theme/icons-react';
import { createFileRoute } from '@tanstack/react-router';
export const Route = createFileRoute('/app/settings/developer')({
component: RouteComponent,
@ -32,42 +30,13 @@ function RouteComponent() {
</SettingsRow>
<SettingsRow.Header>Dev Tools</SettingsRow.Header>
<SettingsRow description="WARNING! This requires a restart to apply. Logs out debug info" icon={<Code02Icon />} title="Log Debug Info">
<SettingsRow description="WARNING! This requires a restart to apply. Logs out debug info" title="Log Debug Info">
<SettingsSwitch setting={createSetting('log_debug_info')} />
</SettingsRow>
<SettingsRow
description="Enable The Tanstack Dev Tools"
title="Tanstack Dev Tools"
>
<SettingsSwitch setting={createSetting('show_tanstack_dev_tools')} />
</SettingsRow>
<SettingsRow
description="Open Dev Tools"
title="Open Dev Tools"
>
<Button onPress={bindings.debug.openDevTools} size="normal">Open</Button>
</SettingsRow>
<SettingsRow.Header>Onboarding</SettingsRow.Header>
<SettingsRow
description="Open Onboarding"
title="Open Onboarding"
>
<Link to="/onboarding">
<Button size="normal">Open</Button>
</Link>
</SettingsRow>
<SettingsRow
description="Seen onboarding"
title="Seen Onboarding"
>
<SettingsSwitch setting={createSetting('seen_onboarding')} />
</SettingsRow>
<SettingsRow
description="Use Grid On Mods List"
title="Use Grid On Mods List"
>
<SettingsSwitch setting={createSetting('mod_list_use_grid')} />
<SettingsRow description="Enable The Tanstack Dev Tools and shows debug page" title="Show Dev stuff">
<SettingsSwitch setting={createSetting('show_tanstack_dev_tools')} />
</SettingsRow>
</div>

View file

@ -9,6 +9,7 @@ export interface ToastData {
title: string;
message?: string | undefined;
}
export const ToastTypes: Array<ToastData['type']> = ['info', 'success', 'warning', 'error'];
export interface ToastOptions extends ToastData {
progress?: number;

View file

@ -1,3 +1,4 @@
use crate::store::State;
use std::time::{SystemTime, UNIX_EPOCH};
use tauri::Runtime;
use tauri_plugin_os::{arch, family, locale, platform, type_, version};
@ -18,6 +19,7 @@ pub struct DebugInfoData {
pub commit_hash: String,
pub build_timestamp: String,
pub build_version: String,
pub has_dev_settings: bool,
}
#[onelauncher_macro::specta]
@ -82,6 +84,9 @@ pub trait TauriLauncherDebugApi {
#[taurpc(alias = "getFullDebugInfoParsedString")]
async fn get_full_debug_info_parsed_string() -> String;
#[taurpc(alias = "hasDevSettings")]
async fn get_has_dev_settings() -> bool;
}
#[taurpc::ipc_type]
@ -158,6 +163,11 @@ impl TauriLauncherDebugApi for TauriLauncherDebugApiImpl {
crate::build::PKG_VERSION.to_string()
}
async fn get_has_dev_settings(self) -> bool {
let state = State::get().await.expect("failed to get state");
return state.settings.read().await.show_tanstack_dev_tools;
}
async fn get_full_debug_info(self) -> DebugInfoData {
DebugInfoData {
in_dev: self.clone().is_in_dev().await,
@ -171,6 +181,7 @@ impl TauriLauncherDebugApi for TauriLauncherDebugApiImpl {
commit_hash: self.clone().get_git_commit_hash().await,
build_timestamp: self.clone().get_build_timestamp().await,
build_version: self.clone().get_package_version().await,
has_dev_settings: self.clone().get_has_dev_settings().await,
}
}
@ -189,6 +200,10 @@ impl TauriLauncherDebugApi for TauriLauncherDebugApiImpl {
DebugInfoParsedLine::new("Commit Hash", info.commit_hash),
DebugInfoParsedLine::new("Build Timestamp", info.build_timestamp),
DebugInfoParsedLine::new("Version", info.build_version),
DebugInfoParsedLine::new(
"Has Dev Settings",
if info.has_dev_settings { "yes" } else { "no" },
),
]
}