import { createSelector } from '@reduxjs/toolkit'; import { isEqual } from 'lodash'; import * as InvokeAI from '../../app/invokeai'; import { useAppDispatch, useAppSelector } from '../../app/store'; import { RootState } from '../../app/store'; import { setActiveTab, setAllParameters, setInitialImage, setSeed, setShouldShowImageDetails, } from '../options/optionsSlice'; import DeleteImageModal from './DeleteImageModal'; import { SystemState } from '../system/systemSlice'; import IAIButton from '../../common/components/IAIButton'; import { runESRGAN, runFacetool } from '../../app/socketio/actions'; import IAIIconButton from '../../common/components/IAIIconButton'; import { MdDelete, MdFace, MdHd, MdImage, MdInfo, } from 'react-icons/md'; import InvokePopover from './InvokePopover'; import UpscaleOptions from '../options/AdvancedOptions/Upscale/UpscaleOptions'; import FaceRestoreOptions from '../options/AdvancedOptions/FaceRestore/FaceRestoreOptions'; import { useHotkeys } from 'react-hotkeys-hook'; import { useToast } from '@chakra-ui/react'; import { FaCopy, FaPaintBrush, FaSeedling } from 'react-icons/fa'; import { setImageToInpaint } from '../tabs/Inpainting/inpaintingSlice'; import { hoverableImageSelector } from './gallerySliceSelectors'; const systemSelector = createSelector( (state: RootState) => state.system, (system: SystemState) => { return { isProcessing: system.isProcessing, isConnected: system.isConnected, isGFPGANAvailable: system.isGFPGANAvailable, isESRGANAvailable: system.isESRGANAvailable, }; }, { memoizeOptions: { resultEqualityCheck: isEqual, }, } ); type CurrentImageButtonsProps = { image: InvokeAI.Image; }; /** * Row of buttons for common actions: * Use as init image, use all params, use seed, upscale, fix faces, details, delete. */ const CurrentImageButtons = ({ image }: CurrentImageButtonsProps) => { const dispatch = useAppDispatch(); const { activeTabName } = useAppSelector(hoverableImageSelector); const shouldShowImageDetails = useAppSelector( (state: RootState) => state.options.shouldShowImageDetails ); const toast = useToast(); const intermediateImage = useAppSelector( (state: RootState) => state.gallery.intermediateImage ); const upscalingLevel = useAppSelector( (state: RootState) => state.options.upscalingLevel ); const facetoolStrength = useAppSelector( (state: RootState) => state.options.facetoolStrength ); const { isProcessing, isConnected, isGFPGANAvailable, isESRGANAvailable } = useAppSelector(systemSelector); const handleClickUseAsInitialImage = () => { dispatch(setInitialImage(image)); dispatch(setActiveTab(1)); }; useHotkeys( 'shift+i', () => { if (image) { handleClickUseAsInitialImage(); toast({ title: 'Sent To Image To Image', status: 'success', duration: 2500, isClosable: true, }); } else { toast({ title: 'No Image Loaded', description: 'No image found to send to image to image module.', status: 'error', duration: 2500, isClosable: true, }); } }, [image] ); const handleClickUseAllParameters = () => image.metadata && dispatch(setAllParameters(image.metadata)); useHotkeys( 'a', () => { if (['txt2img', 'img2img'].includes(image?.metadata?.image?.type)) { handleClickUseAllParameters(); toast({ title: 'Parameters Set', status: 'success', duration: 2500, isClosable: true, }); } else { toast({ title: 'Parameters Not Set', description: 'No metadata found for this image.', status: 'error', duration: 2500, isClosable: true, }); } }, [image] ); const handleClickUseSeed = () => image.metadata && dispatch(setSeed(image.metadata.image.seed)); useHotkeys( 's', () => { if (image?.metadata?.image?.seed) { handleClickUseSeed(); toast({ title: 'Seed Set', status: 'success', duration: 2500, isClosable: true, }); } else { toast({ title: 'Seed Not Set', description: 'Could not find seed for this image.', status: 'error', duration: 2500, isClosable: true, }); } }, [image] ); const handleClickUpscale = () => dispatch(runESRGAN(image)); useHotkeys( 'u', () => { if ( isESRGANAvailable && Boolean(!intermediateImage) && isConnected && !isProcessing && upscalingLevel ) { handleClickUpscale(); } else { toast({ title: 'Upscaling Failed', status: 'error', duration: 2500, isClosable: true, }); } }, [ image, isESRGANAvailable, intermediateImage, isConnected, isProcessing, upscalingLevel, ] ); const handleClickFixFaces = () => dispatch(runFacetool(image)); useHotkeys( 'r', () => { if ( isGFPGANAvailable && Boolean(!intermediateImage) && isConnected && !isProcessing && facetoolStrength ) { handleClickFixFaces(); } else { toast({ title: 'Face Restoration Failed', status: 'error', duration: 2500, isClosable: true, }); } }, [ image, isGFPGANAvailable, intermediateImage, isConnected, isProcessing, facetoolStrength, ] ); const handleClickShowImageDetails = () => dispatch(setShouldShowImageDetails(!shouldShowImageDetails)); const handleSendToInpainting = () => { dispatch(setImageToInpaint(image)); if (activeTabName !== 'inpainting') { dispatch(setActiveTab('inpainting')); } toast({ title: 'Sent to Inpainting', status: 'success', duration: 2500, isClosable: true, }); }; useHotkeys( 'i', () => { if (image) { handleClickShowImageDetails(); } else { toast({ title: 'Failed to load metadata', status: 'error', duration: 2500, isClosable: true, }); } }, [image, shouldShowImageDetails] ); return (