mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): migrate all metadata recall logic to new system
This commit is contained in:
parent
0cb2579109
commit
6a2ae5caa4
6
invokeai/frontend/web/src/common/util/objectKeys.ts
Normal file
6
invokeai/frontend/web/src/common/util/objectKeys.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* Get the keys of an object. This is a wrapper around `Object.keys` that types the result as an array of the keys of the object.
|
||||||
|
* @param obj The object to get the keys of.
|
||||||
|
* @returns The keys of the object.
|
||||||
|
*/
|
||||||
|
export const objectKeys = <T extends Record<string, unknown>>(obj: T) => Object.keys(obj) as Array<keyof T>;
|
@ -7,11 +7,12 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|||||||
import { DeleteImageButton } from 'features/deleteImageModal/components/DeleteImageButton';
|
import { DeleteImageButton } from 'features/deleteImageModal/components/DeleteImageButton';
|
||||||
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
|
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
|
||||||
import SingleSelectionMenuItems from 'features/gallery/components/ImageContextMenu/SingleSelectionMenuItems';
|
import SingleSelectionMenuItems from 'features/gallery/components/ImageContextMenu/SingleSelectionMenuItems';
|
||||||
|
import { useImageActions } from 'features/gallery/hooks/useImageActions';
|
||||||
import { sentImageToImg2Img } from 'features/gallery/store/actions';
|
import { sentImageToImg2Img } from 'features/gallery/store/actions';
|
||||||
import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors';
|
import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors';
|
||||||
import { selectGallerySlice } from 'features/gallery/store/gallerySlice';
|
import { selectGallerySlice } from 'features/gallery/store/gallerySlice';
|
||||||
|
import { parseAndRecallImageDimensions } from 'features/metadata/util/handlers';
|
||||||
import ParamUpscalePopover from 'features/parameters/components/Upscale/ParamUpscaleSettings';
|
import ParamUpscalePopover from 'features/parameters/components/Upscale/ParamUpscaleSettings';
|
||||||
import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters';
|
|
||||||
import { initialImageSelected } from 'features/parameters/store/actions';
|
import { initialImageSelected } from 'features/parameters/store/actions';
|
||||||
import { useIsQueueMutationInProgress } from 'features/queue/hooks/useIsQueueMutationInProgress';
|
import { useIsQueueMutationInProgress } from 'features/queue/hooks/useIsQueueMutationInProgress';
|
||||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||||
@ -33,7 +34,6 @@ import {
|
|||||||
PiRulerBold,
|
PiRulerBold,
|
||||||
} from 'react-icons/pi';
|
} from 'react-icons/pi';
|
||||||
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
|
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
|
||||||
import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata';
|
|
||||||
|
|
||||||
const selectShouldDisableToolbarButtons = createSelector(
|
const selectShouldDisableToolbarButtons = createSelector(
|
||||||
selectSystemSlice,
|
selectSystemSlice,
|
||||||
@ -58,11 +58,10 @@ const CurrentImageButtons = () => {
|
|||||||
const toaster = useAppToaster();
|
const toaster = useAppToaster();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { recallBothPrompts, recallSeed, recallWidthAndHeight, recallAllParameters } = useRecallParameters();
|
|
||||||
|
|
||||||
const { currentData: imageDTO } = useGetImageDTOQuery(lastSelectedImage?.image_name ?? skipToken);
|
const { currentData: imageDTO } = useGetImageDTOQuery(lastSelectedImage?.image_name ?? skipToken);
|
||||||
|
|
||||||
const { metadata, isLoading: isLoadingMetadata } = useDebouncedMetadata(lastSelectedImage?.image_name);
|
const { recallAll, remix, recallSeed, recallPrompts, hasMetadata, hasSeed, hasPrompts, isLoadingMetadata } =
|
||||||
|
useImageActions(lastSelectedImage?.image_name);
|
||||||
|
|
||||||
const { getAndLoadEmbeddedWorkflow, getAndLoadEmbeddedWorkflowResult } = useGetAndLoadEmbeddedWorkflow({});
|
const { getAndLoadEmbeddedWorkflow, getAndLoadEmbeddedWorkflowResult } = useGetAndLoadEmbeddedWorkflow({});
|
||||||
|
|
||||||
@ -74,51 +73,16 @@ const CurrentImageButtons = () => {
|
|||||||
}, [getAndLoadEmbeddedWorkflow, lastSelectedImage]);
|
}, [getAndLoadEmbeddedWorkflow, lastSelectedImage]);
|
||||||
|
|
||||||
useHotkeys('w', handleLoadWorkflow, [lastSelectedImage]);
|
useHotkeys('w', handleLoadWorkflow, [lastSelectedImage]);
|
||||||
|
useHotkeys('a', recallAll, [recallAll]);
|
||||||
const handleClickUseAllParameters = useCallback(() => {
|
useHotkeys('s', recallSeed, [recallSeed]);
|
||||||
recallAllParameters(metadata);
|
useHotkeys('p', recallPrompts, [recallPrompts]);
|
||||||
}, [metadata, recallAllParameters]);
|
useHotkeys('r', remix, [remix]);
|
||||||
|
|
||||||
useHotkeys('a', handleClickUseAllParameters, [metadata]);
|
|
||||||
|
|
||||||
const handleUseSeed = useCallback(() => {
|
|
||||||
recallSeed(metadata?.seed);
|
|
||||||
}, [metadata?.seed, recallSeed]);
|
|
||||||
|
|
||||||
useHotkeys('s', handleUseSeed, [metadata]);
|
|
||||||
|
|
||||||
const handleUsePrompt = useCallback(() => {
|
|
||||||
recallBothPrompts(
|
|
||||||
metadata?.positive_prompt,
|
|
||||||
metadata?.negative_prompt,
|
|
||||||
metadata?.positive_style_prompt,
|
|
||||||
metadata?.negative_style_prompt
|
|
||||||
);
|
|
||||||
}, [
|
|
||||||
metadata?.negative_prompt,
|
|
||||||
metadata?.positive_prompt,
|
|
||||||
metadata?.positive_style_prompt,
|
|
||||||
metadata?.negative_style_prompt,
|
|
||||||
recallBothPrompts,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useHotkeys('p', handleUsePrompt, [metadata]);
|
|
||||||
|
|
||||||
const handleRemixImage = useCallback(() => {
|
|
||||||
// Recalls all metadata parameters except seed
|
|
||||||
recallAllParameters({
|
|
||||||
...metadata,
|
|
||||||
seed: undefined,
|
|
||||||
});
|
|
||||||
}, [metadata, recallAllParameters]);
|
|
||||||
|
|
||||||
useHotkeys('r', handleRemixImage, [metadata]);
|
|
||||||
|
|
||||||
const handleUseSize = useCallback(() => {
|
const handleUseSize = useCallback(() => {
|
||||||
recallWidthAndHeight(metadata?.width, metadata?.height);
|
parseAndRecallImageDimensions(lastSelectedImage);
|
||||||
}, [metadata?.width, metadata?.height, recallWidthAndHeight]);
|
}, [lastSelectedImage]);
|
||||||
|
|
||||||
useHotkeys('d', handleUseSize, [metadata]);
|
useHotkeys('d', handleUseSize, [handleUseSize]);
|
||||||
|
|
||||||
const handleSendToImageToImage = useCallback(() => {
|
const handleSendToImageToImage = useCallback(() => {
|
||||||
dispatch(sentImageToImg2Img());
|
dispatch(sentImageToImg2Img());
|
||||||
@ -216,36 +180,30 @@ const CurrentImageButtons = () => {
|
|||||||
icon={<PiArrowsCounterClockwiseBold />}
|
icon={<PiArrowsCounterClockwiseBold />}
|
||||||
tooltip={`${t('parameters.remixImage')} (R)`}
|
tooltip={`${t('parameters.remixImage')} (R)`}
|
||||||
aria-label={`${t('parameters.remixImage')} (R)`}
|
aria-label={`${t('parameters.remixImage')} (R)`}
|
||||||
isDisabled={!metadata?.positive_prompt}
|
isDisabled={!hasMetadata}
|
||||||
onClick={handleRemixImage}
|
onClick={remix}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
isLoading={isLoadingMetadata}
|
isLoading={isLoadingMetadata}
|
||||||
icon={<PiQuotesBold />}
|
icon={<PiQuotesBold />}
|
||||||
tooltip={`${t('parameters.usePrompt')} (P)`}
|
tooltip={`${t('parameters.usePrompt')} (P)`}
|
||||||
aria-label={`${t('parameters.usePrompt')} (P)`}
|
aria-label={`${t('parameters.usePrompt')} (P)`}
|
||||||
isDisabled={!metadata?.positive_prompt}
|
isDisabled={!hasPrompts}
|
||||||
onClick={handleUsePrompt}
|
onClick={recallPrompts}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
isLoading={isLoadingMetadata}
|
isLoading={isLoadingMetadata}
|
||||||
icon={<PiPlantBold />}
|
icon={<PiPlantBold />}
|
||||||
tooltip={`${t('parameters.useSeed')} (S)`}
|
tooltip={`${t('parameters.useSeed')} (S)`}
|
||||||
aria-label={`${t('parameters.useSeed')} (S)`}
|
aria-label={`${t('parameters.useSeed')} (S)`}
|
||||||
isDisabled={metadata?.seed === null || metadata?.seed === undefined}
|
isDisabled={!hasSeed}
|
||||||
onClick={handleUseSeed}
|
onClick={recallSeed}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
isLoading={isLoadingMetadata}
|
isLoading={isLoadingMetadata}
|
||||||
icon={<PiRulerBold />}
|
icon={<PiRulerBold />}
|
||||||
tooltip={`${t('parameters.useSize')} (D)`}
|
tooltip={`${t('parameters.useSize')} (D)`}
|
||||||
aria-label={`${t('parameters.useSize')} (D)`}
|
aria-label={`${t('parameters.useSize')} (D)`}
|
||||||
isDisabled={
|
|
||||||
metadata?.height === null ||
|
|
||||||
metadata?.height === undefined ||
|
|
||||||
metadata?.width === null ||
|
|
||||||
metadata?.width === undefined
|
|
||||||
}
|
|
||||||
onClick={handleUseSize}
|
onClick={handleUseSize}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
@ -253,8 +211,8 @@ const CurrentImageButtons = () => {
|
|||||||
icon={<PiAsteriskBold />}
|
icon={<PiAsteriskBold />}
|
||||||
tooltip={`${t('parameters.useAll')} (A)`}
|
tooltip={`${t('parameters.useAll')} (A)`}
|
||||||
aria-label={`${t('parameters.useAll')} (A)`}
|
aria-label={`${t('parameters.useAll')} (A)`}
|
||||||
isDisabled={!metadata}
|
isDisabled={!hasMetadata}
|
||||||
onClick={handleClickUseAllParameters}
|
onClick={recallAll}
|
||||||
/>
|
/>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
|
|
||||||
|
@ -8,8 +8,8 @@ import { useDownloadImage } from 'common/hooks/useDownloadImage';
|
|||||||
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
||||||
import { imagesToChangeSelected, isModalOpenChanged } from 'features/changeBoardModal/store/slice';
|
import { imagesToChangeSelected, isModalOpenChanged } from 'features/changeBoardModal/store/slice';
|
||||||
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
|
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
|
||||||
|
import { useImageActions } from 'features/gallery/hooks/useImageActions';
|
||||||
import { sentImageToCanvas, sentImageToImg2Img } from 'features/gallery/store/actions';
|
import { sentImageToCanvas, sentImageToImg2Img } from 'features/gallery/store/actions';
|
||||||
import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters';
|
|
||||||
import { initialImageSelected } from 'features/parameters/store/actions';
|
import { initialImageSelected } from 'features/parameters/store/actions';
|
||||||
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||||
@ -33,7 +33,6 @@ import {
|
|||||||
PiTrashSimpleBold,
|
PiTrashSimpleBold,
|
||||||
} from 'react-icons/pi';
|
} from 'react-icons/pi';
|
||||||
import { useStarImagesMutation, useUnstarImagesMutation } from 'services/api/endpoints/images';
|
import { useStarImagesMutation, useUnstarImagesMutation } from 'services/api/endpoints/images';
|
||||||
import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata';
|
|
||||||
import type { ImageDTO } from 'services/api/types';
|
import type { ImageDTO } from 'services/api/types';
|
||||||
|
|
||||||
type SingleSelectionMenuItemsProps = {
|
type SingleSelectionMenuItemsProps = {
|
||||||
@ -49,7 +48,9 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
|
|||||||
const isCanvasEnabled = useFeatureStatus('unifiedCanvas').isFeatureEnabled;
|
const isCanvasEnabled = useFeatureStatus('unifiedCanvas').isFeatureEnabled;
|
||||||
const customStarUi = useStore($customStarUI);
|
const customStarUi = useStore($customStarUI);
|
||||||
const { downloadImage } = useDownloadImage();
|
const { downloadImage } = useDownloadImage();
|
||||||
const { metadata, isLoading: isLoadingMetadata } = useDebouncedMetadata(imageDTO?.image_name);
|
|
||||||
|
const { recallAll, remix, recallSeed, recallPrompts, hasMetadata, hasSeed, hasPrompts, isLoadingMetadata } =
|
||||||
|
useImageActions(imageDTO?.image_name);
|
||||||
|
|
||||||
const { getAndLoadEmbeddedWorkflow, getAndLoadEmbeddedWorkflowResult } = useGetAndLoadEmbeddedWorkflow({});
|
const { getAndLoadEmbeddedWorkflow, getAndLoadEmbeddedWorkflowResult } = useGetAndLoadEmbeddedWorkflow({});
|
||||||
|
|
||||||
@ -69,28 +70,6 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
|
|||||||
dispatch(imagesToDeleteSelected([imageDTO]));
|
dispatch(imagesToDeleteSelected([imageDTO]));
|
||||||
}, [dispatch, imageDTO]);
|
}, [dispatch, imageDTO]);
|
||||||
|
|
||||||
const { recallBothPrompts, recallSeed, recallAllParameters } = useRecallParameters();
|
|
||||||
|
|
||||||
// Recall parameters handlers
|
|
||||||
const handleRecallPrompt = useCallback(() => {
|
|
||||||
recallBothPrompts(
|
|
||||||
metadata?.positive_prompt,
|
|
||||||
metadata?.negative_prompt,
|
|
||||||
metadata?.positive_style_prompt,
|
|
||||||
metadata?.negative_style_prompt
|
|
||||||
);
|
|
||||||
}, [
|
|
||||||
metadata?.negative_prompt,
|
|
||||||
metadata?.positive_prompt,
|
|
||||||
metadata?.positive_style_prompt,
|
|
||||||
metadata?.negative_style_prompt,
|
|
||||||
recallBothPrompts,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const handleRecallSeed = useCallback(() => {
|
|
||||||
recallSeed(metadata?.seed);
|
|
||||||
}, [metadata?.seed, recallSeed]);
|
|
||||||
|
|
||||||
const handleSendToImageToImage = useCallback(() => {
|
const handleSendToImageToImage = useCallback(() => {
|
||||||
dispatch(sentImageToImg2Img());
|
dispatch(sentImageToImg2Img());
|
||||||
dispatch(initialImageSelected(imageDTO));
|
dispatch(initialImageSelected(imageDTO));
|
||||||
@ -111,18 +90,6 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
|
|||||||
});
|
});
|
||||||
}, [dispatch, imageDTO, t, toaster, optimalDimension]);
|
}, [dispatch, imageDTO, t, toaster, optimalDimension]);
|
||||||
|
|
||||||
const handleUseAllParameters = useCallback(() => {
|
|
||||||
recallAllParameters(metadata);
|
|
||||||
}, [metadata, recallAllParameters]);
|
|
||||||
|
|
||||||
const handleRemixImage = useCallback(() => {
|
|
||||||
// Recalls all metadata parameters except seed
|
|
||||||
recallAllParameters({
|
|
||||||
...metadata,
|
|
||||||
seed: undefined,
|
|
||||||
});
|
|
||||||
}, [metadata, recallAllParameters]);
|
|
||||||
|
|
||||||
const handleChangeBoard = useCallback(() => {
|
const handleChangeBoard = useCallback(() => {
|
||||||
dispatch(imagesToChangeSelected([imageDTO]));
|
dispatch(imagesToChangeSelected([imageDTO]));
|
||||||
dispatch(isModalOpenChanged(true));
|
dispatch(isModalOpenChanged(true));
|
||||||
@ -171,33 +138,29 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={isLoadingMetadata ? <SpinnerIcon /> : <PiArrowsCounterClockwiseBold />}
|
icon={isLoadingMetadata ? <SpinnerIcon /> : <PiArrowsCounterClockwiseBold />}
|
||||||
onClickCapture={handleRemixImage}
|
onClickCapture={remix}
|
||||||
isDisabled={
|
isDisabled={isLoadingMetadata || !hasMetadata}
|
||||||
isLoadingMetadata || (metadata?.positive_prompt === undefined && metadata?.negative_prompt === undefined)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{t('parameters.remixImage')}
|
{t('parameters.remixImage')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={isLoadingMetadata ? <SpinnerIcon /> : <PiQuotesBold />}
|
icon={isLoadingMetadata ? <SpinnerIcon /> : <PiQuotesBold />}
|
||||||
onClickCapture={handleRecallPrompt}
|
onClickCapture={recallPrompts}
|
||||||
isDisabled={
|
isDisabled={isLoadingMetadata || !hasPrompts}
|
||||||
isLoadingMetadata || (metadata?.positive_prompt === undefined && metadata?.negative_prompt === undefined)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{t('parameters.usePrompt')}
|
{t('parameters.usePrompt')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={isLoadingMetadata ? <SpinnerIcon /> : <PiPlantBold />}
|
icon={isLoadingMetadata ? <SpinnerIcon /> : <PiPlantBold />}
|
||||||
onClickCapture={handleRecallSeed}
|
onClickCapture={recallSeed}
|
||||||
isDisabled={isLoadingMetadata || metadata?.seed === undefined}
|
isDisabled={isLoadingMetadata || !hasSeed}
|
||||||
>
|
>
|
||||||
{t('parameters.useSeed')}
|
{t('parameters.useSeed')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={isLoadingMetadata ? <SpinnerIcon /> : <PiAsteriskBold />}
|
icon={isLoadingMetadata ? <SpinnerIcon /> : <PiAsteriskBold />}
|
||||||
onClickCapture={handleUseAllParameters}
|
onClickCapture={recallAll}
|
||||||
isDisabled={isLoadingMetadata || !metadata}
|
isDisabled={isLoadingMetadata || !hasMetadata}
|
||||||
>
|
>
|
||||||
{t('parameters.useAll')}
|
{t('parameters.useAll')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
@ -1,109 +0,0 @@
|
|||||||
import { ExternalLink, Flex, IconButton, Text, Tooltip } from '@invoke-ai/ui-library';
|
|
||||||
import { skipToken } from '@reduxjs/toolkit/query';
|
|
||||||
import { get } from 'lodash-es';
|
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { PiArrowBendUpLeftBold } from 'react-icons/pi';
|
|
||||||
import { useGetModelConfigQuery } from 'services/api/endpoints/models';
|
|
||||||
|
|
||||||
type MetadataItemProps = {
|
|
||||||
isLink?: boolean;
|
|
||||||
label: string;
|
|
||||||
metadata: unknown;
|
|
||||||
propertyName: string;
|
|
||||||
onRecall?: (value: unknown) => void;
|
|
||||||
labelPosition?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component to display an individual metadata item or parameter.
|
|
||||||
*/
|
|
||||||
const ImageMetadataItem = ({
|
|
||||||
label,
|
|
||||||
metadata,
|
|
||||||
propertyName,
|
|
||||||
onRecall: _onRecall,
|
|
||||||
isLink,
|
|
||||||
labelPosition,
|
|
||||||
}: MetadataItemProps) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const value = useMemo(() => get(metadata, propertyName), [metadata, propertyName]);
|
|
||||||
const onRecall = useCallback(() => {
|
|
||||||
if (!_onRecall) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_onRecall(value);
|
|
||||||
}, [_onRecall, value]);
|
|
||||||
|
|
||||||
if (!value) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex gap={2}>
|
|
||||||
{_onRecall && (
|
|
||||||
<Tooltip label={t('metadata.recallParameter', { parameter: label })}>
|
|
||||||
<IconButton
|
|
||||||
aria-label={t('metadata.recallParameter', { parameter: label })}
|
|
||||||
icon={<PiArrowBendUpLeftBold />}
|
|
||||||
size="xs"
|
|
||||||
variant="ghost"
|
|
||||||
fontSize={20}
|
|
||||||
onClick={onRecall}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
<Flex direction={labelPosition ? 'column' : 'row'}>
|
|
||||||
<Text fontWeight="semibold" whiteSpace="pre-wrap" pr={2}>
|
|
||||||
{label}:
|
|
||||||
</Text>
|
|
||||||
{isLink ? (
|
|
||||||
<ExternalLink href={value.toString()} label={value.toString()} />
|
|
||||||
) : (
|
|
||||||
<Text overflowY="scroll" wordBreak="break-all">
|
|
||||||
{value.toString()}
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(ImageMetadataItem);
|
|
||||||
|
|
||||||
type VAEMetadataItemProps = {
|
|
||||||
label: string;
|
|
||||||
modelKey?: string;
|
|
||||||
onClick: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const VAEMetadataItem = memo(({ label, modelKey, onClick }: VAEMetadataItemProps) => {
|
|
||||||
const { data: modelConfig } = useGetModelConfigQuery(modelKey ?? skipToken);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ImageMetadataItem label={label} value={modelKey ? modelConfig?.name ?? modelKey : 'Default'} onClick={onClick} />
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
VAEMetadataItem.displayName = 'VAEMetadataItem';
|
|
||||||
|
|
||||||
type ModelMetadataItemProps = {
|
|
||||||
label: string;
|
|
||||||
modelKey?: string;
|
|
||||||
|
|
||||||
extra?: string;
|
|
||||||
onClick: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ModelMetadataItem = memo(({ label, modelKey, extra, onClick }: ModelMetadataItemProps) => {
|
|
||||||
const { data: modelConfig } = useGetModelConfigQuery(modelKey ?? skipToken);
|
|
||||||
const value = useMemo(() => {
|
|
||||||
if (modelConfig) {
|
|
||||||
return `${modelConfig.name}${extra ?? ''}`;
|
|
||||||
}
|
|
||||||
return `${modelKey}${extra ?? ''}`;
|
|
||||||
}, [extra, modelConfig, modelKey]);
|
|
||||||
return <ImageMetadataItem label={label} value={value} onClick={onClick} />;
|
|
||||||
});
|
|
||||||
|
|
||||||
ModelMetadataItem.displayName = 'ModelMetadataItem';
|
|
@ -0,0 +1,62 @@
|
|||||||
|
import { handlers, parseAndRecallAllMetadata, parseAndRecallPrompts } from 'features/metadata/util/handlers';
|
||||||
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata';
|
||||||
|
|
||||||
|
export const useImageActions = (image_name?: string) => {
|
||||||
|
const { metadata, isLoading: isLoadingMetadata } = useDebouncedMetadata(image_name);
|
||||||
|
const [hasMetadata, setHasMetadata] = useState(false);
|
||||||
|
const [hasSeed, setHasSeed] = useState(false);
|
||||||
|
const [hasPrompts, setHasPrompts] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const parseMetadata = async () => {
|
||||||
|
if (metadata) {
|
||||||
|
setHasMetadata(true);
|
||||||
|
try {
|
||||||
|
await handlers.seed.parse(metadata);
|
||||||
|
setHasSeed(true);
|
||||||
|
} catch {
|
||||||
|
setHasSeed(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const promptParseResults = await Promise.allSettled([
|
||||||
|
handlers.positivePrompt.parse(metadata),
|
||||||
|
handlers.negativePrompt.parse(metadata),
|
||||||
|
handlers.sdxlPositiveStylePrompt.parse(metadata),
|
||||||
|
handlers.sdxlNegativeStylePrompt.parse(metadata),
|
||||||
|
]);
|
||||||
|
if (promptParseResults.some((result) => result.status === 'fulfilled')) {
|
||||||
|
setHasPrompts(true);
|
||||||
|
} else {
|
||||||
|
setHasPrompts(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setHasMetadata(false);
|
||||||
|
setHasSeed(false);
|
||||||
|
setHasPrompts(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
parseMetadata();
|
||||||
|
}, [metadata]);
|
||||||
|
|
||||||
|
const recallAll = useCallback(() => {
|
||||||
|
parseAndRecallAllMetadata(metadata);
|
||||||
|
}, [metadata]);
|
||||||
|
|
||||||
|
const remix = useCallback(() => {
|
||||||
|
// Recalls all metadata parameters except seed
|
||||||
|
parseAndRecallAllMetadata(metadata, ['seed']);
|
||||||
|
}, [metadata]);
|
||||||
|
|
||||||
|
const recallSeed = useCallback(() => {
|
||||||
|
handlers.seed.parse(metadata).then((seed) => {
|
||||||
|
handlers.seed.recall && handlers.seed.recall(seed);
|
||||||
|
});
|
||||||
|
}, [metadata]);
|
||||||
|
|
||||||
|
const recallPrompts = useCallback(() => {
|
||||||
|
parseAndRecallPrompts(metadata);
|
||||||
|
}, [metadata]);
|
||||||
|
|
||||||
|
return { recallAll, remix, recallSeed, recallPrompts , hasMetadata, hasSeed, hasPrompts, isLoadingMetadata };
|
||||||
|
};
|
@ -1,3 +1,4 @@
|
|||||||
|
import { objectKeys } from 'common/util/objectKeys';
|
||||||
import { toast } from 'common/util/toast';
|
import { toast } from 'common/util/toast';
|
||||||
import type { ControlAdapterConfig } from 'features/controlAdapters/store/types';
|
import type { ControlAdapterConfig } from 'features/controlAdapters/store/types';
|
||||||
import type { LoRA } from 'features/lora/store/loraSlice';
|
import type { LoRA } from 'features/lora/store/loraSlice';
|
||||||
@ -312,3 +313,75 @@ export const handlers = {
|
|||||||
renderItemValue: renderControlAdapterValue,
|
renderItemValue: renderControlAdapterValue,
|
||||||
}),
|
}),
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
export const parseAndRecallPrompts = async (metadata: unknown) => {
|
||||||
|
const results = await Promise.allSettled([
|
||||||
|
handlers.positivePrompt.parse(metadata).then((positivePrompt) => {
|
||||||
|
if (!handlers.positivePrompt.recall) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handlers.positivePrompt?.recall(positivePrompt);
|
||||||
|
}),
|
||||||
|
handlers.negativePrompt.parse(metadata).then((negativePrompt) => {
|
||||||
|
if (!handlers.negativePrompt.recall) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handlers.negativePrompt?.recall(negativePrompt);
|
||||||
|
}),
|
||||||
|
handlers.sdxlPositiveStylePrompt.parse(metadata).then((sdxlPositiveStylePrompt) => {
|
||||||
|
if (!handlers.sdxlPositiveStylePrompt.recall) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handlers.sdxlPositiveStylePrompt?.recall(sdxlPositiveStylePrompt);
|
||||||
|
}),
|
||||||
|
handlers.sdxlNegativeStylePrompt.parse(metadata).then((sdxlNegativeStylePrompt) => {
|
||||||
|
if (!handlers.sdxlNegativeStylePrompt.recall) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handlers.sdxlNegativeStylePrompt?.recall(sdxlNegativeStylePrompt);
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
if (results.some((result) => result.status === 'fulfilled')) {
|
||||||
|
parameterSetToast(t('metadata.allPrompts'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const parseAndRecallImageDimensions = async (metadata: unknown) => {
|
||||||
|
const results = await Promise.allSettled([
|
||||||
|
handlers.width.parse(metadata).then((width) => {
|
||||||
|
if (!handlers.width.recall) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handlers.width?.recall(width);
|
||||||
|
}),
|
||||||
|
handlers.height.parse(metadata).then((height) => {
|
||||||
|
if (!handlers.height.recall) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handlers.height?.recall(height);
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
if (results.some((result) => result.status === 'fulfilled')) {
|
||||||
|
parameterSetToast(t('metadata.imageDimensions'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const parseAndRecallAllMetadata = async (metadata: unknown, skip: (keyof typeof handlers)[] = []) => {
|
||||||
|
const results = await Promise.allSettled(
|
||||||
|
objectKeys(handlers)
|
||||||
|
.filter((key) => !skip.includes(key))
|
||||||
|
.map((key) => {
|
||||||
|
const { parse, recall } = handlers[key];
|
||||||
|
return parse(metadata).then((value) => {
|
||||||
|
if (!recall) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* @ts-expect-error The return type of parse and the input type of recall are guaranteed to be compatible. */
|
||||||
|
recall(value);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
if (results.some((result) => result.status === 'fulfilled')) {
|
||||||
|
parameterSetToast(t('toast.parametersSet'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -203,62 +203,6 @@ const recallIPAdapters: MetadataRecallFunc<IPAdapterConfig[]> = (ipAdapters) =>
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const recallPrompts = (_metadata: unknown) => {
|
|
||||||
// recallPositivePrompt(metadata);
|
|
||||||
// recallNegativePrompt(metadata);
|
|
||||||
// recallSDXLPositiveStylePrompt(metadata);
|
|
||||||
// recallSDXLNegativeStylePrompt(metadata);
|
|
||||||
// parameterNotSetToast(t('metadata.allPrompts'));
|
|
||||||
// parameterSetToast(t('metadata.allPrompts'));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallWidthAndHeight = (_metadata: unknown) => {
|
|
||||||
// recallWidth(metadata);
|
|
||||||
// recallHeight(metadata);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallAll = async (_metadata: unknown) => {
|
|
||||||
// if (!metadata) {
|
|
||||||
// allParameterNotSetToast();
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// // Update the main model first, as other parameters may depend on it.
|
|
||||||
// await recallModel(metadata);
|
|
||||||
// await Promise.allSettled([
|
|
||||||
// // Core parameters
|
|
||||||
// recallCFGScale(metadata),
|
|
||||||
// recallCFGRescaleMultiplier(metadata),
|
|
||||||
// recallPositivePrompt(metadata),
|
|
||||||
// recallNegativePrompt(metadata),
|
|
||||||
// recallScheduler(metadata),
|
|
||||||
// recallSeed(metadata),
|
|
||||||
// recallSteps(metadata),
|
|
||||||
// recallWidth(metadata),
|
|
||||||
// recallHeight(metadata),
|
|
||||||
// recallStrength(metadata),
|
|
||||||
// recallHRFEnabled(metadata),
|
|
||||||
// recallHRFMethod(metadata),
|
|
||||||
// recallHRFStrength(metadata),
|
|
||||||
// // SDXL parameters
|
|
||||||
// recallSDXLPositiveStylePrompt(metadata),
|
|
||||||
// recallSDXLNegativeStylePrompt(metadata),
|
|
||||||
// recallRefinerSteps(metadata),
|
|
||||||
// recallRefinerCFGScale(metadata),
|
|
||||||
// recallRefinerScheduler(metadata),
|
|
||||||
// recallRefinerPositiveAestheticScore(metadata),
|
|
||||||
// recallRefinerNegativeAestheticScore(metadata),
|
|
||||||
// recallRefinerStart(metadata),
|
|
||||||
// // Models
|
|
||||||
// recallVAE(metadata),
|
|
||||||
// recallRefinerModel(metadata),
|
|
||||||
// recallAllLoRAs(metadata),
|
|
||||||
// recallControlNets(metadata),
|
|
||||||
// recallT2IAdapters(metadata),
|
|
||||||
// recallIPAdapters(metadata),
|
|
||||||
// ]);
|
|
||||||
// allParameterSetToast();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallers = {
|
export const recallers = {
|
||||||
positivePrompt: recallPositivePrompt,
|
positivePrompt: recallPositivePrompt,
|
||||||
negativePrompt: recallNegativePrompt,
|
negativePrompt: recallNegativePrompt,
|
||||||
|
@ -2,7 +2,7 @@ import { Flex, IconButton, Spacer, Text } from '@invoke-ai/ui-library';
|
|||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { useImageUploadButton } from 'common/hooks/useImageUploadButton';
|
import { useImageUploadButton } from 'common/hooks/useImageUploadButton';
|
||||||
import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters';
|
import { parseAndRecallImageDimensions } from 'features/metadata/util/handlers';
|
||||||
import { clearInitialImage, selectGenerationSlice } from 'features/parameters/store/generationSlice';
|
import { clearInitialImage, selectGenerationSlice } from 'features/parameters/store/generationSlice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
@ -19,7 +19,6 @@ const postUploadAction: PostUploadAction = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const InitialImageDisplay = () => {
|
const InitialImageDisplay = () => {
|
||||||
const { recallWidthAndHeight } = useRecallParameters();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const initialImage = useAppSelector(selectInitialImage);
|
const initialImage = useAppSelector(selectInitialImage);
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@ -34,9 +33,9 @@ const InitialImageDisplay = () => {
|
|||||||
|
|
||||||
const handleUseSizeInitialImage = useCallback(() => {
|
const handleUseSizeInitialImage = useCallback(() => {
|
||||||
if (initialImage) {
|
if (initialImage) {
|
||||||
recallWidthAndHeight(initialImage.width, initialImage.height);
|
parseAndRecallImageDimensions(initialImage);
|
||||||
}
|
}
|
||||||
}, [initialImage, recallWidthAndHeight]);
|
}, [initialImage]);
|
||||||
|
|
||||||
useHotkeys('shift+d', handleUseSizeInitialImage, [initialImage]);
|
useHotkeys('shift+d', handleUseSizeInitialImage, [initialImage]);
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import { skipToken } from '@reduxjs/toolkit/query';
|
|||||||
import { useAppToaster } from 'app/components/Toaster';
|
import { useAppToaster } from 'app/components/Toaster';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
||||||
|
import { parseAndRecallAllMetadata } from 'features/metadata/util/handlers';
|
||||||
import { initialImageSelected } from 'features/parameters/store/actions';
|
import { initialImageSelected } from 'features/parameters/store/actions';
|
||||||
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import { setActiveTab } from 'features/ui/store/uiSlice';
|
import { setActiveTab } from 'features/ui/store/uiSlice';
|
||||||
@ -9,14 +10,12 @@ import { t } from 'i18next';
|
|||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
import { useGetImageDTOQuery, useGetImageMetadataQuery } from 'services/api/endpoints/images';
|
import { useGetImageDTOQuery, useGetImageMetadataQuery } from 'services/api/endpoints/images';
|
||||||
|
|
||||||
import { useRecallParameters } from './useRecallParameters';
|
|
||||||
|
|
||||||
export const usePreselectedImage = (selectedImage?: {
|
export const usePreselectedImage = (selectedImage?: {
|
||||||
imageName: string;
|
imageName: string;
|
||||||
action: 'sendToImg2Img' | 'sendToCanvas' | 'useAllParameters';
|
action: 'sendToImg2Img' | 'sendToCanvas' | 'useAllParameters';
|
||||||
}) => {
|
}) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { recallAllParameters } = useRecallParameters();
|
|
||||||
const optimalDimension = useAppSelector(selectOptimalDimension);
|
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||||
const toaster = useAppToaster();
|
const toaster = useAppToaster();
|
||||||
|
|
||||||
@ -45,10 +44,8 @@ export const usePreselectedImage = (selectedImage?: {
|
|||||||
|
|
||||||
const handleUseAllMetadata = useCallback(() => {
|
const handleUseAllMetadata = useCallback(() => {
|
||||||
if (selectedImageMetadata) {
|
if (selectedImageMetadata) {
|
||||||
recallAllParameters(selectedImageMetadata);
|
parseAndRecallAllMetadata(selectedImageMetadata);
|
||||||
}
|
}
|
||||||
// disabled because `recallAllParameters` changes the model, but its dep to prepare LoRAs has model as a dep. this introduces circular logic that causes infinite re-renders
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [selectedImageMetadata]);
|
}, [selectedImageMetadata]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -1,858 +0,0 @@
|
|||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|
||||||
import { getStore } from 'app/store/nanostores/store';
|
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
|
||||||
import { toast } from 'common/util/toast';
|
|
||||||
import { CONTROLNET_PROCESSORS } from 'features/controlAdapters/store/constants';
|
|
||||||
import { controlAdapterRecalled, controlAdaptersReset } from 'features/controlAdapters/store/controlAdaptersSlice';
|
|
||||||
import type { ControlNetConfig, IPAdapterConfig, T2IAdapterConfig } from 'features/controlAdapters/store/types';
|
|
||||||
import {
|
|
||||||
initialControlNet,
|
|
||||||
initialIPAdapter,
|
|
||||||
initialT2IAdapter,
|
|
||||||
} from 'features/controlAdapters/util/buildControlAdapter';
|
|
||||||
import { setHrfEnabled, setHrfMethod, setHrfStrength } from 'features/hrf/store/hrfSlice';
|
|
||||||
import type { LoRA } from 'features/lora/store/loraSlice';
|
|
||||||
import { defaultLoRAConfig, loraRecalled, lorasCleared } from 'features/lora/store/loraSlice';
|
|
||||||
import type { ModelIdentifierWithBase } from 'features/nodes/types/common';
|
|
||||||
import {
|
|
||||||
zControlField,
|
|
||||||
zIPAdapterField,
|
|
||||||
zModelIdentifierWithBase,
|
|
||||||
zT2IAdapterField,
|
|
||||||
} from 'features/nodes/types/common';
|
|
||||||
import { initialImageSelected, modelSelected } from 'features/parameters/store/actions';
|
|
||||||
import {
|
|
||||||
heightRecalled,
|
|
||||||
selectGenerationSlice,
|
|
||||||
setCfgRescaleMultiplier,
|
|
||||||
setCfgScale,
|
|
||||||
setImg2imgStrength,
|
|
||||||
setNegativePrompt,
|
|
||||||
setPositivePrompt,
|
|
||||||
setScheduler,
|
|
||||||
setSeed,
|
|
||||||
setSteps,
|
|
||||||
vaeSelected,
|
|
||||||
widthRecalled,
|
|
||||||
} from 'features/parameters/store/generationSlice';
|
|
||||||
import {
|
|
||||||
isParameterCFGRescaleMultiplier,
|
|
||||||
isParameterCFGScale,
|
|
||||||
isParameterHeight,
|
|
||||||
isParameterHRFEnabled,
|
|
||||||
isParameterHRFMethod,
|
|
||||||
isParameterLoRAWeight,
|
|
||||||
isParameterNegativePrompt,
|
|
||||||
isParameterNegativeStylePromptSDXL,
|
|
||||||
isParameterPositivePrompt,
|
|
||||||
isParameterPositiveStylePromptSDXL,
|
|
||||||
isParameterScheduler,
|
|
||||||
isParameterSDXLRefinerNegativeAestheticScore,
|
|
||||||
isParameterSDXLRefinerPositiveAestheticScore,
|
|
||||||
isParameterSDXLRefinerStart,
|
|
||||||
isParameterSeed,
|
|
||||||
isParameterSteps,
|
|
||||||
isParameterStrength,
|
|
||||||
isParameterWidth,
|
|
||||||
} from 'features/parameters/types/parameterSchemas';
|
|
||||||
import {
|
|
||||||
fetchControlNetModel,
|
|
||||||
fetchIPAdapterModel,
|
|
||||||
fetchLoRAModel,
|
|
||||||
fetchMainModelConfig,
|
|
||||||
fetchRefinerModelConfig,
|
|
||||||
fetchT2IAdapterModel,
|
|
||||||
fetchVAEModelConfig,
|
|
||||||
getModelKey,
|
|
||||||
raiseIfBaseIncompatible,
|
|
||||||
} from 'features/parameters/util/modelFetchingHelpers';
|
|
||||||
import {
|
|
||||||
refinerModelChanged,
|
|
||||||
setNegativeStylePromptSDXL,
|
|
||||||
setPositiveStylePromptSDXL,
|
|
||||||
setRefinerCFGScale,
|
|
||||||
setRefinerNegativeAestheticScore,
|
|
||||||
setRefinerPositiveAestheticScore,
|
|
||||||
setRefinerScheduler,
|
|
||||||
setRefinerStart,
|
|
||||||
setRefinerSteps,
|
|
||||||
} from 'features/sdxl/store/sdxlSlice';
|
|
||||||
import { t } from 'i18next';
|
|
||||||
import { get, isArray, isNil } from 'lodash-es';
|
|
||||||
import { useCallback } from 'react';
|
|
||||||
import type { BaseModelType, ImageDTO } from 'services/api/types';
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
|
|
||||||
const selectModel = createMemoizedSelector(selectGenerationSlice, (generation) => generation.model);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A function that recalls from metadata from the full metadata object.
|
|
||||||
*/
|
|
||||||
type MetadataRecallFunc = (metadata: unknown, withToast?: boolean) => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A function that recalls metadata from a specific metadata item.
|
|
||||||
*/
|
|
||||||
type MetadataItemRecallFunc = (metadataItem: unknown, withToast?: boolean) => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Raised when metadata recall fails.
|
|
||||||
*/
|
|
||||||
export class MetadataRecallError extends Error {
|
|
||||||
/**
|
|
||||||
* Create MetadataRecallError
|
|
||||||
* @param {String} message
|
|
||||||
*/
|
|
||||||
constructor(message: string) {
|
|
||||||
super(message);
|
|
||||||
this.name = this.constructor.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class InvalidMetadataPropertyType extends MetadataRecallError {}
|
|
||||||
|
|
||||||
const getProperty = <T = unknown>(
|
|
||||||
obj: unknown,
|
|
||||||
property: string,
|
|
||||||
typeGuard: (val: unknown) => val is T = (val: unknown): val is T => true
|
|
||||||
): T => {
|
|
||||||
const val = get(obj, property) as unknown;
|
|
||||||
if (typeGuard(val)) {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
throw new InvalidMetadataPropertyType(`Property ${property} is not of expected type`);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCurrentBase = () => selectModel(getStore().getState())?.base;
|
|
||||||
|
|
||||||
const parameterSetToast = (parameter: string, description?: string) => {
|
|
||||||
toast({
|
|
||||||
title: t('toast.parameterSet', { parameter }),
|
|
||||||
description,
|
|
||||||
status: 'info',
|
|
||||||
duration: 2500,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const parameterNotSetToast = (parameter: string, description?: string) => {
|
|
||||||
toast({
|
|
||||||
title: t('toast.parameterNotSet', { parameter }),
|
|
||||||
description,
|
|
||||||
status: 'warning',
|
|
||||||
duration: 2500,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const allParameterSetToast = (description?: string) => {
|
|
||||||
toast({
|
|
||||||
title: t('toast.parametersSet'),
|
|
||||||
status: 'info',
|
|
||||||
description,
|
|
||||||
duration: 2500,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const allParameterNotSetToast = (description?: string) => {
|
|
||||||
toast({
|
|
||||||
title: t('toast.parametersNotSet'),
|
|
||||||
status: 'warning',
|
|
||||||
description,
|
|
||||||
duration: 2500,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const recall = (callback: () => void, parameter: string, withToast = true) => {
|
|
||||||
try {
|
|
||||||
callback();
|
|
||||||
withToast && parameterSetToast(parameter);
|
|
||||||
} catch (e) {
|
|
||||||
withToast && parameterNotSetToast(parameter, (e as Error).message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const recallAsync = async (callback: () => Promise<void>, parameter: string, withToast = true) => {
|
|
||||||
try {
|
|
||||||
await callback();
|
|
||||||
withToast && parameterSetToast(parameter);
|
|
||||||
} catch (e) {
|
|
||||||
withToast && parameterNotSetToast(parameter, (e as Error).message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallPositivePrompt: MetadataRecallFunc = (metadata: unknown, withToast = true) => {
|
|
||||||
recall(
|
|
||||||
() => {
|
|
||||||
const positive_prompt = getProperty(metadata, 'positive_prompt', isParameterPositivePrompt);
|
|
||||||
getStore().dispatch(setPositivePrompt(positive_prompt));
|
|
||||||
},
|
|
||||||
t('metadata.positivePrompt'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallNegativePrompt: MetadataRecallFunc = (metadata: unknown, withToast = true) => {
|
|
||||||
recall(
|
|
||||||
() => {
|
|
||||||
const negative_prompt = getProperty(metadata, 'negative_prompt', isParameterNegativePrompt);
|
|
||||||
getStore().dispatch(setNegativePrompt(negative_prompt));
|
|
||||||
},
|
|
||||||
t('metadata.negativePrompt'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallSDXLPositiveStylePrompt: MetadataRecallFunc = (metadata: unknown, withToast = true) => {
|
|
||||||
recall(
|
|
||||||
() => {
|
|
||||||
const positive_style_prompt = getProperty(metadata, 'positive_style_prompt', isParameterPositiveStylePromptSDXL);
|
|
||||||
getStore().dispatch(setPositiveStylePromptSDXL(positive_style_prompt));
|
|
||||||
},
|
|
||||||
t('sdxl.posStylePrompt'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallSDXLNegativeStylePrompt: MetadataRecallFunc = (metadata: unknown, withToast = true) => {
|
|
||||||
recall(
|
|
||||||
() => {
|
|
||||||
const negative_style_prompt = getProperty(metadata, 'negative_style_prompt', isParameterNegativeStylePromptSDXL);
|
|
||||||
getStore().dispatch(setNegativeStylePromptSDXL(negative_style_prompt));
|
|
||||||
},
|
|
||||||
t('sdxl.negStylePrompt'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallSeed: MetadataRecallFunc = (metadata: unknown, withToast = true) => {
|
|
||||||
recall(
|
|
||||||
() => {
|
|
||||||
const seed = getProperty(metadata, 'seed', isParameterSeed);
|
|
||||||
getStore().dispatch(setSeed(seed));
|
|
||||||
},
|
|
||||||
t('metadata.seed'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallCFGScale: MetadataRecallFunc = (metadata: unknown, withToast = true) => {
|
|
||||||
recall(
|
|
||||||
() => {
|
|
||||||
const cfg_scale = getProperty(metadata, 'cfg_scale', isParameterCFGScale);
|
|
||||||
getStore().dispatch(setCfgScale(cfg_scale));
|
|
||||||
},
|
|
||||||
t('metadata.cfgScale'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallCFGRescaleMultiplier: MetadataRecallFunc = (metadata: unknown, withToast = true) => {
|
|
||||||
recall(
|
|
||||||
() => {
|
|
||||||
const cfg_rescale_multiplier = getProperty(metadata, 'cfg_rescale_multiplier', isParameterCFGRescaleMultiplier);
|
|
||||||
getStore().dispatch(setCfgRescaleMultiplier(cfg_rescale_multiplier));
|
|
||||||
},
|
|
||||||
t('metadata.cfgRescaleMultiplier'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallScheduler: MetadataRecallFunc = (metadata: unknown, withToast = true) => {
|
|
||||||
recall(
|
|
||||||
() => {
|
|
||||||
const scheduler = getProperty(metadata, 'scheduler', isParameterScheduler);
|
|
||||||
getStore().dispatch(setScheduler(scheduler));
|
|
||||||
},
|
|
||||||
t('metadata.scheduler'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallWidth: MetadataRecallFunc = (metadata: unknown, withToast = true) => {
|
|
||||||
recall(
|
|
||||||
() => {
|
|
||||||
const width = getProperty(metadata, 'width', isParameterWidth);
|
|
||||||
getStore().dispatch(widthRecalled(width));
|
|
||||||
},
|
|
||||||
t('metadata.width'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallHeight: MetadataRecallFunc = (metadata: unknown, withToast = true) => {
|
|
||||||
recall(
|
|
||||||
() => {
|
|
||||||
const height = getProperty(metadata, 'height', isParameterHeight);
|
|
||||||
getStore().dispatch(heightRecalled(height));
|
|
||||||
},
|
|
||||||
t('metadata.height'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallSteps: MetadataRecallFunc = (metadata: unknown, withToast = true) => {
|
|
||||||
recall(
|
|
||||||
() => {
|
|
||||||
const steps = getProperty(metadata, 'steps', isParameterSteps);
|
|
||||||
getStore().dispatch(setSteps(steps));
|
|
||||||
},
|
|
||||||
t('metadata.steps'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallStrength: MetadataRecallFunc = (metadata: unknown, withToast = true) => {
|
|
||||||
recall(
|
|
||||||
() => {
|
|
||||||
const strength = getProperty(metadata, 'strength', isParameterStrength);
|
|
||||||
getStore().dispatch(setImg2imgStrength(strength));
|
|
||||||
},
|
|
||||||
t('metadata.strength'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallHRFEnabled: MetadataRecallFunc = (metadata: unknown, withToast = true) => {
|
|
||||||
recall(
|
|
||||||
() => {
|
|
||||||
const hrf_enabled = getProperty(metadata, 'hrf_enabled', isParameterHRFEnabled);
|
|
||||||
getStore().dispatch(setHrfEnabled(hrf_enabled));
|
|
||||||
},
|
|
||||||
t('hrf.metadata.enabled'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallHRFStrength: MetadataRecallFunc = (metadata: unknown, withToast = true) => {
|
|
||||||
recall(
|
|
||||||
() => {
|
|
||||||
const hrf_strength = getProperty(metadata, 'hrf_strength', isParameterStrength);
|
|
||||||
getStore().dispatch(setHrfStrength(hrf_strength));
|
|
||||||
},
|
|
||||||
t('hrf.metadata.strength'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallHRFMethod: MetadataRecallFunc = (metadata: unknown, withToast = true) => {
|
|
||||||
recall(
|
|
||||||
() => {
|
|
||||||
const hrf_method = getProperty(metadata, 'hrf_method', isParameterHRFMethod);
|
|
||||||
getStore().dispatch(setHrfMethod(hrf_method));
|
|
||||||
},
|
|
||||||
t('hrf.metadata.method'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallRefinerSteps: MetadataRecallFunc = (metadata: unknown, withToast = true) => {
|
|
||||||
recall(
|
|
||||||
() => {
|
|
||||||
const refiner_steps = getProperty(metadata, 'refiner_steps', isParameterSteps);
|
|
||||||
getStore().dispatch(setRefinerSteps(refiner_steps));
|
|
||||||
},
|
|
||||||
t('sdxl.steps'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallRefinerCFGScale: MetadataRecallFunc = (metadata: unknown, withToast = true) => {
|
|
||||||
recall(
|
|
||||||
() => {
|
|
||||||
const refiner_cfg_scale = getProperty(metadata, 'refiner_cfg_scale', isParameterCFGScale);
|
|
||||||
getStore().dispatch(setRefinerCFGScale(refiner_cfg_scale));
|
|
||||||
},
|
|
||||||
t('sdxl.cfgScale'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallRefinerScheduler: MetadataRecallFunc = (metadata: unknown, withToast = true) => {
|
|
||||||
recall(
|
|
||||||
() => {
|
|
||||||
const refiner_scheduler = getProperty(metadata, 'refiner_scheduler', isParameterScheduler);
|
|
||||||
getStore().dispatch(setRefinerScheduler(refiner_scheduler));
|
|
||||||
},
|
|
||||||
t('sdxl.cfgScale'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallRefinerPositiveAestheticScore: MetadataRecallFunc = (metadata: unknown, withToast = true) => {
|
|
||||||
recall(
|
|
||||||
() => {
|
|
||||||
const refiner_positive_aesthetic_score = getProperty(
|
|
||||||
metadata,
|
|
||||||
'refiner_positive_aesthetic_score',
|
|
||||||
isParameterSDXLRefinerPositiveAestheticScore
|
|
||||||
);
|
|
||||||
getStore().dispatch(setRefinerPositiveAestheticScore(refiner_positive_aesthetic_score));
|
|
||||||
},
|
|
||||||
t('sdxl.posAestheticScore'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallRefinerNegativeAestheticScore: MetadataRecallFunc = (metadata: unknown, withToast = true) => {
|
|
||||||
recall(
|
|
||||||
() => {
|
|
||||||
const refiner_negative_aesthetic_score = getProperty(
|
|
||||||
metadata,
|
|
||||||
'refiner_negative_aesthetic_score',
|
|
||||||
isParameterSDXLRefinerNegativeAestheticScore
|
|
||||||
);
|
|
||||||
getStore().dispatch(setRefinerNegativeAestheticScore(refiner_negative_aesthetic_score));
|
|
||||||
},
|
|
||||||
t('sdxl.negAestheticScore'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const recallRefinerStart: MetadataRecallFunc = (metadata: unknown, withToast = true) => {
|
|
||||||
recall(
|
|
||||||
() => {
|
|
||||||
const refiner_start = getProperty(metadata, 'refiner_start', isParameterSDXLRefinerStart);
|
|
||||||
getStore().dispatch(setRefinerStart(refiner_start));
|
|
||||||
},
|
|
||||||
t('sdxl.refinerStart'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const prepareMainModelMetadataItem = async (model: unknown): Promise<ModelIdentifierWithBase> => {
|
|
||||||
const key = await getModelKey(model, 'main');
|
|
||||||
const mainModel = await fetchMainModelConfig(key);
|
|
||||||
return zModelIdentifierWithBase.parse(mainModel);
|
|
||||||
};
|
|
||||||
|
|
||||||
const recallModelAsync: MetadataRecallFunc = async (metadata: unknown, withToast = true) => {
|
|
||||||
await recallAsync(
|
|
||||||
async () => {
|
|
||||||
const modelMetadataItem = getProperty(metadata, 'model');
|
|
||||||
const model = await prepareMainModelMetadataItem(modelMetadataItem);
|
|
||||||
getStore().dispatch(modelSelected(model));
|
|
||||||
},
|
|
||||||
t('metadata.model'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const prepareRefinerMetadataItem = async (
|
|
||||||
model: unknown,
|
|
||||||
currentBase: BaseModelType | undefined
|
|
||||||
): Promise<ModelIdentifierWithBase> => {
|
|
||||||
const key = await getModelKey(model, 'main');
|
|
||||||
const refinerModel = await fetchRefinerModelConfig(key);
|
|
||||||
raiseIfBaseIncompatible('sdxl-refiner', currentBase, 'Refiner incompatible with currently-selected model');
|
|
||||||
return zModelIdentifierWithBase.parse(refinerModel);
|
|
||||||
};
|
|
||||||
|
|
||||||
const recallRefinerModelAsync: MetadataRecallFunc = async (metadata: unknown, withToast = true) => {
|
|
||||||
await recallAsync(
|
|
||||||
async () => {
|
|
||||||
const refinerMetadataItem = getProperty(metadata, 'refiner_model');
|
|
||||||
const refiner = await prepareRefinerMetadataItem(refinerMetadataItem, getCurrentBase());
|
|
||||||
getStore().dispatch(refinerModelChanged(refiner));
|
|
||||||
},
|
|
||||||
t('sdxl.refinerModel'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const prepareVAEMetadataItem = async (
|
|
||||||
vae: unknown,
|
|
||||||
currentBase: BaseModelType | undefined
|
|
||||||
): Promise<ModelIdentifierWithBase> => {
|
|
||||||
const key = await getModelKey(vae, 'vae');
|
|
||||||
const vaeModel = await fetchVAEModelConfig(key);
|
|
||||||
raiseIfBaseIncompatible(vaeModel.base, currentBase, 'VAE incompatible with currently-selected model');
|
|
||||||
return zModelIdentifierWithBase.parse(vaeModel);
|
|
||||||
};
|
|
||||||
|
|
||||||
const recallVAEAsync: MetadataRecallFunc = async (metadata: unknown, withToast = true) => {
|
|
||||||
await recallAsync(
|
|
||||||
async () => {
|
|
||||||
const currentBase = getCurrentBase();
|
|
||||||
const vaeMetadataItem = getProperty(metadata, 'vae');
|
|
||||||
if (isNil(vaeMetadataItem)) {
|
|
||||||
getStore().dispatch(vaeSelected(null));
|
|
||||||
} else {
|
|
||||||
const vae = await prepareVAEMetadataItem(vaeMetadataItem, currentBase);
|
|
||||||
getStore().dispatch(vaeSelected(vae));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
t('metadata.vae'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const prepareLoRAMetadataItem = async (
|
|
||||||
loraMetadataItem: unknown,
|
|
||||||
currentBase: BaseModelType | undefined
|
|
||||||
): Promise<LoRA> => {
|
|
||||||
const lora = getProperty(loraMetadataItem, 'lora');
|
|
||||||
const weight = getProperty(loraMetadataItem, 'weight');
|
|
||||||
const key = await getModelKey(lora, 'lora');
|
|
||||||
const loraModel = await fetchLoRAModel(key);
|
|
||||||
raiseIfBaseIncompatible(loraModel.base, currentBase, 'LoRA incompatible with currently-selected model');
|
|
||||||
return {
|
|
||||||
key: loraModel.key,
|
|
||||||
base: loraModel.base,
|
|
||||||
weight: isParameterLoRAWeight(weight) ? weight : defaultLoRAConfig.weight,
|
|
||||||
isEnabled: true,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const recallLoRAAsync: MetadataItemRecallFunc = async (metadataItem: unknown, withToast = true) => {
|
|
||||||
await recallAsync(
|
|
||||||
async () => {
|
|
||||||
const currentBase = getCurrentBase();
|
|
||||||
const lora = await prepareLoRAMetadataItem(metadataItem, currentBase);
|
|
||||||
getStore().dispatch(loraRecalled(lora));
|
|
||||||
},
|
|
||||||
t('models.lora'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const prepareControlNetMetadataItem = async (
|
|
||||||
metadataItem: unknown,
|
|
||||||
currentBase: BaseModelType | undefined
|
|
||||||
): Promise<ControlNetConfig> => {
|
|
||||||
const control_model = getProperty(metadataItem, 'control_model');
|
|
||||||
const key = await getModelKey(control_model, 'controlnet');
|
|
||||||
const controlNetModel = await fetchControlNetModel(key);
|
|
||||||
raiseIfBaseIncompatible(controlNetModel.base, currentBase, 'ControlNet incompatible with currently-selected model');
|
|
||||||
|
|
||||||
const image = zControlField.shape.image.nullish().catch(null).parse(getProperty(metadataItem, 'image'));
|
|
||||||
const control_weight = zControlField.shape.control_weight
|
|
||||||
.nullish()
|
|
||||||
.catch(null)
|
|
||||||
.parse(getProperty(metadataItem, 'control_weight'));
|
|
||||||
const begin_step_percent = zControlField.shape.begin_step_percent
|
|
||||||
.nullish()
|
|
||||||
.catch(null)
|
|
||||||
.parse(getProperty(metadataItem, 'begin_step_percent'));
|
|
||||||
const end_step_percent = zControlField.shape.end_step_percent
|
|
||||||
.nullish()
|
|
||||||
.catch(null)
|
|
||||||
.parse(getProperty(metadataItem, 'end_step_percent'));
|
|
||||||
const control_mode = zControlField.shape.control_mode
|
|
||||||
.nullish()
|
|
||||||
.catch(null)
|
|
||||||
.parse(getProperty(metadataItem, 'control_mode'));
|
|
||||||
const resize_mode = zControlField.shape.resize_mode
|
|
||||||
.nullish()
|
|
||||||
.catch(null)
|
|
||||||
.parse(getProperty(metadataItem, 'resize_mode'));
|
|
||||||
|
|
||||||
// We don't save the original image that was processed into a control image, only the processed image
|
|
||||||
const processorType = 'none';
|
|
||||||
const processorNode = CONTROLNET_PROCESSORS.none.default;
|
|
||||||
|
|
||||||
const controlnet: ControlNetConfig = {
|
|
||||||
type: 'controlnet',
|
|
||||||
isEnabled: true,
|
|
||||||
model: zModelIdentifierWithBase.parse(controlNetModel),
|
|
||||||
weight: typeof control_weight === 'number' ? control_weight : initialControlNet.weight,
|
|
||||||
beginStepPct: begin_step_percent ?? initialControlNet.beginStepPct,
|
|
||||||
endStepPct: end_step_percent ?? initialControlNet.endStepPct,
|
|
||||||
controlMode: control_mode ?? initialControlNet.controlMode,
|
|
||||||
resizeMode: resize_mode ?? initialControlNet.resizeMode,
|
|
||||||
controlImage: image?.image_name ?? null,
|
|
||||||
processedControlImage: image?.image_name ?? null,
|
|
||||||
processorType,
|
|
||||||
processorNode,
|
|
||||||
shouldAutoConfig: true,
|
|
||||||
id: uuidv4(),
|
|
||||||
};
|
|
||||||
|
|
||||||
return controlnet;
|
|
||||||
};
|
|
||||||
|
|
||||||
const recallControlNetAsync: MetadataItemRecallFunc = async (metadataItem: unknown, withToast = true) => {
|
|
||||||
await recallAsync(
|
|
||||||
async () => {
|
|
||||||
const currentBase = getCurrentBase();
|
|
||||||
const controlNetConfig = await prepareControlNetMetadataItem(metadataItem, currentBase);
|
|
||||||
getStore().dispatch(controlAdapterRecalled(controlNetConfig));
|
|
||||||
},
|
|
||||||
t('common.controlNet'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const prepareT2IAdapterMetadataItem = async (
|
|
||||||
metadataItem: unknown,
|
|
||||||
currentBase: BaseModelType | undefined
|
|
||||||
): Promise<T2IAdapterConfig> => {
|
|
||||||
const t2i_adapter_model = getProperty(metadataItem, 't2i_adapter_model');
|
|
||||||
const key = await getModelKey(t2i_adapter_model, 't2i_adapter');
|
|
||||||
const t2iAdapterModel = await fetchT2IAdapterModel(key);
|
|
||||||
raiseIfBaseIncompatible(t2iAdapterModel.base, currentBase, 'T2I Adapter incompatible with currently-selected model');
|
|
||||||
|
|
||||||
const image = zT2IAdapterField.shape.image.nullish().catch(null).parse(getProperty(metadataItem, 'image'));
|
|
||||||
const weight = zT2IAdapterField.shape.weight.nullish().catch(null).parse(getProperty(metadataItem, 'weight'));
|
|
||||||
const begin_step_percent = zT2IAdapterField.shape.begin_step_percent
|
|
||||||
.nullish()
|
|
||||||
.catch(null)
|
|
||||||
.parse(getProperty(metadataItem, 'begin_step_percent'));
|
|
||||||
const end_step_percent = zT2IAdapterField.shape.end_step_percent
|
|
||||||
.nullish()
|
|
||||||
.catch(null)
|
|
||||||
.parse(getProperty(metadataItem, 'end_step_percent'));
|
|
||||||
const resize_mode = zT2IAdapterField.shape.resize_mode
|
|
||||||
.nullish()
|
|
||||||
.catch(null)
|
|
||||||
.parse(getProperty(metadataItem, 'resize_mode'));
|
|
||||||
|
|
||||||
// We don't save the original image that was processed into a control image, only the processed image
|
|
||||||
const processorType = 'none';
|
|
||||||
const processorNode = CONTROLNET_PROCESSORS.none.default;
|
|
||||||
|
|
||||||
const t2iAdapter: T2IAdapterConfig = {
|
|
||||||
type: 't2i_adapter',
|
|
||||||
isEnabled: true,
|
|
||||||
model: zModelIdentifierWithBase.parse(t2iAdapterModel),
|
|
||||||
weight: typeof weight === 'number' ? weight : initialT2IAdapter.weight,
|
|
||||||
beginStepPct: begin_step_percent ?? initialT2IAdapter.beginStepPct,
|
|
||||||
endStepPct: end_step_percent ?? initialT2IAdapter.endStepPct,
|
|
||||||
resizeMode: resize_mode ?? initialT2IAdapter.resizeMode,
|
|
||||||
controlImage: image?.image_name ?? null,
|
|
||||||
processedControlImage: image?.image_name ?? null,
|
|
||||||
processorType,
|
|
||||||
processorNode,
|
|
||||||
shouldAutoConfig: true,
|
|
||||||
id: uuidv4(),
|
|
||||||
};
|
|
||||||
|
|
||||||
return t2iAdapter;
|
|
||||||
};
|
|
||||||
|
|
||||||
const recallT2IAdapterAsync: MetadataItemRecallFunc = async (metadataItem: unknown, withToast = true) => {
|
|
||||||
await recallAsync(
|
|
||||||
async () => {
|
|
||||||
const currentBase = getCurrentBase();
|
|
||||||
const t2iAdapterConfig = await prepareT2IAdapterMetadataItem(metadataItem, currentBase);
|
|
||||||
getStore().dispatch(controlAdapterRecalled(t2iAdapterConfig));
|
|
||||||
},
|
|
||||||
t('common.t2iAdapter'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const prepareIPAdapterMetadataItem = async (
|
|
||||||
metadataItem: unknown,
|
|
||||||
currentBase: BaseModelType | undefined
|
|
||||||
): Promise<IPAdapterConfig> => {
|
|
||||||
const ip_adapter_model = getProperty(metadataItem, 'ip_adapter_model');
|
|
||||||
const key = await getModelKey(ip_adapter_model, 'ip_adapter');
|
|
||||||
const ipAdapterModel = await fetchIPAdapterModel(key);
|
|
||||||
raiseIfBaseIncompatible(ipAdapterModel.base, currentBase, 'T2I Adapter incompatible with currently-selected model');
|
|
||||||
|
|
||||||
const image = zIPAdapterField.shape.image.nullish().catch(null).parse(getProperty(metadataItem, 'image'));
|
|
||||||
const weight = zIPAdapterField.shape.weight.nullish().catch(null).parse(getProperty(metadataItem, 'weight'));
|
|
||||||
const begin_step_percent = zIPAdapterField.shape.begin_step_percent
|
|
||||||
.nullish()
|
|
||||||
.catch(null)
|
|
||||||
.parse(getProperty(metadataItem, 'begin_step_percent'));
|
|
||||||
const end_step_percent = zIPAdapterField.shape.end_step_percent
|
|
||||||
.nullish()
|
|
||||||
.catch(null)
|
|
||||||
.parse(getProperty(metadataItem, 'end_step_percent'));
|
|
||||||
|
|
||||||
const ipAdapter: IPAdapterConfig = {
|
|
||||||
id: uuidv4(),
|
|
||||||
type: 'ip_adapter',
|
|
||||||
isEnabled: true,
|
|
||||||
model: zModelIdentifierWithBase.parse(ipAdapterModel),
|
|
||||||
controlImage: image?.image_name ?? null,
|
|
||||||
weight: weight ?? initialIPAdapter.weight,
|
|
||||||
beginStepPct: begin_step_percent ?? initialIPAdapter.beginStepPct,
|
|
||||||
endStepPct: end_step_percent ?? initialIPAdapter.endStepPct,
|
|
||||||
};
|
|
||||||
|
|
||||||
return ipAdapter;
|
|
||||||
};
|
|
||||||
|
|
||||||
const recallIPAdapterAsync: MetadataItemRecallFunc = async (metadataItem: unknown, withToast) => {
|
|
||||||
await recallAsync(
|
|
||||||
async () => {
|
|
||||||
const currentBase = getCurrentBase();
|
|
||||||
const ipAdapterConfig = await prepareIPAdapterMetadataItem(metadataItem, currentBase);
|
|
||||||
getStore().dispatch(controlAdapterRecalled(ipAdapterConfig));
|
|
||||||
},
|
|
||||||
t('common.ipAdapter'),
|
|
||||||
withToast
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useRecallParameters = () => {
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
|
|
||||||
const recallBothPrompts = useCallback<MetadataRecallFunc>(
|
|
||||||
(metadata: unknown) => {
|
|
||||||
const positive_prompt = getProperty(metadata, 'positive_prompt');
|
|
||||||
const negative_prompt = getProperty(metadata, 'negative_prompt');
|
|
||||||
const positive_style_prompt = getProperty(metadata, 'positive_style_prompt');
|
|
||||||
const negative_style_prompt = getProperty(metadata, 'negative_style_prompt');
|
|
||||||
if (
|
|
||||||
isParameterPositivePrompt(positive_prompt) ||
|
|
||||||
isParameterNegativePrompt(negative_prompt) ||
|
|
||||||
isParameterPositiveStylePromptSDXL(positive_style_prompt) ||
|
|
||||||
isParameterNegativeStylePromptSDXL(negative_style_prompt)
|
|
||||||
) {
|
|
||||||
if (isParameterPositivePrompt(positive_prompt)) {
|
|
||||||
dispatch(setPositivePrompt(positive_prompt));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isParameterNegativePrompt(negative_prompt)) {
|
|
||||||
dispatch(setNegativePrompt(negative_prompt));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isParameterPositiveStylePromptSDXL(positive_style_prompt)) {
|
|
||||||
dispatch(setPositiveStylePromptSDXL(positive_style_prompt));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isParameterPositiveStylePromptSDXL(negative_style_prompt)) {
|
|
||||||
dispatch(setNegativeStylePromptSDXL(negative_style_prompt));
|
|
||||||
}
|
|
||||||
|
|
||||||
parameterSetToast(t('metadata.allPrompts'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
parameterNotSetToast(t('metadata.allPrompts'));
|
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
const recallWidthAndHeight = useCallback<MetadataRecallFunc>(
|
|
||||||
(metadata: unknown) => {
|
|
||||||
const width = getProperty(metadata, 'width');
|
|
||||||
const height = getProperty(metadata, 'height');
|
|
||||||
|
|
||||||
if (!isParameterWidth(width)) {
|
|
||||||
allParameterNotSetToast();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!isParameterHeight(height)) {
|
|
||||||
allParameterNotSetToast();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
dispatch(heightRecalled(height));
|
|
||||||
dispatch(widthRecalled(width));
|
|
||||||
allParameterSetToast();
|
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
const sendToImageToImage = useCallback(
|
|
||||||
(image: ImageDTO) => {
|
|
||||||
dispatch(initialImageSelected(image));
|
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
const recallAllParameters = useCallback<MetadataRecallFunc>(
|
|
||||||
async (metadata: unknown) => {
|
|
||||||
if (!metadata) {
|
|
||||||
allParameterNotSetToast();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await recallModelAsync(metadata, false);
|
|
||||||
|
|
||||||
recallCFGScale(metadata, false);
|
|
||||||
recallCFGRescaleMultiplier(metadata, false);
|
|
||||||
recallPositivePrompt(metadata, false);
|
|
||||||
recallNegativePrompt(metadata, false);
|
|
||||||
recallScheduler(metadata, false);
|
|
||||||
recallSeed(metadata, false);
|
|
||||||
recallSteps(metadata, false);
|
|
||||||
recallWidth(metadata, false);
|
|
||||||
recallHeight(metadata, false);
|
|
||||||
recallStrength(metadata, false);
|
|
||||||
recallHRFEnabled(metadata, false);
|
|
||||||
recallHRFMethod(metadata, false);
|
|
||||||
recallHRFStrength(metadata, false);
|
|
||||||
|
|
||||||
// SDXL
|
|
||||||
recallSDXLPositiveStylePrompt(metadata, false);
|
|
||||||
recallSDXLNegativeStylePrompt(metadata, false);
|
|
||||||
recallRefinerSteps(metadata, false);
|
|
||||||
recallRefinerCFGScale(metadata, false);
|
|
||||||
recallRefinerScheduler(metadata, false);
|
|
||||||
recallRefinerPositiveAestheticScore(metadata, false);
|
|
||||||
recallRefinerNegativeAestheticScore(metadata, false);
|
|
||||||
recallRefinerStart(metadata, false);
|
|
||||||
|
|
||||||
await recallVAEAsync(metadata, false);
|
|
||||||
await recallRefinerModelAsync(metadata, false);
|
|
||||||
|
|
||||||
dispatch(lorasCleared());
|
|
||||||
const loraMetadataArray = getProperty(metadata, 'loras');
|
|
||||||
if (isArray(loraMetadataArray)) {
|
|
||||||
loraMetadataArray.forEach(async (loraMetadataItem) => {
|
|
||||||
await recallLoRAAsync(loraMetadataItem, false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(controlAdaptersReset());
|
|
||||||
const controlNetMetadataArray = getProperty(metadata, 'controlnets');
|
|
||||||
if (isArray(controlNetMetadataArray)) {
|
|
||||||
controlNetMetadataArray.forEach(async (controlNetMetadataItem) => {
|
|
||||||
await recallControlNetAsync(controlNetMetadataItem, false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const ipAdapterMetadataArray = getProperty(metadata, 'ipAdapters');
|
|
||||||
if (isArray(ipAdapterMetadataArray)) {
|
|
||||||
ipAdapterMetadataArray.forEach(async (ipAdapterMetadataItem) => {
|
|
||||||
await recallIPAdapterAsync(ipAdapterMetadataItem, false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const t2iAdapterMetadataArray = getProperty(metadata, 't2iAdapters');
|
|
||||||
if (isArray(t2iAdapterMetadataArray)) {
|
|
||||||
t2iAdapterMetadataArray.forEach(async (t2iAdapterMetadataItem) => {
|
|
||||||
await recallT2IAdapterAsync(t2iAdapterMetadataItem, false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
allParameterSetToast();
|
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
recallBothPrompts,
|
|
||||||
recallPositivePrompt,
|
|
||||||
recallNegativePrompt,
|
|
||||||
recallSDXLPositiveStylePrompt,
|
|
||||||
recallSDXLNegativeStylePrompt,
|
|
||||||
recallSeed,
|
|
||||||
recallCFGScale,
|
|
||||||
recallCFGRescaleMultiplier,
|
|
||||||
recallModel: recallModelAsync,
|
|
||||||
recallScheduler,
|
|
||||||
recallVaeModel: recallVAEAsync,
|
|
||||||
recallSteps,
|
|
||||||
recallWidth,
|
|
||||||
recallHeight,
|
|
||||||
recallWidthAndHeight,
|
|
||||||
recallStrength,
|
|
||||||
recallHRFEnabled,
|
|
||||||
recallHRFStrength,
|
|
||||||
recallHRFMethod,
|
|
||||||
recallLoRA: recallLoRAAsync,
|
|
||||||
recallControlNet: recallControlNetAsync,
|
|
||||||
recallIPAdapter: recallIPAdapterAsync,
|
|
||||||
recallT2IAdapter: recallT2IAdapterAsync,
|
|
||||||
recallAllParameters,
|
|
||||||
recallRefinerModel: recallRefinerModelAsync,
|
|
||||||
sendToImageToImage,
|
|
||||||
};
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user