Skip to content

Commit 28031ea

Browse files
feat(ui): display canvas generation mode in status text
- use the existing logic to determine if generation is txt2img, img2img, inpaint or outpaint - technically `outpaint` and `inpaint` are the same, just display "Inpaint" if its either - debounce this by 1s to prevent jank
1 parent 00d3cd4 commit 28031ea

File tree

8 files changed

+81
-18
lines changed

8 files changed

+81
-18
lines changed

invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedCanvas.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export const addUserInvokedCanvasListener = () => {
4040
const state = getState();
4141

4242
// Build canvas blobs
43-
const canvasBlobsAndImageData = await getCanvasData(state);
43+
const canvasBlobsAndImageData = await getCanvasData(state.canvas);
4444

4545
if (!canvasBlobsAndImageData) {
4646
log.error('Unable to create canvas data');

invokeai/frontend/web/src/features/canvas/components/IAICanvasStatusText.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { Box, Flex } from '@chakra-ui/react';
22
import { createSelector } from '@reduxjs/toolkit';
33
import { useAppSelector } from 'app/store/storeHooks';
44
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
5+
import GenerationModeStatusText from 'features/parameters/components/Parameters/Canvas/GenerationModeStatusText';
56
import { isEqual } from 'lodash-es';
6-
77
import { useTranslation } from 'react-i18next';
88
import roundToHundreth from '../util/roundToHundreth';
99
import IAICanvasStatusTextCursorPos from './IAICanvasStatusText/IAICanvasStatusTextCursorPos';
@@ -110,6 +110,7 @@ const IAICanvasStatusText = () => {
110110
},
111111
}}
112112
>
113+
<GenerationModeStatusText />
113114
<Box
114115
style={{
115116
color: activeLayerColor,

invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolbar.tsx

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@ import { Box, ButtonGroup, Flex } from '@chakra-ui/react';
22
import { createSelector } from '@reduxjs/toolkit';
33
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
44
import IAIIconButton from 'common/components/IAIIconButton';
5+
import IAIMantineSelect from 'common/components/IAIMantineSelect';
6+
import { useImageUploadButton } from 'common/hooks/useImageUploadButton';
57
import { useSingleAndDoubleClick } from 'common/hooks/useSingleAndDoubleClick';
8+
import {
9+
canvasCopiedToClipboard,
10+
canvasDownloadedAsImage,
11+
canvasMerged,
12+
canvasSavedToGallery,
13+
} from 'features/canvas/store/actions';
614
import {
715
canvasSelector,
816
isStagingSelector,
@@ -21,16 +29,8 @@ import {
2129
} from 'features/canvas/store/canvasTypes';
2230
import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
2331
import { systemSelector } from 'features/system/store/systemSelectors';
32+
import { useCopyImageToClipboard } from 'features/ui/hooks/useCopyImageToClipboard';
2433
import { isEqual } from 'lodash-es';
25-
26-
import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect';
27-
import { useImageUploadButton } from 'common/hooks/useImageUploadButton';
28-
import {
29-
canvasCopiedToClipboard,
30-
canvasDownloadedAsImage,
31-
canvasMerged,
32-
canvasSavedToGallery,
33-
} from 'features/canvas/store/actions';
3434
import { useHotkeys } from 'react-hotkeys-hook';
3535
import { useTranslation } from 'react-i18next';
3636
import {
@@ -48,7 +48,6 @@ import IAICanvasRedoButton from './IAICanvasRedoButton';
4848
import IAICanvasSettingsButtonPopover from './IAICanvasSettingsButtonPopover';
4949
import IAICanvasToolChooserOptions from './IAICanvasToolChooserOptions';
5050
import IAICanvasUndoButton from './IAICanvasUndoButton';
51-
import { useCopyImageToClipboard } from 'features/ui/hooks/useCopyImageToClipboard';
5251

5352
export const selector = createSelector(
5453
[systemSelector, canvasSelector, isStagingSelector],
@@ -220,7 +219,7 @@ const IAICanvasToolbar = () => {
220219
}}
221220
>
222221
<Box w={24}>
223-
<IAIMantineSearchableSelect
222+
<IAIMantineSelect
224223
tooltip={`${t('unifiedCanvas.layer')} (Q)`}
225224
value={layer}
226225
data={LAYER_NAMES_DICT}

invokeai/frontend/web/src/features/canvas/store/canvasSlice.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
CanvasState,
3131
CanvasTool,
3232
Dimensions,
33+
GenerationMode,
3334
isCanvasAnyLine,
3435
isCanvasBaseImage,
3536
isCanvasMaskLine,
@@ -858,6 +859,9 @@ export const canvasSlice = createSlice({
858859
state.isMovingBoundingBox = false;
859860
state.isTransformingBoundingBox = false;
860861
},
862+
generationModeChanged: (state, action: PayloadAction<GenerationMode>) => {
863+
state.generationMode = action.payload;
864+
},
861865
},
862866
extraReducers: (builder) => {
863867
builder.addCase(sessionCanceled.pending, (state) => {
@@ -955,6 +959,7 @@ export const {
955959
stagingAreaInitialized,
956960
canvasSessionIdChanged,
957961
setShouldAntialias,
962+
generationModeChanged,
958963
} = canvasSlice.actions;
959964

960965
export default canvasSlice.reducer;

invokeai/frontend/web/src/features/canvas/store/canvasTypes.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,7 @@ export interface CanvasState {
168168
stageDimensions: Dimensions;
169169
stageScale: number;
170170
tool: CanvasTool;
171+
generationMode?: GenerationMode;
171172
}
173+
174+
export type GenerationMode = 'txt2img' | 'img2img' | 'inpaint' | 'outpaint';

invokeai/frontend/web/src/features/canvas/util/getCanvasData.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { logger } from 'app/logging/logger';
2-
import { RootState } from 'app/store/store';
3-
import { isCanvasMaskLine } from '../store/canvasTypes';
2+
import { CanvasState, isCanvasMaskLine } from '../store/canvasTypes';
43
import createMaskStage from './createMaskStage';
54
import { getCanvasBaseLayer, getCanvasStage } from './konvaInstanceProvider';
65
import { konvaNodeToBlob } from './konvaNodeToBlob';
@@ -9,7 +8,7 @@ import { konvaNodeToImageData } from './konvaNodeToImageData';
98
/**
109
* Gets Blob and ImageData objects for the base and mask layers
1110
*/
12-
export const getCanvasData = async (state: RootState) => {
11+
export const getCanvasData = async (canvasState: CanvasState) => {
1312
const log = logger('canvas');
1413

1514
const canvasBaseLayer = getCanvasBaseLayer();
@@ -26,7 +25,7 @@ export const getCanvasData = async (state: RootState) => {
2625
boundingBoxDimensions,
2726
isMaskEnabled,
2827
shouldPreserveMaskedArea,
29-
} = state.canvas;
28+
} = canvasState;
3029

3130
const boundingBox = {
3231
...boundingBoxCoordinates,

invokeai/frontend/web/src/features/canvas/util/getCanvasGenerationMode.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ import {
22
areAnyPixelsBlack,
33
getImageDataTransparency,
44
} from 'common/util/arrayBuffer';
5+
import { GenerationMode } from '../store/canvasTypes';
56

67
export const getCanvasGenerationMode = (
78
baseImageData: ImageData,
89
maskImageData: ImageData
9-
) => {
10+
): GenerationMode => {
1011
const {
1112
isPartiallyTransparent: baseIsPartiallyTransparent,
1213
isFullyTransparent: baseIsFullyTransparent,
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { Box } from '@chakra-ui/react';
2+
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
3+
import { generationModeChanged } from 'features/canvas/store/canvasSlice';
4+
import { getCanvasData } from 'features/canvas/util/getCanvasData';
5+
import { getCanvasGenerationMode } from 'features/canvas/util/getCanvasGenerationMode';
6+
import { useDebounce } from 'react-use';
7+
8+
const GENERATION_MODE_NAME_MAP = {
9+
txt2img: 'Text to Image',
10+
img2img: 'Image to Image',
11+
inpaint: 'Inpaint',
12+
outpaint: 'Inpaint',
13+
};
14+
15+
export const useGenerationMode = () => {
16+
const dispatch = useAppDispatch();
17+
const canvasState = useAppSelector((state) => state.canvas);
18+
19+
useDebounce(
20+
async () => {
21+
// Build canvas blobs
22+
const canvasBlobsAndImageData = await getCanvasData(canvasState);
23+
24+
if (!canvasBlobsAndImageData) {
25+
return;
26+
}
27+
28+
const { baseImageData, maskImageData } = canvasBlobsAndImageData;
29+
30+
// Determine the generation mode
31+
const generationMode = getCanvasGenerationMode(
32+
baseImageData,
33+
maskImageData
34+
);
35+
36+
dispatch(generationModeChanged(generationMode));
37+
},
38+
1000,
39+
[dispatch, canvasState, generationModeChanged]
40+
);
41+
};
42+
43+
const GenerationModeStatusText = () => {
44+
const generationMode = useAppSelector((state) => state.canvas.generationMode);
45+
46+
useGenerationMode();
47+
48+
return (
49+
<Box>
50+
Mode: {generationMode ? GENERATION_MODE_NAME_MAP[generationMode] : '...'}
51+
</Box>
52+
);
53+
};
54+
55+
export default GenerationModeStatusText;

0 commit comments

Comments
 (0)