mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
Merge branch 'main' of https://github.com/invoke-ai/InvokeAI into responsive-ui
This commit is contained in:
@@ -6,19 +6,18 @@ import {
|
||||
Box,
|
||||
Flex,
|
||||
} from '@chakra-ui/react';
|
||||
import { Feature } from 'app/features';
|
||||
import GuideIcon from 'common/components/GuideIcon';
|
||||
import { ReactNode } from 'react';
|
||||
import { ParametersAccordionItem } from '../ParametersAccordion';
|
||||
|
||||
export interface InvokeAccordionItemProps {
|
||||
header: string;
|
||||
content: ReactNode;
|
||||
feature?: Feature;
|
||||
additionalHeaderComponents?: ReactNode;
|
||||
}
|
||||
type InvokeAccordionItemProps = {
|
||||
accordionItem: ParametersAccordionItem;
|
||||
};
|
||||
|
||||
export default function InvokeAccordionItem(props: InvokeAccordionItemProps) {
|
||||
const { header, feature, content, additionalHeaderComponents } = props;
|
||||
export default function InvokeAccordionItem({
|
||||
accordionItem,
|
||||
}: InvokeAccordionItemProps) {
|
||||
const { header, feature, content, additionalHeaderComponents } =
|
||||
accordionItem;
|
||||
|
||||
return (
|
||||
<AccordionItem>
|
||||
@@ -32,7 +31,7 @@ export default function InvokeAccordionItem(props: InvokeAccordionItemProps) {
|
||||
<AccordionIcon />
|
||||
</Flex>
|
||||
</AccordionButton>
|
||||
<AccordionPanel>{content}</AccordionPanel>
|
||||
<AccordionPanel p={4}>{content}</AccordionPanel>
|
||||
</AccordionItem>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -115,9 +115,7 @@ const InfillAndScalingSettings = () => {
|
||||
onChange={handleChangeBoundingBoxScaleMethod}
|
||||
/>
|
||||
<IAISlider
|
||||
isInputDisabled={!isManual}
|
||||
isResetDisabled={!isManual}
|
||||
isSliderDisabled={!isManual}
|
||||
isDisabled={!isManual}
|
||||
label={t('parameters.scaledWidth')}
|
||||
min={64}
|
||||
max={1024}
|
||||
@@ -132,9 +130,7 @@ const InfillAndScalingSettings = () => {
|
||||
handleReset={handleResetScaledWidth}
|
||||
/>
|
||||
<IAISlider
|
||||
isInputDisabled={!isManual}
|
||||
isResetDisabled={!isManual}
|
||||
isSliderDisabled={!isManual}
|
||||
isDisabled={!isManual}
|
||||
label={t('parameters.scaledHeight')}
|
||||
min={64}
|
||||
max={1024}
|
||||
@@ -155,9 +151,7 @@ const InfillAndScalingSettings = () => {
|
||||
onChange={(e) => dispatch(setInfillMethod(e.target.value))}
|
||||
/>
|
||||
<IAISlider
|
||||
isInputDisabled={infillMethod !== 'tile'}
|
||||
isResetDisabled={infillMethod !== 'tile'}
|
||||
isSliderDisabled={infillMethod !== 'tile'}
|
||||
isDisabled={infillMethod !== 'tile'}
|
||||
label={t('parameters.tileSize')}
|
||||
min={16}
|
||||
max={64}
|
||||
|
||||
@@ -18,9 +18,7 @@ export default function CodeformerFidelity() {
|
||||
|
||||
return (
|
||||
<IAISlider
|
||||
isSliderDisabled={!isGFPGANAvailable}
|
||||
isInputDisabled={!isGFPGANAvailable}
|
||||
isResetDisabled={!isGFPGANAvailable}
|
||||
isDisabled={!isGFPGANAvailable}
|
||||
label={t('parameters.codeformerFidelity')}
|
||||
step={0.05}
|
||||
min={0}
|
||||
|
||||
@@ -18,9 +18,7 @@ export default function FaceRestoreStrength() {
|
||||
|
||||
return (
|
||||
<IAISlider
|
||||
isSliderDisabled={!isGFPGANAvailable}
|
||||
isInputDisabled={!isGFPGANAvailable}
|
||||
isResetDisabled={!isGFPGANAvailable}
|
||||
isDisabled={!isGFPGANAvailable}
|
||||
label={t('parameters.strength')}
|
||||
step={0.05}
|
||||
min={0}
|
||||
|
||||
@@ -12,6 +12,10 @@ export default function ImageFit() {
|
||||
(state: RootState) => state.generation.shouldFitToWidthHeight
|
||||
);
|
||||
|
||||
const isImageToImageEnabled = useAppSelector(
|
||||
(state: RootState) => state.generation.isImageToImageEnabled
|
||||
);
|
||||
|
||||
const handleChangeFit = (e: ChangeEvent<HTMLInputElement>) =>
|
||||
dispatch(setShouldFitToWidthHeight(e.target.checked));
|
||||
|
||||
@@ -19,6 +23,7 @@ export default function ImageFit() {
|
||||
|
||||
return (
|
||||
<IAISwitch
|
||||
isDisabled={!isImageToImageEnabled}
|
||||
label={t('parameters.imageFit')}
|
||||
isChecked={shouldFitToWidthHeight}
|
||||
onChange={handleChangeFit}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { VStack } from '@chakra-ui/react';
|
||||
import { Flex, Image, VStack } from '@chakra-ui/react';
|
||||
import ImageFit from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageFit';
|
||||
import ImageToImageStrength from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageStrength';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import InitialImagePreview from './InitialImagePreview';
|
||||
|
||||
export default function ImageToImageSettings() {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<VStack gap={2} alignItems="stretch">
|
||||
<InitialImagePreview />
|
||||
<ImageToImageStrength label={t('parameters.img2imgStrength')} />
|
||||
<ImageFit />
|
||||
</VStack>
|
||||
|
||||
@@ -14,6 +14,9 @@ export default function ImageToImageStrength(props: ImageToImageStrengthProps) {
|
||||
const img2imgStrength = useAppSelector(
|
||||
(state: RootState) => state.generation.img2imgStrength
|
||||
);
|
||||
const isImageToImageEnabled = useAppSelector(
|
||||
(state: RootState) => state.generation.isImageToImageEnabled
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
@@ -37,6 +40,7 @@ export default function ImageToImageStrength(props: ImageToImageStrengthProps) {
|
||||
inputWidth={22}
|
||||
withReset
|
||||
handleReset={handleImg2ImgStrengthReset}
|
||||
isDisabled={!isImageToImageEnabled}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAISwitch from 'common/components/IAISwitch';
|
||||
import { isImageToImageEnabledChanged } from 'features/parameters/store/generationSlice';
|
||||
import { ChangeEvent } from 'react';
|
||||
|
||||
export default function ImageToImageToggle() {
|
||||
const isImageToImageEnabled = useAppSelector(
|
||||
(state: RootState) => state.generation.isImageToImageEnabled
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleChange = (e: ChangeEvent<HTMLInputElement>) =>
|
||||
dispatch(isImageToImageEnabledChanged(e.target.checked));
|
||||
|
||||
return (
|
||||
<IAISwitch
|
||||
isChecked={isImageToImageEnabled}
|
||||
width="auto"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
import { Box, Flex, Image, Spinner, Text } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import SelectImagePlaceholder from 'common/components/SelectImagePlaceholder';
|
||||
import { useGetUrl } from 'common/util/getUrl';
|
||||
import useGetImageByNameAndType from 'features/gallery/hooks/useGetImageByName';
|
||||
import { selectResultsById } from 'features/gallery/store/resultsSlice';
|
||||
import {
|
||||
clearInitialImage,
|
||||
initialImageSelected,
|
||||
} from 'features/parameters/store/generationSlice';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { isEqual } from 'lodash';
|
||||
import { DragEvent, useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ImageType } from 'services/api';
|
||||
import ImageToImageOverlay from 'common/components/ImageToImageOverlay';
|
||||
|
||||
const initialImagePreviewSelector = createSelector(
|
||||
[(state: RootState) => state],
|
||||
(state) => {
|
||||
const { initialImage } = state.generation;
|
||||
const image = selectResultsById(state, initialImage as string);
|
||||
|
||||
return {
|
||||
initialImage: image,
|
||||
};
|
||||
},
|
||||
{ memoizeOptions: { resultEqualityCheck: isEqual } }
|
||||
);
|
||||
|
||||
const InitialImagePreview = () => {
|
||||
const isImageToImageEnabled = useAppSelector(
|
||||
(state: RootState) => state.generation.isImageToImageEnabled
|
||||
);
|
||||
const { initialImage } = useAppSelector(initialImagePreviewSelector);
|
||||
const { getUrl } = useGetUrl();
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [isLoaded, setIsLoaded] = useState(false);
|
||||
const getImageByNameAndType = useGetImageByNameAndType();
|
||||
|
||||
const onError = () => {
|
||||
dispatch(
|
||||
addToast({
|
||||
title: t('toast.parametersFailed'),
|
||||
description: t('toast.parametersFailedDesc'),
|
||||
status: 'error',
|
||||
isClosable: true,
|
||||
})
|
||||
);
|
||||
dispatch(clearInitialImage());
|
||||
setIsLoaded(false);
|
||||
};
|
||||
|
||||
const handleDrop = useCallback(
|
||||
(e: DragEvent<HTMLDivElement>) => {
|
||||
setIsLoaded(false);
|
||||
const name = e.dataTransfer.getData('invokeai/imageName');
|
||||
const type = e.dataTransfer.getData('invokeai/imageType') as ImageType;
|
||||
|
||||
if (!name || !type) {
|
||||
return;
|
||||
}
|
||||
|
||||
const image = getImageByNameAndType(name, type);
|
||||
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(initialImageSelected(image.name));
|
||||
},
|
||||
[getImageByNameAndType, dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
height: 'full',
|
||||
width: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'relative',
|
||||
}}
|
||||
onDrop={handleDrop}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
height: 'full',
|
||||
width: 'full',
|
||||
opacity: isImageToImageEnabled ? 1 : 0.5,
|
||||
filter: isImageToImageEnabled ? 'none' : 'auto',
|
||||
blur: '5px',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
{initialImage?.url && (
|
||||
<>
|
||||
<Image
|
||||
sx={{
|
||||
fit: 'contain',
|
||||
borderRadius: 'base',
|
||||
}}
|
||||
src={getUrl(initialImage?.url)}
|
||||
onError={onError}
|
||||
onLoad={() => {
|
||||
setIsLoaded(true);
|
||||
}}
|
||||
fallback={
|
||||
<Flex
|
||||
sx={{ h: 36, alignItems: 'center', justifyContent: 'center' }}
|
||||
>
|
||||
<Spinner color="grey" w="5rem" h="5rem" />
|
||||
</Flex>
|
||||
}
|
||||
/>
|
||||
{isLoaded && (
|
||||
<ImageToImageOverlay
|
||||
setIsLoaded={setIsLoaded}
|
||||
image={initialImage}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{!initialImage?.url && <SelectImagePlaceholder />}
|
||||
</Box>
|
||||
{!isImageToImageEnabled && (
|
||||
<Flex
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'absolute',
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
fontWeight={500}
|
||||
fontSize="md"
|
||||
userSelect="none"
|
||||
color="base.200"
|
||||
>
|
||||
Image to Image is Disabled
|
||||
</Text>
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default InitialImagePreview;
|
||||
@@ -51,9 +51,7 @@ export const HiresStrength = () => {
|
||||
// inputWidth={22}
|
||||
withReset
|
||||
handleReset={handleHiResStrengthReset}
|
||||
isSliderDisabled={!hiresFix}
|
||||
isInputDisabled={!hiresFix}
|
||||
isResetDisabled={!hiresFix}
|
||||
isDisabled={!hiresFix}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -30,9 +30,7 @@ export default function UpscaleDenoisingStrength() {
|
||||
withSliderMarks
|
||||
withInput
|
||||
withReset
|
||||
isSliderDisabled={!isESRGANAvailable}
|
||||
isInputDisabled={!isESRGANAvailable}
|
||||
isResetDisabled={!isESRGANAvailable}
|
||||
isDisabled={!isESRGANAvailable}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,9 +27,7 @@ export default function UpscaleStrength() {
|
||||
withSliderMarks
|
||||
withInput
|
||||
withReset
|
||||
isSliderDisabled={!isESRGANAvailable}
|
||||
isInputDisabled={!isESRGANAvailable}
|
||||
isResetDisabled={!isESRGANAvailable}
|
||||
isDisabled={!isESRGANAvailable}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,9 +24,7 @@ export default function VariationAmount() {
|
||||
step={0.01}
|
||||
min={0}
|
||||
max={1}
|
||||
isSliderDisabled={!shouldGenerateVariations}
|
||||
isInputDisabled={!shouldGenerateVariations}
|
||||
isResetDisabled={!shouldGenerateVariations}
|
||||
isDisabled={!shouldGenerateVariations}
|
||||
onChange={(v) => dispatch(setVariationAmount(v))}
|
||||
handleReset={() => dispatch(setVariationAmount(0.1))}
|
||||
withInput
|
||||
|
||||
@@ -19,9 +19,7 @@ export default function MainHeight() {
|
||||
|
||||
return shouldUseSliders ? (
|
||||
<IAISlider
|
||||
isSliderDisabled={activeTabName === 'unifiedCanvas'}
|
||||
isInputDisabled={activeTabName === 'unifiedCanvas'}
|
||||
isResetDisabled={activeTabName === 'unifiedCanvas'}
|
||||
isDisabled={activeTabName === 'unifiedCanvas'}
|
||||
label={t('parameters.height')}
|
||||
value={height}
|
||||
min={64}
|
||||
|
||||
@@ -19,9 +19,7 @@ export default function MainWidth() {
|
||||
|
||||
return shouldUseSliders ? (
|
||||
<IAISlider
|
||||
isSliderDisabled={activeTabName === 'unifiedCanvas'}
|
||||
isInputDisabled={activeTabName === 'unifiedCanvas'}
|
||||
isResetDisabled={activeTabName === 'unifiedCanvas'}
|
||||
isDisabled={activeTabName === 'unifiedCanvas'}
|
||||
label={t('parameters.width')}
|
||||
value={width}
|
||||
min={64}
|
||||
|
||||
@@ -1,63 +1,92 @@
|
||||
import { Accordion } from '@chakra-ui/react';
|
||||
import { RootState } from 'app/store';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { Feature } from 'app/features';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import { setOpenAccordions } from 'features/system/store/systemSlice';
|
||||
import { ReactElement } from 'react';
|
||||
import InvokeAccordionItem, {
|
||||
InvokeAccordionItemProps,
|
||||
} from './AccordionItems/InvokeAccordionItem';
|
||||
import { tabMap } from 'features/ui/store/tabMap';
|
||||
import { uiSelector } from 'features/ui/store/uiSelectors';
|
||||
import { openAccordionItemsChanged } from 'features/ui/store/uiSlice';
|
||||
import { filter } from 'lodash';
|
||||
import { ReactNode, useCallback } from 'react';
|
||||
import InvokeAccordionItem from './AccordionItems/InvokeAccordionItem';
|
||||
|
||||
type ParametersAccordionType = {
|
||||
[parametersAccordionKey: string]: InvokeAccordionItemProps;
|
||||
const parametersAccordionSelector = createSelector([uiSelector], (uiSlice) => {
|
||||
const {
|
||||
activeTab,
|
||||
openLinearAccordionItems,
|
||||
openUnifiedCanvasAccordionItems,
|
||||
disabledParameterPanels,
|
||||
} = uiSlice;
|
||||
|
||||
let openAccordions: number[] = [];
|
||||
|
||||
if (tabMap[activeTab] === 'linear') {
|
||||
openAccordions = openLinearAccordionItems;
|
||||
}
|
||||
|
||||
if (tabMap[activeTab] === 'unifiedCanvas') {
|
||||
openAccordions = openUnifiedCanvasAccordionItems;
|
||||
}
|
||||
|
||||
return {
|
||||
openAccordions,
|
||||
disabledParameterPanels,
|
||||
};
|
||||
});
|
||||
|
||||
export type ParametersAccordionItem = {
|
||||
name: string;
|
||||
header: string;
|
||||
content: ReactNode;
|
||||
feature?: Feature;
|
||||
additionalHeaderComponents?: ReactNode;
|
||||
};
|
||||
|
||||
type ParametersAccordionsType = {
|
||||
accordionInfo: ParametersAccordionType;
|
||||
export type ParametersAccordionItems = {
|
||||
[parametersAccordionKey: string]: ParametersAccordionItem;
|
||||
};
|
||||
|
||||
type ParametersAccordionProps = {
|
||||
accordionItems: ParametersAccordionItems;
|
||||
};
|
||||
|
||||
/**
|
||||
* Main container for generation and processing parameters.
|
||||
*/
|
||||
const ParametersAccordion = (props: ParametersAccordionsType) => {
|
||||
const { accordionInfo } = props;
|
||||
|
||||
const openAccordions = useAppSelector(
|
||||
(state: RootState) => state.system.openAccordions
|
||||
const ParametersAccordion = ({ accordionItems }: ParametersAccordionProps) => {
|
||||
const { openAccordions, disabledParameterPanels } = useAppSelector(
|
||||
parametersAccordionSelector
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
/**
|
||||
* Stores accordion state in redux so preferred UI setup is retained.
|
||||
*/
|
||||
const handleChangeAccordionState = (openAccordions: number | number[]) =>
|
||||
dispatch(setOpenAccordions(openAccordions));
|
||||
|
||||
const renderAccordions = () => {
|
||||
const accordionsToRender: ReactElement[] = [];
|
||||
if (accordionInfo) {
|
||||
Object.keys(accordionInfo).forEach((key) => {
|
||||
const { header, feature, content, additionalHeaderComponents } =
|
||||
accordionInfo[key];
|
||||
accordionsToRender.push(
|
||||
<InvokeAccordionItem
|
||||
key={key}
|
||||
header={header}
|
||||
feature={feature}
|
||||
content={content}
|
||||
additionalHeaderComponents={additionalHeaderComponents}
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
return accordionsToRender;
|
||||
const handleChangeAccordionState = (openAccordions: number | number[]) => {
|
||||
dispatch(
|
||||
openAccordionItemsChanged(
|
||||
Array.isArray(openAccordions) ? openAccordions : [openAccordions]
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
// Render function for accordion items
|
||||
const renderAccordionItems = useCallback(() => {
|
||||
// Filter out disabled accordions
|
||||
const filteredAccordionItems = filter(
|
||||
accordionItems,
|
||||
(item) => disabledParameterPanels.indexOf(item.name) === -1
|
||||
);
|
||||
|
||||
return filteredAccordionItems.map((accordionItem) => (
|
||||
<InvokeAccordionItem
|
||||
key={accordionItem.name}
|
||||
accordionItem={accordionItem}
|
||||
/>
|
||||
));
|
||||
}, [disabledParameterPanels, accordionItems]);
|
||||
|
||||
return (
|
||||
<Accordion
|
||||
defaultIndex={openAccordions}
|
||||
allowMultiple
|
||||
reduceMotion
|
||||
onChange={handleChangeAccordionState}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
@@ -65,7 +94,7 @@ const ParametersAccordion = (props: ParametersAccordionsType) => {
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
{renderAccordions()}
|
||||
{renderAccordionItems()}
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { cancelProcessing } from 'app/socketio/actions';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAIIconButton, {
|
||||
IAIIconButtonProps,
|
||||
@@ -9,16 +8,36 @@ import {
|
||||
SystemState,
|
||||
setCancelAfter,
|
||||
setCancelType,
|
||||
cancelScheduled,
|
||||
cancelTypeChanged,
|
||||
CancelType,
|
||||
} from 'features/system/store/systemSlice';
|
||||
import { isEqual } from 'lodash';
|
||||
import { useEffect, useCallback, memo } from 'react';
|
||||
import { ButtonSpinner, ButtonGroup } from '@chakra-ui/react';
|
||||
import {
|
||||
ButtonSpinner,
|
||||
ButtonGroup,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuList,
|
||||
MenuOptionGroup,
|
||||
MenuItemOption,
|
||||
IconButton,
|
||||
} from '@chakra-ui/react';
|
||||
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { MdCancel, MdCancelScheduleSend } from 'react-icons/md';
|
||||
import {
|
||||
MdArrowDropDown,
|
||||
MdArrowDropUp,
|
||||
MdCancel,
|
||||
MdCancelScheduleSend,
|
||||
} from 'react-icons/md';
|
||||
|
||||
import IAISimpleMenu from 'common/components/IAISimpleMenu';
|
||||
import { sessionCanceled } from 'services/thunks/session';
|
||||
import { FaChevronDown } from 'react-icons/fa';
|
||||
import { BiChevronDown } from 'react-icons/bi';
|
||||
|
||||
const cancelButtonSelector = createSelector(
|
||||
systemSelector,
|
||||
@@ -29,8 +48,11 @@ const cancelButtonSelector = createSelector(
|
||||
isCancelable: system.isCancelable,
|
||||
currentIteration: system.currentIteration,
|
||||
totalIterations: system.totalIterations,
|
||||
cancelType: system.cancelOptions.cancelType,
|
||||
cancelAfter: system.cancelOptions.cancelAfter,
|
||||
// cancelType: system.cancelOptions.cancelType,
|
||||
// cancelAfter: system.cancelOptions.cancelAfter,
|
||||
sessionId: system.sessionId,
|
||||
cancelType: system.cancelType,
|
||||
isCancelScheduled: system.isCancelScheduled,
|
||||
};
|
||||
},
|
||||
{
|
||||
@@ -56,16 +78,34 @@ const CancelButton = (
|
||||
currentIteration,
|
||||
totalIterations,
|
||||
cancelType,
|
||||
cancelAfter,
|
||||
isCancelScheduled,
|
||||
// cancelAfter,
|
||||
sessionId,
|
||||
} = useAppSelector(cancelButtonSelector);
|
||||
|
||||
const handleClickCancel = useCallback(() => {
|
||||
dispatch(cancelProcessing());
|
||||
dispatch(setCancelAfter(null));
|
||||
}, [dispatch]);
|
||||
if (!sessionId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cancelType === 'scheduled') {
|
||||
dispatch(cancelScheduled());
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(sessionCanceled({ sessionId }));
|
||||
}, [dispatch, sessionId, cancelType]);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const isCancelScheduled = cancelAfter === null ? false : true;
|
||||
const handleCancelTypeChanged = useCallback(
|
||||
(value: string | string[]) => {
|
||||
const newCancelType = Array.isArray(value) ? value[0] : value;
|
||||
dispatch(cancelTypeChanged(newCancelType as CancelType));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
// const isCancelScheduled = cancelAfter === null ? false : true;
|
||||
|
||||
useHotkeys(
|
||||
'shift+x',
|
||||
@@ -77,22 +117,22 @@ const CancelButton = (
|
||||
[isConnected, isProcessing, isCancelable]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (cancelAfter !== null && cancelAfter < currentIteration) {
|
||||
handleClickCancel();
|
||||
}
|
||||
}, [cancelAfter, currentIteration, handleClickCancel]);
|
||||
// useEffect(() => {
|
||||
// if (cancelAfter !== null && cancelAfter < currentIteration) {
|
||||
// handleClickCancel();
|
||||
// }
|
||||
// }, [cancelAfter, currentIteration, handleClickCancel]);
|
||||
|
||||
const cancelMenuItems = [
|
||||
{
|
||||
item: t('parameters.cancel.immediate'),
|
||||
onClick: () => dispatch(setCancelType('immediate')),
|
||||
},
|
||||
{
|
||||
item: t('parameters.cancel.schedule'),
|
||||
onClick: () => dispatch(setCancelType('scheduled')),
|
||||
},
|
||||
];
|
||||
// const cancelMenuItems = [
|
||||
// {
|
||||
// item: t('parameters.cancel.immediate'),
|
||||
// onClick: () => dispatch(cancelTypeChanged('immediate')),
|
||||
// },
|
||||
// {
|
||||
// item: t('parameters.cancel.schedule'),
|
||||
// onClick: () => dispatch(cancelTypeChanged('scheduled')),
|
||||
// },
|
||||
// ];
|
||||
|
||||
return (
|
||||
<ButtonGroup isAttached width={btnGroupWidth}>
|
||||
@@ -121,29 +161,40 @@ const CancelButton = (
|
||||
? t('parameters.cancel.isScheduled')
|
||||
: t('parameters.cancel.schedule')
|
||||
}
|
||||
isDisabled={
|
||||
!isConnected ||
|
||||
!isProcessing ||
|
||||
!isCancelable ||
|
||||
currentIteration === totalIterations
|
||||
}
|
||||
onClick={() => {
|
||||
// If a cancel request has already been made, and the user clicks again before the next iteration has been processed, stop the request.
|
||||
if (isCancelScheduled) dispatch(setCancelAfter(null));
|
||||
else dispatch(setCancelAfter(currentIteration));
|
||||
}}
|
||||
isDisabled={!isConnected || !isProcessing || !isCancelable}
|
||||
onClick={handleClickCancel}
|
||||
colorScheme="error"
|
||||
{...rest}
|
||||
/>
|
||||
)}
|
||||
<IAISimpleMenu
|
||||
menuItems={cancelMenuItems}
|
||||
iconTooltip={t('parameters.cancel.setType')}
|
||||
menuButtonProps={{
|
||||
colorScheme: 'error',
|
||||
minWidth: 5,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Menu closeOnSelect={false}>
|
||||
<MenuButton
|
||||
as={IconButton}
|
||||
tooltip={t('parameters.cancel.setType')}
|
||||
aria-label={t('parameters.cancel.setType')}
|
||||
icon={<BiChevronDown />}
|
||||
paddingX={0}
|
||||
paddingY={0}
|
||||
colorScheme="error"
|
||||
minWidth={5}
|
||||
/>
|
||||
<MenuList minWidth="240px">
|
||||
<MenuOptionGroup
|
||||
value={cancelType}
|
||||
title="Cancel Type"
|
||||
type="radio"
|
||||
onChange={handleCancelTypeChanged}
|
||||
>
|
||||
<MenuItemOption value="immediate">
|
||||
{t('parameters.cancel.immediate')}
|
||||
</MenuItemOption>
|
||||
<MenuItemOption value="scheduled">
|
||||
{t('parameters.cancel.schedule')}
|
||||
</MenuItemOption>
|
||||
</MenuOptionGroup>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
</ButtonGroup>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaPlay } from 'react-icons/fa';
|
||||
import { linearGraphBuilt, sessionCreated } from 'services/thunks/session';
|
||||
|
||||
interface InvokeButton
|
||||
extends Omit<IAIButtonProps | IAIIconButtonProps, 'aria-label'> {
|
||||
@@ -24,7 +25,8 @@ export default function InvokeButton(props: InvokeButton) {
|
||||
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||
|
||||
const handleClickGenerate = () => {
|
||||
dispatch(generateImage(activeTabName));
|
||||
// dispatch(generateImage(activeTabName));
|
||||
dispatch(linearGraphBuilt());
|
||||
};
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
Reference in New Issue
Block a user