refactor(ui): ref images (WIP)

This commit is contained in:
psychedelicious
2025-06-13 13:08:03 +10:00
parent 5a2f5c105d
commit 48e2e7e4a1
29 changed files with 235 additions and 237 deletions

View File

@@ -5,7 +5,7 @@ import {
useAddInpaintMask,
useAddRasterLayer,
useAddRegionalGuidance,
useAddRegionalReferenceImage,
useAddNewRegionalGuidanceWithARefImage,
} from 'features/controlLayers/hooks/addLayerHooks';
import { useIsEntityTypeEnabled } from 'features/controlLayers/hooks/useIsEntityTypeEnabled';
import { memo } from 'react';
@@ -18,7 +18,7 @@ export const CanvasAddEntityButtons = memo(() => {
const addRegionalGuidance = useAddRegionalGuidance();
const addRasterLayer = useAddRasterLayer();
const addControlLayer = useAddControlLayer();
const addRegionalReferenceImage = useAddRegionalReferenceImage();
const addRegionalReferenceImage = useAddNewRegionalGuidanceWithARefImage();
const isRegionalGuidanceEnabled = useIsEntityTypeEnabled('regional_guidance');
const isControlLayerEnabled = useIsEntityTypeEnabled('control_layer');
const isInpaintLayerEnabled = useIsEntityTypeEnabled('inpaint_mask');

View File

@@ -2,7 +2,7 @@ import { MenuGroup } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
import { ControlLayerMenuItems } from 'features/controlLayers/components/ControlLayer/ControlLayerMenuItems';
import { InpaintMaskMenuItems } from 'features/controlLayers/components/InpaintMask/InpaintMaskMenuItems';
import { IPAdapterMenuItems } from 'features/controlLayers/components/IPAdapter/IPAdapterMenuItems';
import { IPAdapterMenuItems } from 'features/controlLayers/components/RefImage/IPAdapterMenuItems';
import { RasterLayerMenuItems } from 'features/controlLayers/components/RasterLayer/RasterLayerMenuItems';
import { RegionalGuidanceMenuItems } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItems';
import { CanvasEntityStateGate } from 'features/controlLayers/contexts/CanvasEntityStateGate';

View File

@@ -4,7 +4,7 @@ import {
useAddInpaintMask,
useAddRasterLayer,
useAddRegionalGuidance,
useAddRegionalReferenceImage,
useAddNewRegionalGuidanceWithARefImage,
} from 'features/controlLayers/hooks/addLayerHooks';
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
import { useIsEntityTypeEnabled } from 'features/controlLayers/hooks/useIsEntityTypeEnabled';
@@ -17,7 +17,7 @@ export const EntityListGlobalActionBarAddLayerMenu = memo(() => {
const isBusy = useCanvasIsBusy();
const addInpaintMask = useAddInpaintMask();
const addRegionalGuidance = useAddRegionalGuidance();
const addRegionalReferenceImage = useAddRegionalReferenceImage();
const addRegionalReferenceImage = useAddNewRegionalGuidanceWithARefImage();
const addRasterLayer = useAddRasterLayer();
const addControlLayer = useAddControlLayer();
const isRegionalGuidanceEnabled = useIsEntityTypeEnabled('regional_guidance');

View File

@@ -1,29 +0,0 @@
import { Spacer } from '@invoke-ai/ui-library';
import { CanvasEntityContainer } from 'features/controlLayers/components/CanvasEntityList/CanvasEntityContainer';
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
import { CanvasEntityHeaderCommonActions } from 'features/controlLayers/components/common/CanvasEntityHeaderCommonActions';
import { CanvasEntityEditableTitle } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
import { IPAdapterSettings } from 'features/controlLayers/components/IPAdapter/IPAdapterSettings';
import { RefImageIdContext } from 'features/controlLayers/contexts/RefImageIdContext';
import { memo } from 'react';
type Props = {
id: string;
};
export const IPAdapter = memo(({ id }: Props) => {
return (
<RefImageIdContext.Provider value={id}>
<CanvasEntityContainer>
<CanvasEntityHeader ps={4} py={5}>
<CanvasEntityEditableTitle />
<Spacer />
<CanvasEntityHeaderCommonActions />
</CanvasEntityHeader>
<IPAdapterSettings />
</CanvasEntityContainer>
</RefImageIdContext.Provider>
);
});
IPAdapter.displayName = 'IPAdapter';

View File

@@ -2,7 +2,7 @@
import type { FlexProps, SystemStyleObject } from '@invoke-ai/ui-library';
import { Flex } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
import { RefImagePreview } from 'features/controlLayers/components/IPAdapter/IPAdapterPreview';
import { RefImage } from 'features/controlLayers/components/RefImage/RefImage';
import { RefImageIdContext } from 'features/controlLayers/contexts/RefImageIdContext';
import { selectRefImageEntityIds } from 'features/controlLayers/store/refImagesSlice';
import { memo } from 'react';
@@ -27,7 +27,7 @@ export const RefImageList = memo((props: FlexProps) => {
<Flex gap={2} {...props}>
{ids.map((id) => (
<RefImageIdContext.Provider key={id} value={id}>
<RefImagePreview />
<RefImage />
</RefImageIdContext.Provider>
))}
</Flex>

View File

@@ -3,7 +3,7 @@ import { IconMenuItemGroup } from 'common/components/IconMenuItem';
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
import { CanvasEntityMenuItemsDelete } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDelete';
import { CanvasEntityMenuItemsDuplicate } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDuplicate';
import { IPAdapterMenuItemPullBbox } from 'features/controlLayers/components/IPAdapter/IPAdapterMenuItemPullBbox';
import { IPAdapterMenuItemPullBbox } from 'features/controlLayers/components/RefImage/IPAdapterMenuItemPullBbox';
import { memo } from 'react';
export const IPAdapterMenuItems = memo(() => {

View File

@@ -2,12 +2,12 @@ import { Flex } from '@invoke-ai/ui-library';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { BeginEndStepPct } from 'features/controlLayers/components/common/BeginEndStepPct';
import { CLIPVisionModel } from 'features/controlLayers/components/common/CLIPVisionModel';
import { FLUXReduxImageInfluence } from 'features/controlLayers/components/common/FLUXReduxImageInfluence';
import { IPAdapterCLIPVisionModel } from 'features/controlLayers/components/common/IPAdapterCLIPVisionModel';
import { Weight } from 'features/controlLayers/components/common/Weight';
import { GlobalReferenceImageModel } from 'features/controlLayers/components/IPAdapter/GlobalReferenceImageModel';
import { IPAdapterMethod } from 'features/controlLayers/components/IPAdapter/IPAdapterMethod';
import { IPAdapterSettingsEmptyState } from 'features/controlLayers/components/IPAdapter/IPAdapterSettingsEmptyState';
import { IPAdapterMethod } from 'features/controlLayers/components/RefImage/IPAdapterMethod';
import { RefImageModel } from 'features/controlLayers/components/RefImage/RefImageModel';
import { RefImageNoImageState } from 'features/controlLayers/components/RefImage/RefImageNoImageState';
import { useRefImageIdContext } from 'features/controlLayers/contexts/RefImageIdContext';
import { selectIsFLUX } from 'features/controlLayers/store/paramsSlice';
import {
@@ -22,10 +22,12 @@ import {
selectRefImageEntityOrThrow,
selectRefImagesSlice,
} from 'features/controlLayers/store/refImagesSlice';
import type {
CLIPVisionModelV2,
FLUXReduxImageInfluence as FLUXReduxImageInfluenceType,
IPMethodV2,
import {
type CLIPVisionModelV2,
type FLUXReduxImageInfluence as FLUXReduxImageInfluenceType,
type IPMethodV2,
isFLUXReduxConfig,
isIPAdapterConfig,
} from 'features/controlLayers/store/types';
import type { SetGlobalReferenceImageDndTargetData } from 'features/dnd/dnd';
import { setGlobalReferenceImageDndTarget } from 'features/dnd/dnd';
@@ -33,7 +35,7 @@ import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import type { ApiModelConfig, FLUXReduxModelConfig, ImageDTO, IPAdapterModelConfig } from 'services/api/types';
import { IPAdapterImagePreview } from './IPAdapterImagePreview';
import { RefImageImage } from './RefImageImage';
const buildSelectConfig = (id: string) =>
createSelector(
@@ -45,8 +47,8 @@ const IPAdapterSettingsContent = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const id = useRefImageIdContext();
const selectIPAdapter = useMemo(() => buildSelectConfig(id), [id]);
const ipAdapter = useAppSelector(selectIPAdapter);
const selectConfig = useMemo(() => buildSelectConfig(id), [id]);
const config = useAppSelector(selectConfig);
const onChangeBeginEndStepPct = useCallback(
(beginEndStepPct: [number, number]) => {
@@ -98,8 +100,8 @@ const IPAdapterSettingsContent = memo(() => {
);
const dndTargetData = useMemo<SetGlobalReferenceImageDndTargetData>(
() => setGlobalReferenceImageDndTarget.getData({ id }, ipAdapter.image?.image_name),
[id, ipAdapter.image?.image_name]
() => setGlobalReferenceImageDndTarget.getData({ id }, config.image?.image_name),
[id, config.image?.image_name]
);
// const pullBboxIntoIPAdapter = usePullBboxIntoGlobalReferenceImage(id);
// const isBusy = useCanvasIsBusy();
@@ -109,9 +111,9 @@ const IPAdapterSettingsContent = memo(() => {
return (
<Flex flexDir="column" gap={2} position="relative" w="full">
<Flex gap={2} alignItems="center" w="full">
<GlobalReferenceImageModel modelKey={ipAdapter.model?.key ?? null} onChangeModel={onChangeModel} />
{ipAdapter.type === 'ip_adapter' && (
<CLIPVisionModel model={ipAdapter.clipVisionModel} onChange={onChangeCLIPVisionModel} />
<RefImageModel modelKey={config.model?.key ?? null} onChangeModel={onChangeModel} />
{isIPAdapterConfig(config) && (
<IPAdapterCLIPVisionModel model={config.clipVisionModel} onChange={onChangeCLIPVisionModel} />
)}
{/* <IconButton
onClick={pullBboxIntoIPAdapter}
@@ -123,24 +125,24 @@ const IPAdapterSettingsContent = memo(() => {
/> */}
</Flex>
<Flex gap={2} w="full">
{ipAdapter.type === 'ip_adapter' && (
{isIPAdapterConfig(config) && (
<Flex flexDir="column" gap={2} w="full">
{!isFLUX && <IPAdapterMethod method={ipAdapter.method} onChange={onChangeIPMethod} />}
<Weight weight={ipAdapter.weight} onChange={onChangeWeight} />
<BeginEndStepPct beginEndStepPct={ipAdapter.beginEndStepPct} onChange={onChangeBeginEndStepPct} />
{!isFLUX && <IPAdapterMethod method={config.method} onChange={onChangeIPMethod} />}
<Weight weight={config.weight} onChange={onChangeWeight} />
<BeginEndStepPct beginEndStepPct={config.beginEndStepPct} onChange={onChangeBeginEndStepPct} />
</Flex>
)}
{ipAdapter.type === 'flux_redux' && (
{isFLUXReduxConfig(config) && (
<Flex flexDir="column" gap={2} w="full" alignItems="flex-start">
<FLUXReduxImageInfluence
imageInfluence={ipAdapter.imageInfluence ?? 'lowest'}
imageInfluence={config.imageInfluence ?? 'lowest'}
onChange={onChangeFLUXReduxImageInfluence}
/>
</Flex>
)}
<Flex alignItems="center" justifyContent="center" h={32} w={32} aspectRatio="1/1" flexGrow={1}>
<IPAdapterImagePreview
image={ipAdapter.image}
<RefImageImage
image={config.image}
onChangeImage={onChangeImage}
dndTarget={setGlobalReferenceImageDndTarget}
dndTargetData={dndTargetData}
@@ -166,7 +168,7 @@ export const IPAdapterSettings = memo(() => {
const hasImage = useAppSelector(selectIPAdapterHasImage);
if (!hasImage) {
return <IPAdapterSettingsEmptyState />;
return <RefImageNoImageState />;
}
return <IPAdapterSettingsContent />;

View File

@@ -14,7 +14,7 @@ import { skipToken } from '@reduxjs/toolkit/query';
import { useAppSelector } from 'app/store/storeHooks';
import { useDisclosure } from 'common/hooks/useBoolean';
import { useFilterableOutsideClick } from 'common/hooks/useFilterableOutsideClick';
import { IPAdapterSettings } from 'features/controlLayers/components/IPAdapter/IPAdapterSettings';
import { IPAdapterSettings } from 'features/controlLayers/components/RefImage/IPAdapterSettings';
import { useRefImageIdContext } from 'features/controlLayers/contexts/RefImageIdContext';
import { selectRefImageEntityOrThrow, selectRefImagesSlice } from 'features/controlLayers/store/refImagesSlice';
import type { ImageWithDims } from 'features/controlLayers/store/types';
@@ -34,15 +34,12 @@ const sx: SystemStyleObject = {
transitionDuration: '0.2s',
};
export const RefImagePreview = memo(() => {
export const RefImage = memo(() => {
const id = useRefImageIdContext();
const ref = useRef<HTMLDivElement>(null);
const disclosure = useDisclosure(false);
const selectEntity = useMemo(
() =>
createSelector(selectRefImagesSlice, (refImages) =>
selectRefImageEntityOrThrow(refImages, id, 'RefImagePreview')
),
() => createSelector(selectRefImagesSlice, (refImages) => selectRefImageEntityOrThrow(refImages, id, 'RefImage')),
[id]
);
const entity = useAppSelector(selectEntity);
@@ -66,7 +63,7 @@ export const RefImagePreview = memo(() => {
</Popover>
);
});
RefImagePreview.displayName = 'RefImagePreview';
RefImage.displayName = 'RefImage';
const Thumbnail = memo(({ image }: { image: ImageWithDims | null }) => {
const { data: imageDTO } = useGetImageDTOQuery(image?.image_name ?? skipToken);

View File

@@ -21,7 +21,7 @@ type Props<T extends typeof setGlobalReferenceImageDndTarget | typeof setRegiona
dndTargetData: ReturnType<T['getData']>;
};
export const IPAdapterImagePreview = memo(
export const RefImageImage = memo(
<T extends typeof setGlobalReferenceImageDndTarget | typeof setRegionalGuidanceReferenceImageDndTarget>({
image,
onChangeImage,
@@ -77,4 +77,4 @@ export const IPAdapterImagePreview = memo(
}
);
IPAdapterImagePreview.displayName = 'IPAdapterImagePreview';
RefImageImage.displayName = 'RefImageImage';

View File

@@ -12,7 +12,7 @@ type Props = {
onChangeModel: (modelConfig: IPAdapterModelConfig | FLUXReduxModelConfig | ApiModelConfig) => void;
};
export const GlobalReferenceImageModel = memo(({ modelKey, onChangeModel }: Props) => {
export const RefImageModel = memo(({ modelKey, onChangeModel }: Props) => {
const { t } = useTranslation();
const currentBaseModel = useAppSelector(selectBase);
const [modelConfigs, { isLoading }] = useGlobalReferenceImageModels();
@@ -60,4 +60,4 @@ export const GlobalReferenceImageModel = memo(({ modelKey, onChangeModel }: Prop
);
});
GlobalReferenceImageModel.displayName = 'GlobalReferenceImageModel';
RefImageModel.displayName = 'RefImageModel';

View File

@@ -13,7 +13,7 @@ import { memo, useCallback, useMemo } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import type { ImageDTO } from 'services/api/types';
export const IPAdapterSettingsEmptyState = memo(() => {
export const RefImageNoImageState = memo(() => {
const { t } = useTranslation();
const id = useRefImageIdContext();
const dispatch = useAppDispatch();
@@ -66,4 +66,4 @@ export const IPAdapterSettingsEmptyState = memo(() => {
);
});
IPAdapterSettingsEmptyState.displayName = 'IPAdapterSettingsEmptyState';
RefImageNoImageState.displayName = 'RefImageNoImageState';

View File

@@ -3,9 +3,9 @@ import { useAppSelector } from 'app/store/storeHooks';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import {
buildSelectValidRegionalGuidanceActions,
useAddRegionalGuidanceIPAdapter,
useAddRegionalGuidanceNegativePrompt,
useAddRegionalGuidancePositivePrompt,
useAddRefImageToExistingRegionalGuidance,
useAddNegativePromptToExistingRegionalGuidance,
useAddPositivePromptToExistingRegionalGuidance,
} from 'features/controlLayers/hooks/addLayerHooks';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -14,9 +14,9 @@ import { PiPlusBold } from 'react-icons/pi';
export const RegionalGuidanceAddPromptsIPAdapterButtons = () => {
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
const { t } = useTranslation();
const addRegionalGuidanceIPAdapter = useAddRegionalGuidanceIPAdapter(entityIdentifier);
const addRegionalGuidancePositivePrompt = useAddRegionalGuidancePositivePrompt(entityIdentifier);
const addRegionalGuidanceNegativePrompt = useAddRegionalGuidanceNegativePrompt(entityIdentifier);
const addRegionalGuidanceIPAdapter = useAddRefImageToExistingRegionalGuidance(entityIdentifier);
const addRegionalGuidancePositivePrompt = useAddPositivePromptToExistingRegionalGuidance(entityIdentifier);
const addRegionalGuidanceNegativePrompt = useAddNegativePromptToExistingRegionalGuidance(entityIdentifier);
const selectValidActions = useMemo(
() => buildSelectValidRegionalGuidanceActions(entityIdentifier),

View File

@@ -2,11 +2,11 @@ import { Flex, IconButton, Spacer, Text } from '@invoke-ai/ui-library';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { BeginEndStepPct } from 'features/controlLayers/components/common/BeginEndStepPct';
import { CLIPVisionModel } from 'features/controlLayers/components/common/CLIPVisionModel';
import { IPAdapterCLIPVisionModel } from 'features/controlLayers/components/common/IPAdapterCLIPVisionModel';
import { FLUXReduxImageInfluence } from 'features/controlLayers/components/common/FLUXReduxImageInfluence';
import { Weight } from 'features/controlLayers/components/common/Weight';
import { IPAdapterImagePreview } from 'features/controlLayers/components/IPAdapter/IPAdapterImagePreview';
import { IPAdapterMethod } from 'features/controlLayers/components/IPAdapter/IPAdapterMethod';
import { RefImageImage } from 'features/controlLayers/components/RefImage/RefImageImage';
import { IPAdapterMethod } from 'features/controlLayers/components/RefImage/IPAdapterMethod';
import { RegionalGuidanceIPAdapterSettingsEmptyState } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceIPAdapterSettingsEmptyState';
import { RegionalReferenceImageModel } from 'features/controlLayers/components/RegionalGuidance/RegionalReferenceImageModel';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
@@ -142,7 +142,7 @@ const RegionalGuidanceIPAdapterSettingsContent = memo(({ referenceImageId }: Pro
<Flex gap={2} alignItems="center" w="full">
<RegionalReferenceImageModel modelKey={config.model?.key ?? null} onChangeModel={onChangeModel} />
{config.type === 'ip_adapter' && (
<CLIPVisionModel model={config.clipVisionModel} onChange={onChangeCLIPVisionModel} />
<IPAdapterCLIPVisionModel model={config.clipVisionModel} onChange={onChangeCLIPVisionModel} />
)}
<IconButton
onClick={pullBboxIntoIPAdapter}
@@ -170,7 +170,7 @@ const RegionalGuidanceIPAdapterSettingsContent = memo(({ referenceImageId }: Pro
</Flex>
)}
<Flex alignItems="center" justifyContent="center" h={32} w={32} aspectRatio="1/1" flexGrow={1}>
<IPAdapterImagePreview
<RefImageImage
image={config.image}
onChangeImage={onChangeImage}
dndTarget={setRegionalGuidanceReferenceImageDndTarget}

View File

@@ -3,9 +3,9 @@ import { useAppSelector } from 'app/store/storeHooks';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import {
buildSelectValidRegionalGuidanceActions,
useAddRegionalGuidanceIPAdapter,
useAddRegionalGuidanceNegativePrompt,
useAddRegionalGuidancePositivePrompt,
useAddRefImageToExistingRegionalGuidance,
useAddNegativePromptToExistingRegionalGuidance,
useAddPositivePromptToExistingRegionalGuidance,
} from 'features/controlLayers/hooks/addLayerHooks';
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
import { memo, useMemo } from 'react';
@@ -15,9 +15,9 @@ export const RegionalGuidanceMenuItemsAddPromptsAndIPAdapter = memo(() => {
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
const { t } = useTranslation();
const isBusy = useCanvasIsBusy();
const addRegionalGuidanceIPAdapter = useAddRegionalGuidanceIPAdapter(entityIdentifier);
const addRegionalGuidancePositivePrompt = useAddRegionalGuidancePositivePrompt(entityIdentifier);
const addRegionalGuidanceNegativePrompt = useAddRegionalGuidanceNegativePrompt(entityIdentifier);
const addRegionalGuidanceIPAdapter = useAddRefImageToExistingRegionalGuidance(entityIdentifier);
const addRegionalGuidancePositivePrompt = useAddPositivePromptToExistingRegionalGuidance(entityIdentifier);
const addRegionalGuidanceNegativePrompt = useAddNegativePromptToExistingRegionalGuidance(entityIdentifier);
const selectValidActions = useMemo(
() => buildSelectValidRegionalGuidanceActions(entityIdentifier),
[entityIdentifier]

View File

@@ -2,7 +2,7 @@ import type { FlexProps } from '@invoke-ai/ui-library';
import { ContextMenu, Flex, MenuList } from '@invoke-ai/ui-library';
import { ControlLayerMenuItems } from 'features/controlLayers/components/ControlLayer/ControlLayerMenuItems';
import { InpaintMaskMenuItems } from 'features/controlLayers/components/InpaintMask/InpaintMaskMenuItems';
import { IPAdapterMenuItems } from 'features/controlLayers/components/IPAdapter/IPAdapterMenuItems';
import { IPAdapterMenuItems } from 'features/controlLayers/components/RefImage/IPAdapterMenuItems';
import { RasterLayerMenuItems } from 'features/controlLayers/components/RasterLayer/RasterLayerMenuItems';
import { RegionalGuidanceMenuItems } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItems';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';

View File

@@ -22,7 +22,7 @@ type Props = {
onChange: (clipVisionModel: CLIPVisionModelV2) => void;
};
export const CLIPVisionModel = memo(({ model, onChange }: Props) => {
export const IPAdapterCLIPVisionModel = memo(({ model, onChange }: Props) => {
const { t } = useTranslation();
const _onChangeCLIPVisionModel = useCallback<ComboboxOnChange>(
@@ -58,4 +58,4 @@ export const CLIPVisionModel = memo(({ model, onChange }: Props) => {
);
});
CLIPVisionModel.displayName = 'CLIPVisionModel';
IPAdapterCLIPVisionModel.displayName = 'IPAdapterCLIPVisionModel';

View File

@@ -1,6 +1,8 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useAppStore } from 'app/store/nanostores/store';
import type { AppGetState } from 'app/store/store';
import { useAppDispatch } from 'app/store/storeHooks';
import { deepClone } from 'common/util/deepClone';
import { getPrefixedId } from 'features/controlLayers/konva/util';
import {
@@ -20,10 +22,11 @@ import { selectCanvasSlice, selectEntity } from 'features/controlLayers/store/se
import type {
CanvasEntityIdentifier,
CanvasRegionalGuidanceState,
ChatGPT4oReferenceImageConfig,
ControlLoRAConfig,
ControlNetConfig,
FluxKontextReferenceImageConfig,
IPAdapterConfig,
RefImageState,
T2IAdapterConfig,
} from 'features/controlLayers/store/types';
import {
@@ -36,13 +39,9 @@ import {
import { zModelIdentifierField } from 'features/nodes/types/common';
import { useCallback } from 'react';
import { modelConfigsAdapterSelectors, selectModelConfigsQuery } from 'services/api/endpoints/models';
import type {
ControlLoRAModelConfig,
ControlNetModelConfig,
IPAdapterModelConfig,
T2IAdapterModelConfig,
} from 'services/api/types';
import { isControlLayerModelConfig, isIPAdapterModelConfig } from 'services/api/types';
import { selectIPAdapterModels } from 'services/api/hooks/modelsByType';
import type { ControlLoRAModelConfig, ControlNetModelConfig, T2IAdapterModelConfig } from 'services/api/types';
import { isControlLayerModelConfig } from 'services/api/types';
/**
* Selects the default control adapter configuration based on the model configurations and the base.
@@ -73,67 +72,69 @@ export const selectDefaultControlAdapter = createSelector(
}
);
export const selectDefaultRefImageConfig = createSelector(
selectMainModelConfig,
selectModelConfigsQuery,
selectBase,
(selectedMainModel, query, base): RefImageState['config'] => {
if (selectedMainModel?.base === 'chatgpt-4o') {
const referenceImage = deepClone(initialChatGPT4oReferenceImage);
referenceImage.model = zModelIdentifierField.parse(selectedMainModel);
return referenceImage;
}
export const getDefaultRefImageConfig = (
getState: AppGetState
): IPAdapterConfig | ChatGPT4oReferenceImageConfig | FluxKontextReferenceImageConfig => {
const state = getState();
if (selectedMainModel?.base === 'flux-kontext') {
const referenceImage = deepClone(initialFluxKontextReferenceImage);
referenceImage.model = zModelIdentifierField.parse(selectedMainModel);
return referenceImage;
}
const mainModelConfig = selectMainModelConfig(state);
const ipAdapterModelConfigs = selectIPAdapterModels(state);
const { data } = query;
let model: IPAdapterModelConfig | null = null;
if (data) {
const modelConfigs = modelConfigsAdapterSelectors.selectAll(data).filter(isIPAdapterModelConfig);
const compatibleModels = modelConfigs.filter((m) => (base ? m.base === base : true));
model = compatibleModels[0] ?? modelConfigs[0] ?? null;
}
const ipAdapter = deepClone(initialIPAdapter);
if (model) {
ipAdapter.model = zModelIdentifierField.parse(model);
if (model.base === 'flux') {
ipAdapter.clipVisionModel = 'ViT-L';
}
}
return ipAdapter;
const base = mainModelConfig?.base;
// For ChatGPT-4o, the ref image model is the model itself.
if (base === 'chatgpt-4o') {
const config = deepClone(initialChatGPT4oReferenceImage);
config.model = zModelIdentifierField.parse(mainModelConfig);
return config;
}
);
/**
* Selects the default IP adapter configuration based on the model configurations and the base.
*
* Be sure to clone the output of this selector before modifying it!
*/
export const selectDefaultIPAdapter = createSelector(
selectModelConfigsQuery,
selectBase,
(query, base): IPAdapterConfig => {
const { data } = query;
let model: IPAdapterModelConfig | null = null;
if (data) {
const modelConfigs = modelConfigsAdapterSelectors.selectAll(data).filter(isIPAdapterModelConfig);
const compatibleModels = modelConfigs.filter((m) => (base ? m.base === base : true));
model = compatibleModels[0] ?? modelConfigs[0] ?? null;
}
const ipAdapter = deepClone(initialIPAdapter);
if (model) {
ipAdapter.model = zModelIdentifierField.parse(model);
if (model.base === 'flux') {
ipAdapter.clipVisionModel = 'ViT-L';
}
}
return ipAdapter;
if (base === 'flux-kontext') {
const config = deepClone(initialFluxKontextReferenceImage);
config.model = zModelIdentifierField.parse(mainModelConfig);
return config;
}
);
// Otherwise, find the first compatible IP Adapter model.
const modelConfig = ipAdapterModelConfigs.find((m) => m.base === base);
// Clone the initial IP Adapter config and set the model if available.
const config = deepClone(initialIPAdapter);
if (modelConfig) {
config.model = zModelIdentifierField.parse(modelConfig);
// FLUX models use a different vision model.
if (modelConfig.base === 'flux') {
config.clipVisionModel = 'ViT-L';
}
}
return config;
};
export const getDefaultRegionalGuidanceRefImageConfig = (getState: AppGetState): IPAdapterConfig => {
// Regional guidance ref images do not support ChatGPT-4o, so we always return the IP Adapter config.
const state = getState();
const mainModelConfig = selectMainModelConfig(state);
const ipAdapterModelConfigs = selectIPAdapterModels(state);
const base = mainModelConfig?.base;
// Find the first compatible IP Adapter model.
const modelConfig = ipAdapterModelConfigs.find((m) => m.base === base);
// Clone the initial IP Adapter config and set the model if available.
const config = deepClone(initialIPAdapter);
if (modelConfig) {
config.model = zModelIdentifierField.parse(modelConfig);
// FLUX models use a different vision model.
if (modelConfig.base === 'flux') {
config.clipVisionModel = 'ViT-L';
}
}
return config;
};
export const useAddControlLayer = () => {
const dispatch = useAppDispatch();
@@ -172,44 +173,46 @@ export const useAddRegionalGuidance = () => {
return func;
};
export const useAddRegionalReferenceImage = () => {
const dispatch = useAppDispatch();
const defaultIPAdapter = useAppSelector(selectDefaultIPAdapter);
export const useAddNewRegionalGuidanceWithARefImage = () => {
const { dispatch, getState } = useAppStore();
const func = useCallback(() => {
const config = getDefaultRegionalGuidanceRefImageConfig(getState);
const overrides: Partial<CanvasRegionalGuidanceState> = {
referenceImages: [
{ id: getPrefixedId('regional_guidance_reference_image'), config: deepClone(defaultIPAdapter) },
],
referenceImages: [{ id: getPrefixedId('regional_guidance_reference_image'), config }],
};
dispatch(rgAdded({ isSelected: true, overrides }));
}, [defaultIPAdapter, dispatch]);
}, [dispatch, getState]);
return func;
};
export const useAddGlobalReferenceImage = () => {
const dispatch = useAppDispatch();
const defaultRefImage = useAppSelector(selectDefaultRefImageConfig);
const { dispatch, getState } = useAppStore();
const func = useCallback(() => {
const overrides = { config: deepClone(defaultRefImage) };
const config = getDefaultRefImageConfig(getState);
const overrides = { config };
dispatch(refImageAdded({ isSelected: true, overrides }));
}, [defaultRefImage, dispatch]);
}, [dispatch, getState]);
return func;
};
export const useAddRegionalGuidanceIPAdapter = (entityIdentifier: CanvasEntityIdentifier<'regional_guidance'>) => {
const dispatch = useAppDispatch();
const defaultIPAdapter = useAppSelector(selectDefaultIPAdapter);
export const useAddRefImageToExistingRegionalGuidance = (
entityIdentifier: CanvasEntityIdentifier<'regional_guidance'>
) => {
const { dispatch, getState } = useAppStore();
const func = useCallback(() => {
dispatch(rgRefImageAdded({ entityIdentifier, overrides: { config: deepClone(defaultIPAdapter) } }));
}, [defaultIPAdapter, dispatch, entityIdentifier]);
const config = getDefaultRegionalGuidanceRefImageConfig(getState);
dispatch(rgRefImageAdded({ entityIdentifier, overrides: { config } }));
}, [dispatch, entityIdentifier, getState]);
return func;
};
export const useAddRegionalGuidancePositivePrompt = (entityIdentifier: CanvasEntityIdentifier<'regional_guidance'>) => {
export const useAddPositivePromptToExistingRegionalGuidance = (
entityIdentifier: CanvasEntityIdentifier<'regional_guidance'>
) => {
const dispatch = useAppDispatch();
const func = useCallback(() => {
dispatch(rgPositivePromptChanged({ entityIdentifier, prompt: '' }));
@@ -218,7 +221,9 @@ export const useAddRegionalGuidancePositivePrompt = (entityIdentifier: CanvasEnt
return func;
};
export const useAddRegionalGuidanceNegativePrompt = (entityIdentifier: CanvasEntityIdentifier<'regional_guidance'>) => {
export const useAddNegativePromptToExistingRegionalGuidance = (
entityIdentifier: CanvasEntityIdentifier<'regional_guidance'>
) => {
const dispatch = useAppDispatch();
const runc = useCallback(() => {
dispatch(rgNegativePromptChanged({ entityIdentifier, prompt: '' }));

View File

@@ -1,9 +1,12 @@
import { logger } from 'app/logging/logger';
import { useAppDispatch, useAppSelector, useAppStore } from 'app/store/storeHooks';
import { useAppDispatch, useAppStore } from 'app/store/storeHooks';
import { deepClone } from 'common/util/deepClone';
import { withResultAsync } from 'common/util/result';
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
import { selectDefaultIPAdapter, selectDefaultRefImageConfig } from 'features/controlLayers/hooks/addLayerHooks';
import {
getDefaultRefImageConfig,
getDefaultRegionalGuidanceRefImageConfig,
} from 'features/controlLayers/hooks/addLayerHooks';
import { getPrefixedId } from 'features/controlLayers/konva/util';
import {
controlLayerAdded,
@@ -18,7 +21,7 @@ import {
selectPositivePrompt,
selectSeed,
} from 'features/controlLayers/store/paramsSlice';
import { refImageAdded,refImageImageChanged } from 'features/controlLayers/store/refImagesSlice';
import { refImageAdded, refImageImageChanged } from 'features/controlLayers/store/refImagesSlice';
import { selectCanvasMetadata } from 'features/controlLayers/store/selectors';
import type {
CanvasControlLayerState,
@@ -167,15 +170,14 @@ export const useSaveBboxToGallery = () => {
export const useNewRegionalReferenceImageFromBbox = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const defaultIPAdapter = useAppSelector(selectDefaultIPAdapter);
const { dispatch, getState } = useAppStore();
const arg = useMemo<UseSaveCanvasArg>(() => {
const onSave = (imageDTO: ImageDTO) => {
const ipAdapter: RegionalGuidanceRefImageState = {
id: getPrefixedId('regional_guidance_reference_image'),
config: {
...deepClone(defaultIPAdapter),
...getDefaultRegionalGuidanceRefImageConfig(getState),
image: imageDTOToImageWithDims(imageDTO),
},
};
@@ -193,21 +195,20 @@ export const useNewRegionalReferenceImageFromBbox = () => {
toastOk: t('controlLayers.newRegionalReferenceImageOk'),
toastError: t('controlLayers.newRegionalReferenceImageError'),
};
}, [defaultIPAdapter, dispatch, t]);
}, [dispatch, getState, t]);
const func = useSaveCanvas(arg);
return func;
};
export const useNewGlobalReferenceImageFromBbox = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const defaultIPAdapter = useAppSelector(selectDefaultRefImageConfig);
const { dispatch, getState } = useAppStore();
const arg = useMemo<UseSaveCanvasArg>(() => {
const onSave = (imageDTO: ImageDTO) => {
const overrides: Partial<RefImageState> = {
config: {
...deepClone(defaultIPAdapter),
...getDefaultRefImageConfig(getState),
image: imageDTOToImageWithDims(imageDTO),
},
};
@@ -221,7 +222,7 @@ export const useNewGlobalReferenceImageFromBbox = () => {
toastOk: t('controlLayers.newGlobalReferenceImageOk'),
toastError: t('controlLayers.newGlobalReferenceImageError'),
};
}, [defaultIPAdapter, dispatch, t]);
}, [dispatch, getState, t]);
const func = useSaveCanvas(arg);
return func;
};

View File

@@ -1,5 +1,5 @@
import { logger } from 'app/logging/logger';
import type { AppDispatch, RootState } from 'app/store/store';
import type { AppDispatch, AppGetState } from 'app/store/store';
import { getPrefixedId } from 'features/controlLayers/konva/util';
import type { CanvasEntityIdentifier, CanvasEntityType } from 'features/controlLayers/store/types';
import { selectComparisonImages } from 'features/gallery/components/ImageViewer/common';
@@ -114,13 +114,13 @@ type DndTarget<TargetData extends DndData, SourceData extends DndData> = {
sourceData: RecordUnknown;
targetData: TargetData;
dispatch: AppDispatch;
getState: () => RootState;
getState: AppGetState;
}) => boolean;
handler: (arg: {
sourceData: SourceData;
targetData: TargetData;
dispatch: AppDispatch;
getState: () => RootState;
getState: AppGetState;
}) => void;
};

View File

@@ -3,8 +3,6 @@ import { useAppStore } from 'app/store/nanostores/store';
import { SubMenuButtonContent, useSubMenu } from 'common/hooks/useSubMenu';
import { NewLayerIcon } from 'features/controlLayers/components/common/icons';
import { useCanvasIsBusySafe } from 'features/controlLayers/hooks/useCanvasIsBusy';
import { refImageAdded } from 'features/controlLayers/store/refImagesSlice';
import { imageDTOToImageWithDims } from 'features/controlLayers/store/util';
import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext';
import { sentImageToCanvas } from 'features/gallery/store/actions';
@@ -75,19 +73,6 @@ export const ImageMenuItemNewLayerFromImageSubMenu = memo(() => {
});
}, [imageDTO, imageViewer, store, t]);
const onClickNewGlobalReferenceImageFromImage = useCallback(() => {
const { dispatch } = store;
dispatch(refImageAdded({ overrides: { config: { image: imageDTOToImageWithDims(imageDTO) } } }));
dispatch(sentImageToCanvas());
dispatch(setActiveTab('canvas'));
imageViewer.close();
toast({
id: 'SENT_TO_CANVAS',
title: t('toast.sentToCanvas'),
status: 'success',
});
}, [imageDTO, imageViewer, store, t]);
const onClickNewRegionalReferenceImageFromImage = useCallback(() => {
const { dispatch, getState } = store;
createNewCanvasEntityFromImage({ imageDTO, type: 'regional_guidance_with_reference_image', dispatch, getState });
@@ -127,13 +112,6 @@ export const ImageMenuItemNewLayerFromImageSubMenu = memo(() => {
>
{t('controlLayers.referenceImageRegional')}
</MenuItem>
<MenuItem
icon={<NewLayerIcon />}
onClickCapture={onClickNewGlobalReferenceImageFromImage}
isDisabled={isBusy}
>
{t('controlLayers.referenceImageGlobal')}
</MenuItem>
</MenuList>
</Menu>
</MenuItem>

View File

@@ -0,0 +1,39 @@
import { MenuItem } from '@invoke-ai/ui-library';
import { useAppStore } from 'app/store/nanostores/store';
import { getDefaultRefImageConfig } from 'features/controlLayers/hooks/addLayerHooks';
import { refImageAdded } from 'features/controlLayers/store/refImagesSlice';
import { imageDTOToImageWithDims } from 'features/controlLayers/store/util';
import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext';
import { toast } from 'features/toast/toast';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiImageBold } from 'react-icons/pi';
export const ImageMenuItemUseAsRefImage = memo(() => {
const { t } = useTranslation();
const store = useAppStore();
const imageDTO = useImageDTOContext();
const imageViewer = useImageViewer();
const onClickNewGlobalReferenceImageFromImage = useCallback(() => {
const { dispatch, getState } = store;
const config = getDefaultRefImageConfig(getState);
config.image = imageDTOToImageWithDims(imageDTO);
dispatch(refImageAdded({ overrides: { config } }));
imageViewer.close();
toast({
id: 'SENT_TO_CANVAS',
title: t('toast.sentToCanvas'),
status: 'success',
});
}, [imageDTO, imageViewer, store, t]);
return (
<MenuItem icon={<PiImageBold />} onClickCapture={onClickNewGlobalReferenceImageFromImage}>
Use as Reference Image
</MenuItem>
);
});
ImageMenuItemUseAsRefImage.displayName = 'ImageMenuItemUseAsRefImage';

View File

@@ -13,6 +13,7 @@ import { ImageMenuItemOpenInViewer } from 'features/gallery/components/ImageCont
import { ImageMenuItemSelectForCompare } from 'features/gallery/components/ImageContextMenu/ImageMenuItemSelectForCompare';
import { ImageMenuItemSendToUpscale } from 'features/gallery/components/ImageContextMenu/ImageMenuItemSendToUpscale';
import { ImageMenuItemStarUnstar } from 'features/gallery/components/ImageContextMenu/ImageMenuItemStarUnstar';
import { ImageMenuItemUseAsRefImage } from 'features/gallery/components/ImageContextMenu/ImageMenuItemUseAsRefImage';
import { ImageDTOContextProvider } from 'features/gallery/contexts/ImageDTOContext';
import { memo } from 'react';
import type { ImageDTO } from 'services/api/types';
@@ -37,6 +38,7 @@ const SingleSelectionMenuItems = ({ imageDTO }: SingleSelectionMenuItemsProps) =
<ImageMenuItemMetadataRecallActions />
<MenuDivider />
<ImageMenuItemSendToUpscale />
<ImageMenuItemUseAsRefImage />
<ImageMenuItemNewCanvasFromImageSubMenu />
<ImageMenuItemNewLayerFromImageSubMenu />
<MenuDivider />

View File

@@ -1,6 +1,9 @@
import type { AppDispatch, RootState } from 'app/store/store';
import type { AppDispatch, AppGetState } from 'app/store/store';
import { deepClone } from 'common/util/deepClone';
import { selectDefaultIPAdapter, selectDefaultRefImageConfig } from 'features/controlLayers/hooks/addLayerHooks';
import {
getDefaultRefImageConfig,
getDefaultRegionalGuidanceRefImageConfig,
} from 'features/controlLayers/hooks/addLayerHooks';
import { CanvasEntityTransformer } from 'features/controlLayers/konva/CanvasEntity/CanvasEntityTransformer';
import { getPrefixedId } from 'features/controlLayers/konva/util';
import {
@@ -77,7 +80,7 @@ export const createNewCanvasEntityFromImage = (arg: {
imageDTO: ImageDTO;
type: CanvasEntityType | 'regional_guidance_with_reference_image';
dispatch: AppDispatch;
getState: () => RootState;
getState: AppGetState;
overrides?: Partial<Pick<CanvasEntityState, 'isEnabled' | 'isLocked' | 'name' | 'position'>>;
}) => {
const { type, imageDTO, dispatch, getState, overrides: _overrides } = arg;
@@ -112,7 +115,7 @@ export const createNewCanvasEntityFromImage = (arg: {
break;
}
case 'regional_guidance_with_reference_image': {
const config = deepClone(selectDefaultIPAdapter(getState()));
const config = getDefaultRegionalGuidanceRefImageConfig(getState);
config.image = imageDTOToImageWithDims(imageDTO);
const referenceImages = [{ id: getPrefixedId('regional_guidance_reference_image'), config }];
dispatch(rgAdded({ overrides: { referenceImages }, isSelected: true }));
@@ -138,7 +141,7 @@ export const newCanvasFromImage = async (arg: {
withResize?: boolean;
withInpaintMask?: boolean;
dispatch: AppDispatch;
getState: () => RootState;
getState: AppGetState;
}) => {
const { type, imageDTO, withResize = false, withInpaintMask = false, dispatch, getState } = arg;
const state = getState();
@@ -242,7 +245,7 @@ export const newCanvasFromImage = async (arg: {
break;
}
case 'reference_image': {
const config = deepClone(selectDefaultRefImageConfig(getState()));
const config = deepClone(getDefaultRefImageConfig(getState));
config.image = imageDTOToImageWithDims(imageDTO);
dispatch(canvasSessionTypeChanged({ type: 'advanced' }));
dispatch(refImageAdded({ overrides: { config }, isSelected: true }));
@@ -253,7 +256,7 @@ export const newCanvasFromImage = async (arg: {
break;
}
case 'regional_guidance_with_reference_image': {
const config = deepClone(selectDefaultIPAdapter(getState()));
const config = getDefaultRegionalGuidanceRefImageConfig(getState);
config.image = imageDTOToImageWithDims(imageDTO);
const referenceImages = [{ id: getPrefixedId('regional_guidance_reference_image'), config }];
dispatch(canvasSessionTypeChanged({ type: 'advanced' }));
@@ -273,7 +276,7 @@ export const replaceCanvasEntityObjectsWithImage = (arg: {
imageDTO: ImageDTO;
entityIdentifier: CanvasEntityIdentifier;
dispatch: AppDispatch;
getState: () => RootState;
getState: AppGetState;
}) => {
const { imageDTO, entityIdentifier, dispatch, getState } = arg;
const imageObject = imageDTOToImageObject(imageDTO);

View File

@@ -1,7 +1,7 @@
import { Box, Textarea } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { usePersistedTextAreaSize } from 'common/hooks/usePersistedTextareaSize';
import { RefImageList } from 'features/controlLayers/components/IPAdapter/IPAdapterList';
import { RefImageList } from 'features/controlLayers/components/RefImage/IPAdapterList';
import { positivePromptChanged, selectBase, selectPositivePrompt } from 'features/controlLayers/store/paramsSlice';
import { ShowDynamicPromptsPreviewButton } from 'features/dynamicPrompts/components/ShowDynamicPromptsPreviewButton';
import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel';

View File

@@ -101,15 +101,15 @@ export const useImagen4Models = buildModelsHook(isImagen4ModelConfig);
export const useChatGPT4oModels = buildModelsHook(isChatGPT4oModelConfig);
export const useFluxKontextModels = buildModelsHook(isFluxKontextModelConfig);
// const buildModelsSelector =
// <T extends AnyModelConfig>(typeGuard: (config: AnyModelConfig) => config is T): Selector<RootState, T[]> =>
// (state) => {
// const result = selectModelConfigsQuery(state);
// if (!result.data) {
// return EMPTY_ARRAY;
// }
// return modelConfigsAdapterSelectors.selectAll(result.data).filter(typeGuard);
// };
const buildModelsSelector =
<T extends AnyModelConfig>(typeGuard: (config: AnyModelConfig) => config is T): Selector<RootState, T[]> =>
(state) => {
const result = selectModelConfigsQuery(state);
if (!result.data) {
return EMPTY_ARRAY;
}
return modelConfigsAdapterSelectors.selectAll(result.data).filter(typeGuard);
};
// export const selectSDMainModels = buildModelsSelector(isNonRefinerNonFluxMainModelConfig);
// export const selectMainModels = buildModelsSelector(isNonRefinerMainModelConfig);
// export const selectNonSDXLMainModels = buildModelsSelector(isNonSDXLMainModelConfig);
@@ -123,7 +123,7 @@ export const useFluxKontextModels = buildModelsHook(isFluxKontextModelConfig);
// export const selectT5EncoderModels = buildModelsSelector(isT5EncoderModelConfig);
// export const selectClipEmbedModels = buildModelsSelector(isClipEmbedModelConfig);
// export const selectSpandrelImageToImageModels = buildModelsSelector(isSpandrelImageToImageModelConfig);
// export const selectIPAdapterModels = buildModelsSelector(isIPAdapterModelConfig);
export const selectIPAdapterModels = buildModelsSelector(isIPAdapterModelConfig);
// export const selectEmbeddingModels = buildModelsSelector(isTIModelConfig);
// export const selectVAEModels = buildModelsSelector(isVAEModelConfig);
// export const selectFluxVAEModels = buildModelsSelector(isFluxVAEModelConfig);

View File

@@ -1,5 +1,5 @@
import { logger } from 'app/logging/logger';
import type { AppDispatch, RootState } from 'app/store/store';
import type { AppDispatch, AppGetState } from 'app/store/store';
import { deepClone } from 'common/util/deepClone';
import { boardIdSelected, galleryViewChanged, imageSelected, offsetChanged } from 'features/gallery/store/gallerySlice';
import { $nodeExecutionStates, upsertExecutionState } from 'features/nodes/hooks/useNodeExecutionState';
@@ -20,7 +20,7 @@ const log = logger('events');
const nodeTypeDenylist = ['load_image', 'image'];
export const buildOnInvocationComplete = (getState: () => RootState, dispatch: AppDispatch) => {
export const buildOnInvocationComplete = (getState: AppGetState, dispatch: AppDispatch) => {
const addImagesToGallery = async (data: S['InvocationCompleteEvent']) => {
if (nodeTypeDenylist.includes(data.invocation.type)) {
log.trace(`Skipping denylisted node type (${data.invocation.type})`);

View File

@@ -1,7 +1,7 @@
import { Button, ExternalLink, Spinner, Text } from '@invoke-ai/ui-library';
import { skipToken } from '@reduxjs/toolkit/query';
import { logger } from 'app/logging/logger';
import type { AppDispatch, RootState } from 'app/store/store';
import type { AppDispatch, AppGetState } from 'app/store/store';
import { useAppDispatch } from 'app/store/storeHooks';
import { getPrefixedId } from 'features/controlLayers/konva/util';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
@@ -41,7 +41,7 @@ const getHFTokenStatus = async (dispatch: AppDispatch): Promise<S['HFTokenStatus
}
};
export const buildOnModelInstallError = (getState: () => RootState, dispatch: AppDispatch) => {
export const buildOnModelInstallError = (getState: AppGetState, dispatch: AppDispatch) => {
return async (data: S['ModelInstallErrorEvent']) => {
log.error({ data }, 'Model install error');