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')}
} onClick={handleClickAddModel}>
{t('modelManager.addModels')}
-
-
-
-
+
+
);
});
-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) => {
-
+ {/* */}
);