refactor(ui): update size/prompts state

This commit is contained in:
psychedelicious 2024-06-15 22:50:31 +10:00
parent 5fc7a03669
commit ccceba7565
19 changed files with 124 additions and 151 deletions

View File

@ -79,15 +79,15 @@ const handleMainModels: ModelHandler = (models, state, dispatch, log) => {
const optimalDimension = getOptimalDimension(defaultModelInList); const optimalDimension = getOptimalDimension(defaultModelInList);
if ( if (
getIsSizeOptimal( getIsSizeOptimal(
state.canvasV2.size.width, state.canvasV2.document.width,
state.canvasV2.size.height, state.canvasV2.document.height,
optimalDimension optimalDimension
) )
) { ) {
return; return;
} }
const { width, height } = calculateNewSize( const { width, height } = calculateNewSize(
state.canvasV2.size.aspectRatio.value, state.canvasV2.document.aspectRatio.value,
optimalDimension * optimalDimension optimalDimension * optimalDimension
); );

View File

@ -1,11 +1,7 @@
import { useStore } from '@nanostores/react'; import { useStore } from '@nanostores/react';
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 { selectControlAdaptersV2Slice } from 'features/controlLayers/store/controlAdaptersSlice';
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice'; import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
import { selectIPAdaptersSlice } from 'features/controlLayers/store/ipAdaptersSlice';
import { selectLayersSlice } from 'features/controlLayers/store/layersSlice';
import { selectRegionalGuidanceSlice } from 'features/controlLayers/store/regionalGuidanceSlice';
import type { CanvasEntity } from 'features/controlLayers/store/types'; import type { CanvasEntity } 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';
@ -40,32 +36,13 @@ const createSelector = (templates: Templates) =>
selectWorkflowSettingsSlice, selectWorkflowSettingsSlice,
selectDynamicPromptsSlice, selectDynamicPromptsSlice,
selectCanvasV2Slice, selectCanvasV2Slice,
selectLayersSlice,
selectControlAdaptersV2Slice,
selectRegionalGuidanceSlice,
selectIPAdaptersSlice,
activeTabNameSelector,
selectUpscalelice, selectUpscalelice,
selectConfigSlice, selectConfigSlice,
activeTabNameSelector,
], ],
( (generation, system, nodes, workflowSettings, dynamicPrompts, canvasV2, upscale, config, activeTabName) => {
generation, const { model, positivePrompt } = generation;
system, const { bbox } = canvasV2;
nodes,
workflowSettings,
dynamicPrompts,
canvasV2,
layersState,
controlAdaptersState,
regionalGuidanceState,
ipAdaptersState,
activeTabName,
upscale,
config
) => {
const { model } = generation;
const { size } = canvasV2;
const { positivePrompt } = canvasV2.prompts;
const { isConnected } = system; const { isConnected } = system;
@ -149,7 +126,7 @@ const createSelector = (templates: Templates) =>
reasons.push({ content: i18n.t('parameters.invoke.noModelSelected') }); reasons.push({ content: i18n.t('parameters.invoke.noModelSelected') });
} }
controlAdaptersState.controlAdapters canvasV2.controlAdapters
.filter((ca) => ca.isEnabled) .filter((ca) => ca.isEnabled)
.forEach((ca, i) => { .forEach((ca, i) => {
const layerLiteral = i18n.t('controlLayers.layers_one'); const layerLiteral = i18n.t('controlLayers.layers_one');
@ -174,7 +151,7 @@ const createSelector = (templates: Templates) =>
// T2I Adapters require images have dimensions that are multiples of 64 (SD1.5) or 32 (SDXL) // T2I Adapters require images have dimensions that are multiples of 64 (SD1.5) or 32 (SDXL)
if (!ca.controlMode) { if (!ca.controlMode) {
const multiple = model?.base === 'sdxl' ? 32 : 64; const multiple = model?.base === 'sdxl' ? 32 : 64;
if (size.width % multiple !== 0 || size.height % multiple !== 0) { if (bbox.width % multiple !== 0 || bbox.height % multiple !== 0) {
problems.push(i18n.t('parameters.invoke.layer.t2iAdapterIncompatibleDimensions', { multiple })); problems.push(i18n.t('parameters.invoke.layer.t2iAdapterIncompatibleDimensions', { multiple }));
} }
} }
@ -185,7 +162,7 @@ const createSelector = (templates: Templates) =>
} }
}); });
ipAdaptersState.ipAdapters canvasV2.ipAdapters
.filter((ipa) => ipa.isEnabled) .filter((ipa) => ipa.isEnabled)
.forEach((ipa, i) => { .forEach((ipa, i) => {
const layerLiteral = i18n.t('controlLayers.layers_one'); const layerLiteral = i18n.t('controlLayers.layers_one');
@ -213,7 +190,7 @@ const createSelector = (templates: Templates) =>
} }
}); });
regionalGuidanceState.regions canvasV2.regions
.filter((rg) => rg.isEnabled) .filter((rg) => rg.isEnabled)
.forEach((rg, i) => { .forEach((rg, i) => {
const layerLiteral = i18n.t('controlLayers.layers_one'); const layerLiteral = i18n.t('controlLayers.layers_one');
@ -250,7 +227,7 @@ const createSelector = (templates: Templates) =>
} }
}); });
layersState.layers canvasV2.layers
.filter((l) => l.isEnabled) .filter((l) => l.isEnabled)
.forEach((l, i) => { .forEach((l, i) => {
const layerLiteral = i18n.t('controlLayers.layers_one'); const layerLiteral = i18n.t('controlLayers.layers_one');

View File

@ -35,7 +35,6 @@ import {
rgLinePointAdded, rgLinePointAdded,
rgRectAdded, rgRectAdded,
rgTranslated, rgTranslated,
selectCanvasV2Slice,
toolBufferChanged, toolBufferChanged,
toolChanged, toolChanged,
} from 'features/controlLayers/store/canvasV2Slice'; } from 'features/controlLayers/store/canvasV2Slice';
@ -65,50 +64,51 @@ const log = logger('controlLayers');
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();
const canvasV2State = useAppSelector(selectCanvasV2Slice); const controlAdapters = useAppSelector((s) => s.canvasV2.controlAdapters);
const ipAdapters = useAppSelector((s) => s.canvasV2.ipAdapters);
const layers = useAppSelector((s) => s.canvasV2.layers);
const regions = useAppSelector((s) => s.canvasV2.regions);
const tool = useAppSelector((s) => s.canvasV2.tool);
const selectedEntityIdentifier = useAppSelector((s) => s.canvasV2.selectedEntityIdentifier);
const maskFillOpacity = useAppSelector((s) => s.canvasV2.maskFillOpacity);
const bbox = useAppSelector((s) => s.canvasV2.bbox);
const lastCursorPos = useStore($lastCursorPos); const lastCursorPos = useStore($lastCursorPos);
const lastMouseDownPos = useStore($lastMouseDownPos); const lastMouseDownPos = useStore($lastMouseDownPos);
const isMouseDown = useStore($isMouseDown); const isMouseDown = useStore($isMouseDown);
const isDrawing = useStore($isDrawing); const isDrawing = useStore($isDrawing);
const selectedEntity = useMemo(() => { const selectedEntity = useMemo(() => {
const identifier = canvasV2State.selectedEntityIdentifier; const identifier = selectedEntityIdentifier;
if (!identifier) { if (!identifier) {
return null; return null;
} else if (identifier.type === 'layer') { } else if (identifier.type === 'layer') {
return canvasV2State.layers.find((i) => i.id === identifier.id) ?? null; return layers.find((i) => i.id === identifier.id) ?? null;
} else if (identifier.type === 'control_adapter') { } else if (identifier.type === 'control_adapter') {
return canvasV2State.controlAdapters.find((i) => i.id === identifier.id) ?? null; return controlAdapters.find((i) => i.id === identifier.id) ?? null;
} else if (identifier.type === 'ip_adapter') { } else if (identifier.type === 'ip_adapter') {
return canvasV2State.ipAdapters.find((i) => i.id === identifier.id) ?? null; return ipAdapters.find((i) => i.id === identifier.id) ?? null;
} else if (identifier.type === 'regional_guidance') { } else if (identifier.type === 'regional_guidance') {
return canvasV2State.regions.find((i) => i.id === identifier.id) ?? null; return regions.find((i) => i.id === identifier.id) ?? null;
} else { } else {
return null; return null;
} }
}, [ }, [controlAdapters, ipAdapters, layers, regions, selectedEntityIdentifier]);
canvasV2State.controlAdapters,
canvasV2State.ipAdapters,
canvasV2State.layers,
canvasV2State.regions,
canvasV2State.selectedEntityIdentifier,
]);
const currentFill = useMemo(() => { const currentFill = useMemo(() => {
if (selectedEntity && selectedEntity.type === 'regional_guidance') { if (selectedEntity && selectedEntity.type === 'regional_guidance') {
return { ...selectedEntity.fill, a: canvasV2State.maskFillOpacity }; return { ...selectedEntity.fill, a: maskFillOpacity };
} }
return canvasV2State.tool.fill; return tool.fill;
}, [canvasV2State.maskFillOpacity, canvasV2State.tool.fill, selectedEntity]); }, [maskFillOpacity, selectedEntity, tool.fill]);
const renderers = useMemo(() => (asPreview ? debouncedRenderers : normalRenderers), [asPreview]); const renderers = useMemo(() => (asPreview ? debouncedRenderers : normalRenderers), [asPreview]);
const dpr = useDevicePixelRatio({ round: false }); const dpr = useDevicePixelRatio({ round: false });
useLayoutEffect(() => { useLayoutEffect(() => {
$toolState.set(canvasV2State.tool); $toolState.set(tool);
$selectedEntity.set(selectedEntity); $selectedEntity.set(selectedEntity);
$bbox.set(canvasV2State.bbox); $bbox.set(bbox);
$currentFill.set(currentFill); $currentFill.set(currentFill);
}, [selectedEntity, canvasV2State.tool, canvasV2State.bbox, currentFill]); }, [selectedEntity, tool, bbox, currentFill]);
const onPosChanged = useCallback( const onPosChanged = useCallback(
(arg: PosChangedArg, entityType: CanvasEntity['type']) => { (arg: PosChangedArg, entityType: CanvasEntity['type']) => {
@ -305,7 +305,7 @@ const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null,
log.trace('Rendering tool preview'); log.trace('Rendering tool preview');
renderers.renderToolPreview( renderers.renderToolPreview(
stage, stage,
canvasV2State.tool, tool,
currentFill, currentFill,
selectedEntity, selectedEntity,
lastCursorPos, lastCursorPos,
@ -315,7 +315,6 @@ const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null,
); );
}, [ }, [
asPreview, asPreview,
canvasV2State.tool,
currentFill, currentFill,
isDrawing, isDrawing,
isMouseDown, isMouseDown,
@ -324,6 +323,7 @@ const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null,
renderers, renderers,
selectedEntity, selectedEntity,
stage, stage,
tool,
]); ]);
useLayoutEffect(() => { useLayoutEffect(() => {
@ -334,8 +334,8 @@ const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null,
log.trace('Rendering bbox preview'); log.trace('Rendering bbox preview');
renderers.renderBboxPreview( renderers.renderBboxPreview(
stage, stage,
canvasV2State.bbox, bbox,
canvasV2State.tool.selected, tool.selected,
$bbox.get, $bbox.get,
onBboxTransformed, onBboxTransformed,
$shift.get, $shift.get,
@ -343,31 +343,31 @@ const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null,
$meta.get, $meta.get,
$alt.get $alt.get
); );
}, [asPreview, canvasV2State.bbox, canvasV2State.tool.selected, onBboxTransformed, renderers, stage]); }, [asPreview, bbox, onBboxTransformed, renderers, stage, tool.selected]);
useLayoutEffect(() => { useLayoutEffect(() => {
log.trace('Rendering layers'); log.trace('Rendering layers');
renderers.renderLayers( renderers.renderLayers(
stage, stage,
canvasV2State.layers, layers,
canvasV2State.controlAdapters, controlAdapters,
canvasV2State.regions, regions,
canvasV2State.maskFillOpacity, maskFillOpacity,
canvasV2State.tool.selected, tool.selected,
selectedEntity, selectedEntity,
getImageDTO, getImageDTO,
onPosChanged onPosChanged
); );
}, [ }, [
stage, controlAdapters,
renderers, layers,
maskFillOpacity,
onPosChanged, onPosChanged,
canvasV2State.tool.selected, regions,
renderers,
selectedEntity, selectedEntity,
canvasV2State.layers, stage,
canvasV2State.controlAdapters, tool.selected,
canvasV2State.regions,
canvasV2State.maskFillOpacity,
]); ]);
// useLayoutEffect(() => { // useLayoutEffect(() => {

View File

@ -21,13 +21,6 @@ import { DEFAULT_RGBA_COLOR } from './types';
const initialState: CanvasV2State = { const initialState: CanvasV2State = {
_version: 3, _version: 3,
selectedEntityIdentifier: null, selectedEntityIdentifier: null,
prompts: {
positivePrompt: '',
negativePrompt: '',
positivePrompt2: '',
negativePrompt2: '',
shouldConcatPrompts: true,
},
tool: { tool: {
selected: 'bbox', selected: 'bbox',
selectedBuffer: null, selectedBuffer: null,
@ -40,7 +33,7 @@ const initialState: CanvasV2State = {
width: 50, width: 50,
}, },
}, },
size: { document: {
width: 512, width: 512,
height: 512, height: 512,
aspectRatio: deepClone(initialAspectRatioState), aspectRatio: deepClone(initialAspectRatioState),
@ -66,41 +59,26 @@ export const canvasV2Slice = createSlice({
...ipAdaptersReducers, ...ipAdaptersReducers,
...controlAdaptersReducers, ...controlAdaptersReducers,
...regionsReducers, ...regionsReducers,
positivePromptChanged: (state, action: PayloadAction<string>) => {
state.prompts.positivePrompt = action.payload;
},
negativePromptChanged: (state, action: PayloadAction<string>) => {
state.prompts.negativePrompt = action.payload;
},
positivePrompt2Changed: (state, action: PayloadAction<string>) => {
state.prompts.positivePrompt2 = action.payload;
},
negativePrompt2Changed: (state, action: PayloadAction<string>) => {
state.prompts.negativePrompt2 = action.payload;
},
shouldConcatPromptsChanged: (state, action: PayloadAction<boolean>) => {
state.prompts.shouldConcatPrompts = action.payload;
},
widthChanged: (state, action: PayloadAction<{ width: number; updateAspectRatio?: boolean; clamp?: boolean }>) => { widthChanged: (state, action: PayloadAction<{ width: number; updateAspectRatio?: boolean; clamp?: boolean }>) => {
const { width, updateAspectRatio, clamp } = action.payload; const { width, updateAspectRatio, clamp } = action.payload;
state.size.width = clamp ? Math.max(roundDownToMultiple(width, 8), 64) : width; state.document.width = clamp ? Math.max(roundDownToMultiple(width, 8), 64) : width;
if (updateAspectRatio) { if (updateAspectRatio) {
state.size.aspectRatio.value = state.size.width / state.size.height; state.document.aspectRatio.value = state.document.width / state.document.height;
state.size.aspectRatio.id = 'Free'; state.document.aspectRatio.id = 'Free';
state.size.aspectRatio.isLocked = false; state.document.aspectRatio.isLocked = false;
} }
}, },
heightChanged: (state, action: PayloadAction<{ height: number; updateAspectRatio?: boolean; clamp?: boolean }>) => { heightChanged: (state, action: PayloadAction<{ height: number; updateAspectRatio?: boolean; clamp?: boolean }>) => {
const { height, updateAspectRatio, clamp } = action.payload; const { height, updateAspectRatio, clamp } = action.payload;
state.size.height = clamp ? Math.max(roundDownToMultiple(height, 8), 64) : height; state.document.height = clamp ? Math.max(roundDownToMultiple(height, 8), 64) : height;
if (updateAspectRatio) { if (updateAspectRatio) {
state.size.aspectRatio.value = state.size.width / state.size.height; state.document.aspectRatio.value = state.document.width / state.document.height;
state.size.aspectRatio.id = 'Free'; state.document.aspectRatio.id = 'Free';
state.size.aspectRatio.isLocked = false; state.document.aspectRatio.isLocked = false;
} }
}, },
aspectRatioChanged: (state, action: PayloadAction<AspectRatioState>) => { aspectRatioChanged: (state, action: PayloadAction<AspectRatioState>) => {
state.size.aspectRatio = action.payload; state.document.aspectRatio = action.payload;
}, },
bboxChanged: (state, action: PayloadAction<IRect>) => { bboxChanged: (state, action: PayloadAction<IRect>) => {
state.bbox = action.payload; state.bbox = action.payload;
@ -144,22 +122,17 @@ export const canvasV2Slice = createSlice({
return; return;
} }
const optimalDimension = getOptimalDimension(newModel); const optimalDimension = getOptimalDimension(newModel);
if (getIsSizeOptimal(state.size.width, state.size.height, optimalDimension)) { if (getIsSizeOptimal(state.document.width, state.document.height, optimalDimension)) {
return; return;
} }
const { width, height } = calculateNewSize(state.size.aspectRatio.value, optimalDimension * optimalDimension); const { width, height } = calculateNewSize(state.document.aspectRatio.value, optimalDimension * optimalDimension);
state.size.width = width; state.document.width = width;
state.size.height = height; state.document.height = height;
}); });
}, },
}); });
export const { export const {
positivePromptChanged,
negativePromptChanged,
positivePrompt2Changed,
negativePrompt2Changed,
shouldConcatPromptsChanged,
widthChanged, widthChanged,
heightChanged, heightChanged,
aspectRatioChanged, aspectRatioChanged,

View File

@ -3,10 +3,6 @@ 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,
ParameterNegativePrompt,
ParameterNegativeStylePromptSDXL,
ParameterPositivePrompt,
ParameterPositiveStylePromptSDXL,
ParameterWidth, ParameterWidth,
} from 'features/parameters/types/parameterSchemas'; } from 'features/parameters/types/parameterSchemas';
import { import {
@ -758,13 +754,6 @@ export type CanvasEntityIdentifier = Pick<CanvasEntity, 'id' | 'type'>;
export type CanvasV2State = { export type CanvasV2State = {
_version: 3; _version: 3;
selectedEntityIdentifier: CanvasEntityIdentifier | null; selectedEntityIdentifier: CanvasEntityIdentifier | null;
prompts: {
positivePrompt: ParameterPositivePrompt;
negativePrompt: ParameterNegativePrompt;
positivePrompt2: ParameterPositiveStylePromptSDXL;
negativePrompt2: ParameterNegativeStylePromptSDXL;
shouldConcatPrompts: boolean;
};
tool: { tool: {
selected: Tool; selected: Tool;
selectedBuffer: Tool | null; selectedBuffer: Tool | null;
@ -777,7 +766,7 @@ export type CanvasV2State = {
}; };
fill: RgbaColor; fill: RgbaColor;
}; };
size: { document: {
width: ParameterWidth; width: ParameterWidth;
height: ParameterHeight; height: ParameterHeight;
aspectRatio: AspectRatioState; aspectRatio: AspectRatioState;

View File

@ -421,7 +421,7 @@ const addInitialImageLayerToGraph = (
) => { ) => {
const { vaePrecision } = state.generation; const { vaePrecision } = state.generation;
const { refinerModel, refinerStart } = state.sdxl; const { refinerModel, refinerStart } = state.sdxl;
const { width, height } = state.canvasV2.size; const { width, height } = state.canvasV2.document;
assert(layer.isEnabled, 'Initial image layer is not enabled'); assert(layer.isEnabled, 'Initial image layer is not enabled');
assert(layer.image, 'Initial image layer has no image'); assert(layer.image, 'Initial image layer has no image');
@ -568,7 +568,7 @@ const buildControlImage = (
const getRGLayerBlobs = async (layerIds?: string[], preview: boolean = false): Promise<Record<string, Blob>> => { const getRGLayerBlobs = async (layerIds?: string[], preview: boolean = false): Promise<Record<string, Blob>> => {
const state = getStore().getState(); const state = getStore().getState();
const { layers } = state.canvasV2; const { layers } = state.canvasV2;
const { width, height } = state.canvasV2.size; const { width, height } = state.canvasV2.document;
const reduxLayers = layers.filter(isRegionalGuidanceLayer); const reduxLayers = layers.filter(isRegionalGuidanceLayer);
const container = document.createElement('div'); const container = document.createElement('div');
const stage = new Konva.Stage({ container, width, height }); const stage = new Konva.Stage({ container, width, height });

View File

@ -74,7 +74,7 @@ export const addHRF = (
vaeSource: Invocation<'vae_loader'> | Invocation<'main_model_loader'> | Invocation<'seamless'> vaeSource: Invocation<'vae_loader'> | Invocation<'main_model_loader'> | Invocation<'seamless'>
): Invocation<'l2i'> => { ): Invocation<'l2i'> => {
const { hrfStrength, hrfEnabled, hrfMethod } = state.hrf; const { hrfStrength, hrfEnabled, hrfMethod } = state.hrf;
const { width, height } = state.canvasV2.size; const { width, height } = state.canvasV2.document;
const optimalDimension = selectOptimalDimension(state); const optimalDimension = selectOptimalDimension(state);
const { newWidth: hrfWidth, newHeight: hrfHeight } = calculateHrfRes(optimalDimension, width, height); const { newWidth: hrfWidth, newHeight: hrfHeight } = calculateHrfRes(optimalDimension, width, height);

View File

@ -40,7 +40,7 @@ export const buildGenerationTabGraph = async (state: RootState): Promise<GraphTy
seed, seed,
vae, vae,
} = state.generation; } = state.generation;
const { width, height } = state.controlLayers.present.size; const { width, height } = state.canvasV2.document;
assert(model, 'No model found in state'); assert(model, 'No model found in state');

View File

@ -36,7 +36,7 @@ export const buildGenerationTabSDXLGraph = async (state: RootState): Promise<Non
vaePrecision, vaePrecision,
vae, vae,
} = state.generation; } = state.generation;
const { width, height } = state.controlLayers.present.size; const { width, height } = state.canvasV2.document;
const { refinerModel, refinerStart } = state.sdxl; const { refinerModel, refinerStart } = state.sdxl;

View File

@ -22,7 +22,7 @@ export const getPresetModifiedPrompts = (
state: RootState state: RootState
): { positivePrompt: string; negativePrompt: string; positiveStylePrompt?: string; negativeStylePrompt?: string } => { ): { positivePrompt: string; negativePrompt: string; positiveStylePrompt?: string; negativeStylePrompt?: string } => {
const { positivePrompt, negativePrompt, positivePrompt2, negativePrompt2, shouldConcatPrompts } = const { positivePrompt, negativePrompt, positivePrompt2, negativePrompt2, shouldConcatPrompts } =
state.canvasV2.prompts; state.generation;
const { activeStylePresetId } = state.stylePreset; const { activeStylePresetId } = state.stylePreset;
if (activeStylePresetId) { if (activeStylePresetId) {

View File

@ -1,9 +1,9 @@
import { Box, Textarea } from '@invoke-ai/ui-library'; import { Box, Textarea } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { negativePromptChanged } from 'features/controlLayers/store/canvasV2Slice';
import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel'; import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel';
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper'; import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
import { ViewModePrompt } from 'features/parameters/components/Prompts/ViewModePrompt'; import { ViewModePrompt } from 'features/parameters/components/Prompts/ViewModePrompt';
import { negativePromptChanged } from 'features/parameters/store/generationSlice';
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton'; import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
import { PromptPopover } from 'features/prompt/PromptPopover'; import { PromptPopover } from 'features/prompt/PromptPopover';
import { usePrompt } from 'features/prompt/usePrompt'; import { usePrompt } from 'features/prompt/usePrompt';
@ -13,7 +13,7 @@ import { useListStylePresetsQuery } from 'services/api/endpoints/stylePresets';
export const ParamNegativePrompt = memo(() => { export const ParamNegativePrompt = memo(() => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const prompt = useAppSelector((s) => s.canvasV2.prompts.negativePrompt); const prompt = useAppSelector((s) => s.generation.negativePrompt);
const viewMode = useAppSelector((s) => s.stylePreset.viewMode); const viewMode = useAppSelector((s) => s.stylePreset.viewMode);
const activeStylePresetId = useAppSelector((s) => s.stylePreset.activeStylePresetId); const activeStylePresetId = useAppSelector((s) => s.stylePreset.activeStylePresetId);

View File

@ -1,10 +1,10 @@
import { Box, Textarea } from '@invoke-ai/ui-library'; import { Box, Textarea } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { positivePromptChanged } from 'features/controlLayers/store/canvasV2Slice';
import { ShowDynamicPromptsPreviewButton } from 'features/dynamicPrompts/components/ShowDynamicPromptsPreviewButton'; import { ShowDynamicPromptsPreviewButton } from 'features/dynamicPrompts/components/ShowDynamicPromptsPreviewButton';
import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel'; import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel';
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper'; import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
import { ViewModePrompt } from 'features/parameters/components/Prompts/ViewModePrompt'; import { ViewModePrompt } from 'features/parameters/components/Prompts/ViewModePrompt';
import { positivePromptChanged } from 'features/parameters/store/generationSlice';
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton'; import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
import { PromptPopover } from 'features/prompt/PromptPopover'; import { PromptPopover } from 'features/prompt/PromptPopover';
import { usePrompt } from 'features/prompt/usePrompt'; import { usePrompt } from 'features/prompt/usePrompt';
@ -17,7 +17,7 @@ import { useListStylePresetsQuery } from 'services/api/endpoints/stylePresets';
export const ParamPositivePrompt = memo(() => { export const ParamPositivePrompt = memo(() => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const prompt = useAppSelector((s) => s.canvasV2.positivePrompt); const prompt = useAppSelector((s) => s.generation.positivePrompt);
const baseModel = useAppSelector((s) => s.generation.model)?.base; const baseModel = useAppSelector((s) => s.generation.model)?.base;
const viewMode = useAppSelector((s) => s.stylePreset.viewMode); const viewMode = useAppSelector((s) => s.stylePreset.viewMode);
const activeStylePresetId = useAppSelector((s) => s.stylePreset.activeStylePresetId); const activeStylePresetId = useAppSelector((s) => s.stylePreset.activeStylePresetId);

View File

@ -24,14 +24,8 @@ const initialGenerationState: GenerationState = {
cfgScale: 7.5, cfgScale: 7.5,
cfgRescaleMultiplier: 0, cfgRescaleMultiplier: 0,
img2imgStrength: 0.75, img2imgStrength: 0.75,
infillMethod: 'patchmatch',
iterations: 1, iterations: 1,
scheduler: 'euler', scheduler: 'euler',
maskBlur: 16,
maskBlurMethod: 'box',
canvasCoherenceMode: 'Gaussian Blur',
canvasCoherenceMinDenoise: 0,
canvasCoherenceEdgeSize: 16,
seed: 0, seed: 0,
shouldRandomizeSeed: true, shouldRandomizeSeed: true,
steps: 50, steps: 50,
@ -43,6 +37,12 @@ const initialGenerationState: GenerationState = {
clipSkip: 0, clipSkip: 0,
shouldUseCpuNoise: true, shouldUseCpuNoise: true,
shouldShowAdvancedOptions: false, shouldShowAdvancedOptions: false,
maskBlur: 16,
maskBlurMethod: 'box',
canvasCoherenceMode: 'Gaussian Blur',
canvasCoherenceMinDenoise: 0,
canvasCoherenceEdgeSize: 16,
infillMethod: 'patchmatch',
infillTileSize: 32, infillTileSize: 32,
infillPatchmatchDownscaleSize: 1, infillPatchmatchDownscaleSize: 1,
infillMosaicTileWidth: 64, infillMosaicTileWidth: 64,
@ -50,6 +50,11 @@ const initialGenerationState: GenerationState = {
infillMosaicMinColor: { r: 0, g: 0, b: 0, a: 1 }, infillMosaicMinColor: { r: 0, g: 0, b: 0, a: 1 },
infillMosaicMaxColor: { r: 255, g: 255, b: 255, a: 1 }, infillMosaicMaxColor: { r: 255, g: 255, b: 255, a: 1 },
infillColorValue: { r: 0, g: 0, b: 0, a: 1 }, infillColorValue: { r: 0, g: 0, b: 0, a: 1 },
positivePrompt: '',
negativePrompt: '',
positivePrompt2: '',
negativePrompt2: '',
shouldConcatPrompts: true,
}; };
export const generationSlice = createSlice({ export const generationSlice = createSlice({
@ -166,6 +171,21 @@ export const generationSlice = createSlice({
setInfillColorValue: (state, action: PayloadAction<RgbaColor>) => { setInfillColorValue: (state, action: PayloadAction<RgbaColor>) => {
state.infillColorValue = action.payload; state.infillColorValue = action.payload;
}, },
positivePromptChanged: (state, action: PayloadAction<string>) => {
state.positivePrompt = action.payload;
},
negativePromptChanged: (state, action: PayloadAction<string>) => {
state.negativePrompt = action.payload;
},
positivePrompt2Changed: (state, action: PayloadAction<string>) => {
state.positivePrompt2 = action.payload;
},
negativePrompt2Changed: (state, action: PayloadAction<string>) => {
state.negativePrompt2 = action.payload;
},
shouldConcatPromptsChanged: (state, action: PayloadAction<boolean>) => {
state.shouldConcatPrompts = action.payload;
},
}, },
extraReducers: (builder) => { extraReducers: (builder) => {
builder.addCase(configChanged, (state, action) => { builder.addCase(configChanged, (state, action) => {
@ -210,6 +230,11 @@ export const {
setInfillMosaicMinColor, setInfillMosaicMinColor,
setInfillMosaicMaxColor, setInfillMosaicMaxColor,
setInfillColorValue, setInfillColorValue,
positivePromptChanged,
negativePromptChanged,
positivePrompt2Changed,
negativePrompt2Changed,
shouldConcatPromptsChanged,
} = generationSlice.actions; } = generationSlice.actions;
export const { selectOptimalDimension } = generationSlice.selectors; export const { selectOptimalDimension } = generationSlice.selectors;

View File

@ -5,6 +5,10 @@ import type {
ParameterCFGScale, ParameterCFGScale,
ParameterMaskBlurMethod, ParameterMaskBlurMethod,
ParameterModel, ParameterModel,
ParameterNegativePrompt,
ParameterNegativeStylePromptSDXL,
ParameterPositivePrompt,
ParameterPositiveStylePromptSDXL,
ParameterPrecision, ParameterPrecision,
ParameterScheduler, ParameterScheduler,
ParameterSeed, ParameterSeed,
@ -45,6 +49,11 @@ export interface GenerationState {
infillMosaicMinColor: RgbaColor; infillMosaicMinColor: RgbaColor;
infillMosaicMaxColor: RgbaColor; infillMosaicMaxColor: RgbaColor;
infillColorValue: RgbaColor; infillColorValue: RgbaColor;
positivePrompt: ParameterPositivePrompt;
negativePrompt: ParameterNegativePrompt;
positivePrompt2: ParameterPositiveStylePromptSDXL;
negativePrompt2: ParameterNegativeStylePromptSDXL;
shouldConcatPrompts: boolean;
} }
export type PayloadActionWithOptimalDimension<T = void> = PayloadAction<T, string, { optimalDimension: number }>; export type PayloadActionWithOptimalDimension<T = void> = PayloadAction<T, string, { optimalDimension: number }>;

View File

@ -1,8 +1,8 @@
import { Box, Textarea } from '@invoke-ai/ui-library'; import { Box, Textarea } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { negativePrompt2Changed } from 'features/controlLayers/store/canvasV2Slice';
import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel'; import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel';
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper'; import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
import { negativePrompt2Changed } from 'features/parameters/store/generationSlice';
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton'; import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
import { PromptPopover } from 'features/prompt/PromptPopover'; import { PromptPopover } from 'features/prompt/PromptPopover';
import { usePrompt } from 'features/prompt/usePrompt'; import { usePrompt } from 'features/prompt/usePrompt';
@ -12,7 +12,7 @@ import { useTranslation } from 'react-i18next';
export const ParamSDXLNegativeStylePrompt = memo(() => { export const ParamSDXLNegativeStylePrompt = memo(() => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const prompt = useAppSelector((s) => s.canvasV2.negativePrompt2); const prompt = useAppSelector((s) => s.generation.negativePrompt2);
const textareaRef = useRef<HTMLTextAreaElement>(null); const textareaRef = useRef<HTMLTextAreaElement>(null);
const { t } = useTranslation(); const { t } = useTranslation();
const handleChange = useCallback( const handleChange = useCallback(

View File

@ -1,8 +1,8 @@
import { Box, Textarea } from '@invoke-ai/ui-library'; import { Box, Textarea } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { positivePrompt2Changed } from 'features/controlLayers/store/canvasV2Slice';
import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel'; import { PromptLabel } from 'features/parameters/components/Prompts/PromptLabel';
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper'; import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
import { positivePrompt2Changed } from 'features/parameters/store/generationSlice';
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton'; import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
import { PromptPopover } from 'features/prompt/PromptPopover'; import { PromptPopover } from 'features/prompt/PromptPopover';
import { usePrompt } from 'features/prompt/usePrompt'; import { usePrompt } from 'features/prompt/usePrompt';
@ -11,7 +11,7 @@ import { useTranslation } from 'react-i18next';
export const ParamSDXLPositiveStylePrompt = memo(() => { export const ParamSDXLPositiveStylePrompt = memo(() => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const prompt = useAppSelector((s) => s.canvasV2.positivePrompt2); const prompt = useAppSelector((s) => s.generation.positivePrompt2);
const textareaRef = useRef<HTMLTextAreaElement>(null); const textareaRef = useRef<HTMLTextAreaElement>(null);
const { t } = useTranslation(); const { t } = useTranslation();
const handleChange = useCallback( const handleChange = useCallback(

View File

@ -1,12 +1,12 @@
import { IconButton, Tooltip } from '@invoke-ai/ui-library'; import { IconButton, Tooltip } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { shouldConcatPromptsChanged } from 'features/controlLayers/store/canvasV2Slice'; import { shouldConcatPromptsChanged } from 'features/parameters/store/generationSlice';
import { memo, useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { PiLinkSimpleBold, PiLinkSimpleBreakBold } from 'react-icons/pi'; import { PiLinkSimpleBold, PiLinkSimpleBreakBold } from 'react-icons/pi';
export const SDXLConcatButton = memo(() => { export const SDXLConcatButton = memo(() => {
const shouldConcatPrompts = useAppSelector((s) => s.canvasV2.shouldConcatPrompts); const shouldConcatPrompts = useAppSelector((s) => s.generation.shouldConcatPrompts);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();

View File

@ -29,7 +29,7 @@ const selector = createMemoizedSelector(
const badges: string[] = []; const badges: string[] = [];
const isSDXL = model?.base === 'sdxl'; const isSDXL = model?.base === 'sdxl';
const { aspectRatio, width, height } = canvasV2.size; const { aspectRatio, width, height } = canvasV2.document;
badges.push(`${width}×${height}`); badges.push(`${width}×${height}`);
badges.push(aspectRatio.id); badges.push(aspectRatio.id);

View File

@ -9,9 +9,9 @@ import { memo, useCallback } from 'react';
export const ImageSizeLinear = memo(() => { export const ImageSizeLinear = memo(() => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const width = useAppSelector((s) => s.canvasV2.size.width); const width = useAppSelector((s) => s.canvasV2.document.width);
const height = useAppSelector((s) => s.canvasV2.size.height); const height = useAppSelector((s) => s.canvasV2.document.height);
const aspectRatioState = useAppSelector((s) => s.canvasV2.size.aspectRatio); const aspectRatioState = useAppSelector((s) => s.canvasV2.document.aspectRatio);
const onChangeWidth = useCallback( const onChangeWidth = useCallback(
(width: number) => { (width: number) => {