feat(OneClient-ChangeLog) (#474)

* feat(OneClient): change log

* feat(OneClient-ChangeLog)

* fix: CHANGE_LOG url

* fix: formatting
This commit is contained in:
Jacob 2026-01-11 21:03:52 +08:00 • committed by GitHub
parent 7050a6b108
commit 682a08765b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 211 additions and 26 deletions

View file

@ -303,8 +303,26 @@ export type VersionType =
*/
"old_beta"
const ARGS_MAP = { 'debug':'{"openDevTools":[],"getArch":[],"getFamily":[],"getOsVersion":[],"getBuildTimestamp":[],"getPackageVersion":[],"isInDev":[],"getLocale":[],"getType":[],"getGitCommitHash":[],"getPlatform":[]}', 'core':'{"getLinkedPackages":["cluster_id"],"installModpack":["modpack","cluster_id"],"removeCape":["access_token"],"changeSkin":["access_token","skin_url","skin_variant"],"getDefaultUser":["fallback"],"getUser":["uuid"],"removeUser":["uuid"],"getLogByName":["id","name"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"getUsersFromAuthor":["provider","author"],"open":["input"],"fetchLoggedInProfile":["access_token"],"searchPackages":["provider","query"],"getClusters":[],"getProfileOrDefault":["name"],"setDefaultUser":["uuid"],"getRunningProcesses":[],"getUsers":[],"getLoadersForVersion":["mc_version"],"getScreenshots":["id"],"getWorlds":["id"],"killProcess":["pid"],"writeSettings":["setting"],"updateClusterProfile":["name","profile"],"convertUsernameUUID":["username_uuid"],"changeCape":["access_token","cape_uuid"],"setDiscordRPCMessage":["message"],"createSettingsProfile":["name"],"removePackage":["cluster_id","package_hash"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"getRunningProcessesByClusterId":["cluster_id"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"updateClusterById":["id","request"],"fetchMinecraftProfile":["uuid"],"getClusterById":["id"],"createCluster":["options"],"getLogs":["id"],"isClusterRunning":["cluster_id"],"openMsaLogin":[],"getGameVersions":[],"refreshAccount":["uuid"],"refreshAccounts":[],"removeCluster":["id"],"launchCluster":["id","uuid","search_for_java"],"readSettings":[],"getPackage":["provider","slug"],"setClusterStage":["id","stage"],"getPackageBody":["provider","body"],"getGlobalProfile":[],"getMultiplePackages":["provider","slugs"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"]}', 'oneclient':'{"getVersions":[],"getClustersGroupedByMajor":[],"checkForUpdate":[],"getBundlesFor":["cluster_id"],"installUpdate":[]}', 'events':'{"message":["event"],"ingress":["event"],"process":["event"]}', 'folders':'{"fromCluster":["folder_name"],"openCluster":["folder_name"]}' }
export type Router = { 'core': { getClusters: () => Promise<ClusterModel[]>,
const ARGS_MAP = { 'oneclient':'{"installUpdate":[],"checkForUpdate":[],"getBundlesFor":["cluster_id"],"getVersions":[],"getClustersGroupedByMajor":[]}', 'events':'{"message":["event"],"process":["event"],"ingress":["event"]}', 'folders':'{"fromCluster":["folder_name"],"openCluster":["folder_name"]}', 'debug':'{"isInDev":[],"getPlatform":[],"getArch":[],"getFamily":[],"getLocale":[],"openDevTools":[],"getOsVersion":[],"getBuildTimestamp":[],"getGitCommitHash":[],"getPackageVersion":[],"getType":[]}', 'core':'{"getLinkedPackages":["cluster_id"],"setDefaultUser":["uuid"],"refreshAccounts":[],"getUsers":[],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"getRunningProcessesByClusterId":["cluster_id"],"getUser":["uuid"],"readSettings":[],"getClusters":[],"getLoadersForVersion":["mc_version"],"removePackage":["cluster_id","package_hash"],"fetchMinecraftProfile":["uuid"],"fetchLoggedInProfile":["access_token"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"changeSkin":["access_token","skin_url","skin_variant"],"removeCape":["access_token"],"getPackageBody":["provider","body"],"setDiscordRPCMessage":["message"],"setClusterStage":["id","stage"],"writeSettings":["setting"],"open":["input"],"getLogByName":["id","name"],"getRunningProcesses":[],"searchPackages":["provider","query"],"createSettingsProfile":["name"],"getLogs":["id"],"getProfileOrDefault":["name"],"openMsaLogin":[],"convertUsernameUUID":["username_uuid"],"getScreenshots":["id"],"createCluster":["options"],"removeCluster":["id"],"isClusterRunning":["cluster_id"],"getDefaultUser":["fallback"],"getPackage":["provider","slug"],"installModpack":["modpack","cluster_id"],"launchCluster":["id","uuid","search_for_java"],"getGlobalProfile":[],"refreshAccount":["uuid"],"changeCape":["access_token","cape_uuid"],"getUsersFromAuthor":["provider","author"],"killProcess":["pid"],"getClusterById":["id"],"getWorlds":["id"],"updateClusterProfile":["name","profile"],"getGameVersions":[],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"updateClusterById":["id","request"],"getMultiplePackages":["provider","slugs"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"removeUser":["uuid"]}' }
export type Router = { 'folders': { fromCluster: (folderName: string) => Promise<string>,
openCluster: (folderName: string) => Promise<null> },
'debug': { openDevTools: () => Promise<void>,
isInDev: () => Promise<boolean>,
getArch: () => Promise<string>,
getFamily: () => Promise<string>,
getLocale: () => Promise<string | null>,
getType: () => Promise<string>,
getPlatform: () => Promise<string>,
getOsVersion: () => Promise<string>,
getGitCommitHash: () => Promise<string>,
getBuildTimestamp: () => Promise<string>,
getPackageVersion: () => Promise<string> },
'oneclient': { getClustersGroupedByMajor: () => Promise<Partial<{ [key in number]: ClusterModel[] }>>,
getBundlesFor: (clusterId: number) => Promise<ModpackArchive[]>,
getVersions: () => Promise<OnlineClusterManifest>,
checkForUpdate: () => Promise<Update | null>,
installUpdate: () => Promise<null> },
'core': { getClusters: () => Promise<ClusterModel[]>,
getClusterById: (id: number) => Promise<ClusterModel | null>,
removeCluster: (id: number) => Promise<null>,
createCluster: (options: CreateCluster) => Promise<ClusterModel>,
@ -355,27 +373,9 @@ 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>,
checkForUpdate: () => Promise<Update | null>,
installUpdate: () => Promise<null> },
'events': { ingress: (event: IngressPayload) => Promise<void>,
message: (event: MessagePayload) => Promise<void>,
process: (event: ProcessPayload) => Promise<void> },
'folders': { fromCluster: (folderName: string) => Promise<string>,
openCluster: (folderName: string) => Promise<null> },
'debug': { openDevTools: () => Promise<void>,
isInDev: () => Promise<boolean>,
getArch: () => Promise<string>,
getFamily: () => Promise<string>,
getLocale: () => Promise<string | null>,
getType: () => Promise<string>,
getPlatform: () => Promise<string>,
getOsVersion: () => Promise<string>,
getGitCommitHash: () => Promise<string>,
getBuildTimestamp: () => Promise<string>,
getPackageVersion: () => Promise<string> } };
process: (event: ProcessPayload) => Promise<void> } };
export type { InferCommandOutput }

View file

@ -46,6 +46,7 @@ const ResolvedPathNames: Record<URLPath, string> = {
'/app/settings/appearance': 'Viewing Settings',
'/app/settings/developer': 'Viewing Settings',
'/app/settings/minecraft': 'Viewing Settings',
'/app/settings/changelog': 'Viewing Settings',
'/app/accounts': 'Viewing Accounts',
'/app/clusters': 'Viewing Clusters',
'/onboarding': 'Preparing OneClient',

View file

@ -0,0 +1,128 @@
import { bindings } from '@/main';
import { Sidebar } from '@/routes/app/settings/route';
import { useCommand } from '@onelauncher/common';
import { createFileRoute } from '@tanstack/react-router';
import { ChevronDownIcon } from '@untitled-theme/icons-react';
import { useEffect, useState } from 'react';
import { twMerge } from 'tailwind-merge';
export const Route = createFileRoute('/app/settings/changelog')({
component: RouteComponent,
});
interface VersionDropdownProps {
version: string;
changes: Array<string>;
active?: boolean;
current?: boolean;
}
function VersionDropdown({ version, changes, active, current }: VersionDropdownProps) {
const [isOpen, setIsOpen] = useState<boolean>(current ?? active ?? false);
return (
<div className={twMerge('bg-page-elevated px-4 rounded-lg mb-4', isOpen ? 'py-4' : 'py-2 pt-4')}>
<div className="flex flex-row justify-between" onClick={() => setIsOpen(prev => !prev)}>
<h1 className="text-xl font-semibold mb-2 cursor-pointer">{version}{current ? ' (Currently Installed)' : ''} </h1>
<ChevronDownIcon className={twMerge((isOpen ? '' : 'rotate-90'), 'duration-500')} />
</div>
<div
className={twMerge(isOpen ? 'opacity-100 h-auto' : 'opacity-0 h-0 overflow-hidden', 'transition-all duration-300 ease-out')}
style={{ transitionProperty: 'opacity, height' }}
>
{isOpen && (
<div>
{changes.length > 0
? (
<ul className="list-disc pl-6">
{changes.map((change, index) => (
<li className="px-1 text-fg-secondary" key={index}>
{change}
</li>
))}
</ul>
)
: <p>No changes recorded for this version.</p>}
</div>
)}
</div>
</div>
);
};
interface ChangelogGroup {
version: string;
changes: Array<string>;
}
function RouteComponent() {
const [data, setData] = useState<string>('');
const [isLoading, setIsLoading] = useState<boolean>(true);
const [error, setError] = useState<any | null>(null);
const { data: version } = useCommand(['getPackageVersion'], () => bindings.debug.getPackageVersion());
useEffect(() => {
const getData = async () => {
try {
const response = await fetch('https://raw.githubusercontent.com/Polyfrost/OneLauncher/refs/heads/oneclient/main/docs/OneClient/CHANGE_LOG.md');
if (!response.ok)
throw new Error('Failed to fetch changelog');
const data = await response.text();
setData(data);
}
catch (error: unknown) {
if (error instanceof Error)
setError(error.message);
else
setError('An unknown error occurred');
}
finally {
setIsLoading(false);
}
};
getData();
}, []);
if (isLoading)
return <div>Loading...</div>;
if (error)
return <div>Error: {error}</div>;
const changelogGroups = data.split('\n').reduce<Array<ChangelogGroup>>((acc, line) => {
if (line.startsWith('# ')) {
acc.push({ version: line.replaceAll('# ', '').trim(), changes: [] });
}
else if (line.startsWith('- ')) {
if (acc.length > 0)
acc[acc.length - 1].changes.push(line.slice(2).trim());
}
else if (line === '###') {
return acc;
}
return acc;
}, []);
return (
<Sidebar.Page>
<div className="h-full">
{changelogGroups.map((group, groupIndex) => (
group.version
? (
<VersionDropdown
active={groupIndex === 0 ? true : undefined}
changes={group.changes}
current={group.version === version ? true : undefined}
key={groupIndex}
version={group.version}
/>
)
: null
))}
</div>
</Sidebar.Page>
);
}

View file

@ -1,7 +1,7 @@
import type { JSX } from 'react';
import { SheetPage } from '@/components';
import { createFileRoute, Outlet, useNavigate, useRouterState } from '@tanstack/react-router';
import { CodeSnippet02Icon, Rocket02Icon, Sliders04Icon } from '@untitled-theme/icons-react';
import { CodeSnippet02Icon, RefreshCcw02Icon, Rocket02Icon, Sliders04Icon } from '@untitled-theme/icons-react';
import { useEffect } from 'react';
export const Route = createFileRoute('/app/settings')({
@ -43,7 +43,7 @@ function RouteComponent() {
[<Sliders04Icon key="mcsettings" />, 'Minecraft settings', '/minecraft'],
],
'About': [
// [<RefreshCcw02Icon key="changelog" />, 'Changelog', '/'],
[<RefreshCcw02Icon key="changelog" />, 'Changelog', '/changelog'],
// [<MessageTextSquare01Icon key="feedback" />, 'Feedback', '/'],
[<CodeSnippet02Icon key="dev" />, 'Developer Options', '/developer'],
],

View file

@ -154,8 +154,8 @@ function AppShell({
children,
}: PropsWithChildren) {
const { isFirstStep, currentStepIndex } = Route.useLoaderData();
const { data: version } = useCommand(["getPackageVersion"], () => bindings.debug.getPackageVersion());
const { data: isInDev } = useCommand(["isInDev"], () => bindings.debug.isInDev());
const { data: version } = useCommand(['getPackageVersion'], () => bindings.debug.getPackageVersion());
const { data: isInDev } = useCommand(['isInDev'], () => bindings.debug.isInDev());
return (
<div className="flex flex-col h-full w-full">
@ -184,7 +184,7 @@ function AppShell({
<div className="p-4 text-xs text-fg-secondary">
<p>OneClient by Polyfrost</p>
<p>Version {version}</p>
<p>{isInDev ? "Development" : "Release"} Build</p>
<p>{isInDev ? 'Development' : 'Release'} Build</p>
</div>
</div>

View file

@ -0,0 +1,56 @@
# 1.0.0-alpha.6
###
- Refresh accounts automatically
# 1.0.0-alpha.5
###
- Actually fix decorations showing on Linux
- Fix JDK downloading error on Linux
- Fix RPC spamming Modrinth requests
- Add scroll bar on skins and capes
- Add a default animation speed to skin manager
- Select the player's selected cape by default
- Remove SkyClient provider option in mod browser
- Track external mods properly
- Fix not being able to be set memory to 0
- Add units to memory input
- Fix resolution default in settings
# 1.0.0-alpha.4
###
- Only turn on decorations on macOS
- Actually implement "enabled" for mods / categories
- Fix AppImage for Linux
# 1.0.0-alpha.3
###
- Make back button always go to home page
- Make MS login window bigger
- Fix lack of window rounding on macOS
- Fix copyright info on macOS
- Fix cluster being stuck on downloading
- Fix MS Auth failing sometimes
- Fix copy/paste not working on macOS
- Kill cluster overview page
# 1.0.0-alpha.2
###
- Fix reading Minecraft options.txt
- Don't search for external JDKs in OneClient (fixes 32-bit issue)
- Remove max concurrent request limit
- We're hoping this fixes issues with Microsoft logins
- Implement caching for modrinth projects/versions
- Downloads should be significantly faster
- Disable missing changelog/feedback page
- Implement app updates (Cluster/mod updates will come soon)
# 1.0.0-alpha.1
###
- Initial Alpha Release