mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-13 18:25:28 -05:00
feat(ui): ref image recall w/ old canvas metadata backup
This commit is contained in:
@@ -245,6 +245,7 @@ export const {
|
||||
refImageIPAdapterBeginEndStepPctChanged,
|
||||
refImageFLUXReduxImageInfluenceChanged,
|
||||
refImageIsEnabledToggled,
|
||||
refImageRecalled,
|
||||
} = refImagesSlice.actions;
|
||||
|
||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||
|
||||
@@ -300,10 +300,9 @@ const zCanvasEntityBase = z.object({
|
||||
isLocked: z.boolean(),
|
||||
});
|
||||
|
||||
const zRefImageState = z.object({
|
||||
export const zRefImageState = z.object({
|
||||
id: zId,
|
||||
isEnabled: z.boolean().default(true),
|
||||
// This should be named `referenceImage` but we need to keep it as `ipAdapter` for backwards compatibility
|
||||
config: z.discriminatedUnion('type', [
|
||||
zIPAdapterConfig,
|
||||
zFLUXReduxConfig,
|
||||
@@ -579,7 +578,7 @@ const zCanvasState = z.object({
|
||||
});
|
||||
export type CanvasState = z.infer<typeof zCanvasState>;
|
||||
|
||||
const zRefImagesState = z.object({
|
||||
export const zRefImagesState = z.object({
|
||||
selectedEntityId: z.string().nullable().default(null),
|
||||
isPanelOpen: z.boolean().default(false),
|
||||
entities: z.array(zRefImageState).default(() => []),
|
||||
@@ -594,6 +593,11 @@ export const getInitialRefImagesState = () => deepClone(INITIAL_REF_IMAGES_STATE
|
||||
const CANVAS_INITIAL_STATE = zCanvasState.parse({});
|
||||
export const getInitialCanvasState = () => deepClone(CANVAS_INITIAL_STATE);
|
||||
|
||||
export const zCanvasReferenceImageState_OLD = zCanvasEntityBase.extend({
|
||||
type: z.literal('reference_image'),
|
||||
ipAdapter: z.discriminatedUnion('type', [zIPAdapterConfig, zFLUXReduxConfig, zChatGPT4oReferenceImageConfig]),
|
||||
});
|
||||
|
||||
export const zCanvasMetadata = z.object({
|
||||
inpaintMasks: z.array(zCanvasInpaintMaskState),
|
||||
rasterLayers: z.array(zCanvasRasterLayerState),
|
||||
|
||||
@@ -56,6 +56,7 @@ const ImageMetadataActions = (props: Props) => {
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.RefinerDenoisingStart} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.RefinerSteps} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.CanvasLayers} />
|
||||
<CollectionMetadataDatum metadata={metadata} handler={MetadataHandlers.RefImages} />
|
||||
<CollectionMetadataDatum metadata={metadata} handler={MetadataHandlers.LoRAs} />
|
||||
</Flex>
|
||||
);
|
||||
|
||||
@@ -32,7 +32,9 @@ import {
|
||||
shouldConcatPromptsChanged,
|
||||
vaeSelected,
|
||||
} from 'features/controlLayers/store/paramsSlice';
|
||||
import { type CanvasMetadata, type LoRA, zCanvasMetadata } from 'features/controlLayers/store/types';
|
||||
import { refImageRecalled } from 'features/controlLayers/store/refImagesSlice';
|
||||
import type { CanvasMetadata, LoRA, RefImageState } from 'features/controlLayers/store/types';
|
||||
import { zCanvasMetadata, zCanvasReferenceImageState_OLD, zRefImageState } from 'features/controlLayers/store/types';
|
||||
import type { ModelIdentifierField } from 'features/nodes/types/common';
|
||||
import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||
import { zModelIdentifier } from 'features/nodes/types/v2/common';
|
||||
@@ -768,6 +770,50 @@ const CanvasLayers: SingleMetadataHandler<CanvasMetadata> = {
|
||||
};
|
||||
//#endregion CanvasLayers
|
||||
|
||||
//#region RefImages
|
||||
const RefImages: CollectionMetadataHandler<RefImageState[]> = {
|
||||
[CollectionMetadataKey]: true,
|
||||
type: 'RefImages',
|
||||
parse: async (metadata) => {
|
||||
try {
|
||||
// First attempt to parse from the v6 slot
|
||||
const raw = getProperty(metadata, 'ref_images');
|
||||
// This validator fetches all referenced images. If any do not exist, validation fails. The logic for this is in
|
||||
// the zImageWithDims schema.
|
||||
const parsed = await z.array(zRefImageState).parseAsync(raw);
|
||||
return Promise.resolve(parsed);
|
||||
} catch {
|
||||
// Fall back to extracting from canvas metadata]
|
||||
const raw = getProperty(metadata, 'canvas_v2_metadata.referenceImages.entities');
|
||||
// This validator fetches all referenced images. If any do not exist, validation fails. The logic for this is in
|
||||
// the zImageWithDims schema.
|
||||
const oldParsed = await z.array(zCanvasReferenceImageState_OLD).parseAsync(raw);
|
||||
const parsed: RefImageState[] = oldParsed.map(({ id, ipAdapter, isEnabled }) => ({
|
||||
id,
|
||||
config: ipAdapter,
|
||||
isEnabled,
|
||||
}));
|
||||
return parsed;
|
||||
}
|
||||
},
|
||||
recall: (value, store) => {
|
||||
for (const data of value) {
|
||||
store.dispatch(refImageRecalled({ data: { ...data, id: getPrefixedId('reference_image') } }));
|
||||
}
|
||||
},
|
||||
recallOne: (data, store) => {
|
||||
store.dispatch(refImageRecalled({ data: { ...data, id: getPrefixedId('reference_image') } }));
|
||||
},
|
||||
LabelComponent: () => <MetadataLabel i18nKey="controlLayers.referenceImage" />,
|
||||
ValueComponent: ({ value }: CollectionMetadataValueProps<RefImageState[]>) => {
|
||||
if (value.config.model) {
|
||||
return <MetadataPrimitiveValue value={value.config.model.name} />;
|
||||
}
|
||||
return <MetadataPrimitiveValue value="No model" />;
|
||||
},
|
||||
};
|
||||
//#endregion RefImages
|
||||
|
||||
export const MetadataHandlers = {
|
||||
CreatedBy,
|
||||
GenerationMode,
|
||||
@@ -797,8 +843,8 @@ export const MetadataHandlers = {
|
||||
VAEModel,
|
||||
LoRAs,
|
||||
CanvasLayers,
|
||||
// TODO:
|
||||
// Ref images
|
||||
RefImages,
|
||||
// TODO: These had parsers in the prev implementation, but they were never actually used?
|
||||
// controlNet: parseControlNet,
|
||||
// controlNets: parseAllControlNets,
|
||||
// t2iAdapter: parseT2IAdapter,
|
||||
@@ -808,9 +854,6 @@ export const MetadataHandlers = {
|
||||
// controlNetToControlLayer: parseControlNetToControlAdapterLayer,
|
||||
// t2iAdapterToControlAdapterLayer: parseT2IAdapterToControlAdapterLayer,
|
||||
// ipAdapterToIPAdapterLayer: parseIPAdapterToIPAdapterLayer,
|
||||
// layer: parseLayer,
|
||||
// layers: parseLayers,
|
||||
// canvasV2Metadata: parseCanvasV2Metadata,
|
||||
} as const;
|
||||
|
||||
const successToast = (parameter: ReactNode) => {
|
||||
|
||||
Reference in New Issue
Block a user