Prettier.

This commit is contained in:
Kent Keirsey
2025-06-22 19:22:07 -04:00
committed by psychedelicious
parent 188cf37f48
commit ee4bc49bd4
3 changed files with 143 additions and 130 deletions

View File

@@ -815,7 +815,8 @@
"inplaceInstall": "In-place install",
"inplaceInstallDesc": "Install models without copying the files. When using the model, it will be loaded from its this location. If disabled, the model file(s) will be copied into the Invoke-managed models directory during installation.",
"install": "Install",
"installAll": "Install All", "installRepo": "Install Repo",
"installAll": "Install All",
"installRepo": "Install Repo",
"ipAdapters": "IP Adapters",
"learnMoreAboutSupportedModels": "Learn more about the models we support",
"load": "Load",
@@ -869,7 +870,8 @@
"sigLip": "SigLIP",
"spandrelImageToImage": "Image to Image (Spandrel)",
"starterBundles": "Starter Bundles",
"starterBundleHelpText": "Easily install all models needed to get started with a base model, including a main model, controlnets, IP adapters, and more. Selecting a bundle will skip any models that you already have installed.", "starterModels": "Starter Models",
"starterBundleHelpText": "Easily install all models needed to get started with a base model, including a main model, controlnets, IP adapters, and more. Selecting a bundle will skip any models that you already have installed.",
"starterModels": "Starter Models",
"starterModelsInModelManager": "Starter Models can be found in Model Manager",
"launchpadTab": "Launchpad",
"launchpad": {
@@ -878,7 +880,8 @@
"manualInstall": "Manual Installation",
"urlDescription": "Install models from a URL or local file path. Perfect for specific models you want to add.",
"huggingFaceDescription": "Browse and install models directly from HuggingFace repositories.",
"scanFolderDescription": "Scan a local folder to automatically detect and install models.", "recommendedModels": "Recommended Models",
"scanFolderDescription": "Scan a local folder to automatically detect and install models.",
"recommendedModels": "Recommended Models",
"exploreStarter": "Browse all available starter models",
"quickStart": "Quick Start Bundles",
"bundleDescription": "Each bundle includes essential models for each model family and curated base models to get started",

View File

@@ -7,7 +7,7 @@ import { flatMap, negate, uniqWith } from 'lodash-es';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiFolderOpenBold, PiLinkBold, PiStarBold } from 'react-icons/pi';
import { SiHuggingface } from "react-icons/si";
import { SiHuggingface } from 'react-icons/si';
import { useGetStarterModelsQuery, useInstallModelMutation } from 'services/api/endpoints/models';
export const LaunchpadForm = memo(() => {
@@ -16,49 +16,52 @@ export const LaunchpadForm = memo(() => {
const { getIsInstalled, buildModelInstallArg } = useBuildModelInstallArg();
const { data: starterModelsData } = useGetStarterModelsQuery();
// Function to install models from a bundle
const installBundle = useCallback((bundleName: string) => {
if (!starterModelsData?.starter_bundles) {
return;
}
const bundle = starterModelsData.starter_bundles[bundleName];
if (!bundle) {
return;
}
const installBundle = useCallback(
(bundleName: string) => {
if (!starterModelsData?.starter_bundles) {
return;
}
// Flatten the models and remove duplicates, which is expected as models can have the same dependencies
const flattenedModels = flatMap(bundle, flattenStarterModel);
const uniqueModels = uniqWith(
flattenedModels,
(m1, m2) => m1.source === m2.source || (m1.name === m2.name && m1.base === m2.base && m1.type === m2.type)
);
// We want to install models that are not installed and skip models that are already installed
const install = uniqueModels.filter(negate(getIsInstalled)).map(buildModelInstallArg);
const skip = uniqueModels.filter(getIsInstalled).map(buildModelInstallArg);
const bundle = starterModelsData.starter_bundles[bundleName];
if (!bundle) {
return;
}
// Flatten the models and remove duplicates, which is expected as models can have the same dependencies
const flattenedModels = flatMap(bundle, flattenStarterModel);
const uniqueModels = uniqWith(
flattenedModels,
(m1, m2) => m1.source === m2.source || (m1.name === m2.name && m1.base === m2.base && m1.type === m2.type)
);
// We want to install models that are not installed and skip models that are already installed
const install = uniqueModels.filter(negate(getIsInstalled)).map(buildModelInstallArg);
const skip = uniqueModels.filter(getIsInstalled).map(buildModelInstallArg);
if (install.length === 0) {
toast({
status: 'info',
title: t('modelManager.bundleAlreadyInstalled', { bundleName }),
description: t('modelManager.allModelsAlreadyInstalled'),
});
return;
}
// Install all models in the bundle
install.forEach(installModel);
let description = t('modelManager.installingXModels', { count: install.length });
if (skip.length > 1) {
description += t('modelManager.skippingXDuplicates', { count: skip.length - 1 });
}
if (install.length === 0) {
toast({
status: 'info',
title: t('modelManager.bundleAlreadyInstalled', { bundleName }),
description: t('modelManager.allModelsAlreadyInstalled'),
title: t('modelManager.installingBundle'),
description,
});
return;
}
// Install all models in the bundle
install.forEach(installModel);
let description = t('modelManager.installingXModels', { count: install.length });
if (skip.length > 1) {
description += t('modelManager.skippingXDuplicates', { count: skip.length - 1 });
}
toast({
status: 'info',
title: t('modelManager.installingBundle'),
description,
});
}, [starterModelsData, getIsInstalled, buildModelInstallArg, installModel, t]);
},
[starterModelsData, getIsInstalled, buildModelInstallArg, installModel, t]
);
const navigateToUrlTab = useCallback(() => {
$installModelsTab.set(1); // URL/Local Path tab (now index 1)
@@ -71,7 +74,7 @@ export const LaunchpadForm = memo(() => {
const navigateToScanFolderTab = useCallback(() => {
$installModelsTab.set(3); // Scan Folder tab (now index 3)
}, []);
const navigateToStarterModelsTab = useCallback(() => {
$installModelsTab.set(4); // Starter Models tab (now index 4)
}, []);
@@ -85,90 +88,91 @@ export const LaunchpadForm = memo(() => {
const handleFluxBundleClick = useCallback(() => {
installBundle('flux');
}, [installBundle]); return (
}, [installBundle]);
return (
<Flex flexDir="column" height="100%" gap={3}>
<ScrollableContent>
<Flex flexDir="column" gap={6} p={3}>
{/* Welcome Section */}
<Box>
<Heading size="md" mb={1}>
{t('modelManager.launchpad.welcome')}
</Heading>
<Text color="base.300" fontSize="sm">
{t('modelManager.launchpad.description')}
</Text>
</Box>
{/* Manual Installation Options */}
<Box>
<Heading size="sm" mb={2}>
{t('modelManager.launchpad.manualInstall')}
</Heading> <Grid templateColumns="repeat(auto-fit, minmax(280px, 1fr))" gap={3}>
<LaunchpadCard
title={t('modelManager.urlOrLocalPath')}
description={t('modelManager.launchpad.urlDescription')}
icon={<PiLinkBold size={24} />}
onClick={navigateToUrlTab}
/>
<LaunchpadCard
title={t('modelManager.huggingFace')}
description={t('modelManager.launchpad.huggingFaceDescription')}
icon={<SiHuggingface size={24} />}
onClick={navigateToHuggingFaceTab}
/>
<LaunchpadCard
title={t('modelManager.scanFolder')}
description={t('modelManager.launchpad.scanFolderDescription')}
icon={<PiFolderOpenBold size={24} />}
onClick={navigateToScanFolderTab}
/>
</Grid>
</Box> {/* Recommended Section */}
<Box>
<Heading size="sm" mb={2}>
{t('modelManager.launchpad.recommendedModels')}
</Heading>
<Flex flexDir="column" gap={2}> {/* Starter Model Bundles - More Prominent */}
{/* Welcome Section */}
<Box>
<Heading size="xs" color="base.100" mb={1}>
{t('modelManager.launchpad.quickStart')}
<Heading size="md" mb={1}>
{t('modelManager.launchpad.welcome')}
</Heading>
<Text fontSize="xs" color="base.300" mb={2}>
{t('modelManager.launchpad.bundleDescription')}
</Text> <Grid templateColumns="repeat(auto-fit, minmax(180px, 1fr))" gap={2}>
<LaunchpadBundleCard
title="Stable Diffusion 1.5"
onClick={handleSD15BundleClick}
<Text color="base.300" fontSize="sm">
{t('modelManager.launchpad.description')}
</Text>
</Box>
{/* Manual Installation Options */}
<Box>
<Heading size="sm" mb={2}>
{t('modelManager.launchpad.manualInstall')}
</Heading>{' '}
<Grid templateColumns="repeat(auto-fit, minmax(280px, 1fr))" gap={3}>
<LaunchpadCard
title={t('modelManager.urlOrLocalPath')}
description={t('modelManager.launchpad.urlDescription')}
icon={<PiLinkBold size={24} />}
onClick={navigateToUrlTab}
/>
<LaunchpadBundleCard
title="SDXL"
onClick={handleSDXLBundleClick}
<LaunchpadCard
title={t('modelManager.huggingFace')}
description={t('modelManager.launchpad.huggingFaceDescription')}
icon={<SiHuggingface size={24} />}
onClick={navigateToHuggingFaceTab}
/>
<LaunchpadBundleCard
title="FLUX.1 [dev]"
onClick={handleFluxBundleClick}
<LaunchpadCard
title={t('modelManager.scanFolder')}
description={t('modelManager.launchpad.scanFolderDescription')}
icon={<PiFolderOpenBold size={24} />}
onClick={navigateToScanFolderTab}
/>
</Grid>
</Box> {/* Browse All - Simple Link */}
<Box pt={1} borderTop="1px solid" borderColor="base.700">
<Text fontSize="xs" color="base.400" mb={1}>
{t('modelManager.launchpad.browseAll')}
</Text>
<Button
onClick={navigateToStarterModelsTab}
variant="link" color="invokeBlue.300"
fontSize="sm"
fontWeight="medium"
p={0}
h="auto"
leftIcon={<PiStarBold size={16} />}
_hover={{
color: "invokeBlue.200",
textDecoration: "underline"
}}
>
{t('modelManager.launchpad.exploreStarter')}
</Button> </Box> </Flex> </Box>
</Box>{' '}
{/* Recommended Section */}
<Box>
<Heading size="sm" mb={2}>
{t('modelManager.launchpad.recommendedModels')}
</Heading>
<Flex flexDir="column" gap={2}>
{' '}
{/* Starter Model Bundles - More Prominent */}
<Box>
<Heading size="xs" color="base.100" mb={1}>
{t('modelManager.launchpad.quickStart')}
</Heading>
<Text fontSize="xs" color="base.300" mb={2}>
{t('modelManager.launchpad.bundleDescription')}
</Text>{' '}
<Grid templateColumns="repeat(auto-fit, minmax(180px, 1fr))" gap={2}>
<LaunchpadBundleCard title="Stable Diffusion 1.5" onClick={handleSD15BundleClick} />
<LaunchpadBundleCard title="SDXL" onClick={handleSDXLBundleClick} />
<LaunchpadBundleCard title="FLUX.1 [dev]" onClick={handleFluxBundleClick} />
</Grid>
</Box>{' '}
{/* Browse All - Simple Link */}
<Box pt={1} borderTop="1px solid" borderColor="base.700">
<Text fontSize="xs" color="base.400" mb={1}>
{t('modelManager.launchpad.browseAll')}
</Text>
<Button
onClick={navigateToStarterModelsTab}
variant="link"
color="invokeBlue.300"
fontSize="sm"
fontWeight="medium"
p={0}
h="auto"
leftIcon={<PiStarBold size={16} />}
_hover={{
color: 'invokeBlue.200',
textDecoration: 'underline',
}}
>
{t('modelManager.launchpad.exploreStarter')}
</Button>{' '}
</Box>{' '}
</Flex>{' '}
</Box>
</Flex>
</ScrollableContent>
</Flex>
@@ -186,7 +190,8 @@ interface LaunchpadCardProps {
}
const LaunchpadCard = memo(({ title, description, icon, onClick, variant = 'default' }: LaunchpadCardProps) => {
return ( <Button
return (
<Button
onClick={onClick}
variant="outline"
h="auto"
@@ -221,9 +226,9 @@ const LaunchpadCard = memo(({ title, description, icon, onClick, variant = 'defa
{title}
</Heading>
</Flex>
<Text
fontSize="sm"
color={variant === 'featured' ? 'invokeBlue.200' : 'base.400'}
<Text
fontSize="sm"
color={variant === 'featured' ? 'invokeBlue.200' : 'base.400'}
lineHeight="1.4"
flex="1"
whiteSpace="normal"
@@ -254,19 +259,20 @@ const LaunchpadBundleCard = memo(({ title, onClick }: LaunchpadBundleCardProps)
borderColor="invokeBlue.400"
bg="invokeBlue.950"
_hover={{
bg: "invokeBlue.900",
borderColor: "invokeBlue.300",
transform: "translateY(-2px)",
boxShadow: "0 4px 20px rgba(66, 153, 225, 0.15)",
bg: 'invokeBlue.900',
borderColor: 'invokeBlue.300',
transform: 'translateY(-2px)',
boxShadow: '0 4px 20px rgba(66, 153, 225, 0.15)',
}}
_active={{
transform: "translateY(0px)",
transform: 'translateY(0px)',
}}
transition="all 0.2s"
cursor="pointer"
textAlign="center"
justifyContent="center"
alignItems="center" borderRadius="lg"
alignItems="center"
borderRadius="lg"
whiteSpace="normal"
>
<Text fontSize="sm" fontWeight="bold" color="invokeBlue.100" noOfLines={1}>

View File

@@ -30,13 +30,17 @@ export const InstallModels = memo(() => {
<Button alignItems="center" variant="link" leftIcon={<PiInfoBold />} onClick={onClickLearnMore}>
<Text variant="subtext">{t('modelManager.learnMoreAboutSupportedModels')}</Text>
</Button>
</Flex> <Tabs variant="collapse" height="50%" display="flex" flexDir="column" index={index} onChange={onChange}> <TabList>
</Flex>{' '}
<Tabs variant="collapse" height="50%" display="flex" flexDir="column" index={index} onChange={onChange}>
{' '}
<TabList>
<Tab>{t('modelManager.launchpadTab')}</Tab>
<Tab>{t('modelManager.urlOrLocalPath')}</Tab>
<Tab>{t('modelManager.huggingFace')}</Tab>
<Tab>{t('modelManager.scanFolder')}</Tab>
<Tab>{t('modelManager.starterModels')}</Tab>
</TabList> <TabPanels p={3} height="100%">
</TabList>{' '}
<TabPanels p={3} height="100%">
<TabPanel height="100%">
<LaunchpadForm />
</TabPanel>