Merge branch 'onelauncher/main' of https://github.com/Polyfrost/OneLauncher into onelauncher/main
This commit is contained in:
commit
e45da0d4db
35 changed files with 191 additions and 140 deletions
|
|
@ -20,7 +20,7 @@ interface TargetConfig {
|
||||||
}
|
}
|
||||||
interface BuildTarget {
|
interface BuildTarget {
|
||||||
updater: false | Updater;
|
updater: false | Updater;
|
||||||
standalone: TargetConfig[];
|
standalone: Array<TargetConfig>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const osTargets = {
|
const osTargets = {
|
||||||
|
|
@ -67,7 +67,7 @@ const frontendBundle = 'apps/frontend/dist.tar.xz';
|
||||||
const updaterName = `OneLauncher-Updater-${os}-${arch}`;
|
const updaterName = `OneLauncher-Updater-${os}-${arch}`;
|
||||||
const frontendName = `OneLauncher-Frontend-${os}-${arch}`;
|
const frontendName = `OneLauncher-Frontend-${os}-${arch}`;
|
||||||
|
|
||||||
const globFiles = async (pattern: string): Promise<string[]> => await (await glob.create(pattern)).glob();
|
const globFiles = async (pattern: string): Promise<Array<string>> => await (await glob.create(pattern)).glob();
|
||||||
|
|
||||||
async function uploadFrontend() {
|
async function uploadFrontend() {
|
||||||
if (!(await exists(frontendBundle))) {
|
if (!(await exists(frontendBundle))) {
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,7 @@
|
||||||
"build": "vite build && tsc",
|
"build": "vite build && tsc",
|
||||||
"serve": "vite preview",
|
"serve": "vite preview",
|
||||||
"lint": "eslint",
|
"lint": "eslint",
|
||||||
"format": "prettier",
|
"check": "eslint --fix"
|
||||||
"check": "prettier --write . && eslint --fix"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/poppins": "catalog:",
|
"@fontsource/poppins": "catalog:",
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
// @ts-check
|
|
||||||
|
|
||||||
/** @type {import('prettier').Config} */
|
|
||||||
const config = {
|
|
||||||
semi: true,
|
|
||||||
singleQuote: true,
|
|
||||||
trailingComma: 'all',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default config;
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import { useEffect, type PropsWithChildren } from 'react';
|
import type { PropsWithChildren } from 'react';
|
||||||
import { LoaderSuspense, Navbar } from '@/components';
|
import { LoaderSuspense, Navbar } from '@/components';
|
||||||
import { GameBackground } from '@/components/GameBackground';
|
import { GameBackground } from '@/components/GameBackground';
|
||||||
import useAppShellStore from '@/stores/appShellStore';
|
import useAppShellStore from '@/stores/appShellStore';
|
||||||
import { AnimatedOutlet } from '@onelauncher/common/components';
|
import { AnimatedOutlet } from '@onelauncher/common/components';
|
||||||
import { createFileRoute, useRouter } from '@tanstack/react-router';
|
import { createFileRoute, useRouter } from '@tanstack/react-router';
|
||||||
import { AnimatePresence } from 'motion/react';
|
import { AnimatePresence } from 'motion/react';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
export const Route = createFileRoute('/app')({
|
export const Route = createFileRoute('/app')({
|
||||||
component: RouteComponent,
|
component: RouteComponent,
|
||||||
|
|
@ -40,12 +41,12 @@ function AppShell({
|
||||||
const setPrevLocation = useAppShellStore(state => state.setPrevLocation);
|
const setPrevLocation = useAppShellStore(state => state.setPrevLocation);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsub = router.subscribe("onBeforeNavigate", (e) => {
|
const unsub = router.subscribe('onBeforeNavigate', (e) => {
|
||||||
setPrevLocation(e.fromLocation ?? null);
|
setPrevLocation(e.fromLocation ?? null);
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => unsub();
|
return () => unsub();
|
||||||
}, [router]);
|
}, [router, setPrevLocation]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full w-full">
|
<div className="flex flex-col h-full w-full">
|
||||||
|
|
|
||||||
|
|
@ -25,5 +25,5 @@
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"noUncheckedSideEffectImports": true
|
"noUncheckedSideEffectImports": true
|
||||||
},
|
},
|
||||||
"include": ["**/*.ts", "**/*.tsx", "prettier.config.js", "vite.config.js"]
|
"include": ["**/*.ts", "**/*.tsx", "vite.config.ts"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import type { Provider, SearchResult } from '@/bindings.gen';
|
import type { Provider, SearchResult } from '@/bindings.gen';
|
||||||
import { abbreviateNumber, LOADERS, upperFirst } from '@/utils';
|
import { abbreviateNumber, LOADERS, upperFirst } from '@/utils';
|
||||||
import { Show, Tooltip } from '@onelauncher/common/components';
|
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 { Download01Icon } from '@untitled-theme/icons-react';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { Focusable } from 'react-aria-components';
|
import { Focusable } from 'react-aria-components';
|
||||||
|
|
@ -22,7 +22,7 @@ export function PackageGrid({ items, provider }: { items: Array<SearchResult>; p
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PackageItem({ provider, ...item }: SearchResult & { provider: Provider }) {
|
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 (
|
return (
|
||||||
<Link
|
<Link
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ export function BrowserProvider(props: any) {
|
||||||
categories: null,
|
categories: null,
|
||||||
game_versions: null,
|
game_versions: null,
|
||||||
loaders: null,
|
loaders: null,
|
||||||
package_types: ["mod"]
|
package_types: ['mod'],
|
||||||
},
|
},
|
||||||
query: null,
|
query: null,
|
||||||
limit: 18 as unknown as bigint,
|
limit: 18 as unknown as bigint,
|
||||||
|
|
@ -47,7 +47,7 @@ export function BrowserProvider(props: any) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (query.filters?.categories || query.query)
|
if (query.filters?.categories || query.query)
|
||||||
navigate({ to: '/app/browser/search' });
|
navigate({ to: '/app/browser/search' });
|
||||||
}, [query]);
|
}, [navigate, query]);
|
||||||
|
|
||||||
const context = useMemo<BrowserControllerType>(() => ({
|
const context = useMemo<BrowserControllerType>(() => ({
|
||||||
cluster,
|
cluster,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import type { ClusterModel } from '@/bindings.gen';
|
import type { ClusterModel } from '@/bindings.gen';
|
||||||
import type { ModalProps } from '@/components/overlay/Modal';
|
import type { ModalProps } from '@/components/overlay/Modal';
|
||||||
|
import Modal from '@/components/overlay/Modal';
|
||||||
import { bindings } from '@/main';
|
import { bindings } from '@/main';
|
||||||
import { useCommand } from '@onelauncher/common';
|
import { useCommand } from '@onelauncher/common';
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import Modal from '@/components/overlay/Modal';
|
|
||||||
import { Button, Dropdown } from '@onelauncher/common/components';
|
import { Button, Dropdown } from '@onelauncher/common/components';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
import { Heading } from 'react-aria-components';
|
import { Heading } from 'react-aria-components';
|
||||||
|
|
||||||
export function useRecentCluster() {
|
export function useRecentCluster() {
|
||||||
|
|
@ -58,26 +58,29 @@ export function useClusters() {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChooseClusterModalProps = ModalProps & {
|
type ChooseClusterModalProps = ModalProps & {
|
||||||
clusters?: ClusterModel[];
|
clusters?: Array<ClusterModel>;
|
||||||
confirmText?: string;
|
confirmText?: string;
|
||||||
selected?: (clusters: ClusterModel[]) => number;
|
selected?: (clusters: Array<ClusterModel>) => number;
|
||||||
onSelected?: (cluster: ClusterModel) => void;
|
onSelected?: (cluster: ClusterModel) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function ChooseClusterModal({ clusters: clusterList, selected: defaultSelected, onSelected, confirmText, ...props }: ChooseClusterModalProps) {
|
||||||
export function ChooseClusterModal({clusters: clusterList, selected: defaultSelected, onSelected, confirmText, ...props}: ChooseClusterModalProps) {
|
const clusters = useClusters();
|
||||||
const clusters = useClusters()
|
|
||||||
const [selected, setSelected] = useState(0);
|
const [selected, setSelected] = useState(0);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!clusters) return;
|
if (!clusters)
|
||||||
const newSelected = defaultSelected?.(clusters)
|
return;
|
||||||
if (newSelected) setSelected(newSelected)
|
|
||||||
}, [clusters])
|
const newSelected = defaultSelected?.(clusters);
|
||||||
|
|
||||||
|
if (newSelected)
|
||||||
|
setSelected(newSelected);
|
||||||
|
}, [clusters, defaultSelected]);
|
||||||
|
|
||||||
function chooseCluster() {
|
function chooseCluster() {
|
||||||
if (clusters !== undefined && selected !== undefined) {
|
if (clusters !== undefined) {
|
||||||
const cluster = clusters[selected];
|
const cluster = clusters[selected] as ClusterModel | undefined;
|
||||||
if (cluster !== undefined)
|
if (cluster !== undefined)
|
||||||
onSelected?.(cluster);
|
onSelected?.(cluster);
|
||||||
}
|
}
|
||||||
|
|
@ -87,35 +90,35 @@ export function ChooseClusterModal({clusters: clusterList, selected: defaultSele
|
||||||
<Modal {...props}>
|
<Modal {...props}>
|
||||||
<div className="min-w-sm flex flex-col rounded-lg bg-page text-center p-4 gap-2">
|
<div className="min-w-sm flex flex-col rounded-lg bg-page text-center p-4 gap-2">
|
||||||
|
|
||||||
<Heading slot='title' className='text-xl font-semibold'>Select a Cluster</Heading>
|
<Heading className="text-xl font-semibold" slot="title">Select a Cluster</Heading>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
onSelectionChange={(index) => {
|
onSelectionChange={(index) => {
|
||||||
setSelected(index as number)
|
setSelected(index as number);
|
||||||
}}
|
}}
|
||||||
selectedKey={selected}
|
selectedKey={selected}
|
||||||
>
|
>
|
||||||
{clusters?.map((cluster, index) => (
|
{clusters?.map((cluster, index) => (
|
||||||
<Dropdown.Item id={index} key={cluster.id}>
|
<Dropdown.Item id={index} key={cluster.id}>
|
||||||
{cluster.name}
|
{cluster.name}
|
||||||
</Dropdown.Item>
|
</Dropdown.Item>
|
||||||
))}
|
))}
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
|
||||||
<div className='flex gap-2'>
|
<div className="flex gap-2">
|
||||||
<Button
|
<Button
|
||||||
color="secondary"
|
children="Close"
|
||||||
children="Close"
|
color="secondary"
|
||||||
slot="close"
|
slot="close"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
children={confirmText ?? "Save"}
|
children={confirmText ?? 'Save'}
|
||||||
slot="close"
|
onClick={chooseCluster}
|
||||||
onClick={chooseCluster}
|
slot="close"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
);
|
||||||
|
|
||||||
// return (
|
// return (
|
||||||
// <Modal
|
// <Modal
|
||||||
|
|
|
||||||
|
|
@ -46,20 +46,20 @@ function usePagination(options: PaginationOptions) {
|
||||||
return pages;
|
return pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
function PaginationBtn(props: { page: number }) {
|
function PaginationBtn({ page: pageIndex }: { page: number }) {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
className="p-2 text-lg min-w-8"
|
className="p-2 text-lg min-w-8"
|
||||||
color={props.page === page ? 'primary' : 'secondary'}
|
color={pageIndex === page ? 'primary' : 'secondary'}
|
||||||
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (props.page === page)
|
if (pageIndex === page)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
setPage(props.page);
|
setPage(pageIndex);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className="text-trim">{props.page}</span>
|
<span className="text-trim">{pageIndex}</span>
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
import type { Settings } from '@/bindings.gen';
|
import type { Settings } from '@/bindings.gen';
|
||||||
import { bindings } from '@/main';
|
import { bindings } from '@/main';
|
||||||
import { useCommand } from '@onelauncher/common';
|
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 {
|
interface SettingsControllerType {
|
||||||
settings: Settings | undefined;
|
settings: Settings | undefined;
|
||||||
|
|
@ -12,7 +12,7 @@ interface SettingsControllerType {
|
||||||
setSettingsToSave: (settings: Partial<Settings> | null) => void;
|
setSettingsToSave: (settings: Partial<Settings> | null) => void;
|
||||||
settingsChanged: boolean;
|
settingsChanged: boolean;
|
||||||
setSettingsChanged: (changed: boolean) => void;
|
setSettingsChanged: (changed: boolean) => void;
|
||||||
createSetting: <K extends keyof Settings, V = Settings[K]>(name: K, value: V) => [V, (value: V) => void];
|
createSetting: <K extends keyof Settings, V = Settings[K]>(name: K, value: V) => [V, (v: V) => void];
|
||||||
}
|
}
|
||||||
|
|
||||||
const SettingsContext = createContext<SettingsControllerType | null>(null);
|
const SettingsContext = createContext<SettingsControllerType | null>(null);
|
||||||
|
|
@ -51,14 +51,14 @@ export function SettingsProvider({ children }: SettingsProviderProps) {
|
||||||
return () => window.removeEventListener('beforeunload', handleBeforeUnload);
|
return () => window.removeEventListener('beforeunload', handleBeforeUnload);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const saveChangedSettings = () => {
|
const saveChangedSettings = useCallback(() => {
|
||||||
if (!settings)
|
if (!settings)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
writeQuery.refetch();
|
writeQuery.refetch();
|
||||||
|
|
||||||
setSettingsChanged(false);
|
setSettingsChanged(false);
|
||||||
};
|
}, [settings, writeQuery]);
|
||||||
|
|
||||||
const handleSetSettingsToSave = useCallback((newSettings: Partial<Settings> | null) => {
|
const handleSetSettingsToSave = useCallback((newSettings: Partial<Settings> | null) => {
|
||||||
setSettingsToSave(newSettings);
|
setSettingsToSave(newSettings);
|
||||||
|
|
@ -86,7 +86,7 @@ export function SettingsProvider({ children }: SettingsProviderProps) {
|
||||||
return [value, setValue];
|
return [value, setValue];
|
||||||
}, [settings]);
|
}, [settings]);
|
||||||
|
|
||||||
const controller: SettingsControllerType = {
|
const controller: SettingsControllerType = useMemo(() => ({
|
||||||
settings,
|
settings,
|
||||||
saveChangedSettings,
|
saveChangedSettings,
|
||||||
settingsToSave,
|
settingsToSave,
|
||||||
|
|
@ -94,7 +94,7 @@ export function SettingsProvider({ children }: SettingsProviderProps) {
|
||||||
settingsChanged,
|
settingsChanged,
|
||||||
setSettingsChanged,
|
setSettingsChanged,
|
||||||
createSetting,
|
createSetting,
|
||||||
};
|
}), [createSetting, handleSetSettingsToSave, saveChangedSettings, settings, settingsChanged, settingsToSave]);
|
||||||
|
|
||||||
if (settings === undefined)
|
if (settings === undefined)
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ function Featured() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const featuredImageIndex = useMemo(() => featuredPackage.data?.gallery.findIndex(image => image.featured), [featuredPackage.data]);
|
const featuredImageIndex = useMemo(() => featuredPackage.data?.gallery.findIndex(image => image.featured), [featuredPackage.data]);
|
||||||
const [selectedImage, setSelectedImage] = useState(featuredImageIndex ?? 0);
|
const [selectedImage, setSelectedImage] = useState(featuredImageIndex ?? 0);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Show when={featuredPackage.isSuccess}>
|
<Show when={featuredPackage.isSuccess}>
|
||||||
<div className="flex flex-col gap-y-1">
|
<div className="flex flex-col gap-y-1">
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,12 @@ function RouteComponent() {
|
||||||
if (!includes(PROVIDERS, provider))
|
if (!includes(PROVIDERS, provider))
|
||||||
throw new Error('Invalid provider');
|
throw new Error('Invalid provider');
|
||||||
const packageData = usePackageData(provider, slug, {});
|
const packageData = usePackageData(provider, slug, {});
|
||||||
|
const packageContextValue = useMemo(() => ({
|
||||||
|
pkg: packageData.data,
|
||||||
|
}), [packageData.data]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PackageContext value={{ pkg: packageData.data }}>
|
<PackageContext value={packageContextValue}>
|
||||||
<Show when={packageData.isSuccess && !packageData.isFetching}>
|
<Show when={packageData.isSuccess && !packageData.isFetching}>
|
||||||
<div className="h-full flex flex-1 flex-row items-start gap-x-4">
|
<div className="h-full flex flex-1 flex-row items-start gap-x-4">
|
||||||
<BrowserSidebar package={packageData.data!} />
|
<BrowserSidebar package={packageData.data!} />
|
||||||
|
|
@ -121,8 +124,8 @@ function getPackageUrl(pkg: ManagedPackage): string {
|
||||||
function BrowserSidebar({ package: pkg }: { package: ManagedPackage }) {
|
function BrowserSidebar({ package: pkg }: { package: ManagedPackage }) {
|
||||||
const { provider } = Route.useParams();
|
const { provider } = Route.useParams();
|
||||||
|
|
||||||
const createdAt = useMemo(() => pkg.created ? new Date(pkg.created) : null, []);
|
const createdAt = useMemo(() => pkg.created ? new Date(pkg.created) : null, [pkg.created]);
|
||||||
const updatedAt = useMemo(() => pkg.updated ? new Date(pkg.updated) : null, []);
|
const updatedAt = useMemo(() => pkg.updated ? new Date(pkg.updated) : null, [pkg.updated]);
|
||||||
|
|
||||||
const authors = useCommand('getUsersFromAuthor', () => bindings.core.getUsersFromAuthor(provider as Provider, pkg.author));
|
const authors = useCommand('getUsersFromAuthor', () => bindings.core.getUsersFromAuthor(provider as Provider, pkg.author));
|
||||||
|
|
||||||
|
|
@ -303,7 +306,7 @@ function InstallButton() {
|
||||||
aria-label="cluster" isOpen={open}
|
aria-label="cluster" isOpen={open}
|
||||||
onOpenChange={setOpen}
|
onOpenChange={setOpen}
|
||||||
onSelectionChange={(e) => {
|
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)
|
if (cluster)
|
||||||
browserContext.setCluster(cluster);
|
browserContext.setCluster(cluster);
|
||||||
}}
|
}}
|
||||||
|
|
@ -382,6 +385,7 @@ function Versions() {
|
||||||
limit: 20,
|
limit: 20,
|
||||||
offset,
|
offset,
|
||||||
});
|
});
|
||||||
|
|
||||||
const pagination = usePagination({
|
const pagination = usePagination({
|
||||||
itemsCount: versions?.total as unknown as number,
|
itemsCount: versions?.total as unknown as number,
|
||||||
itemsPerPage: versions?.limit as unknown as number,
|
itemsPerPage: versions?.limit as unknown as number,
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
import type { Filters } from '@/bindings.gen';
|
import type { Filters } from '@/bindings.gen';
|
||||||
|
import type { PropsWithChildren } from 'react';
|
||||||
import ProviderIcon from '@/components/content/ProviderIcon';
|
import ProviderIcon from '@/components/content/ProviderIcon';
|
||||||
import { BrowserProvider, useBrowserContext } from '@/hooks/useBrowser';
|
import { BrowserProvider, useBrowserContext } from '@/hooks/useBrowser';
|
||||||
import { useClusters } from '@/hooks/useCluster';
|
import { useClusters } from '@/hooks/useCluster';
|
||||||
import { PROVIDERS } from '@/utils';
|
import { PROVIDERS } from '@/utils';
|
||||||
import { browserCategories } from '@/utils/browser';
|
import { browserCategories } from '@/utils/browser';
|
||||||
import { AnimatedOutlet, Dropdown, Show, TextField } from '@onelauncher/common/components';
|
import { AnimatedOutlet, Dropdown, Show, TextField } from '@onelauncher/common/components';
|
||||||
import { createFileRoute, Outlet } from '@tanstack/react-router';
|
import { createFileRoute } from '@tanstack/react-router';
|
||||||
import { SearchMdIcon } from '@untitled-theme/icons-react';
|
import { SearchMdIcon } from '@untitled-theme/icons-react';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
|
@ -27,7 +28,7 @@ function RouteComponent() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BrowserLayout(props: any) {
|
export function BrowserLayout({ children }: PropsWithChildren) {
|
||||||
return (
|
return (
|
||||||
<div className="relative h-full flex flex-1 flex-col items-center gap-2">
|
<div className="relative h-full flex flex-1 flex-col items-center gap-2">
|
||||||
<div className="h-full w-full flex flex-1 flex-col items-center gap-y-2">
|
<div className="h-full w-full flex flex-1 flex-col items-center gap-y-2">
|
||||||
|
|
@ -42,7 +43,7 @@ export function BrowserLayout(props: any) {
|
||||||
|
|
||||||
<div className="h-full flex flex-col gap-y-4">
|
<div className="h-full flex flex-col gap-y-4">
|
||||||
<div className="h-full flex-1">
|
<div className="h-full flex-1">
|
||||||
{props.children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -56,11 +57,14 @@ export function BrowserLayout(props: any) {
|
||||||
function BrowserSidebar() {
|
function BrowserSidebar() {
|
||||||
const context = useBrowserContext();
|
const context = useBrowserContext();
|
||||||
const clusters = useClusters();
|
const clusters = useClusters();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const game_versions = context.cluster?.mc_version ? [context.cluster.mc_version] : null;
|
const game_versions = context.cluster?.mc_version ? [context.cluster.mc_version] : null;
|
||||||
const loaders = context.cluster?.mc_loader ? [context.cluster.mc_loader] : null;
|
const loaders = context.cluster?.mc_loader ? [context.cluster.mc_loader] : null;
|
||||||
context.setQuery({ ...context.query, filters: { ...defaultFilters, ...context.query.filters, game_versions, loaders } });
|
context.setQuery({ ...context.query, filters: { ...defaultFilters, ...context.query.filters, game_versions, loaders } });
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- loop
|
||||||
}, [context.cluster]);
|
}, [context.cluster]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-y-4">
|
<div className="flex flex-col gap-y-4">
|
||||||
<div className="flex flex-col gap-y-4">
|
<div className="flex flex-col gap-y-4">
|
||||||
|
|
@ -68,7 +72,7 @@ function BrowserSidebar() {
|
||||||
<h6 className="my-1 uppercase opacity-60">Active Cluster</h6>
|
<h6 className="my-1 uppercase opacity-60">Active Cluster</h6>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
onSelectionChange={(id) => {
|
onSelectionChange={(id) => {
|
||||||
const cluster = clusters?.find(cluster => cluster.id.toString() === id);
|
const cluster = clusters?.find(item => item.id.toString() === id);
|
||||||
context.setCluster(cluster);
|
context.setCluster(cluster);
|
||||||
}}
|
}}
|
||||||
selectedKey={context.cluster?.id.toString()}
|
selectedKey={context.cluster?.id.toString()}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
import type { Paginated, SearchResult } from '@/bindings.gen';
|
import type { Paginated, SearchResult } from '@/bindings.gen';
|
||||||
import type { PaginationOptions } from '@/hooks/usePagination';
|
|
||||||
import { PackageGrid } from '@/components/content/PackageItem';
|
import { PackageGrid } from '@/components/content/PackageItem';
|
||||||
import { useBrowserContext, useBrowserSearch } from '@/hooks/useBrowser';
|
import { useBrowserContext, useBrowserSearch } from '@/hooks/useBrowser';
|
||||||
import usePagination from '@/hooks/usePagination';
|
import usePagination from '@/hooks/usePagination';
|
||||||
import { Show } from '@onelauncher/common/components';
|
|
||||||
import { createFileRoute } from '@tanstack/react-router';
|
import { createFileRoute } from '@tanstack/react-router';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { BrowserLayout } from './route';
|
import { BrowserLayout } from './route';
|
||||||
|
|
||||||
export const Route = createFileRoute('/app/browser/search')({
|
export const Route = createFileRoute('/app/browser/search')({
|
||||||
|
|
@ -25,10 +23,12 @@ function RouteComponent() {
|
||||||
function Search() {
|
function Search() {
|
||||||
const context = useBrowserContext();
|
const context = useBrowserContext();
|
||||||
const search = useBrowserSearch(context.provider, context.query, {
|
const search = useBrowserSearch(context.provider, context.query, {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
search.refetch();
|
search.refetch();
|
||||||
}, [context.provider, context.query, context.cluster]);
|
}, [context.provider, context.query, context.cluster, search]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -44,19 +44,23 @@ function Search() {
|
||||||
function Results({ results }: { results: Paginated<SearchResult> }) {
|
function Results({ results }: { results: Paginated<SearchResult> }) {
|
||||||
const context = useBrowserContext();
|
const context = useBrowserContext();
|
||||||
const [oldTotal, setOldTotal] = useState(results.total);
|
const [oldTotal, setOldTotal] = useState(results.total);
|
||||||
|
|
||||||
const pagination = usePagination({
|
const pagination = usePagination({
|
||||||
itemsCount: results.total as unknown as number,
|
itemsCount: results.total as unknown as number,
|
||||||
itemsPerPage: context.query.limit as unknown as number,
|
itemsPerPage: context.query.limit as unknown as number,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
context.setQuery({ ...context.query, offset: pagination.offset as unknown as bigint });
|
context.setQuery({ ...context.query, offset: pagination.offset as unknown as bigint });
|
||||||
}, [pagination.page]);
|
}, [context, pagination.offset, pagination.page]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (oldTotal === results.total)
|
if (oldTotal === results.total)
|
||||||
return;
|
return;
|
||||||
pagination.reset();
|
pagination.reset();
|
||||||
setOldTotal(results.total);
|
setOldTotal(results.total);
|
||||||
}, [results.total]);
|
}, [oldTotal, pagination, results.total]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="w-full flex justify-end my-2">
|
<div className="w-full flex justify-end my-2">
|
||||||
|
|
|
||||||
|
|
@ -48,9 +48,7 @@ function RouteComponent() {
|
||||||
})();
|
})();
|
||||||
}, [cluster.data?.folder_name]);
|
}, [cluster.data?.folder_name]);
|
||||||
|
|
||||||
const openClusterDir = async () => {
|
const openClusterDir = () => openPath(launcherDir);
|
||||||
openPath(launcherDir);
|
|
||||||
};
|
|
||||||
|
|
||||||
async function handleEditMode() {
|
async function handleEditMode() {
|
||||||
if (edit)
|
if (edit)
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { NewClusterCreate } from '@/components/launcher/cluster/ClusterCreation'
|
||||||
import { useRecentCluster } from '@/hooks/useCluster';
|
import { useRecentCluster } from '@/hooks/useCluster';
|
||||||
import { bindings } from '@/main';
|
import { bindings } from '@/main';
|
||||||
import { formatAsDuration, upperFirst } from '@/utils';
|
import { formatAsDuration, upperFirst } from '@/utils';
|
||||||
import { useCommand, useCommandMut, useCommandSuspense } from '@onelauncher/common';
|
import { useCommand, useCommandMut } from '@onelauncher/common';
|
||||||
import { Button, Show } from '@onelauncher/common/components';
|
import { Button, Show } from '@onelauncher/common/components';
|
||||||
import { createFileRoute, Link } from '@tanstack/react-router';
|
import { createFileRoute, Link } from '@tanstack/react-router';
|
||||||
import { convertFileSrc } from '@tauri-apps/api/core';
|
import { convertFileSrc } from '@tauri-apps/api/core';
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,7 @@ function RouteComponent() {
|
||||||
})();
|
})();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const openLauncherDir = async () => {
|
const openLauncherDir = () => openPath(launcherDir);
|
||||||
openPath(launcherDir);
|
|
||||||
};
|
|
||||||
|
|
||||||
const [discordRpc, setDiscordRpc] = createSetting('discord_enabled', settings?.discord_enabled);
|
const [discordRpc, setDiscordRpc] = createSetting('discord_enabled', settings?.discord_enabled);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,27 +86,28 @@ function Sidebar(props: SidebarProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-56 flex flex-col pr-2">
|
<div className="w-56 flex flex-col pr-2">
|
||||||
{Object.keys(links).map((section, i) => (
|
{Object.keys(links).map(section => (
|
||||||
<div className="flex flex-col gap-y-2" key={i}>
|
<div className="flex flex-col gap-y-2" key={section}>
|
||||||
<div>
|
<div>
|
||||||
<p className="m-1.5 mt-5 text-xs text-fg-secondary font-medium">{section.toUpperCase()}</p>
|
<p className="m-1.5 mt-5 text-xs text-fg-secondary font-medium">{section.toUpperCase()}</p>
|
||||||
<div className="flex flex-col gap-y-1 fill-fg-primary text-fg-primary font-medium">
|
<div className="flex flex-col gap-y-1 fill-fg-primary text-fg-primary font-medium">
|
||||||
{links[section].map((link, i) => {
|
{links[section].map((entry) => {
|
||||||
// eslint yaps too much tbh
|
if (!entry)
|
||||||
if (!link)
|
|
||||||
return '';
|
return '';
|
||||||
|
|
||||||
|
const [Component, label, link, params] = entry;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
className={
|
className={
|
||||||
`px-3 py-1 rounded-md text-md border hover:bg-component-bg-hover active:bg-component-bg-pressed ${isActive(link[2], link[3]) ? 'bg-component-bg border-component-bg-pressed' : 'border-transparent'}`
|
`px-3 py-1 rounded-md text-md border hover:bg-component-bg-hover active:bg-component-bg-pressed ${isActive(link, params) ? 'bg-component-bg border-component-bg-pressed' : 'border-transparent'}`
|
||||||
}
|
}
|
||||||
key={i}
|
key={link}
|
||||||
onClick={() => goto(link[2], link[3])}
|
onClick={() => goto(link, params)}
|
||||||
>
|
>
|
||||||
<span className="flex flex-row items-center gap-x-3 *:w-5">
|
<span className="flex flex-row items-center gap-x-3 *:w-5">
|
||||||
{link[0]}
|
{Component}
|
||||||
{link[1]}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ function RouteComponent() {
|
||||||
setIsForwardButtonEnabled(true);
|
setIsForwardButtonEnabled(true);
|
||||||
}, [currentStepIndex, tasksStage, tasksCompleted]);
|
}, [currentStepIndex, tasksStage, tasksCompleted]);
|
||||||
|
|
||||||
const next = useCallback(async () => {
|
const next = useCallback(() => {
|
||||||
const isLast = currentStepIndex === steps.length - 1;
|
const isLast = currentStepIndex === steps.length - 1;
|
||||||
|
|
||||||
if (isLast) {
|
if (isLast) {
|
||||||
|
|
@ -140,14 +140,17 @@ function RouteComponent() {
|
||||||
setTasksMessage('Starting setup...');
|
setTasksMessage('Starting setup...');
|
||||||
|
|
||||||
const tasksToRun = [
|
const tasksToRun = [
|
||||||
async () => {
|
() => new Promise<void>((resolve) => {
|
||||||
setTasksMessage(`Setting language to ${language?.lang}...`);
|
setTasksMessage(`Setting language to ${language?.lang}...`);
|
||||||
|
|
||||||
console.log('Language task completed for:', language?.lang);
|
console.log('Language task completed for:', language?.lang);
|
||||||
},
|
|
||||||
async () => {
|
resolve();
|
||||||
|
}),
|
||||||
|
() => new Promise<void>((resolve) => {
|
||||||
if (importInstancesMap.size === 0) {
|
if (importInstancesMap.size === 0) {
|
||||||
console.log('No instances selected for import.');
|
console.log('No instances selected for import.');
|
||||||
|
resolve();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,7 +168,9 @@ function RouteComponent() {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
resolve();
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
const results = await Promise.allSettled(tasksToRun.map(task => task()));
|
const results = await Promise.allSettled(tasksToRun.map(task => task()));
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import type { PackageType, Provider } from "@/bindings.gen";
|
import type { PackageType, Provider } from '@/bindings.gen';
|
||||||
|
|
||||||
export const BROWSER_VIEWS: Array<string> = ['grid', 'list'] as const;
|
export const BROWSER_VIEWS: Array<string> = ['grid', 'list'] as const;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,12 +41,12 @@ export function int32ToBigInt([high, low]: [number, number]): bigint {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function randomString(len: number = 6, charSet: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') {
|
export function randomString(len: number = 6, charSet: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') {
|
||||||
let randomString = '';
|
let str = '';
|
||||||
for (let i = 0; i < len; i++) {
|
for (let i = 0; i < len; i++) {
|
||||||
const randomPos = Math.floor(Math.random() * charSet.length);
|
const randomPos = Math.floor(Math.random() * charSet.length);
|
||||||
randomString += charSet.substring(randomPos, randomPos + 1);
|
str += charSet.substring(randomPos, randomPos + 1);
|
||||||
}
|
}
|
||||||
return randomString;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function abbreviateNumber(n: number | bigint, locale: string = 'en-US'): string {
|
export function abbreviateNumber(n: number | bigint, locale: string = 'en-US'): string {
|
||||||
|
|
|
||||||
|
|
@ -27,5 +27,5 @@
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"noUncheckedSideEffectImports": true
|
"noUncheckedSideEffectImports": true
|
||||||
},
|
},
|
||||||
"include": ["**/*.ts", "**/*.tsx", "vite.config.js"]
|
"include": ["**/*.ts", "**/*.tsx", "vite.config.ts"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,22 @@ export default defineConfig(
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}),
|
}),
|
||||||
|
{
|
||||||
|
ignores: [
|
||||||
|
'apps/onelauncher/old_frontend',
|
||||||
|
'apps/*/desktop/capabilities',
|
||||||
|
'apps/*/desktop/gen',
|
||||||
|
'**/*.gen.*',
|
||||||
|
],
|
||||||
|
// overrides: [
|
||||||
|
// {
|
||||||
|
// files: ['vite.config.js'],
|
||||||
|
// parserOptions: {
|
||||||
|
// project: null, // disables type-aware linting for this file
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
rules: {
|
rules: {
|
||||||
'sort-imports': 'off', // Replaced by perfectionist/sort-named-imports',
|
'sort-imports': 'off', // Replaced by perfectionist/sort-named-imports',
|
||||||
|
|
@ -31,7 +47,14 @@ export default defineConfig(
|
||||||
'ts/no-use-before-define': 'off',
|
'ts/no-use-before-define': 'off',
|
||||||
'style/function-paren-newline': ['error', 'consistent'],
|
'style/function-paren-newline': ['error', 'consistent'],
|
||||||
'react/no-context-provider': 'off',
|
'react/no-context-provider': 'off',
|
||||||
"prefer-const": "off" // disabled due to maximum call stack size
|
'prefer-const': 'off', // disabled due to maximum call stack size
|
||||||
|
'eslint-comments/no-unlimited-disable': 'off',
|
||||||
|
'eslint-comments/require-description': ['error', {
|
||||||
|
ignore: [
|
||||||
|
'eslint-disable',
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
'no-shadow': 'off',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prep": "pnpm scripts prep",
|
"prep": "pnpm scripts prep",
|
||||||
"typecheck": "pnpm -r typecheck",
|
"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 -- ",
|
"core": "pnpm --filter @onelauncher/core -- ",
|
||||||
"migrate": "sea-orm-cli migrate -d ./packages/migration",
|
"migrate": "sea-orm-cli migrate -d ./packages/migration",
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ interface MappedCategoryItem {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type MappingList = Record<string, MappedCategoryItem[]>;
|
type MappingList = Record<string, Array<MappedCategoryItem>>;
|
||||||
|
|
||||||
const packageTypes = ['mod', 'modpack', 'shader', 'resourcepack'] as const;
|
const packageTypes = ['mod', 'modpack', 'shader', 'resourcepack'] as const;
|
||||||
type PackageType = typeof packageTypes[number];
|
type PackageType = typeof packageTypes[number];
|
||||||
|
|
@ -43,7 +43,7 @@ async function fetchCategoriesCurseforge(packageType: PackageType): Promise<Mapp
|
||||||
parentCategoryId?: number;
|
parentCategoryId?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const data = (await response.json() as { data: CFCategory[] }).data;
|
const data = (await response.json() as { data: Array<CFCategory> }).data;
|
||||||
|
|
||||||
// TODO: Add more calssIds
|
// TODO: Add more calssIds
|
||||||
const classIds: Record<PackageType, number> = {
|
const classIds: Record<PackageType, number> = {
|
||||||
|
|
@ -79,7 +79,7 @@ async function fetchCategoriesModrinth(packageType: PackageType): Promise<Mappin
|
||||||
header: string;
|
header: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json() as ModrinthCategory[];
|
const data = await response.json() as Array<ModrinthCategory>;
|
||||||
|
|
||||||
const mapping: MappingList = {};
|
const mapping: MappingList = {};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { exec } from 'node:child_process';
|
||||||
import { readFile, writeFile } from 'node:fs/promises';
|
import { readFile, writeFile } from 'node:fs/promises';
|
||||||
import { consola } from 'consola';
|
import { consola } from 'consola';
|
||||||
import mustache from 'mustache';
|
import mustache from 'mustache';
|
||||||
|
|
@ -77,3 +78,16 @@ catch (error) {
|
||||||
consola.error(error);
|
consola.error(error);
|
||||||
env.__exit(1);
|
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);
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ if (cargoConfig.env && typeof cargoConfig.env === 'object')
|
||||||
|
|
||||||
function targetFilter(filter: 'b' | 't') {
|
function targetFilter(filter: 'b' | 't') {
|
||||||
const filters = filter === 'b' ? ['-b', '--bundles'] : ['-t', '--target'];
|
const filters = filter === 'b' ? ['-b', '--bundles'] : ['-t', '--target'];
|
||||||
return (_: string, idx: number, args: string[]) => {
|
return (_: string, idx: number, args: Array<string>) => {
|
||||||
if (idx === 0)
|
if (idx === 0)
|
||||||
return false;
|
return false;
|
||||||
const prev = args[idx - 1] ?? '';
|
const prev = args[idx - 1] ?? '';
|
||||||
|
|
|
||||||
|
|
@ -45,9 +45,9 @@ export async function which(cmd: TemplateStringsArray): Promise<boolean> {
|
||||||
return type() === 'Windows_NT'
|
return type() === 'Windows_NT'
|
||||||
? where(cmd[0]!)
|
? where(cmd[0]!)
|
||||||
: Promise.any(
|
: Promise.any(
|
||||||
Array.from(new Set(process.env.PATH?.split(':')))
|
Array.from(new Set(process.env.PATH?.split(':')))
|
||||||
.map(p => fs.access(join(p, cmd[0]!), fs.constants.X_OK)),
|
.map(p => fs.access(join(p, cmd[0]!), fs.constants.X_OK)),
|
||||||
).then(_ => true, _ => false);
|
).then(_ => true, _ => false);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function awaitLock(file: string): Promise<void> {
|
export async function awaitLock(file: string): Promise<void> {
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ export async function tauriUpdateKey(env: CheckedEnvironment): Promise<string |
|
||||||
return keys.publicKey;
|
return keys.publicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function patchTauri(env: CheckedEnvironment, targets: string[], args: string[]): Promise<string[]> {
|
export async function patchTauri(env: CheckedEnvironment, targets: Array<string>, args: Array<string>): Promise<Array<string>> {
|
||||||
consola.start('patching tauri configuration...');
|
consola.start('patching tauri configuration...');
|
||||||
if (args.findIndex(a => ['--config', '-c'].includes(a)) !== -1)
|
if (args.findIndex(a => ['--config', '-c'].includes(a)) !== -1)
|
||||||
throw new Error('custom tauri build configuration is not supported!');
|
throw new Error('custom tauri build configuration is not supported!');
|
||||||
|
|
@ -55,7 +55,7 @@ export async function patchTauri(env: CheckedEnvironment, targets: string[], arg
|
||||||
const osType = type();
|
const osType = type();
|
||||||
const tauriPatch: {
|
const tauriPatch: {
|
||||||
build: {
|
build: {
|
||||||
features: string[];
|
features: Array<string>;
|
||||||
};
|
};
|
||||||
bundle: {
|
bundle: {
|
||||||
macOS: { minimumSystemVersion: string };
|
macOS: { minimumSystemVersion: string };
|
||||||
|
|
@ -107,10 +107,10 @@ export async function patchTauri(env: CheckedEnvironment, targets: string[], arg
|
||||||
if (
|
if (
|
||||||
(targets.includes('aarch64-apple-darwin')
|
(targets.includes('aarch64-apple-darwin')
|
||||||
|| (targets.length === 0 && process.arch === 'arm64'))
|
|| (targets.length === 0 && process.arch === 'arm64'))
|
||||||
&& (macOSStore.minimumVersion == null || semver.lt(
|
&& (macOSStore.minimumVersion == null || semver.lt(
|
||||||
semver.coerce(macOSStore.minimumVersion)!,
|
semver.coerce(macOSStore.minimumVersion)!,
|
||||||
semver.coerce(macOSStore.defaultArm64)!,
|
semver.coerce(macOSStore.defaultArm64)!,
|
||||||
))
|
))
|
||||||
) {
|
) {
|
||||||
macOSStore.minimumVersion = macOSStore.defaultArm64;
|
macOSStore.minimumVersion = macOSStore.defaultArm64;
|
||||||
consola.debug(`setting minimum macOS version to ${macOSStore.minimumVersion}...`);
|
consola.debug(`setting minimum macOS version to ${macOSStore.minimumVersion}...`);
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,20 @@
|
||||||
[package]
|
[package]
|
||||||
name = "serde_json_path_to_error"
|
name = "serde_json_path_to_error"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
authors = [ "Polyfrost", "Ethan Brierley <ethanboxx@gmail.com>" ]
|
authors = [
|
||||||
|
"Polyfrost",
|
||||||
|
"Ethan Brierley <ethanboxx@gmail.com>"
|
||||||
|
]
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
description = """
|
description = """
|
||||||
A drop in replacement for serde_json where detailed errors are the default
|
A drop in replacement for serde_json where detailed errors are the default
|
||||||
"""
|
"""
|
||||||
keywords = [ "serde", "json", "serialization" ]
|
keywords = [
|
||||||
|
"serde",
|
||||||
|
"json",
|
||||||
|
"serialization"
|
||||||
|
]
|
||||||
documentation = "https://docs.rs/serde_json_path_to_error/"
|
documentation = "https://docs.rs/serde_json_path_to_error/"
|
||||||
repository = "https://github.com/eopb/serde_json_path_to_error"
|
repository = "https://github.com/eopb/serde_json_path_to_error"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
/* eslint-disable eslint-comments/require-description -- i trust that one github discussion user on this */
|
|
||||||
/* eslint-disable react/no-unstable-default-props */
|
/* eslint-disable react/no-unstable-default-props */
|
||||||
|
|
||||||
// Taken from https://github.com/TanStack/router/discussions/823#discussioncomment-12765059
|
// Taken from https://github.com/TanStack/router/discussions/823#discussioncomment-12765059
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import type { JSX, ReactNode } from 'react';
|
import type { JSX } from 'react';
|
||||||
import React, { createContext, useContext } from 'react';
|
import React, { createContext, useCallback, useContext, useMemo } from 'react';
|
||||||
|
|
||||||
interface SelectListContextType {
|
interface SelectListContextType {
|
||||||
onSelect: (value: string) => void;
|
onSelect: (value: string) => void;
|
||||||
|
|
@ -30,7 +30,7 @@ export const SelectList: React.FC<SelectListProps> & { Item: React.FC<SelectList
|
||||||
multiple = false,
|
multiple = false,
|
||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
const handleSelect = (itemValue: string): void => {
|
const handleSelect = useCallback((itemValue: string): void => {
|
||||||
if (multiple) {
|
if (multiple) {
|
||||||
const currentValues = Array.isArray(value) ? value : [];
|
const currentValues = Array.isArray(value) ? value : [];
|
||||||
const newValues = currentValues.includes(itemValue)
|
const newValues = currentValues.includes(itemValue)
|
||||||
|
|
@ -41,20 +41,20 @@ export const SelectList: React.FC<SelectListProps> & { Item: React.FC<SelectList
|
||||||
else {
|
else {
|
||||||
onChange(itemValue);
|
onChange(itemValue);
|
||||||
}
|
}
|
||||||
};
|
}, [multiple, onChange, value]);
|
||||||
|
|
||||||
const isSelected = (itemValue: string): boolean => {
|
const isSelected = useCallback((itemValue: string): boolean => {
|
||||||
if (multiple)
|
if (multiple)
|
||||||
return Array.isArray(value) && value.includes(itemValue);
|
return Array.isArray(value) && value.includes(itemValue);
|
||||||
|
|
||||||
return value === itemValue;
|
return value === itemValue;
|
||||||
};
|
}, [multiple, value]);
|
||||||
|
|
||||||
const contextValue: SelectListContextType = {
|
const contextValue: SelectListContextType = useMemo(() => ({
|
||||||
onSelect: handleSelect,
|
onSelect: handleSelect,
|
||||||
isSelected,
|
isSelected,
|
||||||
multiple,
|
multiple,
|
||||||
};
|
}), [handleSelect, isSelected, multiple]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SelectListContext.Provider value={contextValue}>
|
<SelectListContext.Provider value={contextValue}>
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ catalog:
|
||||||
# ---
|
# ---
|
||||||
# dependencies
|
# dependencies
|
||||||
# ---
|
# ---
|
||||||
|
|
||||||
# react + libraries
|
# react + libraries
|
||||||
react: ^19.0.0
|
react: ^19.0.0
|
||||||
react-aria-components: ^1.8.0
|
react-aria-components: ^1.8.0
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue