From 5ac215a5c20ffbe03786c6a42c6d0d077e55aabb Mon Sep 17 00:00:00 2001 From: LynithDev <61880709+LynithDev@users.noreply.github.com> Date: Tue, 22 Jul 2025 11:16:06 +0200 Subject: [PATCH 1/2] chore: add web_commons build to prepare script --- package.json | 2 +- packages/scripts/prepare.ts | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 43425058..f85c541c 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "scripts": { "prep": "pnpm scripts prep", "typecheck": "pnpm -r typecheck", - "bootstrap": "cargo clean && ./packages/scripts/setup.sh && pnpm install && pnpm prep && pnpm codegen", + "bootstrap": "cargo clean && ./packages/scripts/setup.sh && pnpm install && pnpm prep", "core": "pnpm --filter @onelauncher/core -- ", "migrate": "sea-orm-cli migrate -d ./packages/migration", diff --git a/packages/scripts/prepare.ts b/packages/scripts/prepare.ts index d5e04cca..cefd2b09 100644 --- a/packages/scripts/prepare.ts +++ b/packages/scripts/prepare.ts @@ -1,3 +1,4 @@ +import { exec } from 'node:child_process'; import { readFile, writeFile } from 'node:fs/promises'; import { consola } from 'consola'; import mustache from 'mustache'; @@ -77,3 +78,16 @@ catch (error) { consola.error(error); env.__exit(1); } + +consola.start('building the web_commons package...'); +exec('pnpm -w web_common build', { cwd: env.__root }, (error, stdout, stderr) => { + if (error) { + consola.error(`web_commons build failed: ${error.message}`); + env.__exit(1); + } + if (stderr) + consola.error(stderr); + + consola.success('web_commons package built successfully!'); + env.__exit(0); +}); From cd40d951f0ec3eaff1a2c34b38bf2fedeac38e80 Mon Sep 17 00:00:00 2001 From: LynithDev <61880709+LynithDev@users.noreply.github.com> Date: Tue, 22 Jul 2025 12:45:22 +0200 Subject: [PATCH 2/2] chore: fix eslint warnings --- .../actions/publish-artifacts/src/index.ts | 4 +- apps/oneclient/frontend/package.json | 3 +- apps/oneclient/frontend/prettier.config.js | 10 - .../frontend/src/routes/app/route.tsx | 7 +- apps/oneclient/frontend/tsconfig.json | 2 +- .../{vite.config.js => vite.config.ts} | 0 .../src/components/content/PackageItem.tsx | 4 +- .../frontend/src/hooks/useBrowser.tsx | 4 +- .../frontend/src/hooks/useCluster.tsx | 81 ++++---- .../frontend/src/hooks/usePagination.tsx | 10 +- .../frontend/src/hooks/useSettings.tsx | 12 +- .../frontend/src/routes/app/browser/index.tsx | 1 + .../app/browser/package.$provider.$slug.tsx | 177 ++++++++++-------- .../frontend/src/routes/app/browser/route.tsx | 14 +- .../src/routes/app/browser/search.tsx | 16 +- .../frontend/src/routes/app/cluster/index.tsx | 4 +- .../frontend/src/routes/app/index.tsx | 2 +- .../src/routes/app/settings/index.tsx | 4 +- .../src/routes/app/settings/route.tsx | 21 ++- .../frontend/src/routes/onboarding/route.tsx | 15 +- .../onelauncher/frontend/src/utils/browser.ts | 2 +- apps/onelauncher/frontend/src/utils/index.ts | 6 +- apps/onelauncher/frontend/tsconfig.json | 2 +- .../{vite.config.js => vite.config.ts} | 0 eslint.config.js | 25 ++- packages/scripts/api/fetch_categories.ts | 6 +- packages/scripts/tauri.ts | 2 +- packages/scripts/utils/index.ts | 6 +- packages/scripts/utils/patch.ts | 12 +- packages/serde_json_path_to_error/Cargo.toml | 11 +- .../src/components/AnimatedOutlet.tsx | 1 - .../web_common/src/components/SelectList.tsx | 16 +- pnpm-workspace.yaml | 2 +- 33 files changed, 272 insertions(+), 210 deletions(-) delete mode 100644 apps/oneclient/frontend/prettier.config.js rename apps/oneclient/frontend/{vite.config.js => vite.config.ts} (100%) rename apps/onelauncher/frontend/{vite.config.js => vite.config.ts} (100%) diff --git a/.github/actions/publish-artifacts/src/index.ts b/.github/actions/publish-artifacts/src/index.ts index d9a30ec8..48c5ba53 100644 --- a/.github/actions/publish-artifacts/src/index.ts +++ b/.github/actions/publish-artifacts/src/index.ts @@ -20,7 +20,7 @@ interface TargetConfig { } interface BuildTarget { updater: false | Updater; - standalone: TargetConfig[]; + standalone: Array; } const osTargets = { @@ -67,7 +67,7 @@ const frontendBundle = 'apps/frontend/dist.tar.xz'; const updaterName = `OneLauncher-Updater-${os}-${arch}`; const frontendName = `OneLauncher-Frontend-${os}-${arch}`; -const globFiles = async (pattern: string): Promise => await (await glob.create(pattern)).glob(); +const globFiles = async (pattern: string): Promise> => await (await glob.create(pattern)).glob(); async function uploadFrontend() { if (!(await exists(frontendBundle))) { diff --git a/apps/oneclient/frontend/package.json b/apps/oneclient/frontend/package.json index c97fb0fb..7a5ea661 100644 --- a/apps/oneclient/frontend/package.json +++ b/apps/oneclient/frontend/package.json @@ -8,8 +8,7 @@ "build": "vite build && tsc", "serve": "vite preview", "lint": "eslint", - "format": "prettier", - "check": "prettier --write . && eslint --fix" + "check": "eslint --fix" }, "dependencies": { "@fontsource/poppins": "catalog:", diff --git a/apps/oneclient/frontend/prettier.config.js b/apps/oneclient/frontend/prettier.config.js deleted file mode 100644 index adcd2f4d..00000000 --- a/apps/oneclient/frontend/prettier.config.js +++ /dev/null @@ -1,10 +0,0 @@ -// @ts-check - -/** @type {import('prettier').Config} */ -const config = { - semi: true, - singleQuote: true, - trailingComma: 'all', -}; - -export default config; diff --git a/apps/oneclient/frontend/src/routes/app/route.tsx b/apps/oneclient/frontend/src/routes/app/route.tsx index 6a4fd5bf..5de18320 100644 --- a/apps/oneclient/frontend/src/routes/app/route.tsx +++ b/apps/oneclient/frontend/src/routes/app/route.tsx @@ -1,10 +1,11 @@ -import { useEffect, type PropsWithChildren } from 'react'; +import type { PropsWithChildren } from 'react'; import { LoaderSuspense, Navbar } from '@/components'; import { GameBackground } from '@/components/GameBackground'; import useAppShellStore from '@/stores/appShellStore'; import { AnimatedOutlet } from '@onelauncher/common/components'; import { createFileRoute, useRouter } from '@tanstack/react-router'; import { AnimatePresence } from 'motion/react'; +import { useEffect } from 'react'; export const Route = createFileRoute('/app')({ component: RouteComponent, @@ -40,12 +41,12 @@ function AppShell({ const setPrevLocation = useAppShellStore(state => state.setPrevLocation); useEffect(() => { - const unsub = router.subscribe("onBeforeNavigate", (e) => { + const unsub = router.subscribe('onBeforeNavigate', (e) => { setPrevLocation(e.fromLocation ?? null); }); return () => unsub(); - }, [router]); + }, [router, setPrevLocation]); return (
diff --git a/apps/oneclient/frontend/tsconfig.json b/apps/oneclient/frontend/tsconfig.json index 3dd88644..e442b494 100644 --- a/apps/oneclient/frontend/tsconfig.json +++ b/apps/oneclient/frontend/tsconfig.json @@ -25,5 +25,5 @@ "skipLibCheck": true, "noUncheckedSideEffectImports": true }, - "include": ["**/*.ts", "**/*.tsx", "prettier.config.js", "vite.config.js"] + "include": ["**/*.ts", "**/*.tsx", "vite.config.ts"] } diff --git a/apps/oneclient/frontend/vite.config.js b/apps/oneclient/frontend/vite.config.ts similarity index 100% rename from apps/oneclient/frontend/vite.config.js rename to apps/oneclient/frontend/vite.config.ts diff --git a/apps/onelauncher/frontend/src/components/content/PackageItem.tsx b/apps/onelauncher/frontend/src/components/content/PackageItem.tsx index ef7c9acf..a990c9b7 100644 --- a/apps/onelauncher/frontend/src/components/content/PackageItem.tsx +++ b/apps/onelauncher/frontend/src/components/content/PackageItem.tsx @@ -1,7 +1,7 @@ import type { Provider, SearchResult } from '@/bindings.gen'; import { abbreviateNumber, LOADERS, upperFirst } from '@/utils'; import { Show, Tooltip } from '@onelauncher/common/components'; -import { Link, useNavigate } from '@tanstack/react-router'; +import { Link } from '@tanstack/react-router'; import { Download01Icon } from '@untitled-theme/icons-react'; import { useMemo } from 'react'; import { Focusable } from 'react-aria-components'; @@ -22,7 +22,7 @@ export function PackageGrid({ items, provider }: { items: Array; p } export function PackageItem({ provider, ...item }: SearchResult & { provider: Provider }) { - const loaders = useMemo(() => item.categories.filter(cat => includes(LOADERS, cat)), []); + const loaders = useMemo(() => item.categories.filter(cat => includes(LOADERS, cat)), [item.categories]); return ( { if (query.filters?.categories || query.query) navigate({ to: '/app/browser/search' }); - }, [query]); + }, [navigate, query]); const context = useMemo(() => ({ cluster, diff --git a/apps/onelauncher/frontend/src/hooks/useCluster.tsx b/apps/onelauncher/frontend/src/hooks/useCluster.tsx index c188f7c1..23220181 100644 --- a/apps/onelauncher/frontend/src/hooks/useCluster.tsx +++ b/apps/onelauncher/frontend/src/hooks/useCluster.tsx @@ -1,10 +1,10 @@ import type { ClusterModel } from '@/bindings.gen'; import type { ModalProps } from '@/components/overlay/Modal'; +import Modal from '@/components/overlay/Modal'; import { bindings } from '@/main'; import { useCommand } from '@onelauncher/common'; -import { useEffect, useState } from 'react'; -import Modal from '@/components/overlay/Modal'; import { Button, Dropdown } from '@onelauncher/common/components'; +import { useEffect, useState } from 'react'; import { Heading } from 'react-aria-components'; export function useRecentCluster() { @@ -58,26 +58,29 @@ export function useClusters() { } type ChooseClusterModalProps = ModalProps & { - clusters?: ClusterModel[]; + clusters?: Array; confirmText?: string; - selected?: (clusters: ClusterModel[]) => number; + selected?: (clusters: Array) => number; onSelected?: (cluster: ClusterModel) => void; }; - -export function ChooseClusterModal({clusters: clusterList, selected: defaultSelected, onSelected, confirmText, ...props}: ChooseClusterModalProps) { - const clusters = useClusters() +export function ChooseClusterModal({ clusters: clusterList, selected: defaultSelected, onSelected, confirmText, ...props }: ChooseClusterModalProps) { + const clusters = useClusters(); const [selected, setSelected] = useState(0); useEffect(() => { - if (!clusters) return; - const newSelected = defaultSelected?.(clusters) - if (newSelected) setSelected(newSelected) - }, [clusters]) + if (!clusters) + return; + + const newSelected = defaultSelected?.(clusters); + + if (newSelected) + setSelected(newSelected); + }, [clusters, defaultSelected]); function chooseCluster() { - if (clusters !== undefined && selected !== undefined) { - const cluster = clusters[selected]; + if (clusters !== undefined) { + const cluster = clusters[selected] as ClusterModel | undefined; if (cluster !== undefined) onSelected?.(cluster); } @@ -87,35 +90,35 @@ export function ChooseClusterModal({clusters: clusterList, selected: defaultSele
- Select a Cluster - { - setSelected(index as number) - }} - selectedKey={selected} - > - {clusters?.map((cluster, index) => ( - - {cluster.name} - - ))} - + Select a Cluster + { + setSelected(index as number); + }} + selectedKey={selected} + > + {clusters?.map((cluster, index) => ( + + {cluster.name} + + ))} + -
-
+
+
- ) + ); // return ( // { - if (props.page === page) + if (pageIndex === page) return; - setPage(props.page); + setPage(pageIndex); }} > - {props.page} + {pageIndex} ); } diff --git a/apps/onelauncher/frontend/src/hooks/useSettings.tsx b/apps/onelauncher/frontend/src/hooks/useSettings.tsx index cf820eee..927ea61b 100644 --- a/apps/onelauncher/frontend/src/hooks/useSettings.tsx +++ b/apps/onelauncher/frontend/src/hooks/useSettings.tsx @@ -3,7 +3,7 @@ import type { Settings } from '@/bindings.gen'; import { bindings } from '@/main'; import { useCommand } from '@onelauncher/common'; -import React, { createContext, useCallback, useContext, useEffect, useState } from 'react'; +import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; interface SettingsControllerType { settings: Settings | undefined; @@ -12,7 +12,7 @@ interface SettingsControllerType { setSettingsToSave: (settings: Partial | null) => void; settingsChanged: boolean; setSettingsChanged: (changed: boolean) => void; - createSetting: (name: K, value: V) => [V, (value: V) => void]; + createSetting: (name: K, value: V) => [V, (v: V) => void]; } const SettingsContext = createContext(null); @@ -51,14 +51,14 @@ export function SettingsProvider({ children }: SettingsProviderProps) { return () => window.removeEventListener('beforeunload', handleBeforeUnload); }, []); - const saveChangedSettings = () => { + const saveChangedSettings = useCallback(() => { if (!settings) return; writeQuery.refetch(); setSettingsChanged(false); - }; + }, [settings, writeQuery]); const handleSetSettingsToSave = useCallback((newSettings: Partial | null) => { setSettingsToSave(newSettings); @@ -86,7 +86,7 @@ export function SettingsProvider({ children }: SettingsProviderProps) { return [value, setValue]; }, [settings]); - const controller: SettingsControllerType = { + const controller: SettingsControllerType = useMemo(() => ({ settings, saveChangedSettings, settingsToSave, @@ -94,7 +94,7 @@ export function SettingsProvider({ children }: SettingsProviderProps) { settingsChanged, setSettingsChanged, createSetting, - }; + }), [createSetting, handleSetSettingsToSave, saveChangedSettings, settings, settingsChanged, settingsToSave]); if (settings === undefined) return null; diff --git a/apps/onelauncher/frontend/src/routes/app/browser/index.tsx b/apps/onelauncher/frontend/src/routes/app/browser/index.tsx index f376a604..6d55b3ff 100644 --- a/apps/onelauncher/frontend/src/routes/app/browser/index.tsx +++ b/apps/onelauncher/frontend/src/routes/app/browser/index.tsx @@ -32,6 +32,7 @@ function Featured() { const navigate = useNavigate(); const featuredImageIndex = useMemo(() => featuredPackage.data?.gallery.findIndex(image => image.featured), [featuredPackage.data]); const [selectedImage, setSelectedImage] = useState(featuredImageIndex ?? 0); + return (
diff --git a/apps/onelauncher/frontend/src/routes/app/browser/package.$provider.$slug.tsx b/apps/onelauncher/frontend/src/routes/app/browser/package.$provider.$slug.tsx index e6a763b2..f39d0fb5 100644 --- a/apps/onelauncher/frontend/src/routes/app/browser/package.$provider.$slug.tsx +++ b/apps/onelauncher/frontend/src/routes/app/browser/package.$provider.$slug.tsx @@ -3,6 +3,7 @@ import type { HTMLProps } from 'react'; import Modal from '@/components/overlay/Modal'; import { useBrowserContext, usePackageData, usePackageVersions } from '@/hooks/useBrowser'; import { ChooseClusterModal, useClusters } from '@/hooks/useCluster'; +import usePagination from '@/hooks/usePagination'; import { bindings } from '@/main'; import { abbreviateNumber, formatAsRelative, PROVIDERS, upperFirst } from '@/utils'; import { useCommand } from '@onelauncher/common'; @@ -16,7 +17,6 @@ import Markdown from 'react-markdown'; import rehypeRaw from 'rehype-raw'; import remarkGfm from 'remark-gfm'; import { twMerge } from 'tailwind-merge'; -import usePagination from '@/hooks/usePagination'; export const Route = createFileRoute('/app/browser/package/$provider/$slug')({ component: RouteComponent, @@ -26,10 +26,10 @@ function includes(list: { includes: (arg0: TArray) => boole return list.includes(element as unknown as TArray); } -function CustomA({ href, children, includeIcon, className, ...rest }: { href: string; children: any, includeIcon?: boolean } & HTMLProps) { +function CustomA({ href, children, includeIcon, className, ...rest }: { href: string; children: any; includeIcon?: boolean } & HTMLProps) { return ( { e.preventDefault(); @@ -38,7 +38,7 @@ function CustomA({ href, children, includeIcon, className, ...rest }: { href: st {...rest} > {children} - {includeIcon && } + {includeIcon && } ); } @@ -56,10 +56,14 @@ function RouteComponent() { if (!includes(PROVIDERS, provider)) throw new Error('Invalid provider'); const packageData = usePackageData(provider, slug, {}); - const browserContext = useBrowserContext(); + // const browserContext = useBrowserContext(); + + const packageContextValue = useMemo(() => ({ + pkg: packageData.data, + }), [packageData.data]); return ( - +
@@ -84,14 +88,14 @@ function RouteComponent() {
- + {packageData.data?.gallery.map(item => ( -
+
{item.title &&
{item.title}
}
@@ -113,17 +117,17 @@ function RouteComponent() { function getPackageUrl(pkg: ManagedPackage): string { switch (pkg.provider) { - case 'Modrinth': return `https://modrinth.com/project/${pkg.slug}` - case 'Curseforge': return `https://www.curseforge.com/minecraft/${pkg.package_type}s/${pkg.slug}` - case 'SkyClient': return `` + case 'Modrinth': return `https://modrinth.com/project/${pkg.slug}`; + case 'Curseforge': return `https://www.curseforge.com/minecraft/${pkg.package_type}s/${pkg.slug}`; + case 'SkyClient': return ``; } } function BrowserSidebar({ package: pkg }: { package: ManagedPackage }) { const { provider } = Route.useParams(); - const createdAt = useMemo(() => pkg.created ? new Date(pkg.created) : null, []); - const updatedAt = useMemo(() => pkg.updated ? new Date(pkg.updated) : null, []); + const createdAt = useMemo(() => pkg.created ? new Date(pkg.created) : null, [pkg.created]); + const updatedAt = useMemo(() => pkg.updated ? new Date(pkg.updated) : null, [pkg.updated]); const authors = useCommand('getUsersFromAuthor', () => bindings.core.getUsersFromAuthor(provider as Provider, pkg.author)); @@ -163,18 +167,32 @@ function BrowserSidebar({ package: pkg }: { package: ManagedPackage }) {

Links

-
- +
+ {provider} {' '} Page - {(Object.entries(pkg.links) as [keyof typeof pkg.links, typeof pkg.links[keyof typeof pkg.links]][]).filter(a => a[1]).map(link => - typeof link[1] == "string" - ? - : (link[1] as PackageDonationUrl[]).map(donationLink => ) - - )} + {(Object.entries(pkg.links) as Array<[keyof typeof pkg.links, typeof pkg.links[keyof typeof pkg.links]]>).filter(a => a[1]).map(link => + typeof link[1] == 'string' + ? ( + + ) + : (link[1] as Array).map(donationLink => ( + + )))}
@@ -232,19 +250,20 @@ function BrowserSidebar({ package: pkg }: { package: ManagedPackage }) { function InstallButton() { const triggerRef = useRef(null); const { provider, slug } = Route.useParams(); - if(!includes(PROVIDERS, provider)) throw new Error("invalid provider") + if (!includes(PROVIDERS, provider)) + throw new Error('invalid provider'); const [open, setOpen] = useState(false); const clusters = useClusters(); const browserContext = useBrowserContext(); const { data: versions } = usePackageVersions(provider, slug, { mc_versions: browserContext.cluster ? [browserContext.cluster.mc_version] : [], loaders: browserContext.cluster ? [browserContext.cluster.mc_loader] : [], - limit: 1 + limit: 1, }); const version = useMemo(() => { if (!versions || !browserContext.cluster) return undefined; - return versions.items[0] + return versions.items[0]; }, [browserContext.cluster, versions]); function download() { @@ -268,12 +287,12 @@ function InstallButton() { browserContext.cluster ? version ? ( - - Download latest to -
- {browserContext.cluster.name} -
- ) + + Download latest to +
+ {browserContext.cluster.name} +
+ ) : `No matching version found` : 'Select a Cluster' } @@ -289,7 +308,7 @@ function InstallButton() { aria-label="cluster" isOpen={open} onOpenChange={setOpen} onSelectionChange={(e) => { - const cluster = clusters?.find(cluster => cluster.id as unknown as number === e); + const cluster = clusters?.find(item => item.id as unknown as number === e); if (cluster) browserContext.setCluster(cluster); }} @@ -359,50 +378,60 @@ function colorForType(type: string) { } } -function Versions(){ - const {provider, slug} = Route.useParams() - if(!includes(PROVIDERS, provider)) throw new Error("invalid provider") - const [offset, setOffset] = useState(0) +function Versions() { + const { provider, slug } = Route.useParams(); + + if (!includes(PROVIDERS, provider)) + throw new Error('invalid provider'); + + const [offset, setOffset] = useState(0); + const { data: versions } = usePackageVersions(provider, slug, { limit: 20, - offset + offset, }); + const pagination = usePagination({ itemsCount: versions?.total as unknown as number, - itemsPerPage: versions?.limit as unknown as number - }) - useEffect(()=>{ - setOffset(pagination.offset) - },[pagination.offset]) - useEffect(()=>{ - pagination.reset() - },[versions?.total]) - return
-
- + itemsPerPage: versions?.limit as unknown as number, + }); + + useEffect(() => { + setOffset(pagination.offset); + }, [pagination.offset]); + + useEffect(() => { + pagination.reset(); + }, [pagination, versions?.total]); + + return ( +
+
+ +
+ + + + Name + Game Versions + Loaders + Created + Downloads + + + + {versions?.items.map(item => + )} + +
- - - - Name - Game Versions - Loaders - Created - Downloads - - - - {versions?.items.map(item=> - - )} - -
-
+ ); } -function VersionRow({version}:{version:ManagedVersion}){ - const {provider} = Route.useParams() - if(!includes(PROVIDERS, provider)) throw new Error("invalid provider") +function VersionRow({ version }: { version: ManagedVersion }) { + const { provider } = Route.useParams(); + if (!includes(PROVIDERS, provider)) + throw new Error('invalid provider'); return ( @@ -413,22 +442,22 @@ function VersionRow({version}:{version:ManagedVersion}){ {version.display_name} - {version.mc_versions.join(", ")} - {version.loaders.map(upperFirst).join(", ")} + {version.mc_versions.join(', ')} + {version.loaders.map(upperFirst).join(', ')} {formatAsRelative(Date.parse(version.published))} {version.downloads} -