feat(ui): support recalling canvas metadata

- Add parser, recaller and handler
- Add redux action to rehydrate the whole shebang at once
This commit is contained in:
psychedelicious
2024-09-19 17:58:42 +10:00
parent bd028acdae
commit 362223cd20
4 changed files with 63 additions and 17 deletions

View File

@@ -15,6 +15,7 @@ import {
} from 'features/controlLayers/store/selectors';
import type {
CanvasInpaintMaskState,
CanvasMetadata,
FillStyle,
RegionalGuidanceReferenceImageState,
RgbColor,
@@ -1036,6 +1037,16 @@ export const canvasSlice = createSlice({
break;
}
},
canvasMetadataRecalled: (state, action: PayloadAction<CanvasMetadata>) => {
const newState = resetState(state);
const { controlLayers, inpaintMasks, rasterLayers, referenceImages, regionalGuidance } = action.payload;
newState.controlLayers.entities = controlLayers;
newState.inpaintMasks.entities = inpaintMasks;
newState.rasterLayers.entities = rasterLayers;
newState.referenceImages.entities = referenceImages;
newState.regionalGuidance.entities = regionalGuidance;
return newState;
},
canvasUndo: () => {},
canvasRedo: () => {},
canvasClearHistory: () => {},
@@ -1063,25 +1074,30 @@ export const canvasSlice = createSlice({
});
builder.addCase(canvasReset, (state) => {
const newState = getInitialState();
// We need to retain the optimal dimension across resets, as it is changed only when the model changes. Copy it
// from the old state, then recalculate the bbox size & scaled size.
newState.bbox.optimalDimension = state.bbox.optimalDimension;
const rect = calculateNewSize(
newState.bbox.aspectRatio.value,
newState.bbox.optimalDimension * newState.bbox.optimalDimension
);
newState.bbox.rect.width = rect.width;
newState.bbox.rect.height = rect.height;
syncScaledSize(newState);
return newState;
return resetState(state);
});
},
});
const resetState = (state: CanvasState) => {
const newState = getInitialState();
// We need to retain the optimal dimension across resets, as it is changed only when the model changes. Copy it
// from the old state, then recalculate the bbox size & scaled size.
newState.bbox.optimalDimension = state.bbox.optimalDimension;
const rect = calculateNewSize(
newState.bbox.aspectRatio.value,
newState.bbox.optimalDimension * newState.bbox.optimalDimension
);
newState.bbox.rect.width = rect.width;
newState.bbox.rect.height = rect.height;
syncScaledSize(newState);
return newState;
};
export const {
canvasMetadataRecalled,
canvasUndo,
canvasRedo,
canvasClearHistory,

View File

@@ -285,6 +285,12 @@ export const handlers = {
itemValidator: validators.lora,
renderItemValue: renderLoRAValue,
}),
canvasV2Metadata: buildHandlers({
getLabel: () => t('metadata.canvasV2Metadata'),
parser: parsers.canvasV2Metadata,
recaller: recallers.canvasV2Metadata,
}),
} as const;
type ParsedValue = Awaited<ReturnType<(typeof handlers)[keyof typeof handlers]['parse']>>;

View File

@@ -3,12 +3,13 @@ import { defaultLoRAConfig } from 'features/controlLayers/store/lorasSlice';
import type {
CanvasControlLayerState,
CanvasInpaintMaskState,
CanvasMetadata,
CanvasRasterLayerState,
CanvasReferenceImageState,
CanvasRegionalGuidanceState,
LoRA,
} from 'features/controlLayers/store/types';
import { zCanvasRasterLayerState } from 'features/controlLayers/store/types';
import { zCanvasMetadata, zCanvasRasterLayerState } from 'features/controlLayers/store/types';
import {
imageDTOToImageWithDims,
initialControlNet,
@@ -396,6 +397,11 @@ const parseLayer: MetadataParseFunc<
| CanvasInpaintMaskState
> = (metadataItem) => zCanvasRasterLayerState.parseAsync(metadataItem);
const parseCanvasV2Metadata: MetadataParseFunc<CanvasMetadata> = async (metadata) => {
const metadataItem = await getProperty(metadata, 'canvas_v2_metadata', undefined);
return zCanvasMetadata.parseAsync(metadataItem);
};
const parseLayers: MetadataParseFunc<
(
| CanvasRasterLayerState
@@ -660,4 +666,5 @@ export const parsers = {
ipAdapterToIPAdapterLayer: parseIPAdapterToIPAdapterLayer,
layer: parseLayer,
layers: parseLayers,
canvasV2Metadata: parseCanvasV2Metadata,
} as const;

View File

@@ -1,5 +1,5 @@
import { getStore } from 'app/store/nanostores/store';
import { bboxHeightChanged, bboxWidthChanged } from 'features/controlLayers/store/canvasSlice';
import { bboxHeightChanged, bboxWidthChanged, canvasMetadataRecalled } from 'features/controlLayers/store/canvasSlice';
import { loraAllDeleted, loraRecalled } from 'features/controlLayers/store/lorasSlice';
import {
negativePrompt2Changed,
@@ -22,7 +22,7 @@ import {
t5EncoderModelSelected,
vaeSelected,
} from 'features/controlLayers/store/paramsSlice';
import type { LoRA } from 'features/controlLayers/store/types';
import type { CanvasMetadata, LoRA } from 'features/controlLayers/store/types';
import { setHrfEnabled, setHrfMethod, setHrfStrength } from 'features/hrf/store/hrfSlice';
import type { MetadataRecallFunc } from 'features/metadata/types';
import { modelSelected } from 'features/parameters/store/actions';
@@ -175,6 +175,22 @@ const recallAllLoRAs: MetadataRecallFunc<LoRA[]> = (loras) => {
});
};
const recallCanvasV2Metadata: MetadataRecallFunc<CanvasMetadata> = (canvasMetadata) => {
if (
canvasMetadata.controlLayers.length === 0 &&
canvasMetadata.rasterLayers.length === 0 &&
canvasMetadata.inpaintMasks.length === 0 &&
canvasMetadata.referenceImages.length === 0 &&
canvasMetadata.regionalGuidance.length === 0
) {
// Nothing to recall
return;
}
const { dispatch } = getStore();
dispatch(canvasMetadataRecalled(canvasMetadata));
};
export const recallers = {
positivePrompt: recallPositivePrompt,
negativePrompt: recallNegativePrompt,
@@ -203,4 +219,5 @@ export const recallers = {
lora: recallLoRA,
loras: recallAllLoRAs,
t5EncoderModel: recallT5Encoder,
canvasV2Metadata: recallCanvasV2Metadata,
} as const;