mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-13 13:55:08 -05:00
refactor(ui): ref images (WIP)
This commit is contained in:
@@ -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');
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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';
|
||||
@@ -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>
|
||||
@@ -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(() => {
|
||||
@@ -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 />;
|
||||
@@ -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);
|
||||
@@ -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';
|
||||
@@ -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';
|
||||
@@ -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';
|
||||
@@ -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),
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
@@ -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: '' }));
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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';
|
||||
@@ -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 />
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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})`);
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user