mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
refactor(ui): canvas v2 (wip)
This commit is contained in:
parent
6c1d1588fc
commit
c51253f5f6
@ -4,12 +4,12 @@ import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'
|
|||||||
import type { AppDispatch } from 'app/store/store';
|
import type { AppDispatch } from 'app/store/store';
|
||||||
import { parseify } from 'common/util/serialize';
|
import { parseify } from 'common/util/serialize';
|
||||||
import {
|
import {
|
||||||
caLayerImageChanged,
|
controlAdapterImageChanged,
|
||||||
caLayerModelChanged,
|
controlAdapterModelChanged,
|
||||||
caLayerProcessedImageChanged,
|
controlAdapterProcessedImageChanged,
|
||||||
caLayerProcessorConfigChanged,
|
controlAdapterProcessorConfigChanged,
|
||||||
caLayerProcessorPendingBatchIdChanged,
|
controlAdapterProcessorPendingBatchIdChanged,
|
||||||
caLayerRecalled,
|
controlAdapterRecalled,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { isControlAdapterLayer } from 'features/controlLayers/store/types';
|
import { isControlAdapterLayer } from 'features/controlLayers/store/types';
|
||||||
import { CA_PROCESSOR_DATA } from 'features/controlLayers/util/controlAdapters';
|
import { CA_PROCESSOR_DATA } from 'features/controlLayers/util/controlAdapters';
|
||||||
@ -23,11 +23,11 @@ import { socketInvocationComplete } from 'services/events/actions';
|
|||||||
import { assert } from 'tsafe';
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
const matcher = isAnyOf(
|
const matcher = isAnyOf(
|
||||||
caLayerImageChanged,
|
controlAdapterImageChanged,
|
||||||
caLayerProcessedImageChanged,
|
controlAdapterProcessedImageChanged,
|
||||||
caLayerProcessorConfigChanged,
|
controlAdapterProcessorConfigChanged,
|
||||||
caLayerModelChanged,
|
controlAdapterModelChanged,
|
||||||
caLayerRecalled
|
controlAdapterRecalled
|
||||||
);
|
);
|
||||||
|
|
||||||
const DEBOUNCE_MS = 300;
|
const DEBOUNCE_MS = 300;
|
||||||
@ -46,7 +46,7 @@ const cancelProcessorBatch = async (dispatch: AppDispatch, layerId: string, batc
|
|||||||
} finally {
|
} finally {
|
||||||
req.reset();
|
req.reset();
|
||||||
// Always reset the pending batch ID - the cancel req could fail if the batch doesn't exist
|
// Always reset the pending batch ID - the cancel req could fail if the batch doesn't exist
|
||||||
dispatch(caLayerProcessorPendingBatchIdChanged({ layerId, batchId: null }));
|
dispatch(controlAdapterProcessorPendingBatchIdChanged({ layerId, batchId: null }));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ export const addControlAdapterPreprocessor = (startAppListening: AppStartListeni
|
|||||||
startAppListening({
|
startAppListening({
|
||||||
matcher,
|
matcher,
|
||||||
effect: async (action, { dispatch, getState, getOriginalState, cancelActiveListeners, delay, take, signal }) => {
|
effect: async (action, { dispatch, getState, getOriginalState, cancelActiveListeners, delay, take, signal }) => {
|
||||||
const layerId = caLayerRecalled.match(action) ? action.payload.id : action.payload.layerId;
|
const layerId = controlAdapterRecalled.match(action) ? action.payload.id : action.payload.layerId;
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const originalState = getOriginalState();
|
const originalState = getOriginalState();
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ export const addControlAdapterPreprocessor = (startAppListening: AppStartListeni
|
|||||||
// - If we have no image, we have nothing to process
|
// - If we have no image, we have nothing to process
|
||||||
// - If we have no processor config, we have nothing to process
|
// - If we have no processor config, we have nothing to process
|
||||||
// Clear the processed image and bail
|
// Clear the processed image and bail
|
||||||
dispatch(caLayerProcessedImageChanged({ layerId, imageDTO: null }));
|
dispatch(controlAdapterProcessedImageChanged({ layerId, imageDTO: null }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +132,7 @@ export const addControlAdapterPreprocessor = (startAppListening: AppStartListeni
|
|||||||
const enqueueResult = await req.unwrap();
|
const enqueueResult = await req.unwrap();
|
||||||
// TODO(psyche): Update the pydantic models, pretty sure we will _always_ have a batch_id here, but the model says it's optional
|
// TODO(psyche): Update the pydantic models, pretty sure we will _always_ have a batch_id here, but the model says it's optional
|
||||||
assert(enqueueResult.batch.batch_id, 'Batch ID not returned from queue');
|
assert(enqueueResult.batch.batch_id, 'Batch ID not returned from queue');
|
||||||
dispatch(caLayerProcessorPendingBatchIdChanged({ layerId, batchId: enqueueResult.batch.batch_id }));
|
dispatch(controlAdapterProcessorPendingBatchIdChanged({ layerId, batchId: enqueueResult.batch.batch_id }));
|
||||||
log.debug({ enqueueResult: parseify(enqueueResult) }, t('queue.graphQueued'));
|
log.debug({ enqueueResult: parseify(enqueueResult) }, t('queue.graphQueued'));
|
||||||
|
|
||||||
// Wait for the processor node to complete
|
// Wait for the processor node to complete
|
||||||
@ -155,8 +155,8 @@ export const addControlAdapterPreprocessor = (startAppListening: AppStartListeni
|
|||||||
|
|
||||||
// Whew! We made it. Update the layer with the processed image
|
// Whew! We made it. Update the layer with the processed image
|
||||||
log.debug({ layerId, imageDTO }, 'ControlNet image processed');
|
log.debug({ layerId, imageDTO }, 'ControlNet image processed');
|
||||||
dispatch(caLayerProcessedImageChanged({ layerId, imageDTO }));
|
dispatch(controlAdapterProcessedImageChanged({ layerId, imageDTO }));
|
||||||
dispatch(caLayerProcessorPendingBatchIdChanged({ layerId, batchId: null }));
|
dispatch(controlAdapterProcessorPendingBatchIdChanged({ layerId, batchId: null }));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (signal.aborted) {
|
if (signal.aborted) {
|
||||||
// The listener was canceled - we need to cancel the pending processor batch, if there is one (could have changed by now).
|
// The listener was canceled - we need to cancel the pending processor batch, if there is one (could have changed by now).
|
||||||
@ -174,7 +174,7 @@ export const addControlAdapterPreprocessor = (startAppListening: AppStartListeni
|
|||||||
if (error instanceof Object) {
|
if (error instanceof Object) {
|
||||||
if ('data' in error && 'status' in error) {
|
if ('data' in error && 'status' in error) {
|
||||||
if (error.status === 403) {
|
if (error.status === 403) {
|
||||||
dispatch(caLayerImageChanged({ layerId, imageDTO: null }));
|
dispatch(controlAdapterImageChanged({ layerId, imageDTO: null }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,11 +8,11 @@ import {
|
|||||||
controlAdapterIsEnabledChanged,
|
controlAdapterIsEnabledChanged,
|
||||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
import {
|
import {
|
||||||
caLayerImageChanged,
|
controlAdapterImageChanged,
|
||||||
iiLayerImageChanged,
|
iiLayerImageChanged,
|
||||||
imageAdded,
|
layerImageAdded,
|
||||||
ipaLayerImageChanged,
|
ipAdapterImageChanged,
|
||||||
rgLayerIPAdapterImageChanged,
|
regionalGuidanceIPAdapterImageChanged,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
||||||
import { isValidDrop } from 'features/dnd/util/isValidDrop';
|
import { isValidDrop } from 'features/dnd/util/isValidDrop';
|
||||||
@ -99,7 +99,7 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) =>
|
|||||||
) {
|
) {
|
||||||
const { layerId } = overData.context;
|
const { layerId } = overData.context;
|
||||||
dispatch(
|
dispatch(
|
||||||
caLayerImageChanged({
|
controlAdapterImageChanged({
|
||||||
layerId,
|
layerId,
|
||||||
imageDTO: activeData.payload.imageDTO,
|
imageDTO: activeData.payload.imageDTO,
|
||||||
})
|
})
|
||||||
@ -117,7 +117,7 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) =>
|
|||||||
) {
|
) {
|
||||||
const { layerId } = overData.context;
|
const { layerId } = overData.context;
|
||||||
dispatch(
|
dispatch(
|
||||||
ipaLayerImageChanged({
|
ipAdapterImageChanged({
|
||||||
layerId,
|
layerId,
|
||||||
imageDTO: activeData.payload.imageDTO,
|
imageDTO: activeData.payload.imageDTO,
|
||||||
})
|
})
|
||||||
@ -135,7 +135,7 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) =>
|
|||||||
) {
|
) {
|
||||||
const { layerId, ipAdapterId } = overData.context;
|
const { layerId, ipAdapterId } = overData.context;
|
||||||
dispatch(
|
dispatch(
|
||||||
rgLayerIPAdapterImageChanged({
|
regionalGuidanceIPAdapterImageChanged({
|
||||||
layerId,
|
layerId,
|
||||||
ipAdapterId,
|
ipAdapterId,
|
||||||
imageDTO: activeData.payload.imageDTO,
|
imageDTO: activeData.payload.imageDTO,
|
||||||
@ -172,7 +172,7 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) =>
|
|||||||
) {
|
) {
|
||||||
const { layerId } = overData.context;
|
const { layerId } = overData.context;
|
||||||
dispatch(
|
dispatch(
|
||||||
imageAdded({
|
layerImageAdded({
|
||||||
layerId,
|
layerId,
|
||||||
imageDTO: activeData.payload.imageDTO,
|
imageDTO: activeData.payload.imageDTO,
|
||||||
})
|
})
|
||||||
|
@ -6,10 +6,10 @@ import {
|
|||||||
controlAdapterIsEnabledChanged,
|
controlAdapterIsEnabledChanged,
|
||||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
import {
|
import {
|
||||||
caLayerImageChanged,
|
controlAdapterImageChanged,
|
||||||
iiLayerImageChanged,
|
iiLayerImageChanged,
|
||||||
ipaLayerImageChanged,
|
ipAdapterImageChanged,
|
||||||
rgLayerIPAdapterImageChanged,
|
regionalGuidanceIPAdapterImageChanged,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { selectListBoardsQueryArgs } from 'features/gallery/store/gallerySelectors';
|
import { selectListBoardsQueryArgs } from 'features/gallery/store/gallerySelectors';
|
||||||
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
||||||
@ -122,7 +122,7 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis
|
|||||||
|
|
||||||
if (postUploadAction?.type === 'SET_CA_LAYER_IMAGE') {
|
if (postUploadAction?.type === 'SET_CA_LAYER_IMAGE') {
|
||||||
const { layerId } = postUploadAction;
|
const { layerId } = postUploadAction;
|
||||||
dispatch(caLayerImageChanged({ layerId, imageDTO }));
|
dispatch(controlAdapterImageChanged({ layerId, imageDTO }));
|
||||||
toast({
|
toast({
|
||||||
...DEFAULT_UPLOADED_TOAST,
|
...DEFAULT_UPLOADED_TOAST,
|
||||||
description: t('toast.setControlImage'),
|
description: t('toast.setControlImage'),
|
||||||
@ -131,7 +131,7 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis
|
|||||||
|
|
||||||
if (postUploadAction?.type === 'SET_IPA_LAYER_IMAGE') {
|
if (postUploadAction?.type === 'SET_IPA_LAYER_IMAGE') {
|
||||||
const { layerId } = postUploadAction;
|
const { layerId } = postUploadAction;
|
||||||
dispatch(ipaLayerImageChanged({ layerId, imageDTO }));
|
dispatch(ipAdapterImageChanged({ layerId, imageDTO }));
|
||||||
toast({
|
toast({
|
||||||
...DEFAULT_UPLOADED_TOAST,
|
...DEFAULT_UPLOADED_TOAST,
|
||||||
description: t('toast.setControlImage'),
|
description: t('toast.setControlImage'),
|
||||||
@ -140,7 +140,7 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis
|
|||||||
|
|
||||||
if (postUploadAction?.type === 'SET_RG_LAYER_IP_ADAPTER_IMAGE') {
|
if (postUploadAction?.type === 'SET_RG_LAYER_IP_ADAPTER_IMAGE') {
|
||||||
const { layerId, ipAdapterId } = postUploadAction;
|
const { layerId, ipAdapterId } = postUploadAction;
|
||||||
dispatch(rgLayerIPAdapterImageChanged({ layerId, ipAdapterId, imageDTO }));
|
dispatch(regionalGuidanceIPAdapterImageChanged({ layerId, ipAdapterId, imageDTO }));
|
||||||
toast({
|
toast({
|
||||||
...DEFAULT_UPLOADED_TOAST,
|
...DEFAULT_UPLOADED_TOAST,
|
||||||
description: t('toast.setControlImage'),
|
description: t('toast.setControlImage'),
|
||||||
|
@ -7,14 +7,16 @@ import type { JSONObject } from 'common/types';
|
|||||||
import { canvasPersistConfig, canvasSlice } from 'features/canvas/store/canvasSlice';
|
import { canvasPersistConfig, canvasSlice } from 'features/canvas/store/canvasSlice';
|
||||||
import { changeBoardModalSlice } from 'features/changeBoardModal/store/slice';
|
import { changeBoardModalSlice } from 'features/changeBoardModal/store/slice';
|
||||||
import {
|
import {
|
||||||
controlAdaptersPersistConfig,
|
controlAdaptersV2PersistConfig,
|
||||||
controlAdaptersSlice,
|
controlAdaptersV2Slice,
|
||||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
} from 'features/controlLayers/store/controlAdaptersSlice';
|
||||||
|
import { canvasV2PersistConfig, canvasV2Slice } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
|
import { ipAdaptersPersistConfig, ipAdaptersSlice } from 'features/controlLayers/store/ipAdaptersSlice';
|
||||||
|
import { layersPersistConfig, layersSlice } from 'features/controlLayers/store/layersSlice';
|
||||||
import {
|
import {
|
||||||
controlLayersPersistConfig,
|
regionalGuidancePersistConfig,
|
||||||
controlLayersSlice,
|
regionalGuidanceSlice,
|
||||||
controlLayersUndoableConfig,
|
} from 'features/controlLayers/store/regionalGuidanceSlice';
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
|
||||||
import { deleteImageModalSlice } from 'features/deleteImageModal/store/slice';
|
import { deleteImageModalSlice } from 'features/deleteImageModal/store/slice';
|
||||||
import { dynamicPromptsPersistConfig, dynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
import { dynamicPromptsPersistConfig, dynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
||||||
import { galleryPersistConfig, gallerySlice } from 'features/gallery/store/gallerySlice';
|
import { galleryPersistConfig, gallerySlice } from 'features/gallery/store/gallerySlice';
|
||||||
@ -49,6 +51,7 @@ import { stateSanitizer } from './middleware/devtools/stateSanitizer';
|
|||||||
import { listenerMiddleware } from './middleware/listenerMiddleware';
|
import { listenerMiddleware } from './middleware/listenerMiddleware';
|
||||||
|
|
||||||
const allReducers = {
|
const allReducers = {
|
||||||
|
[api.reducerPath]: api.reducer,
|
||||||
[canvasSlice.name]: canvasSlice.reducer,
|
[canvasSlice.name]: canvasSlice.reducer,
|
||||||
[gallerySlice.name]: gallerySlice.reducer,
|
[gallerySlice.name]: gallerySlice.reducer,
|
||||||
[generationSlice.name]: generationSlice.reducer,
|
[generationSlice.name]: generationSlice.reducer,
|
||||||
@ -56,7 +59,6 @@ const allReducers = {
|
|||||||
[systemSlice.name]: systemSlice.reducer,
|
[systemSlice.name]: systemSlice.reducer,
|
||||||
[configSlice.name]: configSlice.reducer,
|
[configSlice.name]: configSlice.reducer,
|
||||||
[uiSlice.name]: uiSlice.reducer,
|
[uiSlice.name]: uiSlice.reducer,
|
||||||
[controlAdaptersSlice.name]: controlAdaptersSlice.reducer,
|
|
||||||
[dynamicPromptsSlice.name]: dynamicPromptsSlice.reducer,
|
[dynamicPromptsSlice.name]: dynamicPromptsSlice.reducer,
|
||||||
[deleteImageModalSlice.name]: deleteImageModalSlice.reducer,
|
[deleteImageModalSlice.name]: deleteImageModalSlice.reducer,
|
||||||
[changeBoardModalSlice.name]: changeBoardModalSlice.reducer,
|
[changeBoardModalSlice.name]: changeBoardModalSlice.reducer,
|
||||||
@ -66,11 +68,14 @@ const allReducers = {
|
|||||||
[queueSlice.name]: queueSlice.reducer,
|
[queueSlice.name]: queueSlice.reducer,
|
||||||
[workflowSlice.name]: workflowSlice.reducer,
|
[workflowSlice.name]: workflowSlice.reducer,
|
||||||
[hrfSlice.name]: hrfSlice.reducer,
|
[hrfSlice.name]: hrfSlice.reducer,
|
||||||
[controlLayersSlice.name]: undoable(controlLayersSlice.reducer, controlLayersUndoableConfig),
|
[canvasV2Slice.name]: canvasV2Slice.reducer,
|
||||||
[workflowSettingsSlice.name]: workflowSettingsSlice.reducer,
|
[workflowSettingsSlice.name]: workflowSettingsSlice.reducer,
|
||||||
[api.reducerPath]: api.reducer,
|
|
||||||
[upscaleSlice.name]: upscaleSlice.reducer,
|
[upscaleSlice.name]: upscaleSlice.reducer,
|
||||||
[stylePresetSlice.name]: stylePresetSlice.reducer,
|
[stylePresetSlice.name]: stylePresetSlice.reducer,
|
||||||
|
[layersSlice.name]: layersSlice.reducer,
|
||||||
|
[controlAdaptersV2Slice.name]: controlAdaptersV2Slice.reducer,
|
||||||
|
[ipAdaptersSlice.name]: ipAdaptersSlice.reducer,
|
||||||
|
[regionalGuidanceSlice.name]: regionalGuidanceSlice.reducer,
|
||||||
};
|
};
|
||||||
|
|
||||||
const rootReducer = combineReducers(allReducers);
|
const rootReducer = combineReducers(allReducers);
|
||||||
@ -107,16 +112,19 @@ const persistConfigs: { [key in keyof typeof allReducers]?: PersistConfig } = {
|
|||||||
[systemPersistConfig.name]: systemPersistConfig,
|
[systemPersistConfig.name]: systemPersistConfig,
|
||||||
[workflowPersistConfig.name]: workflowPersistConfig,
|
[workflowPersistConfig.name]: workflowPersistConfig,
|
||||||
[uiPersistConfig.name]: uiPersistConfig,
|
[uiPersistConfig.name]: uiPersistConfig,
|
||||||
[controlAdaptersPersistConfig.name]: controlAdaptersPersistConfig,
|
|
||||||
[dynamicPromptsPersistConfig.name]: dynamicPromptsPersistConfig,
|
[dynamicPromptsPersistConfig.name]: dynamicPromptsPersistConfig,
|
||||||
[sdxlPersistConfig.name]: sdxlPersistConfig,
|
[sdxlPersistConfig.name]: sdxlPersistConfig,
|
||||||
[loraPersistConfig.name]: loraPersistConfig,
|
[loraPersistConfig.name]: loraPersistConfig,
|
||||||
[modelManagerV2PersistConfig.name]: modelManagerV2PersistConfig,
|
[modelManagerV2PersistConfig.name]: modelManagerV2PersistConfig,
|
||||||
[hrfPersistConfig.name]: hrfPersistConfig,
|
[hrfPersistConfig.name]: hrfPersistConfig,
|
||||||
[controlLayersPersistConfig.name]: controlLayersPersistConfig,
|
[canvasV2PersistConfig.name]: canvasV2PersistConfig,
|
||||||
[workflowSettingsPersistConfig.name]: workflowSettingsPersistConfig,
|
[workflowSettingsPersistConfig.name]: workflowSettingsPersistConfig,
|
||||||
[upscalePersistConfig.name]: upscalePersistConfig,
|
[upscalePersistConfig.name]: upscalePersistConfig,
|
||||||
[stylePresetPersistConfig.name]: stylePresetPersistConfig,
|
[stylePresetPersistConfig.name]: stylePresetPersistConfig,
|
||||||
|
[layersPersistConfig.name]: layersPersistConfig,
|
||||||
|
[controlAdaptersV2PersistConfig.name]: controlAdaptersV2PersistConfig,
|
||||||
|
[ipAdaptersPersistConfig.name]: ipAdaptersPersistConfig,
|
||||||
|
[regionalGuidancePersistConfig.name]: regionalGuidancePersistConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
const unserialize: UnserializeFunction = (data, key) => {
|
const unserialize: UnserializeFunction = (data, key) => {
|
||||||
|
@ -6,8 +6,8 @@ import {
|
|||||||
selectControlAdaptersSlice,
|
selectControlAdaptersSlice,
|
||||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
||||||
import { selectControlLayersSlice } from 'features/controlLayers/store/controlLayersSlice';
|
import { selectCanvasV2Slice } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import type { Layer } from 'features/controlLayers/store/types';
|
import type { LayerData } from 'features/controlLayers/store/types';
|
||||||
import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
||||||
import { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt';
|
import { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt';
|
||||||
import { $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
import { $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
||||||
@ -24,7 +24,7 @@ import { forEach, upperFirst } from 'lodash-es';
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { getConnectedEdges } from 'reactflow';
|
import { getConnectedEdges } from 'reactflow';
|
||||||
|
|
||||||
const LAYER_TYPE_TO_TKEY: Record<Layer['type'], string> = {
|
const LAYER_TYPE_TO_TKEY: Record<LayerData['type'], string> = {
|
||||||
initial_image_layer: 'controlLayers.globalInitialImage',
|
initial_image_layer: 'controlLayers.globalInitialImage',
|
||||||
control_adapter_layer: 'controlLayers.globalControlAdapter',
|
control_adapter_layer: 'controlLayers.globalControlAdapter',
|
||||||
ip_adapter_layer: 'controlLayers.globalIPAdapter',
|
ip_adapter_layer: 'controlLayers.globalIPAdapter',
|
||||||
@ -41,7 +41,7 @@ const createSelector = (templates: Templates) =>
|
|||||||
selectNodesSlice,
|
selectNodesSlice,
|
||||||
selectWorkflowSettingsSlice,
|
selectWorkflowSettingsSlice,
|
||||||
selectDynamicPromptsSlice,
|
selectDynamicPromptsSlice,
|
||||||
selectControlLayersSlice,
|
selectCanvasV2Slice,
|
||||||
activeTabNameSelector,
|
activeTabNameSelector,
|
||||||
selectUpscalelice,
|
selectUpscalelice,
|
||||||
selectConfigSlice,
|
selectConfigSlice,
|
||||||
|
@ -1,85 +1,170 @@
|
|||||||
import { moveBackward, moveForward, moveToBack, moveToFront } from 'common/util/arrayUtils';
|
import { moveOneToEnd, moveOneToStart, moveToEnd, moveToStart } from 'common/util/arrayUtils';
|
||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
describe('Array Manipulation Functions', () => {
|
describe('Array Manipulation Functions', () => {
|
||||||
const originalArray = ['a', 'b', 'c', 'd'];
|
const originalArray = ['a', 'b', 'c', 'd'];
|
||||||
describe('moveForwardOne', () => {
|
|
||||||
|
describe('moveOneToEnd', () => {
|
||||||
|
describe('with callback', () => {
|
||||||
it('should move an item forward by one position', () => {
|
it('should move an item forward by one position', () => {
|
||||||
const array = [...originalArray];
|
const array = [...originalArray];
|
||||||
const result = moveForward(array, (item) => item === 'b');
|
const result = moveOneToEnd(array, (item) => item === 'b');
|
||||||
expect(result).toEqual(['a', 'c', 'b', 'd']);
|
expect(result).toEqual(['a', 'c', 'b', 'd']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should do nothing if the item is at the end', () => {
|
it('should do nothing if the item is at the end', () => {
|
||||||
const array = [...originalArray];
|
const array = [...originalArray];
|
||||||
const result = moveForward(array, (item) => item === 'd');
|
const result = moveOneToEnd(array, (item) => item === 'd');
|
||||||
expect(result).toEqual(['a', 'b', 'c', 'd']);
|
expect(result).toEqual(['a', 'b', 'c', 'd']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should leave the array unchanged if the item isn't in the array", () => {
|
it("should leave the array unchanged if the item isn't in the array", () => {
|
||||||
const array = [...originalArray];
|
const array = [...originalArray];
|
||||||
const result = moveForward(array, (item) => item === 'z');
|
const result = moveOneToEnd(array, (item) => item === 'z');
|
||||||
expect(result).toEqual(originalArray);
|
expect(result).toEqual(originalArray);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('with item', () => {
|
||||||
|
it('should move an item forward by one position', () => {
|
||||||
|
const array = [...originalArray];
|
||||||
|
const result = moveOneToEnd(array, (item) => item === 'b');
|
||||||
|
expect(result).toEqual(['a', 'c', 'b', 'd']);
|
||||||
|
});
|
||||||
|
|
||||||
describe('moveToFront', () => {
|
it('should do nothing if the item is at the end', () => {
|
||||||
|
const array = [...originalArray];
|
||||||
|
const result = moveOneToEnd(array, (item) => item === 'd');
|
||||||
|
expect(result).toEqual(['a', 'b', 'c', 'd']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should leave the array unchanged if the item isn't in the array", () => {
|
||||||
|
const array = [...originalArray];
|
||||||
|
const result = moveOneToEnd(array, (item) => item === 'z');
|
||||||
|
expect(result).toEqual(originalArray);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('moveToStart', () => {
|
||||||
|
describe('with callback', () => {
|
||||||
it('should move an item to the front', () => {
|
it('should move an item to the front', () => {
|
||||||
const array = [...originalArray];
|
const array = [...originalArray];
|
||||||
const result = moveToFront(array, (item) => item === 'c');
|
const result = moveToStart(array, (item) => item === 'c');
|
||||||
expect(result).toEqual(['c', 'a', 'b', 'd']);
|
expect(result).toEqual(['c', 'a', 'b', 'd']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should do nothing if the item is already at the front', () => {
|
it('should do nothing if the item is already at the front', () => {
|
||||||
const array = [...originalArray];
|
const array = [...originalArray];
|
||||||
const result = moveToFront(array, (item) => item === 'a');
|
const result = moveToStart(array, (item) => item === 'a');
|
||||||
expect(result).toEqual(['a', 'b', 'c', 'd']);
|
expect(result).toEqual(['a', 'b', 'c', 'd']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should leave the array unchanged if the item isn't in the array", () => {
|
it("should leave the array unchanged if the item isn't in the array", () => {
|
||||||
const array = [...originalArray];
|
const array = [...originalArray];
|
||||||
const result = moveToFront(array, (item) => item === 'z');
|
const result = moveToStart(array, (item) => item === 'z');
|
||||||
expect(result).toEqual(originalArray);
|
expect(result).toEqual(originalArray);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('with item', () => {
|
||||||
|
it('should move an item to the front', () => {
|
||||||
|
const array = [...originalArray];
|
||||||
|
const result = moveToStart(array, 'c');
|
||||||
|
expect(result).toEqual(['c', 'a', 'b', 'd']);
|
||||||
|
});
|
||||||
|
|
||||||
describe('moveBackwardsOne', () => {
|
it('should do nothing if the item is already at the front', () => {
|
||||||
|
const array = [...originalArray];
|
||||||
|
const result = moveToStart(array, 'a');
|
||||||
|
expect(result).toEqual(['a', 'b', 'c', 'd']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should leave the array unchanged if the item isn't in the array", () => {
|
||||||
|
const array = [...originalArray];
|
||||||
|
const result = moveToStart(array, 'z');
|
||||||
|
expect(result).toEqual(originalArray);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('moveOneToStart', () => {
|
||||||
|
describe('with callback', () => {
|
||||||
it('should move an item backward by one position', () => {
|
it('should move an item backward by one position', () => {
|
||||||
const array = [...originalArray];
|
const array = [...originalArray];
|
||||||
const result = moveBackward(array, (item) => item === 'c');
|
const result = moveOneToStart(array, (item) => item === 'c');
|
||||||
expect(result).toEqual(['a', 'c', 'b', 'd']);
|
expect(result).toEqual(['a', 'c', 'b', 'd']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should do nothing if the item is at the beginning', () => {
|
it('should do nothing if the item is at the beginning', () => {
|
||||||
const array = [...originalArray];
|
const array = [...originalArray];
|
||||||
const result = moveBackward(array, (item) => item === 'a');
|
const result = moveOneToStart(array, (item) => item === 'a');
|
||||||
expect(result).toEqual(['a', 'b', 'c', 'd']);
|
expect(result).toEqual(['a', 'b', 'c', 'd']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should leave the array unchanged if the item isn't in the array", () => {
|
it("should leave the array unchanged if the item isn't in the array", () => {
|
||||||
const array = [...originalArray];
|
const array = [...originalArray];
|
||||||
const result = moveBackward(array, (item) => item === 'z');
|
const result = moveOneToStart(array, (item) => item === 'z');
|
||||||
expect(result).toEqual(originalArray);
|
expect(result).toEqual(originalArray);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('with item', () => {
|
||||||
|
it('should move an item backward by one position', () => {
|
||||||
|
const array = [...originalArray];
|
||||||
|
const result = moveOneToStart(array, 'c');
|
||||||
|
expect(result).toEqual(['a', 'c', 'b', 'd']);
|
||||||
|
});
|
||||||
|
|
||||||
describe('moveToBack', () => {
|
it('should do nothing if the item is at the beginning', () => {
|
||||||
|
const array = [...originalArray];
|
||||||
|
const result = moveOneToStart(array, 'a');
|
||||||
|
expect(result).toEqual(['a', 'b', 'c', 'd']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should leave the array unchanged if the item isn't in the array", () => {
|
||||||
|
const array = [...originalArray];
|
||||||
|
const result = moveOneToStart(array, 'z');
|
||||||
|
expect(result).toEqual(originalArray);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('moveToEnd', () => {
|
||||||
|
describe('with callback', () => {
|
||||||
it('should move an item to the back', () => {
|
it('should move an item to the back', () => {
|
||||||
const array = [...originalArray];
|
const array = [...originalArray];
|
||||||
const result = moveToBack(array, (item) => item === 'b');
|
const result = moveToEnd(array, (item) => item === 'b');
|
||||||
expect(result).toEqual(['a', 'c', 'd', 'b']);
|
expect(result).toEqual(['a', 'c', 'd', 'b']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should do nothing if the item is already at the back', () => {
|
it('should do nothing if the item is already at the back', () => {
|
||||||
const array = [...originalArray];
|
const array = [...originalArray];
|
||||||
const result = moveToBack(array, (item) => item === 'd');
|
const result = moveToEnd(array, (item) => item === 'd');
|
||||||
expect(result).toEqual(['a', 'b', 'c', 'd']);
|
expect(result).toEqual(['a', 'b', 'c', 'd']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should leave the array unchanged if the item isn't in the array", () => {
|
it("should leave the array unchanged if the item isn't in the array", () => {
|
||||||
const array = [...originalArray];
|
const array = [...originalArray];
|
||||||
const result = moveToBack(array, (item) => item === 'z');
|
const result = moveToEnd(array, (item) => item === 'z');
|
||||||
|
expect(result).toEqual(originalArray);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('with item', () => {
|
||||||
|
it('should move an item to the back', () => {
|
||||||
|
const array = [...originalArray];
|
||||||
|
const result = moveToEnd(array, 'b');
|
||||||
|
expect(result).toEqual(['a', 'c', 'd', 'b']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should do nothing if the item is already at the back', () => {
|
||||||
|
const array = [...originalArray];
|
||||||
|
const result = moveToEnd(array, 'd');
|
||||||
|
expect(result).toEqual(['a', 'b', 'c', 'd']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should leave the array unchanged if the item isn't in the array", () => {
|
||||||
|
const array = [...originalArray];
|
||||||
|
const result = moveToEnd(array, 'z');
|
||||||
expect(result).toEqual(originalArray);
|
expect(result).toEqual(originalArray);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
@ -1,37 +1,45 @@
|
|||||||
export const moveForward = <T>(array: T[], callback: (item: T) => boolean): T[] => {
|
export function moveToStart<T>(array: T[], selectItemCallback: (item: T) => boolean): T[];
|
||||||
const index = array.findIndex(callback);
|
export function moveToStart<T>(array: T[], item: T): T[];
|
||||||
if (index >= 0 && index < array.length - 1) {
|
export function moveToStart<T>(array: T[], arg1: T | ((item: T) => boolean)): T[] {
|
||||||
//@ts-expect-error - These indicies are safe per the previous check
|
const index = arg1 instanceof Function ? array.findIndex(arg1) : array.indexOf(arg1);
|
||||||
[array[index], array[index + 1]] = [array[index + 1], array[index]];
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const moveToFront = <T>(array: T[], callback: (item: T) => boolean): T[] => {
|
|
||||||
const index = array.findIndex(callback);
|
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
const [item] = array.splice(index, 1);
|
const [item] = array.splice(index, 1);
|
||||||
//@ts-expect-error - These indicies are safe per the previous check
|
//@ts-expect-error - These indicies are safe per the previous check
|
||||||
array.unshift(item);
|
array.unshift(item);
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
};
|
}
|
||||||
|
|
||||||
export const moveBackward = <T>(array: T[], callback: (item: T) => boolean): T[] => {
|
export function moveOneToStart<T>(array: T[], selectItemCallback: (item: T) => boolean): T[];
|
||||||
const index = array.findIndex(callback);
|
export function moveOneToStart<T>(array: T[], item: T): T[];
|
||||||
|
export function moveOneToStart<T>(array: T[], arg1: T | ((item: T) => boolean)): T[] {
|
||||||
|
const index = arg1 instanceof Function ? array.findIndex(arg1) : array.indexOf(arg1);
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
//@ts-expect-error - These indicies are safe per the previous check
|
//@ts-expect-error - These indicies are safe per the previous check
|
||||||
[array[index], array[index - 1]] = [array[index - 1], array[index]];
|
[array[index], array[index - 1]] = [array[index - 1], array[index]];
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
};
|
}
|
||||||
|
|
||||||
export const moveToBack = <T>(array: T[], callback: (item: T) => boolean): T[] => {
|
export function moveToEnd<T>(array: T[], selectItemCallback: (item: T) => boolean): T[];
|
||||||
const index = array.findIndex(callback);
|
export function moveToEnd<T>(array: T[], item: T): T[];
|
||||||
|
export function moveToEnd<T>(array: T[], arg1: T | ((item: T) => boolean)): T[] {
|
||||||
|
const index = arg1 instanceof Function ? array.findIndex(arg1) : array.indexOf(arg1);
|
||||||
if (index >= 0 && index < array.length - 1) {
|
if (index >= 0 && index < array.length - 1) {
|
||||||
const [item] = array.splice(index, 1);
|
const [item] = array.splice(index, 1);
|
||||||
//@ts-expect-error - These indicies are safe per the previous check
|
//@ts-expect-error - These indicies are safe per the previous check
|
||||||
array.push(item);
|
array.push(item);
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export function moveOneToEnd<T>(array: T[], selectItemCallback: (item: T) => boolean): T[];
|
||||||
|
export function moveOneToEnd<T>(array: T[], item: T): T[];
|
||||||
|
export function moveOneToEnd<T>(array: T[], arg1: T | ((item: T) => boolean)): T[] {
|
||||||
|
const index = arg1 instanceof Function ? array.findIndex(arg1) : array.indexOf(arg1);
|
||||||
|
if (index >= 0 && index < array.length - 1) {
|
||||||
|
//@ts-expect-error - These indicies are safe per the previous check
|
||||||
|
[array[index], array[index + 1]] = [array[index + 1], array[index]];
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Button, Menu, MenuButton, MenuItem, MenuList } from '@invoke-ai/ui-library';
|
import { Button, Menu, MenuButton, MenuItem, MenuList } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { useAddCALayer, useAddIILayer, useAddIPALayer } from 'features/controlLayers/hooks/addLayerHooks';
|
import { useAddCALayer, useAddIILayer, useAddIPALayer } from 'features/controlLayers/hooks/addLayerHooks';
|
||||||
import { rasterLayerAdded, rgLayerAdded } from 'features/controlLayers/store/controlLayersSlice';
|
import { layerAdded, regionalGuidanceAdded } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PiPlusBold } from 'react-icons/pi';
|
import { PiPlusBold } from 'react-icons/pi';
|
||||||
@ -13,10 +13,10 @@ export const AddLayerButton = memo(() => {
|
|||||||
const [addIPALayer, isAddIPALayerDisabled] = useAddIPALayer();
|
const [addIPALayer, isAddIPALayerDisabled] = useAddIPALayer();
|
||||||
const [addIILayer, isAddIILayerDisabled] = useAddIILayer();
|
const [addIILayer, isAddIILayerDisabled] = useAddIILayer();
|
||||||
const addRGLayer = useCallback(() => {
|
const addRGLayer = useCallback(() => {
|
||||||
dispatch(rgLayerAdded());
|
dispatch(regionalGuidanceAdded());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
const addRasterLayer = useCallback(() => {
|
const addRasterLayer = useCallback(() => {
|
||||||
dispatch(rasterLayerAdded());
|
dispatch(layerAdded());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -3,9 +3,9 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { useAddIPAdapterToIPALayer } from 'features/controlLayers/hooks/addLayerHooks';
|
import { useAddIPAdapterToIPALayer } from 'features/controlLayers/hooks/addLayerHooks';
|
||||||
import {
|
import {
|
||||||
rgLayerNegativePromptChanged,
|
regionalGuidanceNegativePromptChanged,
|
||||||
rgLayerPositivePromptChanged,
|
regionalGuidancePositivePromptChanged,
|
||||||
selectControlLayersSlice,
|
selectCanvasV2Slice,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { isRegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
import { isRegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
@ -22,7 +22,7 @@ export const AddPromptButtons = ({ layerId }: AddPromptButtonProps) => {
|
|||||||
const [addIPAdapter, isAddIPAdapterDisabled] = useAddIPAdapterToIPALayer(layerId);
|
const [addIPAdapter, isAddIPAdapterDisabled] = useAddIPAdapterToIPALayer(layerId);
|
||||||
const selectValidActions = useMemo(
|
const selectValidActions = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createMemoizedSelector(selectControlLayersSlice, (controlLayers) => {
|
createMemoizedSelector(selectCanvasV2Slice, (controlLayers) => {
|
||||||
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
||||||
assert(isRegionalGuidanceLayer(layer), `Layer ${layerId} not found or not an RP layer`);
|
assert(isRegionalGuidanceLayer(layer), `Layer ${layerId} not found or not an RP layer`);
|
||||||
return {
|
return {
|
||||||
@ -34,10 +34,10 @@ export const AddPromptButtons = ({ layerId }: AddPromptButtonProps) => {
|
|||||||
);
|
);
|
||||||
const validActions = useAppSelector(selectValidActions);
|
const validActions = useAppSelector(selectValidActions);
|
||||||
const addPositivePrompt = useCallback(() => {
|
const addPositivePrompt = useCallback(() => {
|
||||||
dispatch(rgLayerPositivePromptChanged({ layerId, prompt: '' }));
|
dispatch(regionalGuidancePositivePromptChanged({ layerId, prompt: '' }));
|
||||||
}, [dispatch, layerId]);
|
}, [dispatch, layerId]);
|
||||||
const addNegativePrompt = useCallback(() => {
|
const addNegativePrompt = useCallback(() => {
|
||||||
dispatch(rgLayerNegativePromptChanged({ layerId, prompt: '' }));
|
dispatch(regionalGuidanceNegativePromptChanged({ layerId, prompt: '' }));
|
||||||
}, [dispatch, layerId]);
|
}, [dispatch, layerId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { ControlAdapter } from 'features/controlLayers/components/ControlAndIPAdapter/ControlAdapter';
|
import { ControlAdapter } from 'features/controlLayers/components/ControlAndIPAdapter/ControlAdapter';
|
||||||
import {
|
import {
|
||||||
caLayerControlModeChanged,
|
|
||||||
caLayerImageChanged,
|
|
||||||
caLayerModelChanged,
|
|
||||||
caLayerProcessedImageChanged,
|
|
||||||
caLayerProcessorConfigChanged,
|
|
||||||
caOrIPALayerBeginEndStepPctChanged,
|
caOrIPALayerBeginEndStepPctChanged,
|
||||||
caOrIPALayerWeightChanged,
|
caOrIPALayerWeightChanged,
|
||||||
|
controlAdapterControlModeChanged,
|
||||||
|
controlAdapterImageChanged,
|
||||||
|
controlAdapterModelChanged,
|
||||||
|
controlAdapterProcessedImageChanged,
|
||||||
|
controlAdapterProcessorConfigChanged,
|
||||||
selectLayerOrThrow,
|
selectLayerOrThrow,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { isControlAdapterLayer } from 'features/controlLayers/store/types';
|
import { isControlAdapterLayer } from 'features/controlLayers/store/types';
|
||||||
@ -46,7 +46,7 @@ export const CALayerControlAdapterWrapper = memo(({ layerId }: Props) => {
|
|||||||
const onChangeControlMode = useCallback(
|
const onChangeControlMode = useCallback(
|
||||||
(controlMode: ControlModeV2) => {
|
(controlMode: ControlModeV2) => {
|
||||||
dispatch(
|
dispatch(
|
||||||
caLayerControlModeChanged({
|
controlAdapterControlModeChanged({
|
||||||
layerId,
|
layerId,
|
||||||
controlMode,
|
controlMode,
|
||||||
})
|
})
|
||||||
@ -64,7 +64,7 @@ export const CALayerControlAdapterWrapper = memo(({ layerId }: Props) => {
|
|||||||
|
|
||||||
const onChangeProcessorConfig = useCallback(
|
const onChangeProcessorConfig = useCallback(
|
||||||
(processorConfig: ProcessorConfig | null) => {
|
(processorConfig: ProcessorConfig | null) => {
|
||||||
dispatch(caLayerProcessorConfigChanged({ layerId, processorConfig }));
|
dispatch(controlAdapterProcessorConfigChanged({ layerId, processorConfig }));
|
||||||
},
|
},
|
||||||
[dispatch, layerId]
|
[dispatch, layerId]
|
||||||
);
|
);
|
||||||
@ -72,7 +72,7 @@ export const CALayerControlAdapterWrapper = memo(({ layerId }: Props) => {
|
|||||||
const onChangeModel = useCallback(
|
const onChangeModel = useCallback(
|
||||||
(modelConfig: ControlNetModelConfig | T2IAdapterModelConfig) => {
|
(modelConfig: ControlNetModelConfig | T2IAdapterModelConfig) => {
|
||||||
dispatch(
|
dispatch(
|
||||||
caLayerModelChanged({
|
controlAdapterModelChanged({
|
||||||
layerId,
|
layerId,
|
||||||
modelConfig,
|
modelConfig,
|
||||||
})
|
})
|
||||||
@ -83,17 +83,17 @@ export const CALayerControlAdapterWrapper = memo(({ layerId }: Props) => {
|
|||||||
|
|
||||||
const onChangeImage = useCallback(
|
const onChangeImage = useCallback(
|
||||||
(imageDTO: ImageDTO | null) => {
|
(imageDTO: ImageDTO | null) => {
|
||||||
dispatch(caLayerImageChanged({ layerId, imageDTO }));
|
dispatch(controlAdapterImageChanged({ layerId, imageDTO }));
|
||||||
},
|
},
|
||||||
[dispatch, layerId]
|
[dispatch, layerId]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onErrorLoadingImage = useCallback(() => {
|
const onErrorLoadingImage = useCallback(() => {
|
||||||
dispatch(caLayerImageChanged({ layerId, imageDTO: null }));
|
dispatch(controlAdapterImageChanged({ layerId, imageDTO: null }));
|
||||||
}, [dispatch, layerId]);
|
}, [dispatch, layerId]);
|
||||||
|
|
||||||
const onErrorLoadingProcessedImage = useCallback(() => {
|
const onErrorLoadingProcessedImage = useCallback(() => {
|
||||||
dispatch(caLayerProcessedImageChanged({ layerId, imageDTO: null }));
|
dispatch(controlAdapterProcessedImageChanged({ layerId, imageDTO: null }));
|
||||||
}, [dispatch, layerId]);
|
}, [dispatch, layerId]);
|
||||||
|
|
||||||
const droppableData = useMemo<CALayerImageDropData>(
|
const droppableData = useMemo<CALayerImageDropData>(
|
||||||
|
@ -11,14 +11,14 @@ import { IILayer } from 'features/controlLayers/components/IILayer/IILayer';
|
|||||||
import { IPALayer } from 'features/controlLayers/components/IPALayer/IPALayer';
|
import { IPALayer } from 'features/controlLayers/components/IPALayer/IPALayer';
|
||||||
import { RasterLayer } from 'features/controlLayers/components/RasterLayer/RasterLayer';
|
import { RasterLayer } from 'features/controlLayers/components/RasterLayer/RasterLayer';
|
||||||
import { RGLayer } from 'features/controlLayers/components/RGLayer/RGLayer';
|
import { RGLayer } from 'features/controlLayers/components/RGLayer/RGLayer';
|
||||||
import { selectControlLayersSlice } from 'features/controlLayers/store/controlLayersSlice';
|
import { selectCanvasV2Slice } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import type { Layer } from 'features/controlLayers/store/types';
|
import type { LayerData } from 'features/controlLayers/store/types';
|
||||||
import { isRenderableLayer } from 'features/controlLayers/store/types';
|
import { isRenderableLayer } from 'features/controlLayers/store/types';
|
||||||
import { partition } from 'lodash-es';
|
import { partition } from 'lodash-es';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const selectLayerIdTypePairs = createMemoizedSelector(selectControlLayersSlice, (controlLayers) => {
|
const selectLayerIdTypePairs = createMemoizedSelector(selectCanvasV2Slice, (controlLayers) => {
|
||||||
const [renderableLayers, ipAdapterLayers] = partition(controlLayers.present.layers, isRenderableLayer);
|
const [renderableLayers, ipAdapterLayers] = partition(controlLayers.present.layers, isRenderableLayer);
|
||||||
return [...ipAdapterLayers, ...renderableLayers].map((l) => ({ id: l.id, type: l.type })).reverse();
|
return [...ipAdapterLayers, ...renderableLayers].map((l) => ({ id: l.id, type: l.type })).reverse();
|
||||||
});
|
});
|
||||||
@ -50,7 +50,7 @@ ControlLayersPanelContent.displayName = 'ControlLayersPanelContent';
|
|||||||
|
|
||||||
type LayerWrapperProps = {
|
type LayerWrapperProps = {
|
||||||
id: string;
|
id: string;
|
||||||
type: Layer['type'];
|
type: LayerData['type'];
|
||||||
};
|
};
|
||||||
|
|
||||||
const LayerWrapper = memo(({ id, type }: LayerWrapperProps) => {
|
const LayerWrapper = memo(({ id, type }: LayerWrapperProps) => {
|
||||||
|
@ -3,10 +3,10 @@ import { IPAdapter } from 'features/controlLayers/components/ControlAndIPAdapter
|
|||||||
import {
|
import {
|
||||||
caOrIPALayerBeginEndStepPctChanged,
|
caOrIPALayerBeginEndStepPctChanged,
|
||||||
caOrIPALayerWeightChanged,
|
caOrIPALayerWeightChanged,
|
||||||
ipaLayerCLIPVisionModelChanged,
|
ipAdapterCLIPVisionModelChanged,
|
||||||
ipaLayerImageChanged,
|
ipAdapterImageChanged,
|
||||||
ipaLayerMethodChanged,
|
ipAdapterMethodChanged,
|
||||||
ipaLayerModelChanged,
|
ipAdapterModelChanged,
|
||||||
selectLayerOrThrow,
|
selectLayerOrThrow,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { isIPAdapterLayer } from 'features/controlLayers/store/types';
|
import { isIPAdapterLayer } from 'features/controlLayers/store/types';
|
||||||
@ -46,28 +46,28 @@ export const IPALayerIPAdapterWrapper = memo(({ layerId }: Props) => {
|
|||||||
|
|
||||||
const onChangeIPMethod = useCallback(
|
const onChangeIPMethod = useCallback(
|
||||||
(method: IPMethodV2) => {
|
(method: IPMethodV2) => {
|
||||||
dispatch(ipaLayerMethodChanged({ layerId, method }));
|
dispatch(ipAdapterMethodChanged({ layerId, method }));
|
||||||
},
|
},
|
||||||
[dispatch, layerId]
|
[dispatch, layerId]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onChangeModel = useCallback(
|
const onChangeModel = useCallback(
|
||||||
(modelConfig: IPAdapterModelConfig) => {
|
(modelConfig: IPAdapterModelConfig) => {
|
||||||
dispatch(ipaLayerModelChanged({ layerId, modelConfig }));
|
dispatch(ipAdapterModelChanged({ layerId, modelConfig }));
|
||||||
},
|
},
|
||||||
[dispatch, layerId]
|
[dispatch, layerId]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onChangeCLIPVisionModel = useCallback(
|
const onChangeCLIPVisionModel = useCallback(
|
||||||
(clipVisionModel: CLIPVisionModelV2) => {
|
(clipVisionModel: CLIPVisionModelV2) => {
|
||||||
dispatch(ipaLayerCLIPVisionModelChanged({ layerId, clipVisionModel }));
|
dispatch(ipAdapterCLIPVisionModelChanged({ layerId, clipVisionModel }));
|
||||||
},
|
},
|
||||||
[dispatch, layerId]
|
[dispatch, layerId]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onChangeImage = useCallback(
|
const onChangeImage = useCallback(
|
||||||
(imageDTO: ImageDTO | null) => {
|
(imageDTO: ImageDTO | null) => {
|
||||||
dispatch(ipaLayerImageChanged({ layerId, imageDTO }));
|
dispatch(ipAdapterImageChanged({ layerId, imageDTO }));
|
||||||
},
|
},
|
||||||
[dispatch, layerId]
|
[dispatch, layerId]
|
||||||
);
|
);
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
layerMovedForward,
|
layerMovedForward,
|
||||||
layerMovedToBack,
|
layerMovedToBack,
|
||||||
layerMovedToFront,
|
layerMovedToFront,
|
||||||
selectControlLayersSlice,
|
selectCanvasV2Slice,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { isRenderableLayer } from 'features/controlLayers/store/types';
|
import { isRenderableLayer } from 'features/controlLayers/store/types';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
@ -21,7 +21,7 @@ export const LayerMenuArrangeActions = memo(({ layerId }: Props) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const selectValidActions = useMemo(
|
const selectValidActions = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createMemoizedSelector(selectControlLayersSlice, (controlLayers) => {
|
createMemoizedSelector(selectCanvasV2Slice, (controlLayers) => {
|
||||||
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
||||||
assert(isRenderableLayer(layer), `Layer ${layerId} not found or not an RP layer`);
|
assert(isRenderableLayer(layer), `Layer ${layerId} not found or not an RP layer`);
|
||||||
const layerIndex = controlLayers.present.layers.findIndex((l) => l.id === layerId);
|
const layerIndex = controlLayers.present.layers.findIndex((l) => l.id === layerId);
|
||||||
|
@ -3,9 +3,9 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { useAddIPAdapterToIPALayer } from 'features/controlLayers/hooks/addLayerHooks';
|
import { useAddIPAdapterToIPALayer } from 'features/controlLayers/hooks/addLayerHooks';
|
||||||
import {
|
import {
|
||||||
rgLayerNegativePromptChanged,
|
regionalGuidanceNegativePromptChanged,
|
||||||
rgLayerPositivePromptChanged,
|
regionalGuidancePositivePromptChanged,
|
||||||
selectControlLayersSlice,
|
selectCanvasV2Slice,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { isRegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
import { isRegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
@ -21,7 +21,7 @@ export const LayerMenuRGActions = memo(({ layerId }: Props) => {
|
|||||||
const [addIPAdapter, isAddIPAdapterDisabled] = useAddIPAdapterToIPALayer(layerId);
|
const [addIPAdapter, isAddIPAdapterDisabled] = useAddIPAdapterToIPALayer(layerId);
|
||||||
const selectValidActions = useMemo(
|
const selectValidActions = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createMemoizedSelector(selectControlLayersSlice, (controlLayers) => {
|
createMemoizedSelector(selectCanvasV2Slice, (controlLayers) => {
|
||||||
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
||||||
assert(isRegionalGuidanceLayer(layer), `Layer ${layerId} not found or not an RP layer`);
|
assert(isRegionalGuidanceLayer(layer), `Layer ${layerId} not found or not an RP layer`);
|
||||||
return {
|
return {
|
||||||
@ -33,10 +33,10 @@ export const LayerMenuRGActions = memo(({ layerId }: Props) => {
|
|||||||
);
|
);
|
||||||
const validActions = useAppSelector(selectValidActions);
|
const validActions = useAppSelector(selectValidActions);
|
||||||
const addPositivePrompt = useCallback(() => {
|
const addPositivePrompt = useCallback(() => {
|
||||||
dispatch(rgLayerPositivePromptChanged({ layerId, prompt: '' }));
|
dispatch(regionalGuidancePositivePromptChanged({ layerId, prompt: '' }));
|
||||||
}, [dispatch, layerId]);
|
}, [dispatch, layerId]);
|
||||||
const addNegativePrompt = useCallback(() => {
|
const addNegativePrompt = useCallback(() => {
|
||||||
dispatch(rgLayerNegativePromptChanged({ layerId, prompt: '' }));
|
dispatch(regionalGuidanceNegativePromptChanged({ layerId, prompt: '' }));
|
||||||
}, [dispatch, layerId]);
|
}, [dispatch, layerId]);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -16,7 +16,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|||||||
import { stopPropagation } from 'common/util/stopPropagation';
|
import { stopPropagation } from 'common/util/stopPropagation';
|
||||||
import {
|
import {
|
||||||
layerOpacityChanged,
|
layerOpacityChanged,
|
||||||
selectControlLayersSlice,
|
selectCanvasV2Slice,
|
||||||
selectLayerOrThrow,
|
selectLayerOrThrow,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { isLayerWithOpacity } from 'features/controlLayers/store/types';
|
import { isLayerWithOpacity } from 'features/controlLayers/store/types';
|
||||||
@ -36,7 +36,7 @@ export const LayerOpacity = memo(({ layerId }: Props) => {
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const selectOpacity = useMemo(
|
const selectOpacity = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createSelector(selectControlLayersSlice, (controlLayers) => {
|
createSelector(selectCanvasV2Slice, (controlLayers) => {
|
||||||
const layer = selectLayerOrThrow(controlLayers.present, layerId, isLayerWithOpacity);
|
const layer = selectLayerOrThrow(controlLayers.present, layerId, isLayerWithOpacity);
|
||||||
return Math.round(layer.opacity * 100);
|
return Math.round(layer.opacity * 100);
|
||||||
}),
|
}),
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { Text } from '@invoke-ai/ui-library';
|
import { Text } from '@invoke-ai/ui-library';
|
||||||
import type { Layer } from 'features/controlLayers/store/types';
|
import type { LayerData } from 'features/controlLayers/store/types';
|
||||||
import { memo, useMemo } from 'react';
|
import { memo, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
type: Layer['type'];
|
type: LayerData['type'];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LayerTitle = memo(({ type }: Props) => {
|
export const LayerTitle = memo(({ type }: Props) => {
|
||||||
|
@ -8,7 +8,7 @@ import { LayerMenu } from 'features/controlLayers/components/LayerCommon/LayerMe
|
|||||||
import { LayerTitle } from 'features/controlLayers/components/LayerCommon/LayerTitle';
|
import { LayerTitle } from 'features/controlLayers/components/LayerCommon/LayerTitle';
|
||||||
import { LayerIsEnabledToggle } from 'features/controlLayers/components/LayerCommon/LayerVisibilityToggle';
|
import { LayerIsEnabledToggle } from 'features/controlLayers/components/LayerCommon/LayerVisibilityToggle';
|
||||||
import { LayerWrapper } from 'features/controlLayers/components/LayerCommon/LayerWrapper';
|
import { LayerWrapper } from 'features/controlLayers/components/LayerCommon/LayerWrapper';
|
||||||
import { layerSelected, selectControlLayersSlice } from 'features/controlLayers/store/controlLayersSlice';
|
import { layerSelected, selectCanvasV2Slice } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { isRegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
import { isRegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -29,7 +29,7 @@ export const RGLayer = memo(({ layerId }: Props) => {
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const selector = useMemo(
|
const selector = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createMemoizedSelector(selectControlLayersSlice, (controlLayers) => {
|
createMemoizedSelector(selectCanvasV2Slice, (controlLayers) => {
|
||||||
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
||||||
assert(isRegionalGuidanceLayer(layer), `Layer ${layerId} not found or not an RP layer`);
|
assert(isRegionalGuidanceLayer(layer), `Layer ${layerId} not found or not an RP layer`);
|
||||||
return {
|
return {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Checkbox, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
import { Checkbox, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { rgLayerAutoNegativeChanged, selectControlLayersSlice } from 'features/controlLayers/store/controlLayersSlice';
|
import { regionalGuidanceAutoNegativeChanged, selectCanvasV2Slice } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { isRegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
import { isRegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
||||||
import type { ChangeEvent } from 'react';
|
import type { ChangeEvent } from 'react';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
@ -15,7 +15,7 @@ type Props = {
|
|||||||
const useAutoNegative = (layerId: string) => {
|
const useAutoNegative = (layerId: string) => {
|
||||||
const selectAutoNegative = useMemo(
|
const selectAutoNegative = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createSelector(selectControlLayersSlice, (controlLayers) => {
|
createSelector(selectCanvasV2Slice, (controlLayers) => {
|
||||||
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
||||||
assert(isRegionalGuidanceLayer(layer), `Layer ${layerId} not found or not an RP layer`);
|
assert(isRegionalGuidanceLayer(layer), `Layer ${layerId} not found or not an RP layer`);
|
||||||
return layer.autoNegative;
|
return layer.autoNegative;
|
||||||
@ -32,7 +32,7 @@ export const RGLayerAutoNegativeCheckbox = memo(({ layerId }: Props) => {
|
|||||||
const autoNegative = useAutoNegative(layerId);
|
const autoNegative = useAutoNegative(layerId);
|
||||||
const onChange = useCallback(
|
const onChange = useCallback(
|
||||||
(e: ChangeEvent<HTMLInputElement>) => {
|
(e: ChangeEvent<HTMLInputElement>) => {
|
||||||
dispatch(rgLayerAutoNegativeChanged({ layerId, autoNegative: e.target.checked ? 'invert' : 'off' }));
|
dispatch(regionalGuidanceAutoNegativeChanged({ layerId, autoNegative: e.target.checked ? 'invert' : 'off' }));
|
||||||
},
|
},
|
||||||
[dispatch, layerId]
|
[dispatch, layerId]
|
||||||
);
|
);
|
||||||
|
@ -4,7 +4,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|||||||
import RgbColorPicker from 'common/components/RgbColorPicker';
|
import RgbColorPicker from 'common/components/RgbColorPicker';
|
||||||
import { stopPropagation } from 'common/util/stopPropagation';
|
import { stopPropagation } from 'common/util/stopPropagation';
|
||||||
import { rgbColorToString } from 'features/canvas/util/colorToString';
|
import { rgbColorToString } from 'features/canvas/util/colorToString';
|
||||||
import { rgLayerPreviewColorChanged, selectControlLayersSlice } from 'features/controlLayers/store/controlLayersSlice';
|
import { rgFillChanged, selectCanvasV2Slice } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { isRegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
import { isRegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
import type { RgbColor } from 'react-colorful';
|
import type { RgbColor } from 'react-colorful';
|
||||||
@ -19,7 +19,7 @@ export const RGLayerColorPicker = memo(({ layerId }: Props) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const selectColor = useMemo(
|
const selectColor = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createMemoizedSelector(selectControlLayersSlice, (controlLayers) => {
|
createMemoizedSelector(selectCanvasV2Slice, (controlLayers) => {
|
||||||
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
||||||
assert(isRegionalGuidanceLayer(layer), `Layer ${layerId} not found or not an vector mask layer`);
|
assert(isRegionalGuidanceLayer(layer), `Layer ${layerId} not found or not an vector mask layer`);
|
||||||
return layer.previewColor;
|
return layer.previewColor;
|
||||||
@ -30,7 +30,7 @@ export const RGLayerColorPicker = memo(({ layerId }: Props) => {
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const onColorChange = useCallback(
|
const onColorChange = useCallback(
|
||||||
(color: RgbColor) => {
|
(color: RgbColor) => {
|
||||||
dispatch(rgLayerPreviewColorChanged({ layerId, color }));
|
dispatch(rgFillChanged({ layerId, color }));
|
||||||
},
|
},
|
||||||
[dispatch, layerId]
|
[dispatch, layerId]
|
||||||
);
|
);
|
||||||
|
@ -2,7 +2,7 @@ import { Divider, Flex } from '@invoke-ai/ui-library';
|
|||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { RGLayerIPAdapterWrapper } from 'features/controlLayers/components/RGLayer/RGLayerIPAdapterWrapper';
|
import { RGLayerIPAdapterWrapper } from 'features/controlLayers/components/RGLayer/RGLayerIPAdapterWrapper';
|
||||||
import { selectControlLayersSlice } from 'features/controlLayers/store/controlLayersSlice';
|
import { selectCanvasV2Slice } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { isRegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
import { isRegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
||||||
import { memo, useMemo } from 'react';
|
import { memo, useMemo } from 'react';
|
||||||
import { assert } from 'tsafe';
|
import { assert } from 'tsafe';
|
||||||
@ -14,7 +14,7 @@ type Props = {
|
|||||||
export const RGLayerIPAdapterList = memo(({ layerId }: Props) => {
|
export const RGLayerIPAdapterList = memo(({ layerId }: Props) => {
|
||||||
const selectIPAdapterIds = useMemo(
|
const selectIPAdapterIds = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createMemoizedSelector(selectControlLayersSlice, (controlLayers) => {
|
createMemoizedSelector(selectCanvasV2Slice, (controlLayers) => {
|
||||||
const layer = controlLayers.present.layers.filter(isRegionalGuidanceLayer).find((l) => l.id === layerId);
|
const layer = controlLayers.present.layers.filter(isRegionalGuidanceLayer).find((l) => l.id === layerId);
|
||||||
assert(layer, `Layer ${layerId} not found`);
|
assert(layer, `Layer ${layerId} not found`);
|
||||||
return layer.ipAdapters;
|
return layer.ipAdapters;
|
||||||
|
@ -2,13 +2,13 @@ import { Flex, IconButton, Spacer, Text } from '@invoke-ai/ui-library';
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { IPAdapter } from 'features/controlLayers/components/ControlAndIPAdapter/IPAdapter';
|
import { IPAdapter } from 'features/controlLayers/components/ControlAndIPAdapter/IPAdapter';
|
||||||
import {
|
import {
|
||||||
rgLayerIPAdapterBeginEndStepPctChanged,
|
regionalGuidanceIPAdapterBeginEndStepPctChanged,
|
||||||
rgLayerIPAdapterCLIPVisionModelChanged,
|
regionalGuidanceIPAdapterCLIPVisionModelChanged,
|
||||||
rgLayerIPAdapterDeleted,
|
regionalGuidanceIPAdapterDeleted,
|
||||||
rgLayerIPAdapterImageChanged,
|
regionalGuidanceIPAdapterImageChanged,
|
||||||
rgLayerIPAdapterMethodChanged,
|
regionalGuidanceIPAdapterMethodChanged,
|
||||||
rgLayerIPAdapterModelChanged,
|
regionalGuidanceIPAdapterModelChanged,
|
||||||
rgLayerIPAdapterWeightChanged,
|
regionalGuidanceIPAdapterWeightChanged,
|
||||||
selectRGLayerIPAdapterOrThrow,
|
selectRGLayerIPAdapterOrThrow,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import type { CLIPVisionModelV2, IPMethodV2 } from 'features/controlLayers/util/controlAdapters';
|
import type { CLIPVisionModelV2, IPMethodV2 } from 'features/controlLayers/util/controlAdapters';
|
||||||
@ -26,14 +26,14 @@ type Props = {
|
|||||||
export const RGLayerIPAdapterWrapper = memo(({ layerId, ipAdapterId, ipAdapterNumber }: Props) => {
|
export const RGLayerIPAdapterWrapper = memo(({ layerId, ipAdapterId, ipAdapterNumber }: Props) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const onDeleteIPAdapter = useCallback(() => {
|
const onDeleteIPAdapter = useCallback(() => {
|
||||||
dispatch(rgLayerIPAdapterDeleted({ layerId, ipAdapterId }));
|
dispatch(regionalGuidanceIPAdapterDeleted({ layerId, ipAdapterId }));
|
||||||
}, [dispatch, ipAdapterId, layerId]);
|
}, [dispatch, ipAdapterId, layerId]);
|
||||||
const ipAdapter = useAppSelector((s) => selectRGLayerIPAdapterOrThrow(s.controlLayers.present, layerId, ipAdapterId));
|
const ipAdapter = useAppSelector((s) => selectRGLayerIPAdapterOrThrow(s.controlLayers.present, layerId, ipAdapterId));
|
||||||
|
|
||||||
const onChangeBeginEndStepPct = useCallback(
|
const onChangeBeginEndStepPct = useCallback(
|
||||||
(beginEndStepPct: [number, number]) => {
|
(beginEndStepPct: [number, number]) => {
|
||||||
dispatch(
|
dispatch(
|
||||||
rgLayerIPAdapterBeginEndStepPctChanged({
|
regionalGuidanceIPAdapterBeginEndStepPctChanged({
|
||||||
layerId,
|
layerId,
|
||||||
ipAdapterId,
|
ipAdapterId,
|
||||||
beginEndStepPct,
|
beginEndStepPct,
|
||||||
@ -45,35 +45,35 @@ export const RGLayerIPAdapterWrapper = memo(({ layerId, ipAdapterId, ipAdapterNu
|
|||||||
|
|
||||||
const onChangeWeight = useCallback(
|
const onChangeWeight = useCallback(
|
||||||
(weight: number) => {
|
(weight: number) => {
|
||||||
dispatch(rgLayerIPAdapterWeightChanged({ layerId, ipAdapterId, weight }));
|
dispatch(regionalGuidanceIPAdapterWeightChanged({ layerId, ipAdapterId, weight }));
|
||||||
},
|
},
|
||||||
[dispatch, ipAdapterId, layerId]
|
[dispatch, ipAdapterId, layerId]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onChangeIPMethod = useCallback(
|
const onChangeIPMethod = useCallback(
|
||||||
(method: IPMethodV2) => {
|
(method: IPMethodV2) => {
|
||||||
dispatch(rgLayerIPAdapterMethodChanged({ layerId, ipAdapterId, method }));
|
dispatch(regionalGuidanceIPAdapterMethodChanged({ layerId, ipAdapterId, method }));
|
||||||
},
|
},
|
||||||
[dispatch, ipAdapterId, layerId]
|
[dispatch, ipAdapterId, layerId]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onChangeModel = useCallback(
|
const onChangeModel = useCallback(
|
||||||
(modelConfig: IPAdapterModelConfig) => {
|
(modelConfig: IPAdapterModelConfig) => {
|
||||||
dispatch(rgLayerIPAdapterModelChanged({ layerId, ipAdapterId, modelConfig }));
|
dispatch(regionalGuidanceIPAdapterModelChanged({ layerId, ipAdapterId, modelConfig }));
|
||||||
},
|
},
|
||||||
[dispatch, ipAdapterId, layerId]
|
[dispatch, ipAdapterId, layerId]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onChangeCLIPVisionModel = useCallback(
|
const onChangeCLIPVisionModel = useCallback(
|
||||||
(clipVisionModel: CLIPVisionModelV2) => {
|
(clipVisionModel: CLIPVisionModelV2) => {
|
||||||
dispatch(rgLayerIPAdapterCLIPVisionModelChanged({ layerId, ipAdapterId, clipVisionModel }));
|
dispatch(regionalGuidanceIPAdapterCLIPVisionModelChanged({ layerId, ipAdapterId, clipVisionModel }));
|
||||||
},
|
},
|
||||||
[dispatch, ipAdapterId, layerId]
|
[dispatch, ipAdapterId, layerId]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onChangeImage = useCallback(
|
const onChangeImage = useCallback(
|
||||||
(imageDTO: ImageDTO | null) => {
|
(imageDTO: ImageDTO | null) => {
|
||||||
dispatch(rgLayerIPAdapterImageChanged({ layerId, ipAdapterId, imageDTO }));
|
dispatch(regionalGuidanceIPAdapterImageChanged({ layerId, ipAdapterId, imageDTO }));
|
||||||
},
|
},
|
||||||
[dispatch, ipAdapterId, layerId]
|
[dispatch, ipAdapterId, layerId]
|
||||||
);
|
);
|
||||||
|
@ -2,7 +2,7 @@ import { Box, Textarea } from '@invoke-ai/ui-library';
|
|||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { RGLayerPromptDeleteButton } from 'features/controlLayers/components/RGLayer/RGLayerPromptDeleteButton';
|
import { RGLayerPromptDeleteButton } from 'features/controlLayers/components/RGLayer/RGLayerPromptDeleteButton';
|
||||||
import { useLayerNegativePrompt } from 'features/controlLayers/hooks/layerStateHooks';
|
import { useLayerNegativePrompt } from 'features/controlLayers/hooks/layerStateHooks';
|
||||||
import { rgLayerNegativePromptChanged } from 'features/controlLayers/store/controlLayersSlice';
|
import { regionalGuidanceNegativePromptChanged } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
||||||
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
||||||
import { PromptPopover } from 'features/prompt/PromptPopover';
|
import { PromptPopover } from 'features/prompt/PromptPopover';
|
||||||
@ -21,7 +21,7 @@ export const RGLayerNegativePrompt = memo(({ layerId }: Props) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const _onChange = useCallback(
|
const _onChange = useCallback(
|
||||||
(v: string) => {
|
(v: string) => {
|
||||||
dispatch(rgLayerNegativePromptChanged({ layerId, prompt: v }));
|
dispatch(regionalGuidanceNegativePromptChanged({ layerId, prompt: v }));
|
||||||
},
|
},
|
||||||
[dispatch, layerId]
|
[dispatch, layerId]
|
||||||
);
|
);
|
||||||
|
@ -2,7 +2,7 @@ import { Box, Textarea } from '@invoke-ai/ui-library';
|
|||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { RGLayerPromptDeleteButton } from 'features/controlLayers/components/RGLayer/RGLayerPromptDeleteButton';
|
import { RGLayerPromptDeleteButton } from 'features/controlLayers/components/RGLayer/RGLayerPromptDeleteButton';
|
||||||
import { useLayerPositivePrompt } from 'features/controlLayers/hooks/layerStateHooks';
|
import { useLayerPositivePrompt } from 'features/controlLayers/hooks/layerStateHooks';
|
||||||
import { rgLayerPositivePromptChanged } from 'features/controlLayers/store/controlLayersSlice';
|
import { regionalGuidancePositivePromptChanged } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
||||||
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
||||||
import { PromptPopover } from 'features/prompt/PromptPopover';
|
import { PromptPopover } from 'features/prompt/PromptPopover';
|
||||||
@ -21,7 +21,7 @@ export const RGLayerPositivePrompt = memo(({ layerId }: Props) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const _onChange = useCallback(
|
const _onChange = useCallback(
|
||||||
(v: string) => {
|
(v: string) => {
|
||||||
dispatch(rgLayerPositivePromptChanged({ layerId, prompt: v }));
|
dispatch(regionalGuidancePositivePromptChanged({ layerId, prompt: v }));
|
||||||
},
|
},
|
||||||
[dispatch, layerId]
|
[dispatch, layerId]
|
||||||
);
|
);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { IconButton, Tooltip } from '@invoke-ai/ui-library';
|
import { IconButton, Tooltip } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import {
|
import {
|
||||||
rgLayerNegativePromptChanged,
|
regionalGuidanceNegativePromptChanged,
|
||||||
rgLayerPositivePromptChanged,
|
regionalGuidancePositivePromptChanged,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -18,9 +18,9 @@ export const RGLayerPromptDeleteButton = memo(({ layerId, polarity }: Props) =>
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const onClick = useCallback(() => {
|
const onClick = useCallback(() => {
|
||||||
if (polarity === 'positive') {
|
if (polarity === 'positive') {
|
||||||
dispatch(rgLayerPositivePromptChanged({ layerId, prompt: null }));
|
dispatch(regionalGuidancePositivePromptChanged({ layerId, prompt: null }));
|
||||||
} else {
|
} else {
|
||||||
dispatch(rgLayerNegativePromptChanged({ layerId, prompt: null }));
|
dispatch(regionalGuidanceNegativePromptChanged({ layerId, prompt: null }));
|
||||||
}
|
}
|
||||||
}, [dispatch, layerId, polarity]);
|
}, [dispatch, layerId, polarity]);
|
||||||
return (
|
return (
|
||||||
|
@ -2,8 +2,8 @@ import { $alt, $ctrl, $meta, $shift, Box, Flex, Heading } from '@invoke-ai/ui-li
|
|||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { logger } from 'app/logging/logger';
|
import { logger } from 'app/logging/logger';
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
||||||
import { HeadsUpDisplay } from 'features/controlLayers/components/HeadsUpDisplay';
|
import { HeadsUpDisplay } from 'features/controlLayers/components/HeadsUpDisplay';
|
||||||
import {
|
import {
|
||||||
BRUSH_SPACING_PCT,
|
BRUSH_SPACING_PCT,
|
||||||
@ -15,16 +15,16 @@ import { setStageEventHandlers } from 'features/controlLayers/konva/events';
|
|||||||
import { debouncedRenderers, renderers as normalRenderers } from 'features/controlLayers/konva/renderers/layers';
|
import { debouncedRenderers, renderers as normalRenderers } from 'features/controlLayers/konva/renderers/layers';
|
||||||
import {
|
import {
|
||||||
$bbox,
|
$bbox,
|
||||||
$brushColor,
|
|
||||||
$brushSize,
|
|
||||||
$brushSpacingPx,
|
$brushSpacingPx,
|
||||||
|
$brushWidth,
|
||||||
|
$fill,
|
||||||
|
$invertScroll,
|
||||||
$isDrawing,
|
$isDrawing,
|
||||||
$isMouseDown,
|
$isMouseDown,
|
||||||
$lastAddedPoint,
|
$lastAddedPoint,
|
||||||
$lastCursorPos,
|
$lastCursorPos,
|
||||||
$lastMouseDownPos,
|
$lastMouseDownPos,
|
||||||
$selectedLayer,
|
$selectedLayer,
|
||||||
$shouldInvertBrushSizeScrollDirection,
|
|
||||||
$spaceKey,
|
$spaceKey,
|
||||||
$stageAttrs,
|
$stageAttrs,
|
||||||
$tool,
|
$tool,
|
||||||
@ -37,15 +37,16 @@ import {
|
|||||||
layerTranslated,
|
layerTranslated,
|
||||||
linePointsAdded,
|
linePointsAdded,
|
||||||
rectAdded,
|
rectAdded,
|
||||||
selectControlLayersSlice,
|
selectCanvasV2Slice,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
|
import { selectLayersSlice } from 'features/controlLayers/store/layersSlice';
|
||||||
|
import { selectRegionalGuidanceSlice } from 'features/controlLayers/store/regionalGuidanceSlice';
|
||||||
import type {
|
import type {
|
||||||
AddBrushLineArg,
|
AddBrushLineArg,
|
||||||
AddEraserLineArg,
|
AddEraserLineArg,
|
||||||
AddPointToLineArg,
|
AddPointToLineArg,
|
||||||
AddRectShapeArg,
|
AddRectShapeArg,
|
||||||
} from 'features/controlLayers/store/types';
|
} from 'features/controlLayers/store/types';
|
||||||
import { isRegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import type { IRect } from 'konva/lib/types';
|
import type { IRect } from 'konva/lib/types';
|
||||||
import { clamp } from 'lodash-es';
|
import { clamp } from 'lodash-es';
|
||||||
@ -60,26 +61,26 @@ Konva.showWarnings = false;
|
|||||||
|
|
||||||
const log = logger('controlLayers');
|
const log = logger('controlLayers');
|
||||||
|
|
||||||
const selectBrushColor = createMemoizedSelector(selectControlLayersSlice, (controlLayers) => {
|
const selectBrushColor = createSelector(
|
||||||
const layer = controlLayers.present.layers
|
selectCanvasV2Slice,
|
||||||
.filter(isRegionalGuidanceLayer)
|
selectLayersSlice,
|
||||||
.find((l) => l.id === controlLayers.present.selectedLayerId);
|
selectRegionalGuidanceSlice,
|
||||||
|
(canvas, layers, regionalGuidance) => {
|
||||||
|
const rg = regionalGuidance.regions.find((i) => i.id === canvas.lastSelectedItem?.id);
|
||||||
|
|
||||||
if (layer) {
|
if (rg) {
|
||||||
return { ...layer.previewColor, a: controlLayers.present.globalMaskLayerOpacity };
|
return rgbaColorToString({ ...rg.fill, a: regionalGuidance.opacity });
|
||||||
}
|
}
|
||||||
|
|
||||||
return controlLayers.present.brushColor;
|
return rgbaColorToString(canvas.tool.fill);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const selectSelectedLayer = createSelector(selectControlLayersSlice, (controlLayers) => {
|
const selectSelectedLayer = createSelector(selectCanvasV2Slice, (controlLayers) => {
|
||||||
return controlLayers.present.layers.find((l) => l.id === controlLayers.present.selectedLayerId) ?? null;
|
return controlLayers.present.layers.find((l) => l.id === controlLayers.present.selectedLayerId) ?? null;
|
||||||
});
|
});
|
||||||
|
|
||||||
const selectLayerCount = createSelector(
|
const selectLayerCount = createSelector(selectCanvasV2Slice, (controlLayers) => controlLayers.present.layers.length);
|
||||||
selectControlLayersSlice,
|
|
||||||
(controlLayers) => controlLayers.present.layers.length
|
|
||||||
);
|
|
||||||
|
|
||||||
const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null, asPreview: boolean) => {
|
const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null, asPreview: boolean) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@ -100,11 +101,11 @@ const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null,
|
|||||||
);
|
);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
$brushColor.set(brushColor);
|
$fill.set(brushColor);
|
||||||
$brushSize.set(state.brushSize);
|
$brushWidth.set(state.brushSize);
|
||||||
$brushSpacingPx.set(brushSpacingPx);
|
$brushSpacingPx.set(brushSpacingPx);
|
||||||
$selectedLayer.set(selectedLayer);
|
$selectedLayer.set(selectedLayer);
|
||||||
$shouldInvertBrushSizeScrollDirection.set(shouldInvertBrushSizeScrollDirection);
|
$invertScroll.set(shouldInvertBrushSizeScrollDirection);
|
||||||
$bbox.set(state.bbox);
|
$bbox.set(state.bbox);
|
||||||
}, [
|
}, [
|
||||||
brushSpacingPx,
|
brushSpacingPx,
|
||||||
@ -196,8 +197,8 @@ const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null,
|
|||||||
setIsDrawing: $isDrawing.set,
|
setIsDrawing: $isDrawing.set,
|
||||||
getIsMouseDown: $isMouseDown.get,
|
getIsMouseDown: $isMouseDown.get,
|
||||||
setIsMouseDown: $isMouseDown.set,
|
setIsMouseDown: $isMouseDown.set,
|
||||||
getBrushColor: $brushColor.get,
|
getBrushColor: $fill.get,
|
||||||
getBrushSize: $brushSize.get,
|
getBrushSize: $brushWidth.get,
|
||||||
getBrushSpacingPx: $brushSpacingPx.get,
|
getBrushSpacingPx: $brushSpacingPx.get,
|
||||||
getSelectedLayer: $selectedLayer.get,
|
getSelectedLayer: $selectedLayer.get,
|
||||||
getLastAddedPoint: $lastAddedPoint.get,
|
getLastAddedPoint: $lastAddedPoint.get,
|
||||||
@ -206,7 +207,7 @@ const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null,
|
|||||||
setLastCursorPos: $lastCursorPos.set,
|
setLastCursorPos: $lastCursorPos.set,
|
||||||
getLastMouseDownPos: $lastMouseDownPos.get,
|
getLastMouseDownPos: $lastMouseDownPos.get,
|
||||||
setLastMouseDownPos: $lastMouseDownPos.set,
|
setLastMouseDownPos: $lastMouseDownPos.set,
|
||||||
getShouldInvert: $shouldInvertBrushSizeScrollDirection.get,
|
getShouldInvert: $invertScroll.get,
|
||||||
getSpaceKey: $spaceKey.get,
|
getSpaceKey: $spaceKey.get,
|
||||||
setStageAttrs: $stageAttrs.set,
|
setStageAttrs: $stageAttrs.set,
|
||||||
onBrushSizeChanged,
|
onBrushSizeChanged,
|
||||||
|
@ -5,7 +5,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|||||||
import {
|
import {
|
||||||
$tool,
|
$tool,
|
||||||
layerReset,
|
layerReset,
|
||||||
selectControlLayersSlice,
|
selectCanvasV2Slice,
|
||||||
selectedLayerDeleted,
|
selectedLayerDeleted,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
@ -20,7 +20,7 @@ import {
|
|||||||
PiRectangleBold,
|
PiRectangleBold,
|
||||||
} from 'react-icons/pi';
|
} from 'react-icons/pi';
|
||||||
|
|
||||||
const selectIsDisabled = createSelector(selectControlLayersSlice, (controlLayers) => {
|
const selectIsDisabled = createSelector(selectCanvasV2Slice, (controlLayers) => {
|
||||||
const selectedLayer = controlLayers.present.layers.find((l) => l.id === controlLayers.present.selectedLayerId);
|
const selectedLayer = controlLayers.present.layers.find((l) => l.id === controlLayers.present.selectedLayerId);
|
||||||
return selectedLayer?.type !== 'regional_guidance_layer' && selectedLayer?.type !== 'raster_layer';
|
return selectedLayer?.type !== 'regional_guidance_layer' && selectedLayer?.type !== 'raster_layer';
|
||||||
});
|
});
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import {
|
import {
|
||||||
caLayerAdded,
|
controlAdapterAdded,
|
||||||
iiLayerAdded,
|
iiLayerAdded,
|
||||||
ipaLayerAdded,
|
ipAdapterAdded,
|
||||||
rgLayerIPAdapterAdded,
|
regionalGuidanceIPAdapterAdded,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { isInitialImageLayer } from 'features/controlLayers/store/types';
|
import { isInitialImageLayer } from 'features/controlLayers/store/types';
|
||||||
import {
|
import {
|
||||||
@ -46,7 +46,7 @@ export const useAddCALayer = () => {
|
|||||||
processorConfig,
|
processorConfig,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch(caLayerAdded(controlAdapter));
|
dispatch(controlAdapterAdded(controlAdapter));
|
||||||
}, [dispatch, model, baseModel]);
|
}, [dispatch, model, baseModel]);
|
||||||
|
|
||||||
return [addCALayer, isDisabled] as const;
|
return [addCALayer, isDisabled] as const;
|
||||||
@ -70,7 +70,7 @@ export const useAddIPALayer = () => {
|
|||||||
const ipAdapter = buildIPAdapter(id, {
|
const ipAdapter = buildIPAdapter(id, {
|
||||||
model: zModelIdentifierField.parse(model),
|
model: zModelIdentifierField.parse(model),
|
||||||
});
|
});
|
||||||
dispatch(ipaLayerAdded(ipAdapter));
|
dispatch(ipAdapterAdded(ipAdapter));
|
||||||
}, [dispatch, model]);
|
}, [dispatch, model]);
|
||||||
|
|
||||||
return [addIPALayer, isDisabled] as const;
|
return [addIPALayer, isDisabled] as const;
|
||||||
@ -94,7 +94,7 @@ export const useAddIPAdapterToIPALayer = (layerId: string) => {
|
|||||||
const ipAdapter = buildIPAdapter(id, {
|
const ipAdapter = buildIPAdapter(id, {
|
||||||
model: zModelIdentifierField.parse(model),
|
model: zModelIdentifierField.parse(model),
|
||||||
});
|
});
|
||||||
dispatch(rgLayerIPAdapterAdded({ layerId, ipAdapter }));
|
dispatch(regionalGuidanceIPAdapterAdded({ layerId, ipAdapter }));
|
||||||
}, [dispatch, model, layerId]);
|
}, [dispatch, model, layerId]);
|
||||||
|
|
||||||
return [addIPAdapter, isDisabled] as const;
|
return [addIPAdapter, isDisabled] as const;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { selectControlLayersSlice } from 'features/controlLayers/store/controlLayersSlice';
|
import { selectCanvasV2Slice } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { isControlAdapterLayer, isRegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
import { isControlAdapterLayer, isRegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { assert } from 'tsafe';
|
import { assert } from 'tsafe';
|
||||||
@ -9,7 +9,7 @@ import { assert } from 'tsafe';
|
|||||||
export const useLayerPositivePrompt = (layerId: string) => {
|
export const useLayerPositivePrompt = (layerId: string) => {
|
||||||
const selectLayer = useMemo(
|
const selectLayer = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createSelector(selectControlLayersSlice, (controlLayers) => {
|
createSelector(selectCanvasV2Slice, (controlLayers) => {
|
||||||
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
||||||
assert(isRegionalGuidanceLayer(layer), `Layer ${layerId} not found or not an RP layer`);
|
assert(isRegionalGuidanceLayer(layer), `Layer ${layerId} not found or not an RP layer`);
|
||||||
assert(layer.positivePrompt !== null, `Layer ${layerId} does not have a positive prompt`);
|
assert(layer.positivePrompt !== null, `Layer ${layerId} does not have a positive prompt`);
|
||||||
@ -24,7 +24,7 @@ export const useLayerPositivePrompt = (layerId: string) => {
|
|||||||
export const useLayerNegativePrompt = (layerId: string) => {
|
export const useLayerNegativePrompt = (layerId: string) => {
|
||||||
const selectLayer = useMemo(
|
const selectLayer = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createSelector(selectControlLayersSlice, (controlLayers) => {
|
createSelector(selectCanvasV2Slice, (controlLayers) => {
|
||||||
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
||||||
assert(isRegionalGuidanceLayer(layer), `Layer ${layerId} not found or not an RP layer`);
|
assert(isRegionalGuidanceLayer(layer), `Layer ${layerId} not found or not an RP layer`);
|
||||||
assert(layer.negativePrompt !== null, `Layer ${layerId} does not have a negative prompt`);
|
assert(layer.negativePrompt !== null, `Layer ${layerId} does not have a negative prompt`);
|
||||||
@ -39,7 +39,7 @@ export const useLayerNegativePrompt = (layerId: string) => {
|
|||||||
export const useLayerIsEnabled = (layerId: string) => {
|
export const useLayerIsEnabled = (layerId: string) => {
|
||||||
const selectLayer = useMemo(
|
const selectLayer = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createSelector(selectControlLayersSlice, (controlLayers) => {
|
createSelector(selectCanvasV2Slice, (controlLayers) => {
|
||||||
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
||||||
assert(layer, `Layer ${layerId} not found`);
|
assert(layer, `Layer ${layerId} not found`);
|
||||||
return layer.isEnabled;
|
return layer.isEnabled;
|
||||||
@ -53,7 +53,7 @@ export const useLayerIsEnabled = (layerId: string) => {
|
|||||||
export const useLayerType = (layerId: string) => {
|
export const useLayerType = (layerId: string) => {
|
||||||
const selectLayer = useMemo(
|
const selectLayer = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createSelector(selectControlLayersSlice, (controlLayers) => {
|
createSelector(selectCanvasV2Slice, (controlLayers) => {
|
||||||
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
||||||
assert(layer, `Layer ${layerId} not found`);
|
assert(layer, `Layer ${layerId} not found`);
|
||||||
return layer.type;
|
return layer.type;
|
||||||
@ -67,7 +67,7 @@ export const useLayerType = (layerId: string) => {
|
|||||||
export const useCALayerOpacity = (layerId: string) => {
|
export const useCALayerOpacity = (layerId: string) => {
|
||||||
const selectLayer = useMemo(
|
const selectLayer = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createMemoizedSelector(selectControlLayersSlice, (controlLayers) => {
|
createMemoizedSelector(selectCanvasV2Slice, (controlLayers) => {
|
||||||
const layer = controlLayers.present.layers.filter(isControlAdapterLayer).find((l) => l.id === layerId);
|
const layer = controlLayers.present.layers.filter(isControlAdapterLayer).find((l) => l.id === layerId);
|
||||||
assert(layer, `Layer ${layerId} not found`);
|
assert(layer, `Layer ${layerId} not found`);
|
||||||
return { opacity: Math.round(layer.opacity * 100), isFilterEnabled: layer.isFilterEnabled };
|
return { opacity: Math.round(layer.opacity * 100), isFilterEnabled: layer.isFilterEnabled };
|
||||||
|
@ -6,7 +6,7 @@ import type {
|
|||||||
AddEraserLineArg,
|
AddEraserLineArg,
|
||||||
AddPointToLineArg,
|
AddPointToLineArg,
|
||||||
AddRectShapeArg,
|
AddRectShapeArg,
|
||||||
Layer,
|
LayerData,
|
||||||
StageAttrs,
|
StageAttrs,
|
||||||
Tool,
|
Tool,
|
||||||
} from 'features/controlLayers/store/types';
|
} from 'features/controlLayers/store/types';
|
||||||
@ -38,7 +38,7 @@ type Arg = {
|
|||||||
getBrushColor: () => RgbaColor;
|
getBrushColor: () => RgbaColor;
|
||||||
getBrushSize: () => number;
|
getBrushSize: () => number;
|
||||||
getBrushSpacingPx: () => number;
|
getBrushSpacingPx: () => number;
|
||||||
getSelectedLayer: () => Layer | null;
|
getSelectedLayer: () => LayerData | null;
|
||||||
getShouldInvert: () => boolean;
|
getShouldInvert: () => boolean;
|
||||||
getSpaceKey: () => boolean;
|
getSpaceKey: () => boolean;
|
||||||
onBrushLineAdded: (arg: AddBrushLineArg) => void;
|
onBrushLineAdded: (arg: AddBrushLineArg) => void;
|
||||||
@ -72,7 +72,7 @@ const updateLastCursorPos = (stage: Konva.Stage, setLastCursorPos: Arg['setLastC
|
|||||||
* @param onPointAddedToLine The callback to add a point to a line
|
* @param onPointAddedToLine The callback to add a point to a line
|
||||||
*/
|
*/
|
||||||
const maybeAddNextPoint = (
|
const maybeAddNextPoint = (
|
||||||
layerId: string,
|
selectedLayer: LayerData,
|
||||||
currentPos: Vector2d,
|
currentPos: Vector2d,
|
||||||
getLastAddedPoint: Arg['getLastAddedPoint'],
|
getLastAddedPoint: Arg['getLastAddedPoint'],
|
||||||
setLastAddedPoint: Arg['setLastAddedPoint'],
|
setLastAddedPoint: Arg['setLastAddedPoint'],
|
||||||
@ -88,7 +88,7 @@ const maybeAddNextPoint = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
setLastAddedPoint(currentPos);
|
setLastAddedPoint(currentPos);
|
||||||
onPointAddedToLine({ layerId, point: [currentPos.x, currentPos.y] });
|
onPointAddedToLine({ layerId, point: [currentPos.x - selectedLayer.x, currentPos.y - selectedLayer.y] });
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setStageEventHandlers = ({
|
export const setStageEventHandlers = ({
|
||||||
@ -158,7 +158,7 @@ export const setStageEventHandlers = ({
|
|||||||
if (tool === 'brush') {
|
if (tool === 'brush') {
|
||||||
onBrushLineAdded({
|
onBrushLineAdded({
|
||||||
layerId: selectedLayer.id,
|
layerId: selectedLayer.id,
|
||||||
points: [pos.x, pos.y, pos.x, pos.y],
|
points: [pos.x - selectedLayer.x, pos.y - selectedLayer.y, pos.x - selectedLayer.x, pos.y - selectedLayer.y],
|
||||||
color: selectedLayer.type === 'raster_layer' ? getBrushColor() : DEFAULT_RGBA_COLOR,
|
color: selectedLayer.type === 'raster_layer' ? getBrushColor() : DEFAULT_RGBA_COLOR,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -166,7 +166,7 @@ export const setStageEventHandlers = ({
|
|||||||
if (tool === 'eraser') {
|
if (tool === 'eraser') {
|
||||||
onEraserLineAdded({
|
onEraserLineAdded({
|
||||||
layerId: selectedLayer.id,
|
layerId: selectedLayer.id,
|
||||||
points: [pos.x, pos.y, pos.x, pos.y],
|
points: [pos.x - selectedLayer.x, pos.y - selectedLayer.y, pos.x - selectedLayer.x, pos.y - selectedLayer.y],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,7 +262,7 @@ export const setStageEventHandlers = ({
|
|||||||
// Start a new line
|
// Start a new line
|
||||||
onBrushLineAdded({
|
onBrushLineAdded({
|
||||||
layerId: selectedLayer.id,
|
layerId: selectedLayer.id,
|
||||||
points: [pos.x, pos.y, pos.x, pos.y],
|
points: [pos.x - selectedLayer.x, pos.y - selectedLayer.y, pos.x - selectedLayer.x, pos.y - selectedLayer.y],
|
||||||
color: selectedLayer.type === 'raster_layer' ? getBrushColor() : DEFAULT_RGBA_COLOR,
|
color: selectedLayer.type === 'raster_layer' ? getBrushColor() : DEFAULT_RGBA_COLOR,
|
||||||
});
|
});
|
||||||
setIsDrawing(true);
|
setIsDrawing(true);
|
||||||
@ -282,7 +282,10 @@ export const setStageEventHandlers = ({
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// Start a new line
|
// Start a new line
|
||||||
onEraserLineAdded({ layerId: selectedLayer.id, points: [pos.x, pos.y, pos.x, pos.y] });
|
onEraserLineAdded({
|
||||||
|
layerId: selectedLayer.id,
|
||||||
|
points: [pos.x - selectedLayer.x, pos.y - selectedLayer.y, pos.x - selectedLayer.x, pos.y - selectedLayer.y],
|
||||||
|
});
|
||||||
setIsDrawing(true);
|
setIsDrawing(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,8 @@ export const RASTER_LAYER_ERASER_LINE_NAME = 'raster_layer.eraser_line';
|
|||||||
export const RASTER_LAYER_RECT_SHAPE_NAME = 'raster_layer.rect_shape';
|
export const RASTER_LAYER_RECT_SHAPE_NAME = 'raster_layer.rect_shape';
|
||||||
export const RASTER_LAYER_IMAGE_NAME = 'raster_layer.image';
|
export const RASTER_LAYER_IMAGE_NAME = 'raster_layer.image';
|
||||||
|
|
||||||
|
export const INPAINT_MASK_LAYER_NAME = 'inpaint_mask_layer';
|
||||||
|
|
||||||
// Getters for non-singleton layer and object IDs
|
// Getters for non-singleton layer and object IDs
|
||||||
export const getRGLayerId = (layerId: string) => `${RG_LAYER_NAME}_${layerId}`;
|
export const getRGLayerId = (layerId: string) => `${RG_LAYER_NAME}_${layerId}`;
|
||||||
export const getRasterLayerId = (layerId: string) => `${RASTER_LAYER_NAME}_${layerId}`;
|
export const getRasterLayerId = (layerId: string) => `${RASTER_LAYER_NAME}_${layerId}`;
|
||||||
|
@ -6,8 +6,8 @@ import {
|
|||||||
RG_LAYER_OBJECT_GROUP_NAME,
|
RG_LAYER_OBJECT_GROUP_NAME,
|
||||||
} from 'features/controlLayers/konva/naming';
|
} from 'features/controlLayers/konva/naming';
|
||||||
import { createBboxRect } from 'features/controlLayers/konva/renderers/objects';
|
import { createBboxRect } from 'features/controlLayers/konva/renderers/objects';
|
||||||
import type { Layer } from 'features/controlLayers/store/types';
|
import type { LayerData } from 'features/controlLayers/store/types';
|
||||||
import { isRegionalGuidanceLayer, isRGOrRasterlayer } from 'features/controlLayers/store/types';
|
import { isRegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import type { IRect } from 'konva/lib/types';
|
import type { IRect } from 'konva/lib/types';
|
||||||
import { assert } from 'tsafe';
|
import { assert } from 'tsafe';
|
||||||
@ -185,10 +185,10 @@ const filterRasterChildren = (node: Konva.Node): boolean => node.name() === RAST
|
|||||||
*/
|
*/
|
||||||
export const updateBboxes = (
|
export const updateBboxes = (
|
||||||
stage: Konva.Stage,
|
stage: Konva.Stage,
|
||||||
layerStates: Layer[],
|
layerStates: LayerData[],
|
||||||
onBboxChanged: (layerId: string, bbox: IRect | null) => void
|
onBboxChanged: (layerId: string, bbox: IRect | null) => void
|
||||||
): void => {
|
): void => {
|
||||||
for (const layerState of layerStates.filter(isRGOrRasterlayer)) {
|
for (const layerState of layerStates) {
|
||||||
const konvaLayer = stage.findOne<Konva.Layer>(`#${layerState.id}`);
|
const konvaLayer = stage.findOne<Konva.Layer>(`#${layerState.id}`);
|
||||||
assert(konvaLayer, `Layer ${layerState.id} not found in stage`);
|
assert(konvaLayer, `Layer ${layerState.id} not found in stage`);
|
||||||
// We only need to recalculate the bbox if the layer has changed
|
// We only need to recalculate the bbox if the layer has changed
|
||||||
|
@ -7,10 +7,11 @@ import { renderBboxPreview, renderToolPreview } from 'features/controlLayers/kon
|
|||||||
import { renderRasterLayer } from 'features/controlLayers/konva/renderers/rasterLayer';
|
import { renderRasterLayer } from 'features/controlLayers/konva/renderers/rasterLayer';
|
||||||
import { renderRGLayer } from 'features/controlLayers/konva/renderers/rgLayer';
|
import { renderRGLayer } from 'features/controlLayers/konva/renderers/rgLayer';
|
||||||
import { mapId, selectRenderableLayers } from 'features/controlLayers/konva/util';
|
import { mapId, selectRenderableLayers } from 'features/controlLayers/konva/util';
|
||||||
import type { Layer, Tool } from 'features/controlLayers/store/types';
|
import type { LayerData, Tool } from 'features/controlLayers/store/types';
|
||||||
import {
|
import {
|
||||||
isControlAdapterLayer,
|
isControlAdapterLayer,
|
||||||
isInitialImageLayer,
|
isInitialImageLayer,
|
||||||
|
isInpaintMaskLayer,
|
||||||
isRasterLayer,
|
isRasterLayer,
|
||||||
isRegionalGuidanceLayer,
|
isRegionalGuidanceLayer,
|
||||||
isRenderableLayer,
|
isRenderableLayer,
|
||||||
@ -34,7 +35,7 @@ import type { ImageDTO } from 'services/api/types';
|
|||||||
*/
|
*/
|
||||||
const renderLayers = (
|
const renderLayers = (
|
||||||
stage: Konva.Stage,
|
stage: Konva.Stage,
|
||||||
layerStates: Layer[],
|
layerStates: LayerData[],
|
||||||
globalMaskLayerOpacity: number,
|
globalMaskLayerOpacity: number,
|
||||||
tool: Tool,
|
tool: Tool,
|
||||||
getImageDTO: (imageName: string) => Promise<ImageDTO | null>,
|
getImageDTO: (imageName: string) => Promise<ImageDTO | null>,
|
||||||
@ -52,15 +53,14 @@ const renderLayers = (
|
|||||||
for (const layer of layerStates) {
|
for (const layer of layerStates) {
|
||||||
if (isRegionalGuidanceLayer(layer)) {
|
if (isRegionalGuidanceLayer(layer)) {
|
||||||
renderRGLayer(stage, layer, globalMaskLayerOpacity, tool, zIndex, onLayerPosChanged);
|
renderRGLayer(stage, layer, globalMaskLayerOpacity, tool, zIndex, onLayerPosChanged);
|
||||||
}
|
} else if (isControlAdapterLayer(layer)) {
|
||||||
if (isControlAdapterLayer(layer)) {
|
|
||||||
renderCALayer(stage, layer, zIndex, getImageDTO);
|
renderCALayer(stage, layer, zIndex, getImageDTO);
|
||||||
}
|
} else if (isInitialImageLayer(layer)) {
|
||||||
if (isInitialImageLayer(layer)) {
|
|
||||||
renderIILayer(stage, layer, zIndex, getImageDTO);
|
renderIILayer(stage, layer, zIndex, getImageDTO);
|
||||||
}
|
} else if (isRasterLayer(layer)) {
|
||||||
if (isRasterLayer(layer)) {
|
|
||||||
renderRasterLayer(stage, layer, tool, zIndex, onLayerPosChanged);
|
renderRasterLayer(stage, layer, tool, zIndex, onLayerPosChanged);
|
||||||
|
} else if (isInpaintMaskLayer(layer)) {
|
||||||
|
//
|
||||||
}
|
}
|
||||||
// IP Adapter layers are not rendered
|
// IP Adapter layers are not rendered
|
||||||
// Increment the z-index for the tool layer
|
// Increment the z-index for the tool layer
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
LAYER_BBOX_NAME,
|
LAYER_BBOX_NAME,
|
||||||
PREVIEW_GENERATION_BBOX_DUMMY_RECT,
|
PREVIEW_GENERATION_BBOX_DUMMY_RECT,
|
||||||
} from 'features/controlLayers/konva/naming';
|
} from 'features/controlLayers/konva/naming';
|
||||||
import type { BrushLine, EraserLine, ImageObject, Layer, RectShape } from 'features/controlLayers/store/types';
|
import type { BrushLine, EraserLine, ImageObject, LayerData, RectShape } from 'features/controlLayers/store/types';
|
||||||
import { DEFAULT_RGBA_COLOR } from 'features/controlLayers/store/types';
|
import { DEFAULT_RGBA_COLOR } from 'features/controlLayers/store/types';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
@ -177,7 +177,7 @@ export const createImageObjectGroup = async (
|
|||||||
* @param layerState The layer state for the layer to create the bounding box for
|
* @param layerState The layer state for the layer to create the bounding box for
|
||||||
* @param konvaLayer The konva layer to attach the bounding box to
|
* @param konvaLayer The konva layer to attach the bounding box to
|
||||||
*/
|
*/
|
||||||
export const createBboxRect = (layerState: Layer, konvaLayer: Konva.Layer): Konva.Rect => {
|
export const createBboxRect = (layerState: LayerData, konvaLayer: Konva.Layer): Konva.Rect => {
|
||||||
const rect = new Konva.Rect({
|
const rect = new Konva.Rect({
|
||||||
id: getLayerBboxId(layerState.id),
|
id: getLayerBboxId(layerState.id),
|
||||||
name: LAYER_BBOX_NAME,
|
name: LAYER_BBOX_NAME,
|
||||||
|
@ -18,7 +18,7 @@ import {
|
|||||||
PREVIEW_TOOL_GROUP_ID,
|
PREVIEW_TOOL_GROUP_ID,
|
||||||
} from 'features/controlLayers/konva/naming';
|
} from 'features/controlLayers/konva/naming';
|
||||||
import { selectRenderableLayers } from 'features/controlLayers/konva/util';
|
import { selectRenderableLayers } from 'features/controlLayers/konva/util';
|
||||||
import type { Layer, RgbaColor, Tool } from 'features/controlLayers/store/types';
|
import type { LayerData, RgbaColor, Tool } from 'features/controlLayers/store/types';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import type { IRect, Vector2d } from 'konva/lib/types';
|
import type { IRect, Vector2d } from 'konva/lib/types';
|
||||||
import { atom } from 'nanostores';
|
import { atom } from 'nanostores';
|
||||||
@ -338,7 +338,7 @@ export const renderToolPreview = (
|
|||||||
stage: Konva.Stage,
|
stage: Konva.Stage,
|
||||||
tool: Tool,
|
tool: Tool,
|
||||||
brushColor: RgbaColor,
|
brushColor: RgbaColor,
|
||||||
selectedLayerType: Layer['type'] | null,
|
selectedLayerType: LayerData['type'] | null,
|
||||||
globalMaskLayerOpacity: number,
|
globalMaskLayerOpacity: number,
|
||||||
cursorPos: Vector2d | null,
|
cursorPos: Vector2d | null,
|
||||||
lastMouseDownPos: Vector2d | null,
|
lastMouseDownPos: Vector2d | null,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
CA_LAYER_NAME,
|
CA_LAYER_NAME,
|
||||||
INITIAL_IMAGE_LAYER_NAME,
|
INITIAL_IMAGE_LAYER_NAME,
|
||||||
|
INPAINT_MASK_LAYER_NAME,
|
||||||
RASTER_LAYER_BRUSH_LINE_NAME,
|
RASTER_LAYER_BRUSH_LINE_NAME,
|
||||||
RASTER_LAYER_ERASER_LINE_NAME,
|
RASTER_LAYER_ERASER_LINE_NAME,
|
||||||
RASTER_LAYER_IMAGE_NAME,
|
RASTER_LAYER_IMAGE_NAME,
|
||||||
@ -98,7 +99,8 @@ export const selectRenderableLayers = (node: Konva.Node): boolean =>
|
|||||||
node.name() === RG_LAYER_NAME ||
|
node.name() === RG_LAYER_NAME ||
|
||||||
node.name() === CA_LAYER_NAME ||
|
node.name() === CA_LAYER_NAME ||
|
||||||
node.name() === INITIAL_IMAGE_LAYER_NAME ||
|
node.name() === INITIAL_IMAGE_LAYER_NAME ||
|
||||||
node.name() === RASTER_LAYER_NAME;
|
node.name() === RASTER_LAYER_NAME ||
|
||||||
|
node.name() === INPAINT_MASK_LAYER_NAME;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Konva selection callback to select RG mask objects. This includes lines and rects.
|
* Konva selection callback to select RG mask objects. This includes lines and rects.
|
||||||
|
@ -0,0 +1,282 @@
|
|||||||
|
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||||
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
|
import type { PersistConfig, RootState } from 'app/store/store';
|
||||||
|
import { moveOneToEnd, moveOneToStart, moveToEnd, moveToStart } from 'common/util/arrayUtils';
|
||||||
|
import type { ControlModeV2, ProcessorConfig } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { buildControlAdapterProcessorV2, imageDTOToImageWithDims } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||||
|
import type { IRect } from 'konva/lib/types';
|
||||||
|
import { isEqual } from 'lodash-es';
|
||||||
|
import type { ControlNetModelConfig, ImageDTO, T2IAdapterModelConfig } from 'services/api/types';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
import type { ControlAdapterConfig, ControlAdapterData, Filter } from './types';
|
||||||
|
|
||||||
|
type ControlAdaptersV2State = {
|
||||||
|
_version: 1;
|
||||||
|
controlAdapters: ControlAdapterData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialState: ControlAdaptersV2State = {
|
||||||
|
_version: 1,
|
||||||
|
controlAdapters: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectCa = (state: ControlAdaptersV2State, id: string) => state.controlAdapters.find((ca) => ca.id === id);
|
||||||
|
|
||||||
|
export const controlAdaptersV2Slice = createSlice({
|
||||||
|
name: 'controlAdaptersV2',
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
caAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<{ id: string; config: ControlAdapterConfig }>) => {
|
||||||
|
const { id, config } = action.payload;
|
||||||
|
state.controlAdapters.push({
|
||||||
|
id,
|
||||||
|
type: 'control_adapter',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
bbox: null,
|
||||||
|
bboxNeedsUpdate: false,
|
||||||
|
isEnabled: true,
|
||||||
|
opacity: 1,
|
||||||
|
filter: 'lightness_to_alpha',
|
||||||
|
processorPendingBatchId: null,
|
||||||
|
...config,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
prepare: (config: ControlAdapterConfig) => ({
|
||||||
|
payload: { id: uuidv4(), config },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
caRecalled: (state, action: PayloadAction<{ data: ControlAdapterData }>) => {
|
||||||
|
state.controlAdapters.push(action.payload.data);
|
||||||
|
},
|
||||||
|
caIsEnabledChanged: (state, action: PayloadAction<{ id: string; isEnabled: boolean }>) => {
|
||||||
|
const { id, isEnabled } = action.payload;
|
||||||
|
const ca = selectCa(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.isEnabled = isEnabled;
|
||||||
|
},
|
||||||
|
caTranslated: (state, action: PayloadAction<{ id: string; x: number; y: number }>) => {
|
||||||
|
const { id, x, y } = action.payload;
|
||||||
|
const ca = selectCa(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.x = x;
|
||||||
|
ca.y = y;
|
||||||
|
},
|
||||||
|
caBboxChanged: (state, action: PayloadAction<{ id: string; bbox: IRect | null }>) => {
|
||||||
|
const { id, bbox } = action.payload;
|
||||||
|
const ca = selectCa(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.bbox = bbox;
|
||||||
|
ca.bboxNeedsUpdate = false;
|
||||||
|
},
|
||||||
|
caDeleted: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
state.controlAdapters = state.controlAdapters.filter((ca) => ca.id !== id);
|
||||||
|
},
|
||||||
|
caOpacityChanged: (state, action: PayloadAction<{ id: string; opacity: number }>) => {
|
||||||
|
const { id, opacity } = action.payload;
|
||||||
|
const ca = selectCa(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.opacity = opacity;
|
||||||
|
},
|
||||||
|
caMovedForwardOne: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const ca = selectCa(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveOneToEnd(state.controlAdapters, ca);
|
||||||
|
},
|
||||||
|
caMovedToFront: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const ca = selectCa(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveToEnd(state.controlAdapters, ca);
|
||||||
|
},
|
||||||
|
caMovedBackwardOne: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const ca = selectCa(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveOneToStart(state.controlAdapters, ca);
|
||||||
|
},
|
||||||
|
caMovedToBack: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const ca = selectCa(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveToStart(state.controlAdapters, ca);
|
||||||
|
},
|
||||||
|
caImageChanged: (state, action: PayloadAction<{ id: string; imageDTO: ImageDTO | null }>) => {
|
||||||
|
const { id, imageDTO } = action.payload;
|
||||||
|
const ca = selectCa(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.bbox = null;
|
||||||
|
ca.bboxNeedsUpdate = true;
|
||||||
|
ca.isEnabled = true;
|
||||||
|
if (imageDTO) {
|
||||||
|
const newImage = imageDTOToImageWithDims(imageDTO);
|
||||||
|
if (isEqual(newImage, ca.image)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.image = newImage;
|
||||||
|
ca.processedImage = null;
|
||||||
|
} else {
|
||||||
|
ca.image = null;
|
||||||
|
ca.processedImage = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
caProcessedImageChanged: (state, action: PayloadAction<{ id: string; imageDTO: ImageDTO | null }>) => {
|
||||||
|
const { id, imageDTO } = action.payload;
|
||||||
|
const ca = selectCa(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.bbox = null;
|
||||||
|
ca.bboxNeedsUpdate = true;
|
||||||
|
ca.isEnabled = true;
|
||||||
|
ca.processedImage = imageDTO ? imageDTOToImageWithDims(imageDTO) : null;
|
||||||
|
},
|
||||||
|
caModelChanged: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{
|
||||||
|
id: string;
|
||||||
|
modelConfig: ControlNetModelConfig | T2IAdapterModelConfig | null;
|
||||||
|
}>
|
||||||
|
) => {
|
||||||
|
const { id, modelConfig } = action.payload;
|
||||||
|
const ca = selectCa(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!modelConfig) {
|
||||||
|
ca.model = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.model = zModelIdentifierField.parse(modelConfig);
|
||||||
|
|
||||||
|
// We may need to convert the CA to match the model
|
||||||
|
if (!ca.controlMode && ca.model.type === 'controlnet') {
|
||||||
|
ca.controlMode = 'balanced';
|
||||||
|
} else if (ca.controlMode && ca.model.type === 't2i_adapter') {
|
||||||
|
ca.controlMode = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const candidateProcessorConfig = buildControlAdapterProcessorV2(modelConfig);
|
||||||
|
if (candidateProcessorConfig?.type !== ca.processorConfig?.type) {
|
||||||
|
// The processor has changed. For example, the previous model was a Canny model and the new model is a Depth
|
||||||
|
// model. We need to use the new processor.
|
||||||
|
ca.processedImage = null;
|
||||||
|
ca.processorConfig = candidateProcessorConfig;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
caControlModeChanged: (state, action: PayloadAction<{ id: string; controlMode: ControlModeV2 }>) => {
|
||||||
|
const { id, controlMode } = action.payload;
|
||||||
|
const ca = selectCa(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.controlMode = controlMode;
|
||||||
|
},
|
||||||
|
caProcessorConfigChanged: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{ id: string; processorConfig: ProcessorConfig | null }>
|
||||||
|
) => {
|
||||||
|
const { id, processorConfig } = action.payload;
|
||||||
|
const ca = selectCa(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.processorConfig = processorConfig;
|
||||||
|
if (!processorConfig) {
|
||||||
|
ca.processedImage = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
caFilterChanged: (state, action: PayloadAction<{ id: string; filter: Filter }>) => {
|
||||||
|
const { id, filter } = action.payload;
|
||||||
|
const ca = selectCa(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.filter = filter;
|
||||||
|
},
|
||||||
|
caProcessorPendingBatchIdChanged: (state, action: PayloadAction<{ id: string; batchId: string | null }>) => {
|
||||||
|
const { id, batchId } = action.payload;
|
||||||
|
const ca = selectCa(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.processorPendingBatchId = batchId;
|
||||||
|
},
|
||||||
|
caWeightChanged: (state, action: PayloadAction<{ id: string; weight: number }>) => {
|
||||||
|
const { id, weight } = action.payload;
|
||||||
|
const ca = selectCa(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.weight = weight;
|
||||||
|
},
|
||||||
|
caBeginEndStepPctChanged: (state, action: PayloadAction<{ id: string; beginEndStepPct: [number, number] }>) => {
|
||||||
|
const { id, beginEndStepPct } = action.payload;
|
||||||
|
const ca = selectCa(state, id);
|
||||||
|
if (!ca) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ca.beginEndStepPct = beginEndStepPct;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const {
|
||||||
|
caAdded,
|
||||||
|
caBboxChanged,
|
||||||
|
caDeleted,
|
||||||
|
caIsEnabledChanged,
|
||||||
|
caMovedBackwardOne,
|
||||||
|
caMovedForwardOne,
|
||||||
|
caMovedToBack,
|
||||||
|
caMovedToFront,
|
||||||
|
caOpacityChanged,
|
||||||
|
caTranslated,
|
||||||
|
caRecalled,
|
||||||
|
caImageChanged,
|
||||||
|
caProcessedImageChanged,
|
||||||
|
caModelChanged,
|
||||||
|
caControlModeChanged,
|
||||||
|
caProcessorConfigChanged,
|
||||||
|
caFilterChanged,
|
||||||
|
caProcessorPendingBatchIdChanged,
|
||||||
|
caWeightChanged,
|
||||||
|
caBeginEndStepPctChanged,
|
||||||
|
} = controlAdaptersV2Slice.actions;
|
||||||
|
|
||||||
|
export const selectControlAdaptersV2Slice = (state: RootState) => state.controlAdaptersV2;
|
||||||
|
|
||||||
|
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||||
|
const migrate = (state: any): any => {
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const controlAdaptersV2PersistConfig: PersistConfig<ControlAdaptersV2State> = {
|
||||||
|
name: controlAdaptersV2Slice.name,
|
||||||
|
initialState,
|
||||||
|
migrate,
|
||||||
|
persistDenylist: [],
|
||||||
|
};
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,140 @@
|
|||||||
|
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||||
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
|
import type { PersistConfig, RootState } from 'app/store/store';
|
||||||
|
import type { CLIPVisionModelV2, IPMethodV2 } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { imageDTOToImageWithDims } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||||
|
import type { ImageDTO, IPAdapterModelConfig } from 'services/api/types';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
import type { IPAdapterConfig, IPAdapterData } from './types';
|
||||||
|
|
||||||
|
type IPAdaptersState = {
|
||||||
|
_version: 1;
|
||||||
|
ipAdapters: IPAdapterData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialState: IPAdaptersState = {
|
||||||
|
_version: 1,
|
||||||
|
ipAdapters: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectIpa = (state: IPAdaptersState, id: string) => state.ipAdapters.find((ipa) => ipa.id === id);
|
||||||
|
|
||||||
|
export const ipAdaptersSlice = createSlice({
|
||||||
|
name: 'ipAdapters',
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
ipaAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<{ id: string; config: IPAdapterConfig }>) => {
|
||||||
|
const { id, config } = action.payload;
|
||||||
|
const layer: IPAdapterData = {
|
||||||
|
id,
|
||||||
|
type: 'ip_adapter',
|
||||||
|
isEnabled: true,
|
||||||
|
...config,
|
||||||
|
};
|
||||||
|
state.ipAdapters.push(layer);
|
||||||
|
},
|
||||||
|
prepare: (config: IPAdapterConfig) => ({ payload: { id: uuidv4(), config } }),
|
||||||
|
},
|
||||||
|
ipaRecalled: (state, action: PayloadAction<{ data: IPAdapterData }>) => {
|
||||||
|
state.ipAdapters.push(action.payload.data);
|
||||||
|
},
|
||||||
|
ipaIsEnabledChanged: (state, action: PayloadAction<{ id: string; isEnabled: boolean }>) => {
|
||||||
|
const { id, isEnabled } = action.payload;
|
||||||
|
const ipa = selectIpa(state, id);
|
||||||
|
if (ipa) {
|
||||||
|
ipa.isEnabled = isEnabled;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ipaDeleted: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
state.ipAdapters = state.ipAdapters.filter((ipa) => ipa.id !== action.payload.id);
|
||||||
|
},
|
||||||
|
ipaImageChanged: (state, action: PayloadAction<{ id: string; imageDTO: ImageDTO | null }>) => {
|
||||||
|
const { id, imageDTO } = action.payload;
|
||||||
|
const ipa = selectIpa(state, id);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipa.image = imageDTO ? imageDTOToImageWithDims(imageDTO) : null;
|
||||||
|
},
|
||||||
|
ipaMethodChanged: (state, action: PayloadAction<{ id: string; method: IPMethodV2 }>) => {
|
||||||
|
const { id, method } = action.payload;
|
||||||
|
const ipa = selectIpa(state, id);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipa.method = method;
|
||||||
|
},
|
||||||
|
ipaModelChanged: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{
|
||||||
|
id: string;
|
||||||
|
modelConfig: IPAdapterModelConfig | null;
|
||||||
|
}>
|
||||||
|
) => {
|
||||||
|
const { id, modelConfig } = action.payload;
|
||||||
|
const ipa = selectIpa(state, id);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (modelConfig) {
|
||||||
|
ipa.model = zModelIdentifierField.parse(modelConfig);
|
||||||
|
} else {
|
||||||
|
ipa.model = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ipaCLIPVisionModelChanged: (state, action: PayloadAction<{ id: string; clipVisionModel: CLIPVisionModelV2 }>) => {
|
||||||
|
const { id, clipVisionModel } = action.payload;
|
||||||
|
const ipa = selectIpa(state, id);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipa.clipVisionModel = clipVisionModel;
|
||||||
|
},
|
||||||
|
ipaWeightChanged: (state, action: PayloadAction<{ id: string; weight: number }>) => {
|
||||||
|
const { id, weight } = action.payload;
|
||||||
|
const ipa = selectIpa(state, id);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipa.weight = weight;
|
||||||
|
},
|
||||||
|
ipaBeginEndStepPctChanged: (state, action: PayloadAction<{ id: string; beginEndStepPct: [number, number] }>) => {
|
||||||
|
const { id, beginEndStepPct } = action.payload;
|
||||||
|
const ipa = selectIpa(state, id);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipa.beginEndStepPct = beginEndStepPct;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const {
|
||||||
|
ipaAdded,
|
||||||
|
ipaRecalled,
|
||||||
|
ipaIsEnabledChanged,
|
||||||
|
ipaDeleted,
|
||||||
|
ipaImageChanged,
|
||||||
|
ipaMethodChanged,
|
||||||
|
ipaModelChanged,
|
||||||
|
ipaCLIPVisionModelChanged,
|
||||||
|
ipaWeightChanged,
|
||||||
|
ipaBeginEndStepPctChanged,
|
||||||
|
} = ipAdaptersSlice.actions;
|
||||||
|
|
||||||
|
export const selectIPAdaptersSlice = (state: RootState) => state.ipAdapters;
|
||||||
|
|
||||||
|
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||||
|
const migrate = (state: any): any => {
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ipAdaptersPersistConfig: PersistConfig<IPAdaptersState> = {
|
||||||
|
name: ipAdaptersSlice.name,
|
||||||
|
initialState,
|
||||||
|
migrate,
|
||||||
|
persistDenylist: [],
|
||||||
|
};
|
@ -0,0 +1,268 @@
|
|||||||
|
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||||
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
|
import type { PersistConfig, RootState } from 'app/store/store';
|
||||||
|
import { moveOneToEnd, moveOneToStart, moveToEnd, moveToStart } from 'common/util/arrayUtils';
|
||||||
|
import { getBrushLineId, getEraserLineId, getImageObjectId, getRectShapeId } from 'features/controlLayers/konva/naming';
|
||||||
|
import type { IRect } from 'konva/lib/types';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
AddBrushLineArg,
|
||||||
|
AddEraserLineArg,
|
||||||
|
AddImageObjectArg,
|
||||||
|
AddPointToLineArg,
|
||||||
|
AddRectShapeArg,
|
||||||
|
LayerData,
|
||||||
|
} from './types';
|
||||||
|
import { isLine } from './types';
|
||||||
|
|
||||||
|
type LayersState = {
|
||||||
|
_version: 1;
|
||||||
|
layers: LayerData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialState: LayersState = { _version: 1, layers: [] };
|
||||||
|
const selectLayer = (state: LayersState, id: string) => state.layers.find((layer) => layer.id === id);
|
||||||
|
|
||||||
|
export const layersSlice = createSlice({
|
||||||
|
name: 'layers',
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
layerAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
state.layers.push({
|
||||||
|
id,
|
||||||
|
type: 'layer',
|
||||||
|
isEnabled: true,
|
||||||
|
bbox: null,
|
||||||
|
bboxNeedsUpdate: false,
|
||||||
|
objects: [],
|
||||||
|
opacity: 1,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
prepare: () => ({ payload: { id: uuidv4() } }),
|
||||||
|
},
|
||||||
|
layerRecalled: (state, action: PayloadAction<{ data: LayerData }>) => {
|
||||||
|
state.layers.push(action.payload.data);
|
||||||
|
},
|
||||||
|
layerIsEnabledChanged: (state, action: PayloadAction<{ id: string; isEnabled: boolean }>) => {
|
||||||
|
const { id, isEnabled } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
layer.isEnabled = isEnabled;
|
||||||
|
},
|
||||||
|
layerTranslated: (state, action: PayloadAction<{ id: string; x: number; y: number }>) => {
|
||||||
|
const { id, x, y } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
layer.x = x;
|
||||||
|
layer.y = y;
|
||||||
|
},
|
||||||
|
layerBboxChanged: (state, action: PayloadAction<{ id: string; bbox: IRect | null }>) => {
|
||||||
|
const { id, bbox } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
layer.bbox = bbox;
|
||||||
|
layer.bboxNeedsUpdate = false;
|
||||||
|
if (bbox === null) {
|
||||||
|
layer.objects = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
layerReset: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
layer.isEnabled = true;
|
||||||
|
layer.objects = [];
|
||||||
|
layer.bbox = null;
|
||||||
|
layer.bboxNeedsUpdate = false;
|
||||||
|
},
|
||||||
|
layerDeleted: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
state.layers = state.layers.filter((l) => l.id !== id);
|
||||||
|
},
|
||||||
|
layerOpacityChanged: (state, action: PayloadAction<{ id: string; opacity: number }>) => {
|
||||||
|
const { id, opacity } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
layer.opacity = opacity;
|
||||||
|
},
|
||||||
|
layerMovedForwardOne: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveOneToEnd(state.layers, layer);
|
||||||
|
},
|
||||||
|
layerMovedToFront: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveToEnd(state.layers, layer);
|
||||||
|
},
|
||||||
|
layerMovedBackwardOne: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveOneToStart(state.layers, layer);
|
||||||
|
},
|
||||||
|
layerMovedToBack: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveToStart(state.layers, layer);
|
||||||
|
},
|
||||||
|
layerBrushLineAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<AddBrushLineArg & { lineId: string }>) => {
|
||||||
|
const { id, points, lineId, color, width } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
layer.objects.push({
|
||||||
|
id: getBrushLineId(id, lineId),
|
||||||
|
type: 'brush_line',
|
||||||
|
points,
|
||||||
|
strokeWidth: width,
|
||||||
|
color,
|
||||||
|
});
|
||||||
|
layer.bboxNeedsUpdate = true;
|
||||||
|
},
|
||||||
|
prepare: (payload: AddBrushLineArg) => ({
|
||||||
|
payload: { ...payload, lineId: uuidv4() },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
layerEraserLineAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<AddEraserLineArg & { lineId: string }>) => {
|
||||||
|
const { id, points, lineId, width } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
layer.objects.push({
|
||||||
|
id: getEraserLineId(id, lineId),
|
||||||
|
type: 'eraser_line',
|
||||||
|
points,
|
||||||
|
strokeWidth: width,
|
||||||
|
});
|
||||||
|
layer.bboxNeedsUpdate = true;
|
||||||
|
},
|
||||||
|
prepare: (payload: AddEraserLineArg) => ({
|
||||||
|
payload: { ...payload, lineId: uuidv4() },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
layerLinePointAdded: (state, action: PayloadAction<AddPointToLineArg>) => {
|
||||||
|
const { id, point } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const lastObject = layer.objects[layer.objects.length - 1];
|
||||||
|
if (!lastObject || !isLine(lastObject)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lastObject.points.push(...point);
|
||||||
|
layer.bboxNeedsUpdate = true;
|
||||||
|
},
|
||||||
|
layerRectAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<AddRectShapeArg & { rectId: string }>) => {
|
||||||
|
const { id, rect, rectId, color } = action.payload;
|
||||||
|
if (rect.height === 0 || rect.width === 0) {
|
||||||
|
// Ignore zero-area rectangles
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
layer.objects.push({
|
||||||
|
type: 'rect_shape',
|
||||||
|
id: getRectShapeId(id, rectId),
|
||||||
|
x: rect.x - layer.x,
|
||||||
|
y: rect.y - layer.y,
|
||||||
|
width: rect.width,
|
||||||
|
height: rect.height,
|
||||||
|
color,
|
||||||
|
});
|
||||||
|
layer.bboxNeedsUpdate = true;
|
||||||
|
},
|
||||||
|
prepare: (payload: AddRectShapeArg) => ({ payload: { ...payload, rectId: uuidv4() } }),
|
||||||
|
},
|
||||||
|
layerImageAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<AddImageObjectArg & { imageId: string }>) => {
|
||||||
|
const { id, imageId, imageDTO } = action.payload;
|
||||||
|
const layer = selectLayer(state, id);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { width, height, image_name: name } = imageDTO;
|
||||||
|
layer.objects.push({
|
||||||
|
type: 'image',
|
||||||
|
id: getImageObjectId(id, imageId),
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
image: { width, height, name },
|
||||||
|
});
|
||||||
|
layer.bboxNeedsUpdate = true;
|
||||||
|
},
|
||||||
|
prepare: (payload: AddImageObjectArg) => ({ payload: { ...payload, imageId: uuidv4() } }),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const {
|
||||||
|
layerAdded,
|
||||||
|
layerDeleted,
|
||||||
|
layerReset,
|
||||||
|
layerMovedForwardOne,
|
||||||
|
layerMovedToFront,
|
||||||
|
layerMovedBackwardOne,
|
||||||
|
layerMovedToBack,
|
||||||
|
layerIsEnabledChanged,
|
||||||
|
layerOpacityChanged,
|
||||||
|
layerTranslated,
|
||||||
|
layerBboxChanged,
|
||||||
|
layerBrushLineAdded,
|
||||||
|
layerEraserLineAdded,
|
||||||
|
layerLinePointAdded,
|
||||||
|
layerRectAdded,
|
||||||
|
layerImageAdded,
|
||||||
|
} = layersSlice.actions;
|
||||||
|
|
||||||
|
export const selectLayersSlice = (state: RootState) => state.layers;
|
||||||
|
|
||||||
|
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||||
|
const migrate = (state: any): any => {
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const layersPersistConfig: PersistConfig<LayersState> = {
|
||||||
|
name: layersSlice.name,
|
||||||
|
initialState,
|
||||||
|
migrate,
|
||||||
|
persistDenylist: [],
|
||||||
|
};
|
@ -0,0 +1,440 @@
|
|||||||
|
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||||
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
|
import type { PersistConfig, RootState } from 'app/store/store';
|
||||||
|
import { moveOneToEnd, moveOneToStart, moveToEnd, moveToStart } from 'common/util/arrayUtils';
|
||||||
|
import { getBrushLineId, getEraserLineId, getRectShapeId } from 'features/controlLayers/konva/naming';
|
||||||
|
import type { CLIPVisionModelV2, IPMethodV2 } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { imageDTOToImageWithDims } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||||
|
import type { ParameterAutoNegative } from 'features/parameters/types/parameterSchemas';
|
||||||
|
import type { IRect } from 'konva/lib/types';
|
||||||
|
import { isEqual } from 'lodash-es';
|
||||||
|
import type { ImageDTO, IPAdapterModelConfig } from 'services/api/types';
|
||||||
|
import { assert } from 'tsafe';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
AddBrushLineArg,
|
||||||
|
AddEraserLineArg,
|
||||||
|
AddPointToLineArg,
|
||||||
|
AddRectShapeArg,
|
||||||
|
IPAdapterData,
|
||||||
|
RegionalGuidanceData,
|
||||||
|
RgbColor,
|
||||||
|
} from './types';
|
||||||
|
import { isLine } from './types';
|
||||||
|
|
||||||
|
type RegionalGuidanceState = {
|
||||||
|
_version: 1;
|
||||||
|
regions: RegionalGuidanceData[];
|
||||||
|
opacity: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialState: RegionalGuidanceState = {
|
||||||
|
_version: 1,
|
||||||
|
regions: [],
|
||||||
|
opacity: 0.3,
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectRg = (state: RegionalGuidanceState, id: string) => state.regions.find((rg) => rg.id === id);
|
||||||
|
|
||||||
|
const DEFAULT_MASK_COLORS: RgbColor[] = [
|
||||||
|
{ r: 121, g: 157, b: 219 }, // rgb(121, 157, 219)
|
||||||
|
{ r: 131, g: 214, b: 131 }, // rgb(131, 214, 131)
|
||||||
|
{ r: 250, g: 225, b: 80 }, // rgb(250, 225, 80)
|
||||||
|
{ r: 220, g: 144, b: 101 }, // rgb(220, 144, 101)
|
||||||
|
{ r: 224, g: 117, b: 117 }, // rgb(224, 117, 117)
|
||||||
|
{ r: 213, g: 139, b: 202 }, // rgb(213, 139, 202)
|
||||||
|
{ r: 161, g: 120, b: 214 }, // rgb(161, 120, 214)
|
||||||
|
];
|
||||||
|
|
||||||
|
const getRGMaskFill = (state: RegionalGuidanceState): RgbColor => {
|
||||||
|
const lastFill = state.regions.slice(-1)[0]?.fill;
|
||||||
|
let i = DEFAULT_MASK_COLORS.findIndex((c) => isEqual(c, lastFill));
|
||||||
|
if (i === -1) {
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
i = (i + 1) % DEFAULT_MASK_COLORS.length;
|
||||||
|
const fill = DEFAULT_MASK_COLORS[i];
|
||||||
|
assert(fill, 'This should never happen');
|
||||||
|
return fill;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const regionalGuidanceSlice = createSlice({
|
||||||
|
name: 'regionalGuidance',
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
rgAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const rg: RegionalGuidanceData = {
|
||||||
|
id,
|
||||||
|
type: 'regional_guidance',
|
||||||
|
isEnabled: true,
|
||||||
|
bbox: null,
|
||||||
|
bboxNeedsUpdate: false,
|
||||||
|
objects: [],
|
||||||
|
fill: getRGMaskFill(state),
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
autoNegative: 'invert',
|
||||||
|
positivePrompt: '',
|
||||||
|
negativePrompt: null,
|
||||||
|
ipAdapters: [],
|
||||||
|
imageCache: null,
|
||||||
|
};
|
||||||
|
state.regions.push(rg);
|
||||||
|
},
|
||||||
|
prepare: () => ({ payload: { id: uuidv4() } }),
|
||||||
|
},
|
||||||
|
rgRecalled: (state, action: PayloadAction<{ data: RegionalGuidanceData }>) => {
|
||||||
|
const { data } = action.payload;
|
||||||
|
state.regions.push(data);
|
||||||
|
},
|
||||||
|
rgIsEnabledToggled: (state, action: PayloadAction<{ id: string; isEnabled: boolean }>) => {
|
||||||
|
const { id, isEnabled } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (rg) {
|
||||||
|
rg.isEnabled = isEnabled;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rgTranslated: (state, action: PayloadAction<{ id: string; x: number; y: number }>) => {
|
||||||
|
const { id, x, y } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (rg) {
|
||||||
|
rg.x = x;
|
||||||
|
rg.y = y;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rgBboxChanged: (state, action: PayloadAction<{ id: string; bbox: IRect | null }>) => {
|
||||||
|
const { id, bbox } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (rg) {
|
||||||
|
rg.bbox = bbox;
|
||||||
|
rg.bboxNeedsUpdate = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rgDeleted: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
state.regions = state.regions.filter((ca) => ca.id !== id);
|
||||||
|
},
|
||||||
|
rgGlobalOpacityChanged: (state, action: PayloadAction<{ opacity: number }>) => {
|
||||||
|
const { opacity } = action.payload;
|
||||||
|
state.opacity = opacity;
|
||||||
|
},
|
||||||
|
rgMovedForwardOne: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveOneToEnd(state.regions, rg);
|
||||||
|
},
|
||||||
|
rgMovedToFront: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveToEnd(state.regions, rg);
|
||||||
|
},
|
||||||
|
rgMovedBackwardOne: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveOneToStart(state.regions, rg);
|
||||||
|
},
|
||||||
|
rgMovedToBack: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveToStart(state.regions, rg);
|
||||||
|
},
|
||||||
|
rgPositivePromptChanged: (state, action: PayloadAction<{ id: string; prompt: string | null }>) => {
|
||||||
|
const { id, prompt } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rg.positivePrompt = prompt;
|
||||||
|
},
|
||||||
|
rgNegativePromptChanged: (state, action: PayloadAction<{ id: string; prompt: string | null }>) => {
|
||||||
|
const { id, prompt } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rg.negativePrompt = prompt;
|
||||||
|
},
|
||||||
|
rgFillChanged: (state, action: PayloadAction<{ id: string; fill: RgbColor }>) => {
|
||||||
|
const { id, fill } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rg.fill = fill;
|
||||||
|
},
|
||||||
|
rgMaskImageUploaded: (state, action: PayloadAction<{ id: string; imageDTO: ImageDTO }>) => {
|
||||||
|
const { id, imageDTO } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rg.imageCache = imageDTOToImageWithDims(imageDTO);
|
||||||
|
},
|
||||||
|
rgAutoNegativeChanged: (state, action: PayloadAction<{ id: string; autoNegative: ParameterAutoNegative }>) => {
|
||||||
|
const { id, autoNegative } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rg.autoNegative = autoNegative;
|
||||||
|
},
|
||||||
|
rgIPAdapterAdded: (state, action: PayloadAction<{ id: string; ipAdapter: IPAdapterData }>) => {
|
||||||
|
const { id, ipAdapter } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rg.ipAdapters.push(ipAdapter);
|
||||||
|
},
|
||||||
|
rgIPAdapterDeleted: (state, action: PayloadAction<{ id: string; ipAdapterId: string }>) => {
|
||||||
|
const { id, ipAdapterId } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rg.ipAdapters = rg.ipAdapters.filter((ipAdapter) => ipAdapter.id !== ipAdapterId);
|
||||||
|
},
|
||||||
|
rgIPAdapterImageChanged: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{ id: string; ipAdapterId: string; imageDTO: ImageDTO | null }>
|
||||||
|
) => {
|
||||||
|
const { id, ipAdapterId, imageDTO } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ipa = rg.ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipa.image = imageDTO ? imageDTOToImageWithDims(imageDTO) : null;
|
||||||
|
},
|
||||||
|
rgIPAdapterWeightChanged: (state, action: PayloadAction<{ id: string; ipAdapterId: string; weight: number }>) => {
|
||||||
|
const { id, ipAdapterId, weight } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ipa = rg.ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipa.weight = weight;
|
||||||
|
},
|
||||||
|
rgIPAdapterBeginEndStepPctChanged: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{ id: string; ipAdapterId: string; beginEndStepPct: [number, number] }>
|
||||||
|
) => {
|
||||||
|
const { id, ipAdapterId, beginEndStepPct } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ipa = rg.ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipa.beginEndStepPct = beginEndStepPct;
|
||||||
|
},
|
||||||
|
rgIPAdapterMethodChanged: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{ id: string; ipAdapterId: string; method: IPMethodV2 }>
|
||||||
|
) => {
|
||||||
|
const { id, ipAdapterId, method } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ipa = rg.ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipa.method = method;
|
||||||
|
},
|
||||||
|
rgIPAdapterModelChanged: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{
|
||||||
|
id: string;
|
||||||
|
ipAdapterId: string;
|
||||||
|
modelConfig: IPAdapterModelConfig | null;
|
||||||
|
}>
|
||||||
|
) => {
|
||||||
|
const { id, ipAdapterId, modelConfig } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ipa = rg.ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (modelConfig) {
|
||||||
|
ipa.model = zModelIdentifierField.parse(modelConfig);
|
||||||
|
} else {
|
||||||
|
ipa.model = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rgIPAdapterCLIPVisionModelChanged: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{ id: string; ipAdapterId: string; clipVisionModel: CLIPVisionModelV2 }>
|
||||||
|
) => {
|
||||||
|
const { id, ipAdapterId, clipVisionModel } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ipa = rg.ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
||||||
|
if (!ipa) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ipa.clipVisionModel = clipVisionModel;
|
||||||
|
},
|
||||||
|
rgBrushLineAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<AddBrushLineArg & { lineId: string }>) => {
|
||||||
|
const { id, points, lineId, color, width } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rg.objects.push({
|
||||||
|
id: getBrushLineId(id, lineId),
|
||||||
|
type: 'brush_line',
|
||||||
|
// Points must be offset by the layer's x and y coordinates
|
||||||
|
// TODO: Handle this in the event listener?
|
||||||
|
points: [points[0] - rg.x, points[1] - rg.y, points[2] - rg.x, points[3] - rg.y],
|
||||||
|
strokeWidth: width,
|
||||||
|
color,
|
||||||
|
});
|
||||||
|
rg.bboxNeedsUpdate = true;
|
||||||
|
rg.imageCache = null;
|
||||||
|
},
|
||||||
|
prepare: (payload: AddBrushLineArg) => ({
|
||||||
|
payload: { ...payload, lineId: uuidv4() },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
rgEraserLineAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<AddEraserLineArg & { lineId: string }>) => {
|
||||||
|
const { id, points, lineId, width } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rg.objects.push({
|
||||||
|
id: getEraserLineId(id, lineId),
|
||||||
|
type: 'eraser_line',
|
||||||
|
// Points must be offset by the layer's x and y coordinates
|
||||||
|
// TODO: Handle this in the event listener?
|
||||||
|
points: [points[0] - rg.x, points[1] - rg.y, points[2] - rg.x, points[3] - rg.y],
|
||||||
|
strokeWidth: width,
|
||||||
|
});
|
||||||
|
rg.bboxNeedsUpdate = true;
|
||||||
|
rg.imageCache = null;
|
||||||
|
},
|
||||||
|
prepare: (payload: AddEraserLineArg) => ({
|
||||||
|
payload: { ...payload, lineId: uuidv4() },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
rgLinePointAdded: (state, action: PayloadAction<AddPointToLineArg>) => {
|
||||||
|
const { id, point } = action.payload;
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const lastObject = rg.objects[rg.objects.length - 1];
|
||||||
|
if (!lastObject || !isLine(lastObject)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Points must be offset by the layer's x and y coordinates
|
||||||
|
// TODO: Handle this in the event listener
|
||||||
|
lastObject.points.push(point[0] - rg.x, point[1] - rg.y);
|
||||||
|
rg.bboxNeedsUpdate = true;
|
||||||
|
rg.imageCache = null;
|
||||||
|
},
|
||||||
|
rgRectAdded: {
|
||||||
|
reducer: (state, action: PayloadAction<AddRectShapeArg & { rectId: string }>) => {
|
||||||
|
const { id, rect, rectId, color } = action.payload;
|
||||||
|
if (rect.height === 0 || rect.width === 0) {
|
||||||
|
// Ignore zero-area rectangles
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const rg = selectRg(state, id);
|
||||||
|
if (!rg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rg.objects.push({
|
||||||
|
type: 'rect_shape',
|
||||||
|
id: getRectShapeId(id, rectId),
|
||||||
|
x: rect.x - rg.x,
|
||||||
|
y: rect.y - rg.y,
|
||||||
|
width: rect.width,
|
||||||
|
height: rect.height,
|
||||||
|
color,
|
||||||
|
});
|
||||||
|
rg.bboxNeedsUpdate = true;
|
||||||
|
rg.imageCache = null;
|
||||||
|
},
|
||||||
|
prepare: (payload: AddRectShapeArg) => ({ payload: { ...payload, rectId: uuidv4() } }),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const {
|
||||||
|
rgAdded,
|
||||||
|
rgRecalled,
|
||||||
|
rgIsEnabledToggled,
|
||||||
|
rgTranslated,
|
||||||
|
rgBboxChanged,
|
||||||
|
rgDeleted,
|
||||||
|
rgGlobalOpacityChanged,
|
||||||
|
rgMovedForwardOne,
|
||||||
|
rgMovedToFront,
|
||||||
|
rgMovedBackwardOne,
|
||||||
|
rgMovedToBack,
|
||||||
|
rgPositivePromptChanged,
|
||||||
|
rgNegativePromptChanged,
|
||||||
|
rgFillChanged,
|
||||||
|
rgMaskImageUploaded,
|
||||||
|
rgAutoNegativeChanged,
|
||||||
|
rgIPAdapterAdded,
|
||||||
|
rgIPAdapterDeleted,
|
||||||
|
rgIPAdapterImageChanged,
|
||||||
|
rgIPAdapterWeightChanged,
|
||||||
|
rgIPAdapterBeginEndStepPctChanged,
|
||||||
|
rgIPAdapterMethodChanged,
|
||||||
|
rgIPAdapterModelChanged,
|
||||||
|
rgIPAdapterCLIPVisionModelChanged,
|
||||||
|
rgBrushLineAdded,
|
||||||
|
rgEraserLineAdded,
|
||||||
|
rgLinePointAdded,
|
||||||
|
rgRectAdded,
|
||||||
|
} = regionalGuidanceSlice.actions;
|
||||||
|
|
||||||
|
export const selectRegionalGuidanceSlice = (state: RootState) => state.regionalGuidance;
|
||||||
|
|
||||||
|
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||||
|
const migrate = (state: any): any => {
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const regionalGuidancePersistConfig: PersistConfig<RegionalGuidanceState> = {
|
||||||
|
name: regionalGuidanceSlice.name,
|
||||||
|
initialState,
|
||||||
|
migrate,
|
||||||
|
persistDenylist: [],
|
||||||
|
};
|
@ -1,9 +1,13 @@
|
|||||||
import {
|
import {
|
||||||
zControlNetConfigV2,
|
zBeginEndStepPct,
|
||||||
|
zCLIPVisionModelV2,
|
||||||
|
zControlModeV2,
|
||||||
|
zId,
|
||||||
zImageWithDims,
|
zImageWithDims,
|
||||||
zIPAdapterConfigV2,
|
zIPMethodV2,
|
||||||
zT2IAdapterConfigV2,
|
zProcessorConfig,
|
||||||
} from 'features/controlLayers/util/controlAdapters';
|
} from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||||
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
|
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
|
||||||
import type {
|
import type {
|
||||||
ParameterHeight,
|
ParameterHeight,
|
||||||
@ -17,7 +21,6 @@ import {
|
|||||||
zAutoNegative,
|
zAutoNegative,
|
||||||
zParameterNegativePrompt,
|
zParameterNegativePrompt,
|
||||||
zParameterPositivePrompt,
|
zParameterPositivePrompt,
|
||||||
zParameterStrength,
|
|
||||||
} from 'features/parameters/types/parameterSchemas';
|
} from 'features/parameters/types/parameterSchemas';
|
||||||
import type { IRect } from 'konva/lib/types';
|
import type { IRect } from 'konva/lib/types';
|
||||||
import type { ImageDTO } from 'services/api/types';
|
import type { ImageDTO } from 'services/api/types';
|
||||||
@ -31,7 +34,7 @@ const zPoints = z.array(z.number()).refine((points) => points.length % 2 === 0,
|
|||||||
message: 'Must have an even number of points',
|
message: 'Must have an even number of points',
|
||||||
});
|
});
|
||||||
const zOLD_VectorMaskLine = z.object({
|
const zOLD_VectorMaskLine = z.object({
|
||||||
id: z.string(),
|
id: zId,
|
||||||
type: z.literal('vector_mask_line'),
|
type: z.literal('vector_mask_line'),
|
||||||
tool: zDrawingTool,
|
tool: zDrawingTool,
|
||||||
strokeWidth: z.number().min(1),
|
strokeWidth: z.number().min(1),
|
||||||
@ -39,7 +42,7 @@ const zOLD_VectorMaskLine = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const zOLD_VectorMaskRect = z.object({
|
const zOLD_VectorMaskRect = z.object({
|
||||||
id: z.string(),
|
id: zId,
|
||||||
type: z.literal('vector_mask_rect'),
|
type: z.literal('vector_mask_rect'),
|
||||||
x: z.number(),
|
x: z.number(),
|
||||||
y: z.number(),
|
y: z.number(),
|
||||||
@ -52,6 +55,7 @@ const zRgbColor = z.object({
|
|||||||
g: z.number().int().min(0).max(255),
|
g: z.number().int().min(0).max(255),
|
||||||
b: z.number().int().min(0).max(255),
|
b: z.number().int().min(0).max(255),
|
||||||
});
|
});
|
||||||
|
export type RgbColor = z.infer<typeof zRgbColor>;
|
||||||
const zRgbaColor = zRgbColor.extend({
|
const zRgbaColor = zRgbColor.extend({
|
||||||
a: z.number().min(0).max(1),
|
a: z.number().min(0).max(1),
|
||||||
});
|
});
|
||||||
@ -61,7 +65,7 @@ export const DEFAULT_RGBA_COLOR: RgbaColor = { r: 255, g: 255, b: 255, a: 1 };
|
|||||||
const zOpacity = z.number().gte(0).lte(1);
|
const zOpacity = z.number().gte(0).lte(1);
|
||||||
|
|
||||||
const zBrushLine = z.object({
|
const zBrushLine = z.object({
|
||||||
id: z.string(),
|
id: zId,
|
||||||
type: z.literal('brush_line'),
|
type: z.literal('brush_line'),
|
||||||
strokeWidth: z.number().min(1),
|
strokeWidth: z.number().min(1),
|
||||||
points: zPoints,
|
points: zPoints,
|
||||||
@ -70,7 +74,7 @@ const zBrushLine = z.object({
|
|||||||
export type BrushLine = z.infer<typeof zBrushLine>;
|
export type BrushLine = z.infer<typeof zBrushLine>;
|
||||||
|
|
||||||
const zEraserline = z.object({
|
const zEraserline = z.object({
|
||||||
id: z.string(),
|
id: zId,
|
||||||
type: z.literal('eraser_line'),
|
type: z.literal('eraser_line'),
|
||||||
strokeWidth: z.number().min(1),
|
strokeWidth: z.number().min(1),
|
||||||
points: zPoints,
|
points: zPoints,
|
||||||
@ -78,7 +82,7 @@ const zEraserline = z.object({
|
|||||||
export type EraserLine = z.infer<typeof zEraserline>;
|
export type EraserLine = z.infer<typeof zEraserline>;
|
||||||
|
|
||||||
const zRectShape = z.object({
|
const zRectShape = z.object({
|
||||||
id: z.string(),
|
id: zId,
|
||||||
type: z.literal('rect_shape'),
|
type: z.literal('rect_shape'),
|
||||||
x: z.number(),
|
x: z.number(),
|
||||||
y: z.number(),
|
y: z.number(),
|
||||||
@ -89,7 +93,7 @@ const zRectShape = z.object({
|
|||||||
export type RectShape = z.infer<typeof zRectShape>;
|
export type RectShape = z.infer<typeof zRectShape>;
|
||||||
|
|
||||||
const zEllipseShape = z.object({
|
const zEllipseShape = z.object({
|
||||||
id: z.string(),
|
id: zId,
|
||||||
type: z.literal('ellipse_shape'),
|
type: z.literal('ellipse_shape'),
|
||||||
x: z.number(),
|
x: z.number(),
|
||||||
y: z.number(),
|
y: z.number(),
|
||||||
@ -100,7 +104,7 @@ const zEllipseShape = z.object({
|
|||||||
export type EllipseShape = z.infer<typeof zEllipseShape>;
|
export type EllipseShape = z.infer<typeof zEllipseShape>;
|
||||||
|
|
||||||
const zPolygonShape = z.object({
|
const zPolygonShape = z.object({
|
||||||
id: z.string(),
|
id: zId,
|
||||||
type: z.literal('polygon_shape'),
|
type: z.literal('polygon_shape'),
|
||||||
points: zPoints,
|
points: zPoints,
|
||||||
color: zRgbaColor,
|
color: zRgbaColor,
|
||||||
@ -108,7 +112,7 @@ const zPolygonShape = z.object({
|
|||||||
export type PolygonShape = z.infer<typeof zPolygonShape>;
|
export type PolygonShape = z.infer<typeof zPolygonShape>;
|
||||||
|
|
||||||
const zImageObject = z.object({
|
const zImageObject = z.object({
|
||||||
id: z.string(),
|
id: zId,
|
||||||
type: z.literal('image'),
|
type: z.literal('image'),
|
||||||
image: zImageWithDims,
|
image: zImageWithDims,
|
||||||
x: z.number(),
|
x: z.number(),
|
||||||
@ -118,7 +122,7 @@ const zImageObject = z.object({
|
|||||||
});
|
});
|
||||||
export type ImageObject = z.infer<typeof zImageObject>;
|
export type ImageObject = z.infer<typeof zImageObject>;
|
||||||
|
|
||||||
const zAnyLayerObject = z.discriminatedUnion('type', [
|
const zLayerObject = z.discriminatedUnion('type', [
|
||||||
zImageObject,
|
zImageObject,
|
||||||
zBrushLine,
|
zBrushLine,
|
||||||
zEraserline,
|
zEraserline,
|
||||||
@ -126,13 +130,7 @@ const zAnyLayerObject = z.discriminatedUnion('type', [
|
|||||||
zEllipseShape,
|
zEllipseShape,
|
||||||
zPolygonShape,
|
zPolygonShape,
|
||||||
]);
|
]);
|
||||||
export type AnyLayerObject = z.infer<typeof zAnyLayerObject>;
|
export type LayerObject = z.infer<typeof zLayerObject>;
|
||||||
|
|
||||||
const zLayerBase = z.object({
|
|
||||||
id: z.string(),
|
|
||||||
isEnabled: z.boolean().default(true),
|
|
||||||
isSelected: z.boolean().default(true),
|
|
||||||
});
|
|
||||||
|
|
||||||
const zRect = z.object({
|
const zRect = z.object({
|
||||||
x: z.number(),
|
x: z.number(),
|
||||||
@ -140,33 +138,36 @@ const zRect = z.object({
|
|||||||
width: z.number().min(1),
|
width: z.number().min(1),
|
||||||
height: z.number().min(1),
|
height: z.number().min(1),
|
||||||
});
|
});
|
||||||
const zRenderableLayerBase = zLayerBase.extend({
|
|
||||||
|
const zLayerData = z.object({
|
||||||
|
id: zId,
|
||||||
|
type: z.literal('layer'),
|
||||||
|
isEnabled: z.boolean(),
|
||||||
x: z.number(),
|
x: z.number(),
|
||||||
y: z.number(),
|
y: z.number(),
|
||||||
bbox: zRect.nullable(),
|
bbox: zRect.nullable(),
|
||||||
bboxNeedsUpdate: z.boolean(),
|
bboxNeedsUpdate: z.boolean(),
|
||||||
});
|
|
||||||
|
|
||||||
const zRasterLayer = zRenderableLayerBase.extend({
|
|
||||||
type: z.literal('raster_layer'),
|
|
||||||
opacity: zOpacity,
|
opacity: zOpacity,
|
||||||
objects: z.array(zAnyLayerObject),
|
objects: z.array(zLayerObject),
|
||||||
});
|
});
|
||||||
export type RasterLayer = z.infer<typeof zRasterLayer>;
|
export type LayerData = z.infer<typeof zLayerData>;
|
||||||
|
|
||||||
const zControlAdapterLayer = zRenderableLayerBase.extend({
|
const zIPAdapterData = z.object({
|
||||||
type: z.literal('control_adapter_layer'),
|
id: zId,
|
||||||
opacity: zOpacity,
|
type: z.literal('ip_adapter'),
|
||||||
isFilterEnabled: z.boolean(),
|
isEnabled: z.boolean(),
|
||||||
controlAdapter: z.discriminatedUnion('type', [zControlNetConfigV2, zT2IAdapterConfigV2]),
|
weight: z.number().gte(-1).lte(2),
|
||||||
|
method: zIPMethodV2,
|
||||||
|
image: zImageWithDims.nullable(),
|
||||||
|
model: zModelIdentifierField.nullable(),
|
||||||
|
clipVisionModel: zCLIPVisionModelV2,
|
||||||
|
beginEndStepPct: zBeginEndStepPct,
|
||||||
});
|
});
|
||||||
export type ControlAdapterLayer = z.infer<typeof zControlAdapterLayer>;
|
export type IPAdapterData = z.infer<typeof zIPAdapterData>;
|
||||||
|
export type IPAdapterConfig = Pick<
|
||||||
const zIPAdapterLayer = zLayerBase.extend({
|
IPAdapterData,
|
||||||
type: z.literal('ip_adapter_layer'),
|
'weight' | 'image' | 'beginEndStepPct' | 'model' | 'clipVisionModel' | 'method'
|
||||||
ipAdapter: zIPAdapterConfigV2,
|
>;
|
||||||
});
|
|
||||||
export type IPAdapterLayer = z.infer<typeof zIPAdapterLayer>;
|
|
||||||
|
|
||||||
const zMaskObject = z
|
const zMaskObject = z
|
||||||
.discriminatedUnion('type', [zOLD_VectorMaskLine, zOLD_VectorMaskRect, zBrushLine, zEraserline, zRectShape])
|
.discriminatedUnion('type', [zOLD_VectorMaskLine, zOLD_VectorMaskRect, zBrushLine, zEraserline, zRectShape])
|
||||||
@ -201,69 +202,109 @@ const zMaskObject = z
|
|||||||
})
|
})
|
||||||
.pipe(z.discriminatedUnion('type', [zBrushLine, zEraserline, zRectShape]));
|
.pipe(z.discriminatedUnion('type', [zBrushLine, zEraserline, zRectShape]));
|
||||||
|
|
||||||
const zOLD_RegionalGuidanceLayer = zRenderableLayerBase.extend({
|
const zRegionalGuidanceData = z.object({
|
||||||
type: z.literal('regional_guidance_layer'),
|
id: zId,
|
||||||
maskObjects: z.array(zMaskObject),
|
type: z.literal('regional_guidance'),
|
||||||
positivePrompt: zParameterPositivePrompt.nullable(),
|
isEnabled: z.boolean(),
|
||||||
negativePrompt: zParameterNegativePrompt.nullable(),
|
x: z.number(),
|
||||||
ipAdapters: z.array(zIPAdapterConfigV2),
|
y: z.number(),
|
||||||
previewColor: zRgbColor,
|
bbox: zRect.nullable(),
|
||||||
autoNegative: zAutoNegative,
|
bboxNeedsUpdate: z.boolean(),
|
||||||
uploadedMaskImage: zImageWithDims.nullable(),
|
|
||||||
});
|
|
||||||
const zRegionalGuidanceLayer = zRenderableLayerBase.extend({
|
|
||||||
type: z.literal('regional_guidance_layer'),
|
|
||||||
objects: z.array(zMaskObject),
|
objects: z.array(zMaskObject),
|
||||||
positivePrompt: zParameterPositivePrompt.nullable(),
|
positivePrompt: zParameterPositivePrompt.nullable(),
|
||||||
negativePrompt: zParameterNegativePrompt.nullable(),
|
negativePrompt: zParameterNegativePrompt.nullable(),
|
||||||
ipAdapters: z.array(zIPAdapterConfigV2),
|
ipAdapters: z.array(zIPAdapterData),
|
||||||
previewColor: zRgbColor,
|
fill: zRgbColor,
|
||||||
autoNegative: zAutoNegative,
|
autoNegative: zAutoNegative,
|
||||||
uploadedMaskImage: zImageWithDims.nullable(),
|
imageCache: zImageWithDims.nullable(),
|
||||||
});
|
});
|
||||||
// TODO(psyche): This doesn't migrate correctly!
|
export type RegionalGuidanceData = z.infer<typeof zRegionalGuidanceData>;
|
||||||
const zRGLayer = z
|
|
||||||
.union([zOLD_RegionalGuidanceLayer, zRegionalGuidanceLayer])
|
|
||||||
.transform((val) => {
|
|
||||||
if ('maskObjects' in val) {
|
|
||||||
const { maskObjects, ...rest } = val;
|
|
||||||
return { ...rest, objects: maskObjects };
|
|
||||||
} else {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.pipe(zRegionalGuidanceLayer);
|
|
||||||
export type RegionalGuidanceLayer = z.infer<typeof zRGLayer>;
|
|
||||||
|
|
||||||
const zInitialImageLayer = zRenderableLayerBase.extend({
|
const zColorFill = z.object({
|
||||||
type: z.literal('initial_image_layer'),
|
type: z.literal('color_fill'),
|
||||||
|
color: zRgbaColor,
|
||||||
|
});
|
||||||
|
const zImageFill = z.object({
|
||||||
|
type: z.literal('image_fill'),
|
||||||
|
src: z.string(),
|
||||||
|
});
|
||||||
|
const zFill = z.discriminatedUnion('type', [zColorFill, zImageFill]);
|
||||||
|
const zInpaintMaskData = z.object({
|
||||||
|
id: zId,
|
||||||
|
type: z.literal('inpaint_mask'),
|
||||||
|
isEnabled: z.boolean(),
|
||||||
|
x: z.number(),
|
||||||
|
y: z.number(),
|
||||||
|
bbox: zRect.nullable(),
|
||||||
|
bboxNeedsUpdate: z.boolean(),
|
||||||
|
maskObjects: z.array(zMaskObject),
|
||||||
|
fill: zFill,
|
||||||
|
imageCache: zImageWithDims.nullable(),
|
||||||
|
});
|
||||||
|
export type InpaintMaskData = z.infer<typeof zInpaintMaskData>;
|
||||||
|
|
||||||
|
const zFilter = z.enum(['none', 'lightness_to_alpha']);
|
||||||
|
export type Filter = z.infer<typeof zFilter>;
|
||||||
|
|
||||||
|
const zControlAdapterData = z.object({
|
||||||
|
id: zId,
|
||||||
|
type: z.literal('control_adapter'),
|
||||||
|
isEnabled: z.boolean(),
|
||||||
|
x: z.number(),
|
||||||
|
y: z.number(),
|
||||||
|
bbox: zRect.nullable(),
|
||||||
|
bboxNeedsUpdate: z.boolean(),
|
||||||
opacity: zOpacity,
|
opacity: zOpacity,
|
||||||
|
filter: zFilter,
|
||||||
|
weight: z.number().gte(-1).lte(2),
|
||||||
image: zImageWithDims.nullable(),
|
image: zImageWithDims.nullable(),
|
||||||
denoisingStrength: zParameterStrength,
|
processedImage: zImageWithDims.nullable(),
|
||||||
|
processorConfig: zProcessorConfig.nullable(),
|
||||||
|
processorPendingBatchId: z.string().nullable().default(null),
|
||||||
|
beginEndStepPct: zBeginEndStepPct,
|
||||||
|
model: zModelIdentifierField.nullable(),
|
||||||
|
controlMode: zControlModeV2.nullable(),
|
||||||
});
|
});
|
||||||
export type InitialImageLayer = z.infer<typeof zInitialImageLayer>;
|
export type ControlAdapterData = z.infer<typeof zControlAdapterData>;
|
||||||
|
export type ControlAdapterConfig = Pick<
|
||||||
|
ControlAdapterData,
|
||||||
|
'weight' | 'image' | 'processedImage' | 'processorConfig' | 'beginEndStepPct' | 'model' | 'controlMode'
|
||||||
|
>;
|
||||||
|
|
||||||
export const zLayer = z.discriminatedUnion('type', [
|
const zCanvasItemIdentifier = z.object({
|
||||||
zRegionalGuidanceLayer,
|
type: z.enum([
|
||||||
zControlAdapterLayer,
|
zLayerData.shape.type.value,
|
||||||
zIPAdapterLayer,
|
zIPAdapterData.shape.type.value,
|
||||||
zInitialImageLayer,
|
zControlAdapterData.shape.type.value,
|
||||||
zRasterLayer,
|
zRegionalGuidanceData.shape.type.value,
|
||||||
]);
|
zInpaintMaskData.shape.type.value,
|
||||||
export type Layer = z.infer<typeof zLayer>;
|
]),
|
||||||
|
id: zId,
|
||||||
|
});
|
||||||
|
type CanvasItemIdentifier = z.infer<typeof zCanvasItemIdentifier>;
|
||||||
|
|
||||||
export type ControlLayersState = {
|
export type CanvasV2State = {
|
||||||
_version: 3;
|
_version: 3;
|
||||||
selectedLayerId: string | null;
|
lastSelectedItem: CanvasItemIdentifier | null;
|
||||||
layers: Layer[];
|
prompts: {
|
||||||
brushSize: number;
|
|
||||||
brushColor: RgbaColor;
|
|
||||||
globalMaskLayerOpacity: number;
|
|
||||||
positivePrompt: ParameterPositivePrompt;
|
positivePrompt: ParameterPositivePrompt;
|
||||||
negativePrompt: ParameterNegativePrompt;
|
negativePrompt: ParameterNegativePrompt;
|
||||||
positivePrompt2: ParameterPositiveStylePromptSDXL;
|
positivePrompt2: ParameterPositiveStylePromptSDXL;
|
||||||
negativePrompt2: ParameterNegativeStylePromptSDXL;
|
negativePrompt2: ParameterNegativeStylePromptSDXL;
|
||||||
shouldConcatPrompts: boolean;
|
shouldConcatPrompts: boolean;
|
||||||
|
};
|
||||||
|
tool: {
|
||||||
|
selected: Tool;
|
||||||
|
selectedBuffer: Tool | null;
|
||||||
|
invertScroll: boolean;
|
||||||
|
brush: {
|
||||||
|
width: number;
|
||||||
|
};
|
||||||
|
eraser: {
|
||||||
|
width: number;
|
||||||
|
};
|
||||||
|
fill: RgbaColor;
|
||||||
|
};
|
||||||
size: {
|
size: {
|
||||||
width: ParameterWidth;
|
width: ParameterWidth;
|
||||||
height: ParameterHeight;
|
height: ParameterHeight;
|
||||||
@ -273,45 +314,13 @@ export type ControlLayersState = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type StageAttrs = { x: number; y: number; width: number; height: number; scale: number };
|
export type StageAttrs = { x: number; y: number; width: number; height: number; scale: number };
|
||||||
export type AddEraserLineArg = { layerId: string; points: [number, number, number, number] };
|
export type AddEraserLineArg = { id: string; points: [number, number, number, number]; width: number };
|
||||||
export type AddBrushLineArg = AddEraserLineArg & { color: RgbaColor };
|
export type AddBrushLineArg = AddEraserLineArg & { color: RgbaColor };
|
||||||
export type AddPointToLineArg = { layerId: string; point: [number, number] };
|
export type AddPointToLineArg = { id: string; point: [number, number] };
|
||||||
export type AddRectShapeArg = { layerId: string; rect: IRect; color: RgbaColor };
|
export type AddRectShapeArg = { id: string; rect: IRect; color: RgbaColor };
|
||||||
export type AddImageObjectArg = { layerId: string; imageDTO: ImageDTO };
|
export type AddImageObjectArg = { id: string; imageDTO: ImageDTO };
|
||||||
|
|
||||||
//#region Type guards
|
//#region Type guards
|
||||||
export const isLine = (obj: AnyLayerObject): obj is BrushLine | EraserLine => {
|
export const isLine = (obj: LayerObject): obj is BrushLine | EraserLine => {
|
||||||
return obj.type === 'brush_line' || obj.type === 'eraser_line';
|
return obj.type === 'brush_line' || obj.type === 'eraser_line';
|
||||||
};
|
};
|
||||||
export const isRegionalGuidanceLayer = (layer?: Layer): layer is RegionalGuidanceLayer => {
|
|
||||||
return layer?.type === 'regional_guidance_layer';
|
|
||||||
};
|
|
||||||
export const isControlAdapterLayer = (layer?: Layer): layer is ControlAdapterLayer => {
|
|
||||||
return layer?.type === 'control_adapter_layer';
|
|
||||||
};
|
|
||||||
export const isIPAdapterLayer = (layer?: Layer): layer is IPAdapterLayer => {
|
|
||||||
return layer?.type === 'ip_adapter_layer';
|
|
||||||
};
|
|
||||||
export const isInitialImageLayer = (layer?: Layer): layer is InitialImageLayer => {
|
|
||||||
return layer?.type === 'initial_image_layer';
|
|
||||||
};
|
|
||||||
export const isRasterLayer = (layer?: Layer): layer is RasterLayer => {
|
|
||||||
return layer?.type === 'raster_layer';
|
|
||||||
};
|
|
||||||
export const isRenderableLayer = (
|
|
||||||
layer?: Layer
|
|
||||||
): layer is RegionalGuidanceLayer | ControlAdapterLayer | InitialImageLayer | RasterLayer => {
|
|
||||||
return (
|
|
||||||
isRegionalGuidanceLayer(layer) || isControlAdapterLayer(layer) || isInitialImageLayer(layer) || isRasterLayer(layer)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
export const isLayerWithOpacity = (layer?: Layer): layer is ControlAdapterLayer | InitialImageLayer | RasterLayer => {
|
|
||||||
return isControlAdapterLayer(layer) || isInitialImageLayer(layer) || isRasterLayer(layer);
|
|
||||||
};
|
|
||||||
export const isCAOrIPALayer = (layer?: Layer): layer is ControlAdapterLayer | IPAdapterLayer => {
|
|
||||||
return isControlAdapterLayer(layer) || isIPAdapterLayer(layer);
|
|
||||||
};
|
|
||||||
export const isRGOrRasterlayer = (layer?: Layer): layer is RegionalGuidanceLayer | RasterLayer => {
|
|
||||||
return isRegionalGuidanceLayer(layer) || isRasterLayer(layer);
|
|
||||||
};
|
|
||||||
//#endregion
|
|
||||||
|
@ -10,7 +10,7 @@ import type {
|
|||||||
} from 'services/api/types';
|
} from 'services/api/types';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
const zId = z.string().min(1);
|
export const zId = z.string().min(1);
|
||||||
|
|
||||||
const zCannyProcessorConfig = z.object({
|
const zCannyProcessorConfig = z.object({
|
||||||
id: zId,
|
id: zId,
|
||||||
@ -120,7 +120,7 @@ const zZoeDepthProcessorConfig = z.object({
|
|||||||
});
|
});
|
||||||
export type ZoeDepthProcessorConfig = z.infer<typeof zZoeDepthProcessorConfig>;
|
export type ZoeDepthProcessorConfig = z.infer<typeof zZoeDepthProcessorConfig>;
|
||||||
|
|
||||||
const zProcessorConfig = z.discriminatedUnion('type', [
|
export const zProcessorConfig = z.discriminatedUnion('type', [
|
||||||
zCannyProcessorConfig,
|
zCannyProcessorConfig,
|
||||||
zColorMapProcessorConfig,
|
zColorMapProcessorConfig,
|
||||||
zContentShuffleProcessorConfig,
|
zContentShuffleProcessorConfig,
|
||||||
@ -145,7 +145,7 @@ export const zImageWithDims = z.object({
|
|||||||
});
|
});
|
||||||
export type ImageWithDims = z.infer<typeof zImageWithDims>;
|
export type ImageWithDims = z.infer<typeof zImageWithDims>;
|
||||||
|
|
||||||
const zBeginEndStepPct = z
|
export const zBeginEndStepPct = z
|
||||||
.tuple([z.number().gte(0).lte(1), z.number().gte(0).lte(1)])
|
.tuple([z.number().gte(0).lte(1), z.number().gte(0).lte(1)])
|
||||||
.refine(([begin, end]) => begin < end, {
|
.refine(([begin, end]) => begin < end, {
|
||||||
message: 'Begin must be less than end',
|
message: 'Begin must be less than end',
|
||||||
@ -161,7 +161,7 @@ const zControlAdapterBase = z.object({
|
|||||||
beginEndStepPct: zBeginEndStepPct,
|
beginEndStepPct: zBeginEndStepPct,
|
||||||
});
|
});
|
||||||
|
|
||||||
const zControlModeV2 = z.enum(['balanced', 'more_prompt', 'more_control', 'unbalanced']);
|
export const zControlModeV2 = z.enum(['balanced', 'more_prompt', 'more_control', 'unbalanced']);
|
||||||
export type ControlModeV2 = z.infer<typeof zControlModeV2>;
|
export type ControlModeV2 = z.infer<typeof zControlModeV2>;
|
||||||
export const isControlModeV2 = (v: unknown): v is ControlModeV2 => zControlModeV2.safeParse(v).success;
|
export const isControlModeV2 = (v: unknown): v is ControlModeV2 => zControlModeV2.safeParse(v).success;
|
||||||
|
|
||||||
@ -178,11 +178,11 @@ export const zT2IAdapterConfigV2 = zControlAdapterBase.extend({
|
|||||||
});
|
});
|
||||||
export type T2IAdapterConfigV2 = z.infer<typeof zT2IAdapterConfigV2>;
|
export type T2IAdapterConfigV2 = z.infer<typeof zT2IAdapterConfigV2>;
|
||||||
|
|
||||||
const zCLIPVisionModelV2 = z.enum(['ViT-H', 'ViT-G']);
|
export const zCLIPVisionModelV2 = z.enum(['ViT-H', 'ViT-G']);
|
||||||
export type CLIPVisionModelV2 = z.infer<typeof zCLIPVisionModelV2>;
|
export type CLIPVisionModelV2 = z.infer<typeof zCLIPVisionModelV2>;
|
||||||
export const isCLIPVisionModelV2 = (v: unknown): v is CLIPVisionModelV2 => zCLIPVisionModelV2.safeParse(v).success;
|
export const isCLIPVisionModelV2 = (v: unknown): v is CLIPVisionModelV2 => zCLIPVisionModelV2.safeParse(v).success;
|
||||||
|
|
||||||
const zIPMethodV2 = z.enum(['full', 'style', 'composition']);
|
export const zIPMethodV2 = z.enum(['full', 'style', 'composition']);
|
||||||
export type IPMethodV2 = z.infer<typeof zIPMethodV2>;
|
export type IPMethodV2 = z.infer<typeof zIPMethodV2>;
|
||||||
export const isIPMethodV2 = (v: unknown): v is IPMethodV2 => zIPMethodV2.safeParse(v).success;
|
export const isIPMethodV2 = (v: unknown): v is IPMethodV2 => zIPMethodV2.safeParse(v).success;
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
||||||
import { selectControlAdaptersSlice } from 'features/controlAdapters/store/controlAdaptersSlice';
|
import { selectControlAdaptersSlice } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
import { selectControlLayersSlice } from 'features/controlLayers/store/controlLayersSlice';
|
import { selectCanvasV2Slice } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { imageDeletionConfirmed } from 'features/deleteImageModal/store/actions';
|
import { imageDeletionConfirmed } from 'features/deleteImageModal/store/actions';
|
||||||
import { getImageUsage, selectImageUsage } from 'features/deleteImageModal/store/selectors';
|
import { getImageUsage, selectImageUsage } from 'features/deleteImageModal/store/selectors';
|
||||||
import {
|
import {
|
||||||
@ -27,7 +27,7 @@ const selectImageUsages = createMemoizedSelector(
|
|||||||
selectCanvasSlice,
|
selectCanvasSlice,
|
||||||
selectNodesSlice,
|
selectNodesSlice,
|
||||||
selectControlAdaptersSlice,
|
selectControlAdaptersSlice,
|
||||||
selectControlLayersSlice,
|
selectCanvasV2Slice,
|
||||||
selectImageUsage,
|
selectImageUsage,
|
||||||
],
|
],
|
||||||
(deleteImageModal, canvas, nodes, controlAdapters, controlLayers, imagesUsage) => {
|
(deleteImageModal, canvas, nodes, controlAdapters, controlLayers, imagesUsage) => {
|
||||||
|
@ -7,8 +7,8 @@ import {
|
|||||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
import type { ControlAdaptersState } from 'features/controlAdapters/store/types';
|
import type { ControlAdaptersState } from 'features/controlAdapters/store/types';
|
||||||
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
||||||
import { selectControlLayersSlice } from 'features/controlLayers/store/controlLayersSlice';
|
import { selectCanvasV2Slice } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import type { ControlLayersState } from 'features/controlLayers/store/types';
|
import type { CanvasV2State } from 'features/controlLayers/store/types';
|
||||||
import {
|
import {
|
||||||
isControlAdapterLayer,
|
isControlAdapterLayer,
|
||||||
isInitialImageLayer,
|
isInitialImageLayer,
|
||||||
@ -28,7 +28,7 @@ export const getImageUsage = (
|
|||||||
canvas: CanvasState,
|
canvas: CanvasState,
|
||||||
nodes: NodesState,
|
nodes: NodesState,
|
||||||
controlAdapters: ControlAdaptersState,
|
controlAdapters: ControlAdaptersState,
|
||||||
controlLayers: ControlLayersState,
|
controlLayers: CanvasV2State,
|
||||||
image_name: string
|
image_name: string
|
||||||
) => {
|
) => {
|
||||||
const isCanvasImage = canvas.layerState.objects.some((obj) => obj.kind === 'image' && obj.imageName === image_name);
|
const isCanvasImage = canvas.layerState.objects.some((obj) => obj.kind === 'image' && obj.imageName === image_name);
|
||||||
@ -75,7 +75,7 @@ export const selectImageUsage = createMemoizedSelector(
|
|||||||
selectCanvasSlice,
|
selectCanvasSlice,
|
||||||
selectNodesSlice,
|
selectNodesSlice,
|
||||||
selectControlAdaptersSlice,
|
selectControlAdaptersSlice,
|
||||||
selectControlLayersSlice,
|
selectCanvasV2Slice,
|
||||||
(deleteImageModal, canvas, nodes, controlAdapters, controlLayers) => {
|
(deleteImageModal, canvas, nodes, controlAdapters, controlLayers) => {
|
||||||
const { imagesToDelete } = deleteImageModal;
|
const { imagesToDelete } = deleteImageModal;
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
||||||
import { selectControlAdaptersSlice } from 'features/controlAdapters/store/controlAdaptersSlice';
|
import { selectControlAdaptersSlice } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
import { selectControlLayersSlice } from 'features/controlLayers/store/controlLayersSlice';
|
import { selectCanvasV2Slice } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import ImageUsageMessage from 'features/deleteImageModal/components/ImageUsageMessage';
|
import ImageUsageMessage from 'features/deleteImageModal/components/ImageUsageMessage';
|
||||||
import { getImageUsage } from 'features/deleteImageModal/store/selectors';
|
import { getImageUsage } from 'features/deleteImageModal/store/selectors';
|
||||||
import type { ImageUsage } from 'features/deleteImageModal/store/types';
|
import type { ImageUsage } from 'features/deleteImageModal/store/types';
|
||||||
@ -42,7 +42,7 @@ const DeleteBoardModal = (props: Props) => {
|
|||||||
const selectImageUsageSummary = useMemo(
|
const selectImageUsageSummary = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createMemoizedSelector(
|
createMemoizedSelector(
|
||||||
[selectCanvasSlice, selectNodesSlice, selectControlAdaptersSlice, selectControlLayersSlice],
|
[selectCanvasSlice, selectNodesSlice, selectControlAdaptersSlice, selectCanvasV2Slice],
|
||||||
(canvas, nodes, controlAdapters, controlLayers) => {
|
(canvas, nodes, controlAdapters, controlLayers) => {
|
||||||
const allImageUsage = (boardImageNames ?? []).map((imageName) =>
|
const allImageUsage = (boardImageNames ?? []).map((imageName) =>
|
||||||
getImageUsage(canvas, nodes, controlAdapters, controlLayers.present, imageName)
|
getImageUsage(canvas, nodes, controlAdapters, controlLayers.present, imageName)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { Layer } from 'features/controlLayers/store/types';
|
import type { LayerData } from 'features/controlLayers/store/types';
|
||||||
import { MetadataItemView } from 'features/metadata/components/MetadataItemView';
|
import { MetadataItemView } from 'features/metadata/components/MetadataItemView';
|
||||||
import type { MetadataHandlers } from 'features/metadata/types';
|
import type { MetadataHandlers } from 'features/metadata/types';
|
||||||
import { handlers } from 'features/metadata/util/handlers';
|
import { handlers } from 'features/metadata/util/handlers';
|
||||||
@ -9,7 +9,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const MetadataLayers = ({ metadata }: Props) => {
|
export const MetadataLayers = ({ metadata }: Props) => {
|
||||||
const [layers, setLayers] = useState<Layer[]>([]);
|
const [layers, setLayers] = useState<LayerData[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const parse = async () => {
|
const parse = async () => {
|
||||||
@ -40,8 +40,8 @@ const MetadataViewLayer = ({
|
|||||||
handlers,
|
handlers,
|
||||||
}: {
|
}: {
|
||||||
label: string;
|
label: string;
|
||||||
layer: Layer;
|
layer: LayerData;
|
||||||
handlers: MetadataHandlers<Layer[], Layer>;
|
handlers: MetadataHandlers<LayerData[], LayerData>;
|
||||||
}) => {
|
}) => {
|
||||||
const onRecall = useCallback(() => {
|
const onRecall = useCallback(() => {
|
||||||
if (!handlers.recallItem) {
|
if (!handlers.recallItem) {
|
||||||
|
@ -2,7 +2,7 @@ import { getStore } from 'app/store/nanostores/store';
|
|||||||
import { deepClone } from 'common/util/deepClone';
|
import { deepClone } from 'common/util/deepClone';
|
||||||
import { objectKeys } from 'common/util/objectKeys';
|
import { objectKeys } from 'common/util/objectKeys';
|
||||||
import { shouldConcatPromptsChanged } from 'features/controlLayers/store/controlLayersSlice';
|
import { shouldConcatPromptsChanged } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import type { Layer } from 'features/controlLayers/store/types';
|
import type { LayerData } from 'features/controlLayers/store/types';
|
||||||
import type { LoRA } from 'features/lora/store/loraSlice';
|
import type { LoRA } from 'features/lora/store/loraSlice';
|
||||||
import type {
|
import type {
|
||||||
AnyControlAdapterConfigMetadata,
|
AnyControlAdapterConfigMetadata,
|
||||||
@ -49,7 +49,7 @@ const renderControlAdapterValue: MetadataRenderValueFunc<AnyControlAdapterConfig
|
|||||||
return `${value.model.key} (${value.model.base.toUpperCase()}) - ${value.weight}`;
|
return `${value.model.key} (${value.model.base.toUpperCase()}) - ${value.weight}`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const renderLayerValue: MetadataRenderValueFunc<Layer> = async (layer) => {
|
const renderLayerValue: MetadataRenderValueFunc<LayerData> = async (layer) => {
|
||||||
if (layer.type === 'initial_image_layer') {
|
if (layer.type === 'initial_image_layer') {
|
||||||
let rendered = t('controlLayers.globalInitialImageLayer');
|
let rendered = t('controlLayers.globalInitialImageLayer');
|
||||||
if (layer.image) {
|
if (layer.image) {
|
||||||
@ -89,7 +89,7 @@ const renderLayerValue: MetadataRenderValueFunc<Layer> = async (layer) => {
|
|||||||
}
|
}
|
||||||
assert(false, 'Unknown layer type');
|
assert(false, 'Unknown layer type');
|
||||||
};
|
};
|
||||||
const renderLayersValue: MetadataRenderValueFunc<Layer[]> = async (layers) => {
|
const renderLayersValue: MetadataRenderValueFunc<LayerData[]> = async (layers) => {
|
||||||
return `${layers.length} ${t('controlLayers.layers', { count: layers.length })}`;
|
return `${layers.length} ${t('controlLayers.layers', { count: layers.length })}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
} from 'features/controlAdapters/util/buildControlAdapter';
|
} from 'features/controlAdapters/util/buildControlAdapter';
|
||||||
import { buildControlAdapterProcessor } from 'features/controlAdapters/util/buildControlAdapterProcessor';
|
import { buildControlAdapterProcessor } from 'features/controlAdapters/util/buildControlAdapterProcessor';
|
||||||
import { getCALayerId, getIPALayerId, INITIAL_IMAGE_LAYER_ID } from 'features/controlLayers/konva/naming';
|
import { getCALayerId, getIPALayerId, INITIAL_IMAGE_LAYER_ID } from 'features/controlLayers/konva/naming';
|
||||||
import type { ControlAdapterLayer, InitialImageLayer, IPAdapterLayer, Layer } from 'features/controlLayers/store/types';
|
import type { ControlAdapterLayer, InitialImageLayer, IPAdapterLayer, LayerData } from 'features/controlLayers/store/types';
|
||||||
import { zLayer } from 'features/controlLayers/store/types';
|
import { zLayer } from 'features/controlLayers/store/types';
|
||||||
import {
|
import {
|
||||||
CA_PROCESSOR_DATA,
|
CA_PROCESSOR_DATA,
|
||||||
@ -431,22 +431,22 @@ const parseAllIPAdapters: MetadataParseFunc<IPAdapterConfigMetadata[]> = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
//#region Control Layers
|
//#region Control Layers
|
||||||
const parseLayer: MetadataParseFunc<Layer> = async (metadataItem) => zLayer.parseAsync(metadataItem);
|
const parseLayer: MetadataParseFunc<LayerData> = async (metadataItem) => zLayer.parseAsync(metadataItem);
|
||||||
|
|
||||||
const parseLayers: MetadataParseFunc<Layer[]> = async (metadata) => {
|
const parseLayers: MetadataParseFunc<LayerData[]> = async (metadata) => {
|
||||||
// We need to support recalling pre-Control Layers metadata into Control Layers. A separate set of parsers handles
|
// We need to support recalling pre-Control Layers metadata into Control Layers. A separate set of parsers handles
|
||||||
// taking pre-CL metadata and parsing it into layers. It doesn't always map 1-to-1, so this is best-effort. For
|
// taking pre-CL metadata and parsing it into layers. It doesn't always map 1-to-1, so this is best-effort. For
|
||||||
// example, CL Control Adapters don't support resize mode, so we simply omit that property.
|
// example, CL Control Adapters don't support resize mode, so we simply omit that property.
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const layers: Layer[] = [];
|
const layers: LayerData[] = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const control_layers = await getProperty(metadata, 'control_layers');
|
const control_layers = await getProperty(metadata, 'control_layers');
|
||||||
const controlLayersRaw = await getProperty(control_layers, 'layers', isArray);
|
const controlLayersRaw = await getProperty(control_layers, 'layers', isArray);
|
||||||
const controlLayersParseResults = await Promise.allSettled(controlLayersRaw.map(parseLayer));
|
const controlLayersParseResults = await Promise.allSettled(controlLayersRaw.map(parseLayer));
|
||||||
const controlLayers = controlLayersParseResults
|
const controlLayers = controlLayersParseResults
|
||||||
.filter((result): result is PromiseFulfilledResult<Layer> => result.status === 'fulfilled')
|
.filter((result): result is PromiseFulfilledResult<LayerData> => result.status === 'fulfilled')
|
||||||
.map((result) => result.value);
|
.map((result) => result.value);
|
||||||
layers.push(...controlLayers);
|
layers.push(...controlLayers);
|
||||||
} catch {
|
} catch {
|
||||||
|
@ -9,18 +9,18 @@ import {
|
|||||||
import { getCALayerId, getIPALayerId, getRGLayerId } from 'features/controlLayers/konva/naming';
|
import { getCALayerId, getIPALayerId, getRGLayerId } from 'features/controlLayers/konva/naming';
|
||||||
import {
|
import {
|
||||||
allLayersDeleted,
|
allLayersDeleted,
|
||||||
caLayerRecalled,
|
controlAdapterRecalled,
|
||||||
heightChanged,
|
heightChanged,
|
||||||
iiLayerRecalled,
|
iiLayerRecalled,
|
||||||
ipaLayerRecalled,
|
ipAdapterRecalled,
|
||||||
negativePrompt2Changed,
|
negativePrompt2Changed,
|
||||||
negativePromptChanged,
|
negativePromptChanged,
|
||||||
positivePrompt2Changed,
|
positivePrompt2Changed,
|
||||||
positivePromptChanged,
|
positivePromptChanged,
|
||||||
rgLayerRecalled,
|
regionalGuidanceRecalled,
|
||||||
widthChanged,
|
widthChanged,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import type { Layer } from 'features/controlLayers/store/types';
|
import type { LayerData } from 'features/controlLayers/store/types';
|
||||||
import { setHrfEnabled, setHrfMethod, setHrfStrength } from 'features/hrf/store/hrfSlice';
|
import { setHrfEnabled, setHrfMethod, setHrfStrength } from 'features/hrf/store/hrfSlice';
|
||||||
import type { LoRA } from 'features/lora/store/loraSlice';
|
import type { LoRA } from 'features/lora/store/loraSlice';
|
||||||
import { loraRecalled, lorasReset } from 'features/lora/store/loraSlice';
|
import { loraRecalled, lorasReset } from 'features/lora/store/loraSlice';
|
||||||
@ -242,7 +242,7 @@ const recallIPAdapters: MetadataRecallFunc<IPAdapterConfigMetadata[]> = (ipAdapt
|
|||||||
};
|
};
|
||||||
|
|
||||||
//#region Control Layers
|
//#region Control Layers
|
||||||
const recallLayer: MetadataRecallFunc<Layer> = async (layer) => {
|
const recallLayer: MetadataRecallFunc<LayerData> = async (layer) => {
|
||||||
const { dispatch } = getStore();
|
const { dispatch } = getStore();
|
||||||
// We need to check for the existence of all images and models when recalling. If they do not exist, SMITE THEM!
|
// We need to check for the existence of all images and models when recalling. If they do not exist, SMITE THEM!
|
||||||
// Also, we need fresh IDs for all objects when recalling, to prevent multiple layers with the same ID.
|
// Also, we need fresh IDs for all objects when recalling, to prevent multiple layers with the same ID.
|
||||||
@ -269,7 +269,7 @@ const recallLayer: MetadataRecallFunc<Layer> = async (layer) => {
|
|||||||
}
|
}
|
||||||
clone.id = getCALayerId(uuidv4());
|
clone.id = getCALayerId(uuidv4());
|
||||||
clone.controlAdapter.id = uuidv4();
|
clone.controlAdapter.id = uuidv4();
|
||||||
dispatch(caLayerRecalled(clone));
|
dispatch(controlAdapterRecalled(clone));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (layer.type === 'ip_adapter_layer') {
|
if (layer.type === 'ip_adapter_layer') {
|
||||||
@ -289,7 +289,7 @@ const recallLayer: MetadataRecallFunc<Layer> = async (layer) => {
|
|||||||
}
|
}
|
||||||
clone.id = getIPALayerId(uuidv4());
|
clone.id = getIPALayerId(uuidv4());
|
||||||
clone.ipAdapter.id = uuidv4();
|
clone.ipAdapter.id = uuidv4();
|
||||||
dispatch(ipaLayerRecalled(clone));
|
dispatch(ipAdapterRecalled(clone));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,7 +315,7 @@ const recallLayer: MetadataRecallFunc<Layer> = async (layer) => {
|
|||||||
ipAdapter.id = uuidv4();
|
ipAdapter.id = uuidv4();
|
||||||
}
|
}
|
||||||
clone.id = getRGLayerId(uuidv4());
|
clone.id = getRGLayerId(uuidv4());
|
||||||
dispatch(rgLayerRecalled(clone));
|
dispatch(regionalGuidanceRecalled(clone));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,7 +325,7 @@ const recallLayer: MetadataRecallFunc<Layer> = async (layer) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const recallLayers: MetadataRecallFunc<Layer[]> = (layers) => {
|
const recallLayers: MetadataRecallFunc<LayerData[]> = (layers) => {
|
||||||
const { dispatch } = getStore();
|
const { dispatch } = getStore();
|
||||||
dispatch(allLayersDeleted());
|
dispatch(allLayersDeleted());
|
||||||
for (const l of layers) {
|
for (const l of layers) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { getStore } from 'app/store/nanostores/store';
|
import { getStore } from 'app/store/nanostores/store';
|
||||||
import type { Layer } from 'features/controlLayers/store/types';
|
import type { LayerData } from 'features/controlLayers/store/types';
|
||||||
import type { LoRA } from 'features/lora/store/loraSlice';
|
import type { LoRA } from 'features/lora/store/loraSlice';
|
||||||
import type {
|
import type {
|
||||||
ControlNetConfigMetadata,
|
ControlNetConfigMetadata,
|
||||||
@ -110,7 +110,7 @@ const validateIPAdapters: MetadataValidateFunc<IPAdapterConfigMetadata[]> = (ipA
|
|||||||
return new Promise((resolve) => resolve(validatedIPAdapters));
|
return new Promise((resolve) => resolve(validatedIPAdapters));
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateLayer: MetadataValidateFunc<Layer> = async (layer) => {
|
const validateLayer: MetadataValidateFunc<LayerData> = async (layer) => {
|
||||||
if (layer.type === 'control_adapter_layer') {
|
if (layer.type === 'control_adapter_layer') {
|
||||||
const model = layer.controlAdapter.model;
|
const model = layer.controlAdapter.model;
|
||||||
assert(model, 'Control Adapter layer missing model');
|
assert(model, 'Control Adapter layer missing model');
|
||||||
@ -132,8 +132,8 @@ const validateLayer: MetadataValidateFunc<Layer> = async (layer) => {
|
|||||||
return layer;
|
return layer;
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateLayers: MetadataValidateFunc<Layer[]> = async (layers) => {
|
const validateLayers: MetadataValidateFunc<LayerData[]> = async (layers) => {
|
||||||
const validatedLayers: Layer[] = [];
|
const validatedLayers: LayerData[] = [];
|
||||||
for (const l of layers) {
|
for (const l of layers) {
|
||||||
try {
|
try {
|
||||||
const validated = await validateLayer(l);
|
const validated = await validateLayer(l);
|
||||||
|
@ -5,8 +5,8 @@ import openBase64ImageInTab from 'common/util/openBase64ImageInTab';
|
|||||||
import { blobToDataURL } from 'features/canvas/util/blobToDataURL';
|
import { blobToDataURL } from 'features/canvas/util/blobToDataURL';
|
||||||
import { RG_LAYER_NAME } from 'features/controlLayers/konva/naming';
|
import { RG_LAYER_NAME } from 'features/controlLayers/konva/naming';
|
||||||
import { renderers } from 'features/controlLayers/konva/renderers/layers';
|
import { renderers } from 'features/controlLayers/konva/renderers/layers';
|
||||||
import { rgLayerMaskImageUploaded } from 'features/controlLayers/store/controlLayersSlice';
|
import { regionalGuidanceMaskImageUploaded } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import type { InitialImageLayer, Layer, RegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
import type { InitialImageLayer, LayerData, RegionalGuidanceLayer } from 'features/controlLayers/store/types';
|
||||||
import {
|
import {
|
||||||
isControlAdapterLayer,
|
isControlAdapterLayer,
|
||||||
isInitialImageLayer,
|
isInitialImageLayer,
|
||||||
@ -70,7 +70,7 @@ export const addControlLayers = async (
|
|||||||
| Invocation<'vae_loader'>
|
| Invocation<'vae_loader'>
|
||||||
| Invocation<'main_model_loader'>
|
| Invocation<'main_model_loader'>
|
||||||
| Invocation<'sdxl_model_loader'>
|
| Invocation<'sdxl_model_loader'>
|
||||||
): Promise<Layer[]> => {
|
): Promise<LayerData[]> => {
|
||||||
const isSDXL = base === 'sdxl';
|
const isSDXL = base === 'sdxl';
|
||||||
|
|
||||||
const validLayers = state.controlLayers.present.layers.filter((l) => isValidLayer(l, base));
|
const validLayers = state.controlLayers.present.layers.filter((l) => isValidLayer(l, base));
|
||||||
@ -492,7 +492,7 @@ const isValidIPAdapter = (ipa: IPAdapterConfigV2, base: BaseModelType): boolean
|
|||||||
return hasModel && modelMatchesBase && hasImage;
|
return hasModel && modelMatchesBase && hasImage;
|
||||||
};
|
};
|
||||||
|
|
||||||
const isValidLayer = (layer: Layer, base: BaseModelType) => {
|
const isValidLayer = (layer: LayerData, base: BaseModelType) => {
|
||||||
if (!layer.isEnabled) {
|
if (!layer.isEnabled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -532,7 +532,7 @@ const getMaskImage = async (layer: RegionalGuidanceLayer, blob: Blob): Promise<I
|
|||||||
req.reset();
|
req.reset();
|
||||||
|
|
||||||
const imageDTO = await req.unwrap();
|
const imageDTO = await req.unwrap();
|
||||||
dispatch(rgLayerMaskImageUploaded({ layerId: layer.id, imageDTO }));
|
dispatch(regionalGuidanceMaskImageUploaded({ layerId: layer.id, imageDTO }));
|
||||||
return imageDTO;
|
return imageDTO;
|
||||||
};
|
};
|
||||||
//#endregion
|
//#endregion
|
||||||
|
@ -2,7 +2,7 @@ import { Divider, Flex, ListItem, Text, Tooltip, UnorderedList } from '@invoke-a
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { useIsReadyToEnqueue } from 'common/hooks/useIsReadyToEnqueue';
|
import { useIsReadyToEnqueue } from 'common/hooks/useIsReadyToEnqueue';
|
||||||
import { selectControlLayersSlice } from 'features/controlLayers/store/controlLayersSlice';
|
import { selectCanvasV2Slice } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
||||||
import { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt';
|
import { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt';
|
||||||
import type { PropsWithChildren } from 'react';
|
import type { PropsWithChildren } from 'react';
|
||||||
@ -12,7 +12,7 @@ import { useEnqueueBatchMutation } from 'services/api/endpoints/queue';
|
|||||||
import { useBoardName } from 'services/api/hooks/useBoardName';
|
import { useBoardName } from 'services/api/hooks/useBoardName';
|
||||||
|
|
||||||
const selectPromptsCount = createSelector(
|
const selectPromptsCount = createSelector(
|
||||||
selectControlLayersSlice,
|
selectCanvasV2Slice,
|
||||||
selectDynamicPromptsSlice,
|
selectDynamicPromptsSlice,
|
||||||
(controlLayers, dynamicPrompts) =>
|
(controlLayers, dynamicPrompts) =>
|
||||||
getShouldProcessPrompt(controlLayers.present.positivePrompt) ? dynamicPrompts.prompts.length : 1
|
getShouldProcessPrompt(controlLayers.present.positivePrompt) ? dynamicPrompts.prompts.length : 1
|
||||||
|
@ -3,7 +3,7 @@ import { Expander, Flex, FormControlGroup, StandaloneAccordion } from '@invoke-a
|
|||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
||||||
import { selectControlLayersSlice } from 'features/controlLayers/store/controlLayersSlice';
|
import { selectCanvasV2Slice } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { HrfSettings } from 'features/hrf/components/HrfSettings';
|
import { HrfSettings } from 'features/hrf/components/HrfSettings';
|
||||||
import { selectHrfSlice } from 'features/hrf/store/hrfSlice';
|
import { selectHrfSlice } from 'features/hrf/store/hrfSlice';
|
||||||
import ParamScaleBeforeProcessing from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaleBeforeProcessing';
|
import ParamScaleBeforeProcessing from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaleBeforeProcessing';
|
||||||
@ -24,7 +24,7 @@ import { ImageSizeCanvas } from './ImageSizeCanvas';
|
|||||||
import { ImageSizeLinear } from './ImageSizeLinear';
|
import { ImageSizeLinear } from './ImageSizeLinear';
|
||||||
|
|
||||||
const selector = createMemoizedSelector(
|
const selector = createMemoizedSelector(
|
||||||
[selectGenerationSlice, selectCanvasSlice, selectHrfSlice, selectControlLayersSlice, activeTabNameSelector],
|
[selectGenerationSlice, selectCanvasSlice, selectHrfSlice, selectCanvasV2Slice, activeTabNameSelector],
|
||||||
(generation, canvas, hrf, controlLayers, activeTabName) => {
|
(generation, canvas, hrf, controlLayers, activeTabName) => {
|
||||||
const { shouldRandomizeSeed, model } = generation;
|
const { shouldRandomizeSeed, model } = generation;
|
||||||
const { hrfEnabled } = hrf;
|
const { hrfEnabled } = hrf;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user