diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx index c2443dde6b..99f50486fd 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx @@ -40,7 +40,7 @@ export const ModelInstallQueue = memo(() => { }, [data]); return ( - + {t('modelManager.installQueue')} - + {data?.map((model) => )} diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/InstallModels.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/InstallModels.tsx index 90f3a578da..0a296c413b 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/InstallModels.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/InstallModels.tsx @@ -1,9 +1,11 @@ -import { Box, Flex, Heading, Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library'; +import { Flex, Heading, Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library'; import { useStore } from '@nanostores/react'; import { StarterModelsForm } from 'features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsForm'; +import ResizeHandle from 'features/ui/components/tabs/ResizeHandle'; import { atom } from 'nanostores'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; +import { Panel, PanelGroup } from 'react-resizable-panels'; import { HuggingFaceForm } from './AddModelPanel/HuggingFaceFolder/HuggingFaceForm'; import { InstallModelForm } from './AddModelPanel/InstallModelForm'; @@ -20,34 +22,41 @@ export const InstallModels = memo(() => { }, []); return ( - - {t('modelManager.addModel')} - - - {t('modelManager.urlOrLocalPath')} - {t('modelManager.huggingFace')} - {t('modelManager.scanFolder')} - {t('modelManager.starterModels')} - - - - - - - - - - - - - - - - - - - - + + + + {t('modelManager.addModel')} + + + {t('modelManager.urlOrLocalPath')} + {t('modelManager.huggingFace')} + {t('modelManager.scanFolder')} + {t('modelManager.starterModels')} + + + + + + + + + + + + + + + + + + + + + {/* */} + + {/* */} + + ); }); diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManager.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerLeftPanel.tsx similarity index 74% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManager.tsx rename to invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerLeftPanel.tsx index a07cb8c10b..c81f11fcb7 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManager.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerLeftPanel.tsx @@ -8,7 +8,7 @@ import { PiPlusBold } from 'react-icons/pi'; import ModelList from './ModelManagerPanel/ModelList'; import { ModelListNavigation } from './ModelManagerPanel/ModelListNavigation'; -export const ModelManager = memo(() => { +export const ModelManagerLeftPanel = memo(() => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const handleClickAddModel = useCallback(() => { @@ -16,19 +16,17 @@ export const ModelManager = memo(() => { }, [dispatch]); return ( - + {t('common.modelManager')} - - - - + + ); }); -ModelManager.displayName = 'ModelManager'; +ModelManagerLeftPanel.displayName = 'ModelManagerLeftPanel'; diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelBaseBadge.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelBaseBadge.tsx index bf07bad58c..a3215d9534 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelBaseBadge.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelBaseBadge.tsx @@ -16,6 +16,10 @@ const BASE_COLOR_MAP: Record = { }; const ModelBaseBadge = ({ base }: Props) => { + if (base === 'any') { + return null; + } + return ( {MODEL_TYPE_SHORT_MAP[base]} diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelImage.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelImage.tsx index d273d92bdb..612d62ea6f 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelImage.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelImage.tsx @@ -15,12 +15,12 @@ const ModelImage = ({ image_url }: Props) => { - + ); } diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelList.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelList.tsx index 755a6e21fb..1e1973f6eb 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelList.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelList.tsx @@ -106,7 +106,7 @@ const ModelList = () => { return ( - + {/* Main Model List */} {isLoadingMainModels && } {!isLoadingMainModels && filteredMainModels.length > 0 && ( diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx index 8bfcbd7351..49bd518f25 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx @@ -20,8 +20,8 @@ type ModelListItemProps = { }; const sx: SystemStyleObject = { - _hover: { bg: 'base.700' }, - "&[aria-selected='true']": { bg: 'base.700' }, + _hover: { bg: 'baseAlpha.100' }, + "&[aria-selected='true']": { bg: 'baseAlpha.100' }, }; const ModelListItem = ({ model }: ModelListItemProps) => { @@ -82,8 +82,8 @@ const ModelListItem = ({ model }: ModelListItemProps) => { w="full" alignItems="center" gap={2} - cursor="pointer" onClick={handleSelectModel} + role="button" > diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListNavigation.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListNavigation.tsx index 766b4f27b8..3da0bc47fe 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListNavigation.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListNavigation.tsx @@ -30,12 +30,12 @@ export const ModelListNavigation = memo(() => { - {!!searchTerm?.length && ( + {Boolean(searchTerm) && ( { const { title, modelList } = props; return ( - - {modelList.map((model) => ( - - ))} - + + + {title} + + + {modelList.map((model) => ( + + ))} + + ); }); diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPane.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPane.tsx index eb85434d36..43a0a972d6 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPane.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPane.tsx @@ -1,4 +1,3 @@ -import { Box } from '@invoke-ai/ui-library'; import { useAppSelector } from 'app/store/storeHooks'; import { memo } from 'react'; @@ -7,11 +6,11 @@ import { Model } from './ModelPanel/Model'; export const ModelPane = memo(() => { const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey); - return ( - - {selectedModelKey ? : } - - ); + if (selectedModelKey) { + return ; + } + + return ; }); ModelPane.displayName = 'ModelPane'; diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelImageUpload.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelImageUpload.tsx index 292835a7b7..82347fba0c 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelImageUpload.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelImageUpload.tsx @@ -6,14 +6,15 @@ import { useDropzone } from 'react-dropzone'; import { useTranslation } from 'react-i18next'; import { PiArrowCounterClockwiseBold, PiUploadSimpleBold } from 'react-icons/pi'; import { useDeleteModelImageMutation, useUpdateModelImageMutation } from 'services/api/endpoints/models'; +import type { AnyModelConfig } from 'services/api/types'; type Props = { - model_key: string | null; - model_image?: string | null; + modelConfig: AnyModelConfig; }; -const ModelImageUpload = ({ model_key, model_image }: Props) => { - const [image, setImage] = useState(model_image || null); +const ModelImageUpload = ({ modelConfig }: Props) => { + const { key, cover_image } = modelConfig; + const [image, setImage] = useState(cover_image || null); const { t } = useTranslation(); const [updateModelImage] = useUpdateModelImageMutation(); @@ -23,11 +24,11 @@ const ModelImageUpload = ({ model_key, model_image }: Props) => { (files: File[]) => { const file = files[0]; - if (!file || !model_key) { + if (!file) { return; } - updateModelImage({ key: model_key, image: file }) + updateModelImage({ key, image: file }) .unwrap() .then(() => { setImage(URL.createObjectURL(file)); @@ -45,15 +46,12 @@ const ModelImageUpload = ({ model_key, model_image }: Props) => { }); }); }, - [model_key, t, updateModelImage] + [key, t, updateModelImage] ); const handleResetImage = useCallback(() => { - if (!model_key) { - return; - } setImage(null); - deleteModelImage(model_key) + deleteModelImage(key) .unwrap() .then(() => { toast({ @@ -69,7 +67,7 @@ const ModelImageUpload = ({ model_key, model_image }: Props) => { status: 'error', }); }); - }, [model_key, t, deleteModelImage]); + }, [deleteModelImage, key, t]); const { getInputProps, getRootProps } = useDropzone({ accept: { 'image/png': ['.png'], 'image/jpeg': ['.jpg', '.jpeg', '.png'] }, diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelHeader.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelHeader.tsx index a30f96b7fc..a1d848a930 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelHeader.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelHeader.tsx @@ -13,10 +13,10 @@ export const ModelHeader = memo(({ modelConfig, children }: Props) => { const { t } = useTranslation(); return ( - + - + {modelConfig.name} diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx index f02a84efb3..840583e414 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx @@ -50,15 +50,17 @@ export const ModelView = memo(({ modelConfig }: Props) => { )} - + {modelConfig.type === 'main' && modelConfig.base !== 'sdxl-refiner' && ( )} {(modelConfig.type === 'controlnet' || modelConfig.type === 't2i_adapter') && ( )} + + {(modelConfig.type === 'main' || modelConfig.type === 'lora') && } - + ); diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/SelectedModelContext.ts b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/SelectedModelContext.ts new file mode 100644 index 0000000000..455f2619c1 --- /dev/null +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/SelectedModelContext.ts @@ -0,0 +1,4 @@ +import { createContext } from 'react'; +import type { AnyModelConfig } from 'services/api/types'; + +export const SelectedModelContext = createContext(null); diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/TriggerPhrases.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/TriggerPhrases.tsx index 3f5133dedf..98bd0400ff 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/TriggerPhrases.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/TriggerPhrases.tsx @@ -99,16 +99,39 @@ export const TriggerPhrases = memo(({ modelConfig }: Props) => { - - {triggerPhrases.map((phrase, index) => ( - - {phrase} - - - ))} - + {triggerPhrases.length > 0 && ( + + {triggerPhrases.map((phrase, index) => ( + + ))} + + )} ); }); TriggerPhrases.displayName = 'TriggerPhrases'; + +type TriggerPhrasesTagProps = { + phrase: string; + onRemoveTriggerPhrase: (phrase: string) => void; + isLoading: boolean; +}; +const TriggerPhrasesTag = memo(({ phrase, onRemoveTriggerPhrase, isLoading }: TriggerPhrasesTagProps) => { + const onClick = useCallback(() => { + onRemoveTriggerPhrase(phrase); + }, [onRemoveTriggerPhrase, phrase]); + + return ( + + {phrase} + + + ); +}); +TriggerPhrasesTag.displayName = 'TriggerPhrasesTag'; diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManagerTab.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManagerTab.tsx index d80f36bd91..2fbf60a4e9 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManagerTab.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManagerTab.tsx @@ -1,14 +1,20 @@ -import { Flex } from '@invoke-ai/ui-library'; -import { ModelManager } from 'features/modelManagerV2/subpanels/ModelManager'; +import { ModelManagerLeftPanel } from 'features/modelManagerV2/subpanels/ModelManagerLeftPanel'; import { ModelPane } from 'features/modelManagerV2/subpanels/ModelPane'; +import ResizeHandle from 'features/ui/components/tabs/ResizeHandle'; import { memo } from 'react'; +import { Panel, PanelGroup } from 'react-resizable-panels'; const ModelManagerTab = () => { return ( - - - - + + + + + + + + + ); }; diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ResizeHandle.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/ResizeHandle.tsx index c5b76de892..7cd381bc27 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/ResizeHandle.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/ResizeHandle.tsx @@ -17,7 +17,7 @@ const ResizeHandle = (props: ResizeHandleProps) => { - + {/* */} );