fix(ui): memoize model manager components

This commit is contained in:
psychedelicious 2024-07-26 07:39:42 +10:00
parent 47414be1e6
commit f8e27b837b
37 changed files with 192 additions and 109 deletions

View File

@ -1,13 +1,13 @@
import { Button, Flex, FormControl, FormErrorMessage, FormHelperText, FormLabel, Input } from '@invoke-ai/ui-library'; import { Button, Flex, FormControl, FormErrorMessage, FormHelperText, FormLabel, Input } from '@invoke-ai/ui-library';
import { useInstallModel } from 'features/modelManagerV2/hooks/useInstallModel'; import { useInstallModel } from 'features/modelManagerV2/hooks/useInstallModel';
import type { ChangeEventHandler } from 'react'; import type { ChangeEventHandler } from 'react';
import { useCallback, useState } from 'react'; import { memo, useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useLazyGetHuggingFaceModelsQuery } from 'services/api/endpoints/models'; import { useLazyGetHuggingFaceModelsQuery } from 'services/api/endpoints/models';
import { HuggingFaceResults } from './HuggingFaceResults'; import { HuggingFaceResults } from './HuggingFaceResults';
export const HuggingFaceForm = () => { export const HuggingFaceForm = memo(() => {
const [huggingFaceRepo, setHuggingFaceRepo] = useState(''); const [huggingFaceRepo, setHuggingFaceRepo] = useState('');
const [displayResults, setDisplayResults] = useState(false); const [displayResults, setDisplayResults] = useState(false);
const [errorMessage, setErrorMessage] = useState(''); const [errorMessage, setErrorMessage] = useState('');
@ -66,4 +66,6 @@ export const HuggingFaceForm = () => {
{data && data.urls && displayResults && <HuggingFaceResults results={data.urls} />} {data && data.urls && displayResults && <HuggingFaceResults results={data.urls} />}
</Flex> </Flex>
); );
}; });
HuggingFaceForm.displayName = 'HuggingFaceForm';

View File

@ -1,13 +1,13 @@
import { Flex, IconButton, Text } from '@invoke-ai/ui-library'; import { Flex, IconButton, Text } from '@invoke-ai/ui-library';
import { useInstallModel } from 'features/modelManagerV2/hooks/useInstallModel'; import { useInstallModel } from 'features/modelManagerV2/hooks/useInstallModel';
import { useCallback } from 'react'; import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { PiPlusBold } from 'react-icons/pi'; import { PiPlusBold } from 'react-icons/pi';
type Props = { type Props = {
result: string; result: string;
}; };
export const HuggingFaceResultItem = ({ result }: Props) => { export const HuggingFaceResultItem = memo(({ result }: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [installModel] = useInstallModel(); const [installModel] = useInstallModel();
@ -27,4 +27,6 @@ export const HuggingFaceResultItem = ({ result }: Props) => {
<IconButton aria-label={t('modelManager.install')} icon={<PiPlusBold />} onClick={onClick} size="sm" /> <IconButton aria-label={t('modelManager.install')} icon={<PiPlusBold />} onClick={onClick} size="sm" />
</Flex> </Flex>
); );
}; });
HuggingFaceResultItem.displayName = 'HuggingFaceResultItem';

View File

@ -11,7 +11,7 @@ import {
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent'; import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
import { useInstallModel } from 'features/modelManagerV2/hooks/useInstallModel'; import { useInstallModel } from 'features/modelManagerV2/hooks/useInstallModel';
import type { ChangeEventHandler } from 'react'; import type { ChangeEventHandler } from 'react';
import { useCallback, useMemo, useState } from 'react'; import { memo, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { PiXBold } from 'react-icons/pi'; import { PiXBold } from 'react-icons/pi';
@ -21,7 +21,7 @@ type HuggingFaceResultsProps = {
results: string[]; results: string[];
}; };
export const HuggingFaceResults = ({ results }: HuggingFaceResultsProps) => { export const HuggingFaceResults = memo(({ results }: HuggingFaceResultsProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [searchTerm, setSearchTerm] = useState(''); const [searchTerm, setSearchTerm] = useState('');
@ -93,4 +93,6 @@ export const HuggingFaceResults = ({ results }: HuggingFaceResultsProps) => {
</Flex> </Flex>
</> </>
); );
}; });
HuggingFaceResults.displayName = 'HuggingFaceResults';

View File

@ -1,7 +1,7 @@
import { Button, Checkbox, Flex, FormControl, FormHelperText, FormLabel, Input } from '@invoke-ai/ui-library'; import { Button, Checkbox, Flex, FormControl, FormHelperText, FormLabel, Input } from '@invoke-ai/ui-library';
import { useInstallModel } from 'features/modelManagerV2/hooks/useInstallModel'; import { useInstallModel } from 'features/modelManagerV2/hooks/useInstallModel';
import { t } from 'i18next'; import { t } from 'i18next';
import { useCallback } from 'react'; import { memo, useCallback } from 'react';
import type { SubmitHandler } from 'react-hook-form'; import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
@ -10,7 +10,7 @@ type SimpleImportModelConfig = {
inplace: boolean; inplace: boolean;
}; };
export const InstallModelForm = () => { export const InstallModelForm = memo(() => {
const [installModel, { isLoading }] = useInstallModel(); const [installModel, { isLoading }] = useInstallModel();
const { register, handleSubmit, formState, reset } = useForm<SimpleImportModelConfig>({ const { register, handleSubmit, formState, reset } = useForm<SimpleImportModelConfig>({
@ -74,4 +74,6 @@ export const InstallModelForm = () => {
</Flex> </Flex>
</form> </form>
); );
}; });
InstallModelForm.displayName = 'InstallModelForm';

View File

@ -2,12 +2,12 @@ import { Box, Button, Flex, Heading } from '@invoke-ai/ui-library';
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent'; import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
import { toast } from 'features/toast/toast'; import { toast } from 'features/toast/toast';
import { t } from 'i18next'; import { t } from 'i18next';
import { useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import { useListModelInstallsQuery, usePruneCompletedModelInstallsMutation } from 'services/api/endpoints/models'; import { useListModelInstallsQuery, usePruneCompletedModelInstallsMutation } from 'services/api/endpoints/models';
import { ModelInstallQueueItem } from './ModelInstallQueueItem'; import { ModelInstallQueueItem } from './ModelInstallQueueItem';
export const ModelInstallQueue = () => { export const ModelInstallQueue = memo(() => {
const { data } = useListModelInstallsQuery(); const { data } = useListModelInstallsQuery();
const [_pruneCompletedModelInstalls] = usePruneCompletedModelInstallsMutation(); const [_pruneCompletedModelInstalls] = usePruneCompletedModelInstallsMutation();
@ -61,4 +61,6 @@ export const ModelInstallQueue = () => {
</Box> </Box>
</Flex> </Flex>
); );
}; });
ModelInstallQueue.displayName = 'ModelInstallQueue';

View File

@ -2,7 +2,7 @@ import { Flex, IconButton, Progress, Text, Tooltip } from '@invoke-ai/ui-library
import { toast } from 'features/toast/toast'; import { toast } from 'features/toast/toast';
import { t } from 'i18next'; import { t } from 'i18next';
import { isNil } from 'lodash-es'; import { isNil } from 'lodash-es';
import { useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import { PiXBold } from 'react-icons/pi'; import { PiXBold } from 'react-icons/pi';
import { useCancelModelInstallMutation } from 'services/api/endpoints/models'; import { useCancelModelInstallMutation } from 'services/api/endpoints/models';
import type { ModelInstallJob } from 'services/api/types'; import type { ModelInstallJob } from 'services/api/types';
@ -25,7 +25,7 @@ const formatBytes = (bytes: number) => {
return `${bytes.toFixed(2)} ${units[i]}`; return `${bytes.toFixed(2)} ${units[i]}`;
}; };
export const ModelInstallQueueItem = (props: ModelListItemProps) => { export const ModelInstallQueueItem = memo((props: ModelListItemProps) => {
const { installJob } = props; const { installJob } = props;
const [deleteImportModel] = useCancelModelInstallMutation(); const [deleteImportModel] = useCancelModelInstallMutation();
@ -124,7 +124,9 @@ export const ModelInstallQueueItem = (props: ModelListItemProps) => {
/> />
</Flex> </Flex>
); );
}; });
ModelInstallQueueItem.displayName = 'ModelInstallQueueItem';
type TooltipLabelProps = { type TooltipLabelProps = {
installJob: ModelInstallJob; installJob: ModelInstallJob;
@ -132,7 +134,7 @@ type TooltipLabelProps = {
source: string; source: string;
}; };
const TooltipLabel = ({ name, source, installJob }: TooltipLabelProps) => { const TooltipLabel = memo(({ name, source, installJob }: TooltipLabelProps) => {
const progressString = useMemo(() => { const progressString = useMemo(() => {
if (installJob.status !== 'downloading' || installJob.bytes === undefined || installJob.total_bytes === undefined) { if (installJob.status !== 'downloading' || installJob.bytes === undefined || installJob.total_bytes === undefined) {
return ''; return '';
@ -156,4 +158,6 @@ const TooltipLabel = ({ name, source, installJob }: TooltipLabelProps) => {
)} )}
</> </>
); );
}; });
TooltipLabel.displayName = 'TooltipLabel';

View File

@ -2,13 +2,13 @@ import { Button, Flex, FormControl, FormErrorMessage, FormHelperText, FormLabel,
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { setScanPath } from 'features/modelManagerV2/store/modelManagerV2Slice'; import { setScanPath } from 'features/modelManagerV2/store/modelManagerV2Slice';
import type { ChangeEventHandler } from 'react'; import type { ChangeEventHandler } from 'react';
import { useCallback, useState } from 'react'; import { memo, useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useLazyScanFolderQuery } from 'services/api/endpoints/models'; import { useLazyScanFolderQuery } from 'services/api/endpoints/models';
import { ScanModelsResults } from './ScanFolderResults'; import { ScanModelsResults } from './ScanFolderResults';
export const ScanModelsForm = () => { export const ScanModelsForm = memo(() => {
const scanPath = useAppSelector((state) => state.modelmanagerV2.scanPath); const scanPath = useAppSelector((state) => state.modelmanagerV2.scanPath);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const [errorMessage, setErrorMessage] = useState(''); const [errorMessage, setErrorMessage] = useState('');
@ -56,4 +56,6 @@ export const ScanModelsForm = () => {
{data && <ScanModelsResults results={data} />} {data && <ScanModelsResults results={data} />}
</Flex> </Flex>
); );
}; });
ScanModelsForm.displayName = 'ScanModelsForm';

View File

@ -1,5 +1,5 @@
import { Badge, Box, Flex, IconButton, Text } from '@invoke-ai/ui-library'; import { Badge, Box, Flex, IconButton, Text } from '@invoke-ai/ui-library';
import { useCallback } from 'react'; import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { PiPlusBold } from 'react-icons/pi'; import { PiPlusBold } from 'react-icons/pi';
import type { ScanFolderResponse } from 'services/api/endpoints/models'; import type { ScanFolderResponse } from 'services/api/endpoints/models';
@ -8,7 +8,7 @@ type Props = {
result: ScanFolderResponse[number]; result: ScanFolderResponse[number];
installModel: (source: string) => void; installModel: (source: string) => void;
}; };
export const ScanModelResultItem = ({ result, installModel }: Props) => { export const ScanModelResultItem = memo(({ result, installModel }: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const handleInstall = useCallback(() => { const handleInstall = useCallback(() => {
@ -30,4 +30,6 @@ export const ScanModelResultItem = ({ result, installModel }: Props) => {
</Box> </Box>
</Flex> </Flex>
); );
}; });
ScanModelResultItem.displayName = 'ScanModelResultItem';

View File

@ -14,7 +14,7 @@ import {
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent'; import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
import { useInstallModel } from 'features/modelManagerV2/hooks/useInstallModel'; import { useInstallModel } from 'features/modelManagerV2/hooks/useInstallModel';
import type { ChangeEvent, ChangeEventHandler } from 'react'; import type { ChangeEvent, ChangeEventHandler } from 'react';
import { useCallback, useMemo, useState } from 'react'; import { memo, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { PiXBold } from 'react-icons/pi'; import { PiXBold } from 'react-icons/pi';
import type { ScanFolderResponse } from 'services/api/endpoints/models'; import type { ScanFolderResponse } from 'services/api/endpoints/models';
@ -25,7 +25,7 @@ type ScanModelResultsProps = {
results: ScanFolderResponse; results: ScanFolderResponse;
}; };
export const ScanModelsResults = ({ results }: ScanModelResultsProps) => { export const ScanModelsResults = memo(({ results }: ScanModelResultsProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [searchTerm, setSearchTerm] = useState(''); const [searchTerm, setSearchTerm] = useState('');
const [inplace, setInplace] = useState(true); const [inplace, setInplace] = useState(true);
@ -116,4 +116,6 @@ export const ScanModelsResults = ({ results }: ScanModelResultsProps) => {
</Flex> </Flex>
</> </>
); );
}; });
ScanModelsResults.displayName = 'ScanModelsResults';

View File

@ -1,7 +1,7 @@
import { Badge, Box, Flex, IconButton, Text } from '@invoke-ai/ui-library'; import { Badge, Box, Flex, IconButton, Text } from '@invoke-ai/ui-library';
import { useInstallModel } from 'features/modelManagerV2/hooks/useInstallModel'; import { useInstallModel } from 'features/modelManagerV2/hooks/useInstallModel';
import ModelBaseBadge from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelBaseBadge'; import ModelBaseBadge from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelBaseBadge';
import { useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { PiPlusBold } from 'react-icons/pi'; import { PiPlusBold } from 'react-icons/pi';
import type { GetStarterModelsResponse } from 'services/api/endpoints/models'; import type { GetStarterModelsResponse } from 'services/api/endpoints/models';
@ -9,7 +9,7 @@ import type { GetStarterModelsResponse } from 'services/api/endpoints/models';
type Props = { type Props = {
result: GetStarterModelsResponse[number]; result: GetStarterModelsResponse[number];
}; };
export const StarterModelsResultItem = ({ result }: Props) => { export const StarterModelsResultItem = memo(({ result }: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const allSources = useMemo(() => { const allSources = useMemo(() => {
const _allSources = [{ source: result.source, config: { name: result.name, description: result.description } }]; const _allSources = [{ source: result.source, config: { name: result.name, description: result.description } }];
@ -47,4 +47,6 @@ export const StarterModelsResultItem = ({ result }: Props) => {
</Box> </Box>
</Flex> </Flex>
); );
}; });
StarterModelsResultItem.displayName = 'StarterModelsResultItem';

View File

@ -1,10 +1,11 @@
import { Flex } from '@invoke-ai/ui-library'; import { Flex } from '@invoke-ai/ui-library';
import { FetchingModelsLoader } from 'features/modelManagerV2/subpanels/ModelManagerPanel/FetchingModelsLoader'; import { FetchingModelsLoader } from 'features/modelManagerV2/subpanels/ModelManagerPanel/FetchingModelsLoader';
import { memo } from 'react';
import { useGetStarterModelsQuery } from 'services/api/endpoints/models'; import { useGetStarterModelsQuery } from 'services/api/endpoints/models';
import { StarterModelsResults } from './StarterModelsResults'; import { StarterModelsResults } from './StarterModelsResults';
export const StarterModelsForm = () => { export const StarterModelsForm = memo(() => {
const { isLoading, data } = useGetStarterModelsQuery(); const { isLoading, data } = useGetStarterModelsQuery();
return ( return (
@ -13,4 +14,6 @@ export const StarterModelsForm = () => {
{data && <StarterModelsResults results={data} />} {data && <StarterModelsResults results={data} />}
</Flex> </Flex>
); );
}; });
StarterModelsForm.displayName = 'StarterModelsForm';

View File

@ -1,7 +1,7 @@
import { Flex, IconButton, Input, InputGroup, InputRightElement } from '@invoke-ai/ui-library'; import { Flex, IconButton, Input, InputGroup, InputRightElement } from '@invoke-ai/ui-library';
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent'; import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
import type { ChangeEventHandler } from 'react'; import type { ChangeEventHandler } from 'react';
import { useCallback, useMemo, useState } from 'react'; import { memo, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { PiXBold } from 'react-icons/pi'; import { PiXBold } from 'react-icons/pi';
import type { GetStarterModelsResponse } from 'services/api/endpoints/models'; import type { GetStarterModelsResponse } from 'services/api/endpoints/models';
@ -12,7 +12,7 @@ type StarterModelsResultsProps = {
results: NonNullable<GetStarterModelsResponse>; results: NonNullable<GetStarterModelsResponse>;
}; };
export const StarterModelsResults = ({ results }: StarterModelsResultsProps) => { export const StarterModelsResults = memo(({ results }: StarterModelsResultsProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [searchTerm, setSearchTerm] = useState(''); const [searchTerm, setSearchTerm] = useState('');
@ -79,4 +79,6 @@ export const StarterModelsResults = ({ results }: StarterModelsResultsProps) =>
</Flex> </Flex>
</Flex> </Flex>
); );
}; });
StarterModelsResults.displayName = 'StarterModelsResults';

View File

@ -2,7 +2,7 @@ import { Box, Flex, Heading, Tab, TabList, TabPanel, TabPanels, Tabs } from '@in
import { useStore } from '@nanostores/react'; import { useStore } from '@nanostores/react';
import { StarterModelsForm } from 'features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsForm'; import { StarterModelsForm } from 'features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsForm';
import { atom } from 'nanostores'; import { atom } from 'nanostores';
import { useCallback } from 'react'; import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { HuggingFaceForm } from './AddModelPanel/HuggingFaceFolder/HuggingFaceForm'; import { HuggingFaceForm } from './AddModelPanel/HuggingFaceFolder/HuggingFaceForm';
@ -12,7 +12,7 @@ import { ScanModelsForm } from './AddModelPanel/ScanFolder/ScanFolderForm';
export const $installModelsTab = atom(0); export const $installModelsTab = atom(0);
export const InstallModels = () => { export const InstallModels = memo(() => {
const { t } = useTranslation(); const { t } = useTranslation();
const index = useStore($installModelsTab); const index = useStore($installModelsTab);
const onChange = useCallback((index: number) => { const onChange = useCallback((index: number) => {
@ -49,4 +49,6 @@ export const InstallModels = () => {
</Box> </Box>
</Flex> </Flex>
); );
}; });
InstallModels.displayName = 'InstallModels';

View File

@ -1,14 +1,14 @@
import { Button, Flex, Heading } from '@invoke-ai/ui-library'; import { Button, Flex, Heading } from '@invoke-ai/ui-library';
import { useAppDispatch } from 'app/store/storeHooks'; import { useAppDispatch } from 'app/store/storeHooks';
import { setSelectedModelKey } from 'features/modelManagerV2/store/modelManagerV2Slice'; import { setSelectedModelKey } from 'features/modelManagerV2/store/modelManagerV2Slice';
import { useCallback } from 'react'; import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { PiPlusBold } from 'react-icons/pi'; import { PiPlusBold } from 'react-icons/pi';
import ModelList from './ModelManagerPanel/ModelList'; import ModelList from './ModelManagerPanel/ModelList';
import { ModelListNavigation } from './ModelManagerPanel/ModelListNavigation'; import { ModelListNavigation } from './ModelManagerPanel/ModelListNavigation';
export const ModelManager = () => { export const ModelManager = memo(() => {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const handleClickAddModel = useCallback(() => { const handleClickAddModel = useCallback(() => {
@ -29,4 +29,6 @@ export const ModelManager = () => {
</Flex> </Flex>
</Flex> </Flex>
); );
}; });
ModelManager.displayName = 'ModelManager';

View File

@ -3,12 +3,12 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { setSearchTerm } from 'features/modelManagerV2/store/modelManagerV2Slice'; import { setSearchTerm } from 'features/modelManagerV2/store/modelManagerV2Slice';
import { t } from 'i18next'; import { t } from 'i18next';
import type { ChangeEventHandler } from 'react'; import type { ChangeEventHandler } from 'react';
import { useCallback } from 'react'; import { memo, useCallback } from 'react';
import { PiXBold } from 'react-icons/pi'; import { PiXBold } from 'react-icons/pi';
import { ModelTypeFilter } from './ModelTypeFilter'; import { ModelTypeFilter } from './ModelTypeFilter';
export const ModelListNavigation = () => { export const ModelListNavigation = memo(() => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const searchTerm = useAppSelector((s) => s.modelmanagerV2.searchTerm); const searchTerm = useAppSelector((s) => s.modelmanagerV2.searchTerm);
@ -49,4 +49,6 @@ export const ModelListNavigation = () => {
</InputGroup> </InputGroup>
</Flex> </Flex>
); );
}; });
ModelListNavigation.displayName = 'ModelListNavigation';

View File

@ -1,4 +1,5 @@
import { StickyScrollable } from 'features/system/components/StickyScrollable'; import { StickyScrollable } from 'features/system/components/StickyScrollable';
import { memo } from 'react';
import type { AnyModelConfig } from 'services/api/types'; import type { AnyModelConfig } from 'services/api/types';
import ModelListItem from './ModelListItem'; import ModelListItem from './ModelListItem';
@ -8,7 +9,7 @@ type ModelListWrapperProps = {
modelList: AnyModelConfig[]; modelList: AnyModelConfig[];
}; };
export const ModelListWrapper = (props: ModelListWrapperProps) => { export const ModelListWrapper = memo((props: ModelListWrapperProps) => {
const { title, modelList } = props; const { title, modelList } = props;
return ( return (
<StickyScrollable title={title} contentSx={{ gap: 1, p: 2 }}> <StickyScrollable title={title} contentSx={{ gap: 1, p: 2 }}>
@ -17,4 +18,6 @@ export const ModelListWrapper = (props: ModelListWrapperProps) => {
))} ))}
</StickyScrollable> </StickyScrollable>
); );
}; });
ModelListWrapper.displayName = 'ModelListWrapper';

View File

@ -2,12 +2,12 @@ import { Button, Menu, MenuButton, MenuItem, MenuList } from '@invoke-ai/ui-libr
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import type { FilterableModelType } from 'features/modelManagerV2/store/modelManagerV2Slice'; import type { FilterableModelType } from 'features/modelManagerV2/store/modelManagerV2Slice';
import { setFilteredModelType } from 'features/modelManagerV2/store/modelManagerV2Slice'; import { setFilteredModelType } from 'features/modelManagerV2/store/modelManagerV2Slice';
import { useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { PiFunnelBold } from 'react-icons/pi'; import { PiFunnelBold } from 'react-icons/pi';
import { objectKeys } from 'tsafe'; import { objectKeys } from 'tsafe';
export const ModelTypeFilter = () => { export const ModelTypeFilter = memo(() => {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const MODEL_TYPE_LABELS: Record<FilterableModelType, string> = useMemo( const MODEL_TYPE_LABELS: Record<FilterableModelType, string> = useMemo(
@ -57,4 +57,6 @@ export const ModelTypeFilter = () => {
</MenuList> </MenuList>
</Menu> </Menu>
); );
}; });
ModelTypeFilter.displayName = 'ModelTypeFilter';

View File

@ -1,14 +1,17 @@
import { Box } from '@invoke-ai/ui-library'; import { Box } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { memo } from 'react';
import { InstallModels } from './InstallModels'; import { InstallModels } from './InstallModels';
import { Model } from './ModelPanel/Model'; import { Model } from './ModelPanel/Model';
export const ModelPane = () => { export const ModelPane = memo(() => {
const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey); const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
return ( return (
<Box layerStyle="first" p={4} borderRadius="base" w="50%" h="full"> <Box layerStyle="first" p={4} borderRadius="base" w="50%" h="full">
{selectedModelKey ? <Model key={selectedModelKey} /> : <InstallModels />} {selectedModelKey ? <Model key={selectedModelKey} /> : <InstallModels />}
</Box> </Box>
); );
}; });
ModelPane.displayName = 'ModelPane';

View File

@ -3,7 +3,7 @@ import { useControlNetOrT2IAdapterDefaultSettings } from 'features/modelManagerV
import { DefaultPreprocessor } from 'features/modelManagerV2/subpanels/ModelPanel/ControlNetOrT2IAdapterDefaultSettings/DefaultPreprocessor'; import { DefaultPreprocessor } from 'features/modelManagerV2/subpanels/ModelPanel/ControlNetOrT2IAdapterDefaultSettings/DefaultPreprocessor';
import type { FormField } from 'features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings'; import type { FormField } from 'features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings';
import { toast } from 'features/toast/toast'; import { toast } from 'features/toast/toast';
import { useCallback } from 'react'; import { memo, useCallback } from 'react';
import type { SubmitHandler } from 'react-hook-form'; import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -19,7 +19,7 @@ type Props = {
modelConfig: ControlNetModelConfig | T2IAdapterModelConfig; modelConfig: ControlNetModelConfig | T2IAdapterModelConfig;
}; };
export const ControlNetOrT2IAdapterDefaultSettings = ({ modelConfig }: Props) => { export const ControlNetOrT2IAdapterDefaultSettings = memo(({ modelConfig }: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const defaultSettingsDefaults = useControlNetOrT2IAdapterDefaultSettings(modelConfig); const defaultSettingsDefaults = useControlNetOrT2IAdapterDefaultSettings(modelConfig);
@ -83,4 +83,6 @@ export const ControlNetOrT2IAdapterDefaultSettings = ({ modelConfig }: Props) =>
</SimpleGrid> </SimpleGrid>
</> </>
); );
}; });
ControlNetOrT2IAdapterDefaultSettings.displayName = 'ControlNetOrT2IAdapterDefaultSettings';

View File

@ -4,7 +4,7 @@ import { InformationalPopover } from 'common/components/InformationalPopover/Inf
import type { ControlNetOrT2IAdapterDefaultSettingsFormData } from 'features/modelManagerV2/subpanels/ModelPanel/ControlNetOrT2IAdapterDefaultSettings/ControlNetOrT2IAdapterDefaultSettings'; import type { ControlNetOrT2IAdapterDefaultSettingsFormData } from 'features/modelManagerV2/subpanels/ModelPanel/ControlNetOrT2IAdapterDefaultSettings/ControlNetOrT2IAdapterDefaultSettings';
import type { FormField } from 'features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings'; import type { FormField } from 'features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings';
import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle'; import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle';
import { useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import type { UseControllerProps } from 'react-hook-form'; import type { UseControllerProps } from 'react-hook-form';
import { useController } from 'react-hook-form'; import { useController } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -28,7 +28,7 @@ const OPTIONS = [
type DefaultSchedulerType = ControlNetOrT2IAdapterDefaultSettingsFormData['preprocessor']; type DefaultSchedulerType = ControlNetOrT2IAdapterDefaultSettingsFormData['preprocessor'];
export function DefaultPreprocessor(props: UseControllerProps<ControlNetOrT2IAdapterDefaultSettingsFormData>) { export const DefaultPreprocessor = memo((props: UseControllerProps<ControlNetOrT2IAdapterDefaultSettingsFormData>) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { field } = useController(props); const { field } = useController(props);
@ -63,4 +63,6 @@ export function DefaultPreprocessor(props: UseControllerProps<ControlNetOrT2IAda
<Combobox isDisabled={isDisabled} value={value} options={OPTIONS} onChange={onChange} /> <Combobox isDisabled={isDisabled} value={value} options={OPTIONS} onChange={onChange} />
</FormControl> </FormControl>
); );
} });
DefaultPreprocessor.displayName = 'DefaultPreprocessor';

View File

@ -2,7 +2,7 @@ import { CompositeNumberInput, CompositeSlider, Flex, FormControl, FormLabel } f
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover'; import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle'; import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle';
import { useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import type { UseControllerProps } from 'react-hook-form'; import type { UseControllerProps } from 'react-hook-form';
import { useController } from 'react-hook-form'; import { useController } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -11,7 +11,7 @@ import type { MainModelDefaultSettingsFormData } from './MainModelDefaultSetting
type DefaultCfgRescaleMultiplierType = MainModelDefaultSettingsFormData['cfgRescaleMultiplier']; type DefaultCfgRescaleMultiplierType = MainModelDefaultSettingsFormData['cfgRescaleMultiplier'];
export function DefaultCfgRescaleMultiplier(props: UseControllerProps<MainModelDefaultSettingsFormData>) { export const DefaultCfgRescaleMultiplier = memo((props: UseControllerProps<MainModelDefaultSettingsFormData>) => {
const { field } = useController(props); const { field } = useController(props);
const sliderMin = useAppSelector((s) => s.config.sd.cfgRescaleMultiplier.sliderMin); const sliderMin = useAppSelector((s) => s.config.sd.cfgRescaleMultiplier.sliderMin);
@ -74,4 +74,6 @@ export function DefaultCfgRescaleMultiplier(props: UseControllerProps<MainModelD
</Flex> </Flex>
</FormControl> </FormControl>
); );
} });
DefaultCfgRescaleMultiplier.displayName = 'DefaultCfgRescaleMultiplier';

View File

@ -2,7 +2,7 @@ import { CompositeNumberInput, CompositeSlider, Flex, FormControl, FormLabel } f
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover'; import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle'; import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle';
import { useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import type { UseControllerProps } from 'react-hook-form'; import type { UseControllerProps } from 'react-hook-form';
import { useController } from 'react-hook-form'; import { useController } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -11,7 +11,7 @@ import type { MainModelDefaultSettingsFormData } from './MainModelDefaultSetting
type DefaultCfgType = MainModelDefaultSettingsFormData['cfgScale']; type DefaultCfgType = MainModelDefaultSettingsFormData['cfgScale'];
export function DefaultCfgScale(props: UseControllerProps<MainModelDefaultSettingsFormData>) { export const DefaultCfgScale = memo((props: UseControllerProps<MainModelDefaultSettingsFormData>) => {
const { field } = useController(props); const { field } = useController(props);
const sliderMin = useAppSelector((s) => s.config.sd.guidance.sliderMin); const sliderMin = useAppSelector((s) => s.config.sd.guidance.sliderMin);
@ -74,4 +74,6 @@ export function DefaultCfgScale(props: UseControllerProps<MainModelDefaultSettin
</Flex> </Flex>
</FormControl> </FormControl>
); );
} });
DefaultCfgScale.displayName = 'DefaultCfgScale';

View File

@ -2,7 +2,7 @@ import { CompositeNumberInput, CompositeSlider, Flex, FormControl, FormLabel } f
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover'; import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle'; import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle';
import { useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import type { UseControllerProps } from 'react-hook-form'; import type { UseControllerProps } from 'react-hook-form';
import { useController } from 'react-hook-form'; import { useController } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -16,7 +16,7 @@ type Props = {
optimalDimension: number; optimalDimension: number;
}; };
export function DefaultHeight({ control, optimalDimension }: Props) { export const DefaultHeight = memo(({ control, optimalDimension }: Props) => {
const { field } = useController({ control, name: 'height' }); const { field } = useController({ control, name: 'height' });
const sliderMin = useAppSelector((s) => s.config.sd.height.sliderMin); const sliderMin = useAppSelector((s) => s.config.sd.height.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.height.sliderMax); const sliderMax = useAppSelector((s) => s.config.sd.height.sliderMax);
@ -78,4 +78,6 @@ export function DefaultHeight({ control, optimalDimension }: Props) {
</Flex> </Flex>
</FormControl> </FormControl>
); );
} });
DefaultHeight.displayName = 'DefaultHeight';

View File

@ -4,7 +4,7 @@ import { InformationalPopover } from 'common/components/InformationalPopover/Inf
import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle'; import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle';
import { SCHEDULER_OPTIONS } from 'features/parameters/types/constants'; import { SCHEDULER_OPTIONS } from 'features/parameters/types/constants';
import { isParameterScheduler } from 'features/parameters/types/parameterSchemas'; import { isParameterScheduler } from 'features/parameters/types/parameterSchemas';
import { useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import type { UseControllerProps } from 'react-hook-form'; import type { UseControllerProps } from 'react-hook-form';
import { useController } from 'react-hook-form'; import { useController } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -13,7 +13,7 @@ import type { MainModelDefaultSettingsFormData } from './MainModelDefaultSetting
type DefaultSchedulerType = MainModelDefaultSettingsFormData['scheduler']; type DefaultSchedulerType = MainModelDefaultSettingsFormData['scheduler'];
export function DefaultScheduler(props: UseControllerProps<MainModelDefaultSettingsFormData>) { export const DefaultScheduler = memo((props: UseControllerProps<MainModelDefaultSettingsFormData>) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { field } = useController(props); const { field } = useController(props);
@ -51,4 +51,6 @@ export function DefaultScheduler(props: UseControllerProps<MainModelDefaultSetti
<Combobox isDisabled={isDisabled} value={value} options={SCHEDULER_OPTIONS} onChange={onChange} /> <Combobox isDisabled={isDisabled} value={value} options={SCHEDULER_OPTIONS} onChange={onChange} />
</FormControl> </FormControl>
); );
} });
DefaultScheduler.displayName = 'DefaultScheduler';

View File

@ -2,7 +2,7 @@ import { CompositeNumberInput, CompositeSlider, Flex, FormControl, FormLabel } f
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover'; import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle'; import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle';
import { useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import type { UseControllerProps } from 'react-hook-form'; import type { UseControllerProps } from 'react-hook-form';
import { useController } from 'react-hook-form'; import { useController } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -11,7 +11,7 @@ import type { MainModelDefaultSettingsFormData } from './MainModelDefaultSetting
type DefaultSteps = MainModelDefaultSettingsFormData['steps']; type DefaultSteps = MainModelDefaultSettingsFormData['steps'];
export function DefaultSteps(props: UseControllerProps<MainModelDefaultSettingsFormData>) { export const DefaultSteps = memo((props: UseControllerProps<MainModelDefaultSettingsFormData>) => {
const { field } = useController(props); const { field } = useController(props);
const sliderMin = useAppSelector((s) => s.config.sd.steps.sliderMin); const sliderMin = useAppSelector((s) => s.config.sd.steps.sliderMin);
@ -74,4 +74,6 @@ export function DefaultSteps(props: UseControllerProps<MainModelDefaultSettingsF
</Flex> </Flex>
</FormControl> </FormControl>
); );
} });
DefaultSteps.displayName = 'DefaultSteps';

View File

@ -4,7 +4,7 @@ import { skipToken } from '@reduxjs/toolkit/query';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover'; import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle'; import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle';
import { useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import type { UseControllerProps } from 'react-hook-form'; import type { UseControllerProps } from 'react-hook-form';
import { useController } from 'react-hook-form'; import { useController } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -15,7 +15,7 @@ import type { MainModelDefaultSettingsFormData } from './MainModelDefaultSetting
type DefaultVaeType = MainModelDefaultSettingsFormData['vae']; type DefaultVaeType = MainModelDefaultSettingsFormData['vae'];
export function DefaultVae(props: UseControllerProps<MainModelDefaultSettingsFormData>) { export const DefaultVae = memo((props: UseControllerProps<MainModelDefaultSettingsFormData>) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { field } = useController(props); const { field } = useController(props);
const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey); const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
@ -64,4 +64,6 @@ export function DefaultVae(props: UseControllerProps<MainModelDefaultSettingsFor
<Combobox isDisabled={isDisabled} value={value} options={compatibleOptions} onChange={onChange} /> <Combobox isDisabled={isDisabled} value={value} options={compatibleOptions} onChange={onChange} />
</FormControl> </FormControl>
); );
} });
DefaultVae.displayName = 'DefaultVae';

View File

@ -3,7 +3,7 @@ import { Combobox, Flex, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover'; import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle'; import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle';
import { isParameterPrecision } from 'features/parameters/types/parameterSchemas'; import { isParameterPrecision } from 'features/parameters/types/parameterSchemas';
import { useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import type { UseControllerProps } from 'react-hook-form'; import type { UseControllerProps } from 'react-hook-form';
import { useController } from 'react-hook-form'; import { useController } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -17,7 +17,7 @@ const options = [
type DefaultVaePrecisionType = MainModelDefaultSettingsFormData['vaePrecision']; type DefaultVaePrecisionType = MainModelDefaultSettingsFormData['vaePrecision'];
export function DefaultVaePrecision(props: UseControllerProps<MainModelDefaultSettingsFormData>) { export const DefaultVaePrecision = memo((props: UseControllerProps<MainModelDefaultSettingsFormData>) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { field } = useController(props); const { field } = useController(props);
@ -52,4 +52,6 @@ export function DefaultVaePrecision(props: UseControllerProps<MainModelDefaultSe
<Combobox isDisabled={isDisabled} value={value} options={options} onChange={onChange} /> <Combobox isDisabled={isDisabled} value={value} options={options} onChange={onChange} />
</FormControl> </FormControl>
); );
} });
DefaultVaePrecision.displayName = 'DefaultVaePrecision';

View File

@ -2,7 +2,7 @@ import { CompositeNumberInput, CompositeSlider, Flex, FormControl, FormLabel } f
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover'; import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle'; import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle';
import { useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import type { UseControllerProps } from 'react-hook-form'; import type { UseControllerProps } from 'react-hook-form';
import { useController } from 'react-hook-form'; import { useController } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -16,7 +16,7 @@ type Props = {
optimalDimension: number; optimalDimension: number;
}; };
export function DefaultWidth({ control, optimalDimension }: Props) { export const DefaultWidth = memo(({ control, optimalDimension }: Props) => {
const { field } = useController({ control, name: 'width' }); const { field } = useController({ control, name: 'width' });
const sliderMin = useAppSelector((s) => s.config.sd.width.sliderMin); const sliderMin = useAppSelector((s) => s.config.sd.width.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.width.sliderMax); const sliderMax = useAppSelector((s) => s.config.sd.width.sliderMax);
@ -78,4 +78,6 @@ export function DefaultWidth({ control, optimalDimension }: Props) {
</Flex> </Flex>
</FormControl> </FormControl>
); );
} });
DefaultWidth.displayName = 'DefaultWidth';

View File

@ -6,7 +6,7 @@ import { DefaultWidth } from 'features/modelManagerV2/subpanels/ModelPanel/MainM
import type { ParameterScheduler } from 'features/parameters/types/parameterSchemas'; import type { ParameterScheduler } from 'features/parameters/types/parameterSchemas';
import { getOptimalDimension } from 'features/parameters/util/optimalDimension'; import { getOptimalDimension } from 'features/parameters/util/optimalDimension';
import { toast } from 'features/toast/toast'; import { toast } from 'features/toast/toast';
import { useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import type { SubmitHandler } from 'react-hook-form'; import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -41,7 +41,7 @@ type Props = {
modelConfig: MainModelConfig; modelConfig: MainModelConfig;
}; };
export const MainModelDefaultSettings = ({ modelConfig }: Props) => { export const MainModelDefaultSettings = memo(({ modelConfig }: Props) => {
const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey); const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
const { t } = useTranslation(); const { t } = useTranslation();
@ -124,4 +124,6 @@ export const MainModelDefaultSettings = ({ modelConfig }: Props) => {
</SimpleGrid> </SimpleGrid>
</> </>
); );
}; });
MainModelDefaultSettings.displayName = 'MainModelDefaultSettings';

View File

@ -1,6 +1,6 @@
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { IAINoContentFallback, IAINoContentFallbackWithSpinner } from 'common/components/IAIImageFallback'; import { IAINoContentFallback, IAINoContentFallbackWithSpinner } from 'common/components/IAIImageFallback';
import { useMemo } from 'react'; import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { PiExclamationMarkBold } from 'react-icons/pi'; import { PiExclamationMarkBold } from 'react-icons/pi';
import { modelConfigsAdapterSelectors, useGetModelConfigsQuery } from 'services/api/endpoints/models'; import { modelConfigsAdapterSelectors, useGetModelConfigsQuery } from 'services/api/endpoints/models';
@ -8,7 +8,7 @@ import { modelConfigsAdapterSelectors, useGetModelConfigsQuery } from 'services/
import { ModelEdit } from './ModelEdit'; import { ModelEdit } from './ModelEdit';
import { ModelView } from './ModelView'; import { ModelView } from './ModelView';
export const Model = () => { export const Model = memo(() => {
const { t } = useTranslation(); const { t } = useTranslation();
const selectedModelMode = useAppSelector((s) => s.modelmanagerV2.selectedModelMode); const selectedModelMode = useAppSelector((s) => s.modelmanagerV2.selectedModelMode);
const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey); const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
@ -42,4 +42,6 @@ export const Model = () => {
} }
return <ModelEdit modelConfig={modelConfig} />; return <ModelEdit modelConfig={modelConfig} />;
}; });
Model.displayName = 'Model';

View File

@ -1,11 +1,12 @@
import { FormControl, FormLabel, Text } from '@invoke-ai/ui-library'; import { FormControl, FormLabel, Text } from '@invoke-ai/ui-library';
import { memo } from 'react';
interface Props { interface Props {
label: string; label: string;
value: string | null | undefined; value: string | null | undefined;
} }
export const ModelAttrView = ({ label, value }: Props) => { export const ModelAttrView = memo(({ label, value }: Props) => {
return ( return (
<FormControl flexDir="column" alignItems="flex-start" gap={0}> <FormControl flexDir="column" alignItems="flex-start" gap={0}>
<FormLabel>{label}</FormLabel> <FormLabel>{label}</FormLabel>
@ -14,4 +15,6 @@ export const ModelAttrView = ({ label, value }: Props) => {
</Text> </Text>
</FormControl> </FormControl>
); );
}; });
ModelAttrView.displayName = 'ModelAttrView';

View File

@ -9,7 +9,7 @@ import {
useDisclosure, useDisclosure,
} from '@invoke-ai/ui-library'; } from '@invoke-ai/ui-library';
import { toast } from 'features/toast/toast'; import { toast } from 'features/toast/toast';
import { useCallback } from 'react'; import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useConvertModelMutation } from 'services/api/endpoints/models'; import { useConvertModelMutation } from 'services/api/endpoints/models';
import type { CheckpointModelConfig } from 'services/api/types'; import type { CheckpointModelConfig } from 'services/api/types';
@ -18,7 +18,7 @@ interface ModelConvertProps {
modelConfig: CheckpointModelConfig; modelConfig: CheckpointModelConfig;
} }
export const ModelConvertButton = ({ modelConfig }: ModelConvertProps) => { export const ModelConvertButton = memo(({ modelConfig }: ModelConvertProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [convertModel, { isLoading }] = useConvertModelMutation(); const [convertModel, { isLoading }] = useConvertModelMutation();
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
@ -90,4 +90,6 @@ export const ModelConvertButton = ({ modelConfig }: ModelConvertProps) => {
</ConfirmationAlertDialog> </ConfirmationAlertDialog>
</> </>
); );
}; });
ModelConvertButton.displayName = 'ModelConvertButton';

View File

@ -14,7 +14,7 @@ import { useAppDispatch } from 'app/store/storeHooks';
import { setSelectedModelMode } from 'features/modelManagerV2/store/modelManagerV2Slice'; import { setSelectedModelMode } from 'features/modelManagerV2/store/modelManagerV2Slice';
import { ModelHeader } from 'features/modelManagerV2/subpanels/ModelPanel/ModelHeader'; import { ModelHeader } from 'features/modelManagerV2/subpanels/ModelPanel/ModelHeader';
import { toast } from 'features/toast/toast'; import { toast } from 'features/toast/toast';
import { useCallback } from 'react'; import { memo, useCallback } from 'react';
import { type SubmitHandler, useForm } from 'react-hook-form'; import { type SubmitHandler, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { PiCheckBold, PiXBold } from 'react-icons/pi'; import { PiCheckBold, PiXBold } from 'react-icons/pi';
@ -33,7 +33,7 @@ const stringFieldOptions = {
validate: (value?: string | null) => (value && value.trim().length > 3) || 'Must be at least 3 characters', validate: (value?: string | null) => (value && value.trim().length > 3) || 'Must be at least 3 characters',
}; };
export const ModelEdit = ({ modelConfig }: Props) => { export const ModelEdit = memo(({ modelConfig }: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [updateModel, { isLoading: isSubmitting }] = useUpdateModelMutation(); const [updateModel, { isLoading: isSubmitting }] = useUpdateModelMutation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
@ -156,4 +156,6 @@ export const ModelEdit = ({ modelConfig }: Props) => {
</Flex> </Flex>
</Flex> </Flex>
); );
}; });
ModelEdit.displayName = 'ModelEdit';

View File

@ -1,11 +1,11 @@
import { Button } from '@invoke-ai/ui-library'; import { Button } from '@invoke-ai/ui-library';
import { useAppDispatch } from 'app/store/storeHooks'; import { useAppDispatch } from 'app/store/storeHooks';
import { setSelectedModelMode } from 'features/modelManagerV2/store/modelManagerV2Slice'; import { setSelectedModelMode } from 'features/modelManagerV2/store/modelManagerV2Slice';
import { useCallback } from 'react'; import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { IoPencil } from 'react-icons/io5'; import { IoPencil } from 'react-icons/io5';
export const ModelEditButton = () => { export const ModelEditButton = memo(() => {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
@ -18,4 +18,6 @@ export const ModelEditButton = () => {
{t('modelManager.edit')} {t('modelManager.edit')}
</Button> </Button>
); );
}; });
ModelEditButton.displayName = 'ModelEditButton';

View File

@ -4,6 +4,7 @@ import { ModelConvertButton } from 'features/modelManagerV2/subpanels/ModelPanel
import { ModelEditButton } from 'features/modelManagerV2/subpanels/ModelPanel/ModelEditButton'; import { ModelEditButton } from 'features/modelManagerV2/subpanels/ModelPanel/ModelEditButton';
import { ModelHeader } from 'features/modelManagerV2/subpanels/ModelPanel/ModelHeader'; import { ModelHeader } from 'features/modelManagerV2/subpanels/ModelPanel/ModelHeader';
import { TriggerPhrases } from 'features/modelManagerV2/subpanels/ModelPanel/TriggerPhrases'; import { TriggerPhrases } from 'features/modelManagerV2/subpanels/ModelPanel/TriggerPhrases';
import { memo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import type { AnyModelConfig } from 'services/api/types'; import type { AnyModelConfig } from 'services/api/types';
@ -14,7 +15,7 @@ type Props = {
modelConfig: AnyModelConfig; modelConfig: AnyModelConfig;
}; };
export const ModelView = ({ modelConfig }: Props) => { export const ModelView = memo(({ modelConfig }: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<Flex flexDir="column" gap={4}> <Flex flexDir="column" gap={4}>
@ -61,4 +62,6 @@ export const ModelView = ({ modelConfig }: Props) => {
</Flex> </Flex>
</Flex> </Flex>
); );
}; });
ModelView.displayName = 'ModelView';

View File

@ -1,4 +1,4 @@
import { Switch } from '@invoke-ai/ui-library'; import { Switch, typedMemo } from '@invoke-ai/ui-library';
import type { ChangeEvent } from 'react'; import type { ChangeEvent } from 'react';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import type { UseControllerProps } from 'react-hook-form'; import type { UseControllerProps } from 'react-hook-form';
@ -6,7 +6,7 @@ import { useController } from 'react-hook-form';
import type { FormField } from './MainModelDefaultSettings/MainModelDefaultSettings'; import type { FormField } from './MainModelDefaultSettings/MainModelDefaultSettings';
export function SettingToggle<T, F extends Record<string, FormField<T>>>(props: UseControllerProps<F>) { export const SettingToggle = typedMemo(<T, F extends Record<string, FormField<T>>>(props: UseControllerProps<F>) => {
const { field } = useController(props); const { field } = useController(props);
const value = useMemo(() => { const value = useMemo(() => {
@ -25,4 +25,6 @@ export function SettingToggle<T, F extends Record<string, FormField<T>>>(props:
); );
return <Switch size="sm" isChecked={value} onChange={onChange} />; return <Switch size="sm" isChecked={value} onChange={onChange} />;
} });
SettingToggle.displayName = 'SettingToggle';

View File

@ -10,7 +10,7 @@ import {
TagLabel, TagLabel,
} from '@invoke-ai/ui-library'; } from '@invoke-ai/ui-library';
import type { ChangeEvent } from 'react'; import type { ChangeEvent } from 'react';
import { useCallback, useMemo, useState } from 'react'; import { memo, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { PiPlusBold } from 'react-icons/pi'; import { PiPlusBold } from 'react-icons/pi';
import { useUpdateModelMutation } from 'services/api/endpoints/models'; import { useUpdateModelMutation } from 'services/api/endpoints/models';
@ -20,7 +20,7 @@ type Props = {
modelConfig: MainModelConfig | LoRAModelConfig; modelConfig: MainModelConfig | LoRAModelConfig;
}; };
export const TriggerPhrases = ({ modelConfig }: Props) => { export const TriggerPhrases = memo(({ modelConfig }: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [phrase, setPhrase] = useState(''); const [phrase, setPhrase] = useState('');
@ -92,7 +92,9 @@ export const TriggerPhrases = ({ modelConfig }: Props) => {
{t('common.add')} {t('common.add')}
</Button> </Button>
</Flex> </Flex>
{!!errors.length && errors.map((error) => <FormErrorMessage key={error}>{error}</FormErrorMessage>)} {errors.map((error) => (
<FormErrorMessage key={error}>{error}</FormErrorMessage>
))}
</Flex> </Flex>
</FormControl> </FormControl>
</form> </form>
@ -107,4 +109,6 @@ export const TriggerPhrases = ({ modelConfig }: Props) => {
</Flex> </Flex>
</Flex> </Flex>
); );
}; });
TriggerPhrases.displayName = 'TriggerPhrases';