diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index c885af9afe5..8b679897138 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -1787,6 +1787,8 @@ "newGallerySessionDesc": "This will clear the canvas and all settings except for your model selection. Generations will be sent to the gallery.", "newCanvasSession": "New Canvas Session", "newCanvasSessionDesc": "This will clear the canvas and all settings except for your model selection. Generations will be staged on the canvas.", + "resetCanvasLayers": "Reset Canvas Layers", + "resetGenerationSettings": "Reset Generation Settings", "replaceCurrent": "Replace Current", "controlLayerEmptyState": "Upload an image, drag an image from the gallery onto this layer, or draw on the canvas to get started.", "controlMode": { diff --git a/invokeai/frontend/web/src/common/components/SessionMenuItems.tsx b/invokeai/frontend/web/src/common/components/SessionMenuItems.tsx new file mode 100644 index 00000000000..0f931b17c44 --- /dev/null +++ b/invokeai/frontend/web/src/common/components/SessionMenuItems.tsx @@ -0,0 +1,42 @@ +import { MenuItem } from '@invoke-ai/ui-library'; +import { useAppDispatch } from 'app/store/storeHooks'; +import { + useNewCanvasSession, + useNewGallerySession, +} from 'features/controlLayers/components/NewSessionConfirmationAlertDialog'; +import { canvasReset } from 'features/controlLayers/store/actions'; +import { paramsReset } from 'features/controlLayers/store/paramsSlice'; +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { PiArrowsCounterClockwiseBold, PiFilePlusBold } from 'react-icons/pi'; + +export const SessionMenuItems = memo(() => { + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + const { newGallerySessionWithDialog } = useNewGallerySession(); + const { newCanvasSessionWithDialog } = useNewCanvasSession(); + const resetCanvasLayers = useCallback(() => { + dispatch(canvasReset()); + }, [dispatch]); + const resetGenerationSettings = useCallback(() => { + dispatch(paramsReset()); + }, [dispatch]); + return ( + <> + } onClick={newGallerySessionWithDialog}> + {t('controlLayers.newGallerySession')} + + } onClick={newCanvasSessionWithDialog}> + {t('controlLayers.newCanvasSession')} + + } onClick={resetCanvasLayers}> + {t('controlLayers.resetCanvasLayers')} + + } onClick={resetGenerationSettings}> + {t('controlLayers.resetGenerationSettings')} + + + ); +}); + +SessionMenuItems.displayName = 'SessionMenuItems'; diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarNewSessionMenuButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarNewSessionMenuButton.tsx index efc87d3ff01..a55c768f9fe 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarNewSessionMenuButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarNewSessionMenuButton.tsx @@ -1,16 +1,11 @@ -import { IconButton, Menu, MenuButton, MenuItem, MenuList } from '@invoke-ai/ui-library'; -import { - useNewCanvasSession, - useNewGallerySession, -} from 'features/controlLayers/components/NewSessionConfirmationAlertDialog'; +import { IconButton, Menu, MenuButton, MenuList } from '@invoke-ai/ui-library'; +import { SessionMenuItems } from 'common/components/SessionMenuItems'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { PiFilePlusBold, PiImageBold, PiPaintBrushBold } from 'react-icons/pi'; +import { PiFilePlusBold } from 'react-icons/pi'; export const CanvasToolbarNewSessionMenuButton = memo(() => { const { t } = useTranslation(); - const { newGallerySessionWithDialog } = useNewGallerySession(); - const { newCanvasSessionWithDialog } = useNewCanvasSession(); return ( { alignSelf="stretch" /> - } onClick={newGallerySessionWithDialog}> - {t('controlLayers.newGallerySession')} - - } onClick={newCanvasSessionWithDialog}> - {t('controlLayers.newCanvasSession')} - + ); diff --git a/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts b/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts index f70dc697d3a..582419a247e 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts @@ -273,24 +273,27 @@ export const paramsSlice = createSlice({ setCanvasCoherenceMinDenoise: (state, action: PayloadAction) => { state.canvasCoherenceMinDenoise = action.payload; }, + paramsReset: (state) => resetState(state), }, extraReducers(builder) { - builder.addMatcher(newSessionRequested, (state) => { - // When a new session is requested, we need to keep the current model selections, plus dependent state - // like VAE precision. Everything else gets reset to default. - const newState = deepClone(initialState); - newState.model = state.model; - newState.vae = state.vae; - newState.fluxVAE = state.fluxVAE; - newState.vaePrecision = state.vaePrecision; - newState.t5EncoderModel = state.t5EncoderModel; - newState.clipEmbedModel = state.clipEmbedModel; - newState.refinerModel = state.refinerModel; - return newState; - }); + builder.addMatcher(newSessionRequested, (state) => resetState(state)); }, }); +const resetState = (state: ParamsState): ParamsState => { + // When a new session is requested, we need to keep the current model selections, plus dependent state + // like VAE precision. Everything else gets reset to default. + const newState = deepClone(initialState); + newState.model = state.model; + newState.vae = state.vae; + newState.fluxVAE = state.fluxVAE; + newState.vaePrecision = state.vaePrecision; + newState.t5EncoderModel = state.t5EncoderModel; + newState.clipEmbedModel = state.clipEmbedModel; + newState.refinerModel = state.refinerModel; + return newState; +}; + export const { setInfillMethod, setInfillTileSize, @@ -334,6 +337,7 @@ export const { setRefinerNegativeAestheticScore, setRefinerStart, modelChanged, + paramsReset, } = paramsSlice.actions; /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ diff --git a/invokeai/frontend/web/src/features/queue/components/QueueActionsMenuButton.tsx b/invokeai/frontend/web/src/features/queue/components/QueueActionsMenuButton.tsx index 1ae76eec4b7..e4ed61b5653 100644 --- a/invokeai/frontend/web/src/features/queue/components/QueueActionsMenuButton.tsx +++ b/invokeai/frontend/web/src/features/queue/components/QueueActionsMenuButton.tsx @@ -1,9 +1,6 @@ import { IconButton, Menu, MenuButton, MenuGroup, MenuItem, MenuList } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; -import { - useNewCanvasSession, - useNewGallerySession, -} from 'features/controlLayers/components/NewSessionConfirmationAlertDialog'; +import { SessionMenuItems } from 'common/components/SessionMenuItems'; import { useClearQueue } from 'features/queue/components/ClearQueueConfirmationAlertDialog'; import { QueueCountBadge } from 'features/queue/components/QueueCountBadge'; import { usePauseProcessor } from 'features/queue/hooks/usePauseProcessor'; @@ -12,16 +9,7 @@ import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; import { setActiveTab } from 'features/ui/store/uiSlice'; import { memo, useCallback, useRef } from 'react'; import { useTranslation } from 'react-i18next'; -import { - PiImageBold, - PiListBold, - PiPaintBrushBold, - PiPauseFill, - PiPlayFill, - PiQueueBold, - PiTrashSimpleBold, - PiXBold, -} from 'react-icons/pi'; +import { PiListBold, PiPauseFill, PiPlayFill, PiQueueBold, PiTrashSimpleBold, PiXBold } from 'react-icons/pi'; export const QueueActionsMenuButton = memo(() => { const ref = useRef(null); @@ -29,8 +17,6 @@ export const QueueActionsMenuButton = memo(() => { const { t } = useTranslation(); const isPauseEnabled = useFeatureStatus('pauseQueue'); const isResumeEnabled = useFeatureStatus('resumeQueue'); - const { newGallerySessionWithDialog } = useNewGallerySession(); - const { newCanvasSessionWithDialog } = useNewCanvasSession(); const clearQueue = useClearQueue(); const { resumeProcessor, @@ -52,12 +38,7 @@ export const QueueActionsMenuButton = memo(() => { } /> - } onClick={newGallerySessionWithDialog}> - {t('controlLayers.newGallerySession')} - - } onClick={newCanvasSessionWithDialog}> - {t('controlLayers.newCanvasSession')} - +