mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
chore(ui): delete unused files
This commit is contained in:
parent
71e298b722
commit
5eaea9dd64
@ -1,43 +0,0 @@
|
||||
import { Box, Flex, Icon } from '@chakra-ui/react';
|
||||
import { memo } from 'react';
|
||||
import { FaExclamation } from 'react-icons/fa';
|
||||
|
||||
const IAIErrorLoadingImageFallback = () => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'relative',
|
||||
height: 'full',
|
||||
width: 'full',
|
||||
'::before': {
|
||||
content: "''",
|
||||
display: 'block',
|
||||
pt: '100%',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineStart: 0,
|
||||
height: 'full',
|
||||
width: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderRadius: 'base',
|
||||
bg: 'base.100',
|
||||
color: 'base.500',
|
||||
_dark: {
|
||||
color: 'base.700',
|
||||
bg: 'base.850',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Icon as={FaExclamation} boxSize={16} opacity={0.7} />
|
||||
</Flex>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(IAIErrorLoadingImageFallback);
|
@ -1,8 +0,0 @@
|
||||
import { chakra } from '@chakra-ui/react';
|
||||
|
||||
/**
|
||||
* Chakra-enabled <form />
|
||||
*/
|
||||
const IAIForm = chakra.form;
|
||||
|
||||
export default IAIForm;
|
@ -1,15 +0,0 @@
|
||||
import { FormErrorMessage, FormErrorMessageProps } from '@chakra-ui/react';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
type IAIFormErrorMessageProps = FormErrorMessageProps & {
|
||||
children: ReactNode | string;
|
||||
};
|
||||
|
||||
export default function IAIFormErrorMessage(props: IAIFormErrorMessageProps) {
|
||||
const { children, ...rest } = props;
|
||||
return (
|
||||
<FormErrorMessage color="error.400" {...rest}>
|
||||
{children}
|
||||
</FormErrorMessage>
|
||||
);
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
import { FormHelperText, FormHelperTextProps } from '@chakra-ui/react';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
type IAIFormHelperTextProps = FormHelperTextProps & {
|
||||
children: ReactNode | string;
|
||||
};
|
||||
|
||||
export default function IAIFormHelperText(props: IAIFormHelperTextProps) {
|
||||
const { children, ...rest } = props;
|
||||
return (
|
||||
<FormHelperText margin={0} color="base.400" {...rest}>
|
||||
{children}
|
||||
</FormHelperText>
|
||||
);
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import { Flex, useColorMode } from '@chakra-ui/react';
|
||||
import { ReactElement } from 'react';
|
||||
import { mode } from 'theme/util/mode';
|
||||
|
||||
export function IAIFormItemWrapper({
|
||||
children,
|
||||
}: {
|
||||
children: ReactElement | ReactElement[];
|
||||
}) {
|
||||
const { colorMode } = useColorMode();
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
padding: 4,
|
||||
rowGap: 4,
|
||||
borderRadius: 'base',
|
||||
width: 'full',
|
||||
bg: mode('base.100', 'base.900')(colorMode),
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Flex>
|
||||
);
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import {
|
||||
Checkbox,
|
||||
CheckboxProps,
|
||||
FormControl,
|
||||
FormControlProps,
|
||||
FormLabel,
|
||||
} from '@chakra-ui/react';
|
||||
import { memo, ReactNode } from 'react';
|
||||
|
||||
type IAIFullCheckboxProps = CheckboxProps & {
|
||||
label: string | ReactNode;
|
||||
formControlProps?: FormControlProps;
|
||||
};
|
||||
|
||||
const IAIFullCheckbox = (props: IAIFullCheckboxProps) => {
|
||||
const { label, formControlProps, ...rest } = props;
|
||||
return (
|
||||
<FormControl {...formControlProps}>
|
||||
<FormLabel>{label}</FormLabel>
|
||||
<Checkbox colorScheme="accent" {...rest} />
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(IAIFullCheckbox);
|
@ -1,23 +0,0 @@
|
||||
import { Flex, Icon } from '@chakra-ui/react';
|
||||
import { memo } from 'react';
|
||||
import { FaImage } from 'react-icons/fa';
|
||||
|
||||
const SelectImagePlaceholder = () => {
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
// bg: 'base.800',
|
||||
borderRadius: 'base',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
aspectRatio: '1/1',
|
||||
}}
|
||||
>
|
||||
<Icon color="base.400" boxSize={32} as={FaImage}></Icon>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(SelectImagePlaceholder);
|
@ -1,24 +0,0 @@
|
||||
import { useBreakpoint } from '@chakra-ui/react';
|
||||
|
||||
export default function useResolution():
|
||||
| 'mobile'
|
||||
| 'tablet'
|
||||
| 'desktop'
|
||||
| 'unknown' {
|
||||
const breakpointValue = useBreakpoint();
|
||||
|
||||
const mobileResolutions = ['base', 'sm'];
|
||||
const tabletResolutions = ['md', 'lg'];
|
||||
const desktopResolutions = ['xl', '2xl'];
|
||||
|
||||
if (mobileResolutions.includes(breakpointValue)) {
|
||||
return 'mobile';
|
||||
}
|
||||
if (tabletResolutions.includes(breakpointValue)) {
|
||||
return 'tablet';
|
||||
}
|
||||
if (desktopResolutions.includes(breakpointValue)) {
|
||||
return 'desktop';
|
||||
}
|
||||
return 'unknown';
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
import dateFormat from 'dateformat';
|
||||
|
||||
/**
|
||||
* Get a `now` timestamp with 1s precision, formatted as ISO datetime.
|
||||
*/
|
||||
export const getTimestamp = () =>
|
||||
dateFormat(new Date(), `yyyy-mm-dd'T'HH:MM:ss:lo`);
|
@ -1,71 +0,0 @@
|
||||
// TODO: Restore variations
|
||||
// Support code from v2.3 in here.
|
||||
|
||||
// export const stringToSeedWeights = (
|
||||
// string: string
|
||||
// ): InvokeAI.SeedWeights | boolean => {
|
||||
// const stringPairs = string.split(',');
|
||||
// const arrPairs = stringPairs.map((p) => p.split(':'));
|
||||
// const pairs = arrPairs.map((p: Array<string>): InvokeAI.SeedWeightPair => {
|
||||
// return { seed: Number(p[0]), weight: Number(p[1]) };
|
||||
// });
|
||||
|
||||
// if (!validateSeedWeights(pairs)) {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// return pairs;
|
||||
// };
|
||||
|
||||
// export const validateSeedWeights = (
|
||||
// seedWeights: InvokeAI.SeedWeights | string
|
||||
// ): boolean => {
|
||||
// return typeof seedWeights === 'string'
|
||||
// ? Boolean(stringToSeedWeights(seedWeights))
|
||||
// : Boolean(
|
||||
// seedWeights.length &&
|
||||
// !seedWeights.some((pair: InvokeAI.SeedWeightPair) => {
|
||||
// const { seed, weight } = pair;
|
||||
// const isSeedValid = !isNaN(parseInt(seed.toString(), 10));
|
||||
// const isWeightValid =
|
||||
// !isNaN(parseInt(weight.toString(), 10)) &&
|
||||
// weight >= 0 &&
|
||||
// weight <= 1;
|
||||
// return !(isSeedValid && isWeightValid);
|
||||
// })
|
||||
// );
|
||||
// };
|
||||
|
||||
// export const seedWeightsToString = (
|
||||
// seedWeights: InvokeAI.SeedWeights
|
||||
// ): string => {
|
||||
// return seedWeights.reduce((acc, pair, i, arr) => {
|
||||
// const { seed, weight } = pair;
|
||||
// acc += `${seed}:${weight}`;
|
||||
// if (i !== arr.length - 1) {
|
||||
// acc += ',';
|
||||
// }
|
||||
// return acc;
|
||||
// }, '');
|
||||
// };
|
||||
|
||||
// export const seedWeightsToArray = (
|
||||
// seedWeights: InvokeAI.SeedWeights
|
||||
// ): Array<Array<number>> => {
|
||||
// return seedWeights.map((pair: InvokeAI.SeedWeightPair) => [
|
||||
// pair.seed,
|
||||
// pair.weight,
|
||||
// ]);
|
||||
// };
|
||||
|
||||
// export const stringToSeedWeightsArray = (
|
||||
// string: string
|
||||
// ): Array<Array<number>> => {
|
||||
// const stringPairs = string.split(',');
|
||||
// const arrPairs = stringPairs.map((p) => p.split(':'));
|
||||
// return arrPairs.map(
|
||||
// (p: Array<string>): Array<number> => [parseInt(p[0], 10), parseFloat(p[1])]
|
||||
// );
|
||||
// };
|
||||
|
||||
export default {};
|
@ -1,16 +0,0 @@
|
||||
import Konva from 'konva';
|
||||
import { IRect } from 'konva/lib/types';
|
||||
|
||||
/**
|
||||
* Converts a Konva node to a dataURL
|
||||
* @param node - The Konva node to convert to a dataURL
|
||||
* @param boundingBox - The bounding box to crop to
|
||||
* @returns A dataURL of the node cropped to the bounding box
|
||||
*/
|
||||
export const konvaNodeToDataURL = (
|
||||
node: Konva.Node,
|
||||
boundingBox: IRect
|
||||
): string => {
|
||||
// get a dataURL of the bbox'd region
|
||||
return node.toDataURL(boundingBox);
|
||||
};
|
@ -1,36 +0,0 @@
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import { useIsReadyToEnqueue } from 'common/hooks/useIsReadyToEnqueue';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useControlAdapterControlImage } from '../hooks/useControlAdapterControlImage';
|
||||
import { controlAdapterImageProcessed } from '../store/actions';
|
||||
|
||||
type Props = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
const ControlAdapterPreprocessButton = ({ id }: Props) => {
|
||||
const controlImage = useControlAdapterControlImage(id);
|
||||
const dispatch = useAppDispatch();
|
||||
const isReady = useIsReadyToEnqueue();
|
||||
|
||||
const handleProcess = useCallback(() => {
|
||||
dispatch(
|
||||
controlAdapterImageProcessed({
|
||||
id,
|
||||
})
|
||||
);
|
||||
}, [id, dispatch]);
|
||||
|
||||
return (
|
||||
<IAIButton
|
||||
size="sm"
|
||||
onClick={handleProcess}
|
||||
isDisabled={Boolean(!controlImage) || !isReady}
|
||||
>
|
||||
Preprocess
|
||||
</IAIButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(ControlAdapterPreprocessButton);
|
@ -1,108 +0,0 @@
|
||||
import { As, Badge, Flex } from '@chakra-ui/react';
|
||||
import IAIDroppable from 'common/components/IAIDroppable';
|
||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||
import { TypesafeDroppableData } from 'features/dnd/types';
|
||||
import { BoardId } from 'features/gallery/store/types';
|
||||
import { ReactNode, memo } from 'react';
|
||||
import BoardContextMenu from '../BoardContextMenu';
|
||||
|
||||
type GenericBoardProps = {
|
||||
board_id: BoardId;
|
||||
droppableData?: TypesafeDroppableData;
|
||||
onClick: () => void;
|
||||
isSelected: boolean;
|
||||
icon: As;
|
||||
label: string;
|
||||
dropLabel?: ReactNode;
|
||||
badgeCount?: number;
|
||||
};
|
||||
|
||||
export const formatBadgeCount = (count: number) =>
|
||||
Intl.NumberFormat('en-US', {
|
||||
notation: 'compact',
|
||||
maximumFractionDigits: 1,
|
||||
}).format(count);
|
||||
|
||||
const GenericBoard = (props: GenericBoardProps) => {
|
||||
const {
|
||||
board_id,
|
||||
droppableData,
|
||||
onClick,
|
||||
isSelected,
|
||||
icon,
|
||||
label,
|
||||
badgeCount,
|
||||
dropLabel,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<BoardContextMenu board_id={board_id}>
|
||||
{(ref) => (
|
||||
<Flex
|
||||
ref={ref}
|
||||
sx={{
|
||||
flexDir: 'column',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
cursor: 'pointer',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
borderRadius: 'base',
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
onClick={onClick}
|
||||
sx={{
|
||||
position: 'relative',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
borderRadius: 'base',
|
||||
w: 'full',
|
||||
aspectRatio: '1/1',
|
||||
overflow: 'hidden',
|
||||
shadow: isSelected ? 'selected.light' : undefined,
|
||||
_dark: { shadow: isSelected ? 'selected.dark' : undefined },
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
<IAINoContentFallback
|
||||
boxSize={8}
|
||||
icon={icon}
|
||||
sx={{
|
||||
border: '2px solid var(--invokeai-colors-base-200)',
|
||||
_dark: { border: '2px solid var(--invokeai-colors-base-800)' },
|
||||
}}
|
||||
/>
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
insetInlineEnd: 0,
|
||||
top: 0,
|
||||
p: 1,
|
||||
}}
|
||||
>
|
||||
{badgeCount !== undefined && (
|
||||
<Badge variant="solid">{formatBadgeCount(badgeCount)}</Badge>
|
||||
)}
|
||||
</Flex>
|
||||
<IAIDroppable data={droppableData} dropLabel={dropLabel} />
|
||||
</Flex>
|
||||
<Flex
|
||||
sx={{
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
fontWeight: isSelected ? 600 : undefined,
|
||||
fontSize: 'sm',
|
||||
color: isSelected ? 'base.900' : 'base.700',
|
||||
_dark: { color: isSelected ? 'base.50' : 'base.200' },
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</Flex>
|
||||
</Flex>
|
||||
)}
|
||||
</BoardContextMenu>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(GenericBoard);
|
@ -1,53 +0,0 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import { boardIdSelected } from 'features/gallery/store/gallerySlice';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useBoardName } from 'services/api/hooks/useBoardName';
|
||||
|
||||
type Props = {
|
||||
board_id: 'images' | 'assets' | 'no_board';
|
||||
};
|
||||
|
||||
const SystemBoardButton = ({ board_id }: Props) => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createSelector(
|
||||
[stateSelector],
|
||||
({ gallery }) => {
|
||||
const { selectedBoardId } = gallery;
|
||||
return { isSelected: selectedBoardId === board_id };
|
||||
},
|
||||
defaultSelectorOptions
|
||||
),
|
||||
[board_id]
|
||||
);
|
||||
|
||||
const { isSelected } = useAppSelector(selector);
|
||||
|
||||
const boardName = useBoardName(board_id);
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
dispatch(boardIdSelected({ boardId: board_id }));
|
||||
}, [board_id, dispatch]);
|
||||
|
||||
return (
|
||||
<IAIButton
|
||||
onClick={handleClick}
|
||||
size="sm"
|
||||
isChecked={isSelected}
|
||||
sx={{
|
||||
flexGrow: 1,
|
||||
borderRadius: 'base',
|
||||
}}
|
||||
>
|
||||
{boardName}
|
||||
</IAIButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(SystemBoardButton);
|
@ -1,22 +0,0 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { memo } from 'react';
|
||||
import { FaEyeSlash } from 'react-icons/fa';
|
||||
|
||||
const CurrentImageHidden = () => {
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'absolute',
|
||||
color: 'base.400',
|
||||
}}
|
||||
>
|
||||
<FaEyeSlash fontSize="25vh" />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(CurrentImageHidden);
|
@ -1,27 +0,0 @@
|
||||
import { Flex, Spinner, SpinnerProps } from '@chakra-ui/react';
|
||||
import { memo } from 'react';
|
||||
|
||||
type ImageFallbackSpinnerProps = SpinnerProps;
|
||||
|
||||
const ImageFallbackSpinner = (props: ImageFallbackSpinnerProps) => {
|
||||
const { size = 'xl', ...rest } = props;
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'absolute',
|
||||
color: 'base.400',
|
||||
minH: 36,
|
||||
minW: 36,
|
||||
}}
|
||||
>
|
||||
<Spinner size={size} {...rest} />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(ImageFallbackSpinner);
|
@ -1,14 +0,0 @@
|
||||
import {
|
||||
ClipInputFieldTemplate,
|
||||
ClipInputFieldValue,
|
||||
FieldComponentProps,
|
||||
} from 'features/nodes/types/types';
|
||||
import { memo } from 'react';
|
||||
|
||||
const ClipInputFieldComponent = (
|
||||
_props: FieldComponentProps<ClipInputFieldValue, ClipInputFieldTemplate>
|
||||
) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
export default memo(ClipInputFieldComponent);
|
@ -1,17 +0,0 @@
|
||||
import {
|
||||
CollectionInputFieldTemplate,
|
||||
CollectionInputFieldValue,
|
||||
FieldComponentProps,
|
||||
} from 'features/nodes/types/types';
|
||||
import { memo } from 'react';
|
||||
|
||||
const CollectionInputFieldComponent = (
|
||||
_props: FieldComponentProps<
|
||||
CollectionInputFieldValue,
|
||||
CollectionInputFieldTemplate
|
||||
>
|
||||
) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
export default memo(CollectionInputFieldComponent);
|
@ -1,17 +0,0 @@
|
||||
import {
|
||||
CollectionItemInputFieldTemplate,
|
||||
CollectionItemInputFieldValue,
|
||||
FieldComponentProps,
|
||||
} from 'features/nodes/types/types';
|
||||
import { memo } from 'react';
|
||||
|
||||
const CollectionItemInputFieldComponent = (
|
||||
_props: FieldComponentProps<
|
||||
CollectionItemInputFieldValue,
|
||||
CollectionItemInputFieldTemplate
|
||||
>
|
||||
) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
export default memo(CollectionItemInputFieldComponent);
|
@ -1,17 +0,0 @@
|
||||
import {
|
||||
ConditioningInputFieldTemplate,
|
||||
ConditioningInputFieldValue,
|
||||
FieldComponentProps,
|
||||
} from 'features/nodes/types/types';
|
||||
import { memo } from 'react';
|
||||
|
||||
const ConditioningInputFieldComponent = (
|
||||
_props: FieldComponentProps<
|
||||
ConditioningInputFieldValue,
|
||||
ConditioningInputFieldTemplate
|
||||
>
|
||||
) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
export default memo(ConditioningInputFieldComponent);
|
@ -1,19 +0,0 @@
|
||||
import {
|
||||
ControlInputFieldTemplate,
|
||||
ControlInputFieldValue,
|
||||
ControlPolymorphicInputFieldTemplate,
|
||||
ControlPolymorphicInputFieldValue,
|
||||
FieldComponentProps,
|
||||
} from 'features/nodes/types/types';
|
||||
import { memo } from 'react';
|
||||
|
||||
const ControlInputFieldComponent = (
|
||||
_props: FieldComponentProps<
|
||||
ControlInputFieldValue | ControlPolymorphicInputFieldValue,
|
||||
ControlInputFieldTemplate | ControlPolymorphicInputFieldTemplate
|
||||
>
|
||||
) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
export default memo(ControlInputFieldComponent);
|
@ -1,17 +0,0 @@
|
||||
import {
|
||||
DenoiseMaskInputFieldTemplate,
|
||||
DenoiseMaskInputFieldValue,
|
||||
FieldComponentProps,
|
||||
} from 'features/nodes/types/types';
|
||||
import { memo } from 'react';
|
||||
|
||||
const DenoiseMaskInputFieldComponent = (
|
||||
_props: FieldComponentProps<
|
||||
DenoiseMaskInputFieldValue,
|
||||
DenoiseMaskInputFieldTemplate
|
||||
>
|
||||
) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
export default memo(DenoiseMaskInputFieldComponent);
|
@ -1,19 +0,0 @@
|
||||
import {
|
||||
IPAdapterInputFieldTemplate,
|
||||
IPAdapterInputFieldValue,
|
||||
FieldComponentProps,
|
||||
IPAdapterPolymorphicInputFieldValue,
|
||||
IPAdapterPolymorphicInputFieldTemplate,
|
||||
} from 'features/nodes/types/types';
|
||||
import { memo } from 'react';
|
||||
|
||||
const IPAdapterInputFieldComponent = (
|
||||
_props: FieldComponentProps<
|
||||
IPAdapterInputFieldValue | IPAdapterPolymorphicInputFieldValue,
|
||||
IPAdapterInputFieldTemplate | IPAdapterPolymorphicInputFieldTemplate
|
||||
>
|
||||
) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
export default memo(IPAdapterInputFieldComponent);
|
@ -1,94 +0,0 @@
|
||||
import {
|
||||
ImageCollectionInputFieldTemplate,
|
||||
ImageCollectionInputFieldValue,
|
||||
FieldComponentProps,
|
||||
} from 'features/nodes/types/types';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import IAIDndImage from 'common/components/IAIDndImage';
|
||||
import IAIDropOverlay from 'common/components/IAIDropOverlay';
|
||||
import { useDroppableTypesafe } from 'features/dnd/hooks/typesafeHooks';
|
||||
import { NodesMultiImageDropData } from 'features/dnd/types';
|
||||
import { isValidDrop } from 'features/dnd/util/isValidDrop';
|
||||
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
|
||||
|
||||
const ImageCollectionInputFieldComponent = (
|
||||
props: FieldComponentProps<
|
||||
ImageCollectionInputFieldValue,
|
||||
ImageCollectionInputFieldTemplate
|
||||
>
|
||||
) => {
|
||||
const { nodeId, field } = props;
|
||||
|
||||
// const dispatch = useAppDispatch();
|
||||
|
||||
// const handleDrop = useCallback(
|
||||
// ({ image_name }: ImageDTO) => {
|
||||
// dispatch(
|
||||
// fieldValueChanged({
|
||||
// nodeId,
|
||||
// fieldName: field.name,
|
||||
// value: uniqBy([...(field.value ?? []), { image_name }], 'image_name'),
|
||||
// })
|
||||
// );
|
||||
// },
|
||||
// [dispatch, field.name, field.value, nodeId]
|
||||
// );
|
||||
|
||||
const droppableData: NodesMultiImageDropData = {
|
||||
id: `node-${nodeId}-${field.name}`,
|
||||
actionType: 'SET_MULTI_NODES_IMAGE',
|
||||
context: { nodeId: nodeId, fieldName: field.name },
|
||||
};
|
||||
|
||||
const {
|
||||
isOver,
|
||||
setNodeRef: setDroppableRef,
|
||||
active,
|
||||
} = useDroppableTypesafe({
|
||||
id: `node_${nodeId}`,
|
||||
data: droppableData,
|
||||
});
|
||||
|
||||
// const handleReset = useCallback(() => {
|
||||
// dispatch(
|
||||
// fieldValueChanged({
|
||||
// nodeId,
|
||||
// fieldName: field.name,
|
||||
// value: undefined,
|
||||
// })
|
||||
// );
|
||||
// }, [dispatch, field.name, nodeId]);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
ref={setDroppableRef}
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'relative',
|
||||
minH: '10rem',
|
||||
}}
|
||||
>
|
||||
{field.value?.map(({ image_name }) => (
|
||||
<ImageSubField key={image_name} imageName={image_name} />
|
||||
))}
|
||||
{isValidDrop(droppableData, active) && <IAIDropOverlay isOver={isOver} />}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(ImageCollectionInputFieldComponent);
|
||||
|
||||
type ImageSubFieldProps = { imageName: string };
|
||||
|
||||
const ImageSubField = (props: ImageSubFieldProps) => {
|
||||
const { currentData: image } = useGetImageDTOQuery(props.imageName);
|
||||
|
||||
return (
|
||||
<IAIDndImage imageDTO={image} isDropDisabled={true} isDragDisabled={true} />
|
||||
);
|
||||
};
|
@ -1,19 +0,0 @@
|
||||
import {
|
||||
LatentsInputFieldTemplate,
|
||||
LatentsInputFieldValue,
|
||||
FieldComponentProps,
|
||||
LatentsPolymorphicInputFieldValue,
|
||||
LatentsPolymorphicInputFieldTemplate,
|
||||
} from 'features/nodes/types/types';
|
||||
import { memo } from 'react';
|
||||
|
||||
const LatentsInputFieldComponent = (
|
||||
_props: FieldComponentProps<
|
||||
LatentsInputFieldValue | LatentsPolymorphicInputFieldValue,
|
||||
LatentsInputFieldTemplate | LatentsPolymorphicInputFieldTemplate
|
||||
>
|
||||
) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
export default memo(LatentsInputFieldComponent);
|
@ -1,19 +0,0 @@
|
||||
import {
|
||||
T2IAdapterInputFieldTemplate,
|
||||
T2IAdapterInputFieldValue,
|
||||
T2IAdapterPolymorphicInputFieldTemplate,
|
||||
T2IAdapterPolymorphicInputFieldValue,
|
||||
FieldComponentProps,
|
||||
} from 'features/nodes/types/types';
|
||||
import { memo } from 'react';
|
||||
|
||||
const T2IAdapterInputFieldComponent = (
|
||||
_props: FieldComponentProps<
|
||||
T2IAdapterInputFieldValue | T2IAdapterPolymorphicInputFieldValue,
|
||||
T2IAdapterInputFieldTemplate | T2IAdapterPolymorphicInputFieldTemplate
|
||||
>
|
||||
) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
export default memo(T2IAdapterInputFieldComponent);
|
@ -1,14 +0,0 @@
|
||||
import {
|
||||
UNetInputFieldTemplate,
|
||||
UNetInputFieldValue,
|
||||
FieldComponentProps,
|
||||
} from 'features/nodes/types/types';
|
||||
import { memo } from 'react';
|
||||
|
||||
const UNetInputFieldComponent = (
|
||||
_props: FieldComponentProps<UNetInputFieldValue, UNetInputFieldTemplate>
|
||||
) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
export default memo(UNetInputFieldComponent);
|
@ -1,14 +0,0 @@
|
||||
import {
|
||||
VaeInputFieldTemplate,
|
||||
VaeInputFieldValue,
|
||||
FieldComponentProps,
|
||||
} from 'features/nodes/types/types';
|
||||
import { memo } from 'react';
|
||||
|
||||
const VaeInputFieldComponent = (
|
||||
_props: FieldComponentProps<VaeInputFieldValue, VaeInputFieldTemplate>
|
||||
) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
export default memo(VaeInputFieldComponent);
|
@ -1,27 +0,0 @@
|
||||
import { NODE_MIN_WIDTH } from 'features/nodes/types/constants';
|
||||
import { memo } from 'react';
|
||||
import { NodeResizeControl, NodeResizerProps } from 'reactflow';
|
||||
|
||||
// this causes https://github.com/invoke-ai/InvokeAI/issues/4140
|
||||
// not using it for now
|
||||
|
||||
const NodeResizer = (props: NodeResizerProps) => {
|
||||
const { ...rest } = props;
|
||||
return (
|
||||
<NodeResizeControl
|
||||
style={{
|
||||
position: 'absolute',
|
||||
border: 'none',
|
||||
background: 'transparent',
|
||||
width: 15,
|
||||
height: 15,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
}}
|
||||
minWidth={NODE_MIN_WIDTH}
|
||||
{...rest}
|
||||
></NodeResizeControl>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(NodeResizer);
|
@ -1,78 +0,0 @@
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||
import { InvocationTemplate, NodeData } from 'features/nodes/types/types';
|
||||
import { memo } from 'react';
|
||||
import NotesTextarea from '../../flow/nodes/Invocation/NotesTextarea';
|
||||
import NodeTitle from '../../flow/nodes/common/NodeTitle';
|
||||
import ScrollableContent from '../ScrollableContent';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selector = createSelector(
|
||||
stateSelector,
|
||||
({ nodes }) => {
|
||||
const lastSelectedNodeId =
|
||||
nodes.selectedNodes[nodes.selectedNodes.length - 1];
|
||||
|
||||
const lastSelectedNode = nodes.nodes.find(
|
||||
(node) => node.id === lastSelectedNodeId
|
||||
);
|
||||
|
||||
const lastSelectedNodeTemplate = lastSelectedNode
|
||||
? nodes.nodeTemplates[lastSelectedNode.data.type]
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
data: lastSelectedNode?.data,
|
||||
template: lastSelectedNodeTemplate,
|
||||
};
|
||||
},
|
||||
defaultSelectorOptions
|
||||
);
|
||||
|
||||
const InspectorDetailsTab = () => {
|
||||
const { data, template } = useAppSelector(selector);
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!template || !data) {
|
||||
return (
|
||||
<IAINoContentFallback label={t('nodes.noNodeSelected')} icon={null} />
|
||||
);
|
||||
}
|
||||
|
||||
return <Content data={data} template={template} />;
|
||||
};
|
||||
|
||||
export default memo(InspectorDetailsTab);
|
||||
|
||||
const Content = (props: { data: NodeData; template: InvocationTemplate }) => {
|
||||
const { data } = props;
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'relative',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
}}
|
||||
>
|
||||
<ScrollableContent>
|
||||
<Flex
|
||||
sx={{
|
||||
flexDir: 'column',
|
||||
position: 'relative',
|
||||
p: 1,
|
||||
gap: 2,
|
||||
w: 'full',
|
||||
}}
|
||||
>
|
||||
<NodeTitle nodeId={data.id} />
|
||||
<NotesTextarea nodeId={data.id} />
|
||||
</Flex>
|
||||
</ScrollableContent>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -1,51 +0,0 @@
|
||||
import { Box, Text } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAITextarea from 'common/components/IAITextarea';
|
||||
import { workflowNotesChanged } from 'features/nodes/store/nodesSlice';
|
||||
import { ChangeEvent, memo, useCallback } from 'react';
|
||||
|
||||
const selector = createSelector(stateSelector, ({ nodes }) => {
|
||||
const { notes } = nodes.workflow;
|
||||
|
||||
return {
|
||||
notes,
|
||||
};
|
||||
});
|
||||
|
||||
const WorkflowNotesTab = () => {
|
||||
const { notes } = useAppSelector(selector);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleChangeNotes = useCallback(
|
||||
(e: ChangeEvent<HTMLTextAreaElement>) => {
|
||||
dispatch(workflowNotesChanged(e.target.value));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<Box sx={{ pos: 'relative', h: 'full' }}>
|
||||
<IAITextarea
|
||||
onChange={handleChangeNotes}
|
||||
value={notes}
|
||||
fontSize="sm"
|
||||
sx={{ h: 'full', resize: 'none' }}
|
||||
/>
|
||||
<Box sx={{ pos: 'absolute', bottom: 2, right: 2 }}>
|
||||
<Text
|
||||
sx={{
|
||||
fontSize: 'xs',
|
||||
opacity: 0.5,
|
||||
userSelect: 'none',
|
||||
}}
|
||||
>
|
||||
{notes.length}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(WorkflowNotesTab);
|
@ -1,11 +0,0 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import { AnyInvocationType } from 'services/events/types';
|
||||
|
||||
export const makeTemplateSelector = (type: AnyInvocationType) =>
|
||||
createSelector(
|
||||
stateSelector,
|
||||
({ nodes }) => nodes.nodeTemplates[type],
|
||||
defaultSelectorOptions
|
||||
);
|
@ -1,21 +0,0 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import IAICollapse from 'common/components/IAICollapse';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ParamBoundingBoxHeight from './ParamBoundingBoxHeight';
|
||||
import ParamBoundingBoxWidth from './ParamBoundingBoxWidth';
|
||||
|
||||
const ParamBoundingBoxCollapse = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<IAICollapse label={t('parameters.boundingBoxHeader')}>
|
||||
<Flex sx={{ gap: 2, flexDirection: 'column' }}>
|
||||
<ParamBoundingBoxWidth />
|
||||
<ParamBoundingBoxHeight />
|
||||
</Flex>
|
||||
</IAICollapse>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(ParamBoundingBoxCollapse);
|
@ -1,39 +0,0 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { setThreshold } from 'features/parameters/store/generationSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selector = createSelector(
|
||||
stateSelector,
|
||||
(state) => {
|
||||
const { threshold } = state.generation;
|
||||
return {
|
||||
threshold,
|
||||
};
|
||||
},
|
||||
defaultSelectorOptions
|
||||
);
|
||||
|
||||
export default function ParamNoiseThreshold() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { threshold } = useAppSelector(selector);
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<IAISlider
|
||||
label={t('parameters.noiseThreshold')}
|
||||
min={0}
|
||||
max={20}
|
||||
step={0.1}
|
||||
onChange={(v) => dispatch(setThreshold(v))}
|
||||
handleReset={() => dispatch(setThreshold(0))}
|
||||
value={threshold}
|
||||
withInput
|
||||
withReset
|
||||
withSliderMarks
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { setPerlin } from 'features/parameters/store/generationSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selector = createSelector(
|
||||
stateSelector,
|
||||
(state) => {
|
||||
const { perlin } = state.generation;
|
||||
return {
|
||||
perlin,
|
||||
};
|
||||
},
|
||||
defaultSelectorOptions
|
||||
);
|
||||
|
||||
export default function ParamPerlinNoise() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { perlin } = useAppSelector(selector);
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<IAISlider
|
||||
label={t('parameters.perlinNoise')}
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.05}
|
||||
onChange={(v) => dispatch(setPerlin(v))}
|
||||
handleReset={() => dispatch(setPerlin(0))}
|
||||
value={perlin}
|
||||
withInput
|
||||
withReset
|
||||
withSliderMarks
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { setVariationAmount } from 'features/parameters/store/generationSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function ParamVariationAmount() {
|
||||
const variationAmount = useAppSelector(
|
||||
(state: RootState) => state.generation.variationAmount
|
||||
);
|
||||
|
||||
const shouldGenerateVariations = useAppSelector(
|
||||
(state: RootState) => state.generation.shouldGenerateVariations
|
||||
);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
return (
|
||||
<IAISlider
|
||||
label={t('parameters.variationAmount')}
|
||||
value={variationAmount}
|
||||
step={0.01}
|
||||
min={0}
|
||||
max={1}
|
||||
isDisabled={!shouldGenerateVariations}
|
||||
onChange={(v) => dispatch(setVariationAmount(v))}
|
||||
handleReset={() => dispatch(setVariationAmount(0.1))}
|
||||
withInput
|
||||
withReset
|
||||
withSliderMarks
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
// TODO: variations
|
||||
|
||||
// import { Flex } from '@chakra-ui/react';
|
||||
// import { createSelector } from '@reduxjs/toolkit';
|
||||
// import { stateSelector } from 'app/store/store';
|
||||
// import { useAppSelector } from 'app/store/storeHooks';
|
||||
// import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
// import IAICollapse from 'common/components/IAICollapse';
|
||||
// import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||
// import { memo } from 'react';
|
||||
// import { useTranslation } from 'react-i18next';
|
||||
// import ParamVariationAmount from './ParamVariationAmount';
|
||||
// import { ParamVariationToggle } from './ParamVariationToggle';
|
||||
// import ParamVariationWeights from './ParamVariationWeights';
|
||||
|
||||
// const selector = createSelector(
|
||||
// stateSelector,
|
||||
// (state) => {
|
||||
// const activeLabel = state.generation.shouldGenerateVariations
|
||||
// ? 'Enabled'
|
||||
// : undefined;
|
||||
|
||||
// return { activeLabel };
|
||||
// },
|
||||
// defaultSelectorOptions
|
||||
// );
|
||||
|
||||
// const ParamVariationCollapse = () => {
|
||||
// const { t } = useTranslation();
|
||||
// const { activeLabel } = useAppSelector(selector);
|
||||
|
||||
// const isVariationEnabled = useFeatureStatus('variation').isFeatureEnabled;
|
||||
|
||||
// if (!isVariationEnabled) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// return (
|
||||
// <IAICollapse label={t('parameters.variations')} activeLabel={activeLabel}>
|
||||
// <Flex sx={{ gap: 2, flexDirection: 'column' }}>
|
||||
// <ParamVariationToggle />
|
||||
// <ParamVariationAmount />
|
||||
// <ParamVariationWeights />
|
||||
// </Flex>
|
||||
// </IAICollapse>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default memo(ParamVariationCollapse);
|
||||
|
||||
export default {};
|
@ -1,24 +0,0 @@
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAISwitch from 'common/components/IAISwitch';
|
||||
import { setShouldGenerateVariations } from 'features/parameters/store/generationSlice';
|
||||
import { ChangeEvent } from 'react';
|
||||
|
||||
export const ParamVariationToggle = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const shouldGenerateVariations = useAppSelector(
|
||||
(state: RootState) => state.generation.shouldGenerateVariations
|
||||
);
|
||||
|
||||
const handleChange = (e: ChangeEvent<HTMLInputElement>) =>
|
||||
dispatch(setShouldGenerateVariations(e.target.checked));
|
||||
|
||||
return (
|
||||
<IAISwitch
|
||||
label="Enable Variations"
|
||||
isChecked={shouldGenerateVariations}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
);
|
||||
};
|
@ -1,41 +0,0 @@
|
||||
// TODO: variations
|
||||
|
||||
// import { RootState } from 'app/store/store';
|
||||
// import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
// import IAIInput from 'common/components/IAIInput';
|
||||
// import { validateSeedWeights } from 'common/util/seedWeightPairs';
|
||||
// import { setSeedWeights } from 'features/parameters/store/generationSlice';
|
||||
// import { ChangeEvent } from 'react';
|
||||
// import { useTranslation } from 'react-i18next';
|
||||
|
||||
// export default function ParamVariationWeights() {
|
||||
// const seedWeights = useAppSelector(
|
||||
// (state: RootState) => state.generation.seedWeights
|
||||
// );
|
||||
|
||||
// const shouldGenerateVariations = useAppSelector(
|
||||
// (state: RootState) => state.generation.shouldGenerateVariations
|
||||
// );
|
||||
|
||||
// const { t } = useTranslation();
|
||||
|
||||
// const dispatch = useAppDispatch();
|
||||
|
||||
// const handleChangeSeedWeights = (e: ChangeEvent<HTMLInputElement>) =>
|
||||
// dispatch(setSeedWeights(e.target.value));
|
||||
|
||||
// return (
|
||||
// <IAIInput
|
||||
// label={t('parameters.seedWeights')}
|
||||
// value={seedWeights}
|
||||
// isInvalid={
|
||||
// shouldGenerateVariations &&
|
||||
// !(validateSeedWeights(seedWeights) || seedWeights === '')
|
||||
// }
|
||||
// isDisabled={!shouldGenerateVariations}
|
||||
// onChange={handleChangeSeedWeights}
|
||||
// />
|
||||
// );
|
||||
// }
|
||||
|
||||
export default {};
|
@ -1,4 +0,0 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
|
||||
export const postprocessingSelector = (state: RootState) =>
|
||||
state.postprocessing;
|
@ -1,18 +0,0 @@
|
||||
import { memo } from 'react';
|
||||
import QueueItemCard from './common/QueueItemCard';
|
||||
import { useGetCurrentQueueItemQuery } from 'services/api/endpoints/queue';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const CurrentQueueItemCard = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data: currentQueueItemData } = useGetCurrentQueueItemQuery();
|
||||
|
||||
return (
|
||||
<QueueItemCard
|
||||
label={t('queue.current')}
|
||||
session_queue_item={currentQueueItemData}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(CurrentQueueItemCard);
|
@ -1,18 +0,0 @@
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useGetNextQueueItemQuery } from 'services/api/endpoints/queue';
|
||||
import QueueItemCard from './common/QueueItemCard';
|
||||
|
||||
const NextQueueItemCard = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data: nextQueueItemData } = useGetNextQueueItemQuery();
|
||||
|
||||
return (
|
||||
<QueueItemCard
|
||||
label={t('queue.next')}
|
||||
session_queue_item={nextQueueItemData}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(NextQueueItemCard);
|
@ -1,41 +0,0 @@
|
||||
import { Flex, Skeleton } from '@chakra-ui/react';
|
||||
import { memo } from 'react';
|
||||
import { COLUMN_WIDTHS } from './constants';
|
||||
|
||||
const QueueItemSkeleton = () => {
|
||||
return (
|
||||
<Flex alignItems="center" p={1.5} gap={4} minH={9} h="full" w="full">
|
||||
<Flex
|
||||
w={COLUMN_WIDTHS.number}
|
||||
justifyContent="flex-end"
|
||||
alignItems="center"
|
||||
>
|
||||
<Skeleton w="full" h="full">
|
||||
|
||||
</Skeleton>
|
||||
</Flex>
|
||||
<Flex w={COLUMN_WIDTHS.statusBadge} alignItems="center">
|
||||
<Skeleton w="full" h="full">
|
||||
|
||||
</Skeleton>
|
||||
</Flex>
|
||||
<Flex w={COLUMN_WIDTHS.time} alignItems="center">
|
||||
<Skeleton w="full" h="full">
|
||||
|
||||
</Skeleton>
|
||||
</Flex>
|
||||
<Flex w={COLUMN_WIDTHS.batchId} alignItems="center">
|
||||
<Skeleton w="full" h="full">
|
||||
|
||||
</Skeleton>
|
||||
</Flex>
|
||||
<Flex w={COLUMN_WIDTHS.fieldValues} alignItems="center" flexGrow={1}>
|
||||
<Skeleton w="full" h="full">
|
||||
|
||||
</Skeleton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(QueueItemSkeleton);
|
@ -1,54 +0,0 @@
|
||||
import { Flex, Heading, Text } from '@chakra-ui/react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useGetQueueStatusQuery } from 'services/api/endpoints/queue';
|
||||
|
||||
const QueueStatusCard = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data: queueStatus } = useGetQueueStatusQuery();
|
||||
|
||||
return (
|
||||
<Flex
|
||||
layerStyle="second"
|
||||
borderRadius="base"
|
||||
p={2}
|
||||
flexDir="column"
|
||||
gap={1}
|
||||
w={96}
|
||||
>
|
||||
<Heading size="md">{t('queue.status')}</Heading>
|
||||
<Text>
|
||||
<Text as="span" fontWeight={600}>
|
||||
{t('queue.pending')}:{' '}
|
||||
</Text>
|
||||
{queueStatus?.queue.pending}
|
||||
</Text>
|
||||
<Text>
|
||||
<Text as="span" fontWeight={600}>
|
||||
{t('queue.in_progress')}:{' '}
|
||||
</Text>
|
||||
{queueStatus?.queue.in_progress}
|
||||
</Text>
|
||||
<Text>
|
||||
<Text as="span" fontWeight={600}>
|
||||
{t('queue.completed')}:{' '}
|
||||
</Text>
|
||||
{queueStatus?.queue.completed}
|
||||
</Text>
|
||||
<Text>
|
||||
<Text as="span" fontWeight={600}>
|
||||
{t('queue.failed')}:{' '}
|
||||
</Text>
|
||||
{queueStatus?.queue.failed}
|
||||
</Text>
|
||||
<Text>
|
||||
<Text as="span" fontWeight={600}>
|
||||
{t('queue.canceled')}:{' '}
|
||||
</Text>
|
||||
{queueStatus?.queue.canceled}
|
||||
</Text>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(QueueStatusCard);
|
@ -1,52 +0,0 @@
|
||||
import { Flex, Heading, Text, Tooltip } from '@chakra-ui/react';
|
||||
import ScrollableContent from 'features/nodes/components/sidePanel/ScrollableContent';
|
||||
import { memo } from 'react';
|
||||
import { components } from 'services/api/schema';
|
||||
|
||||
const QueueItemCard = ({
|
||||
session_queue_item,
|
||||
label,
|
||||
}: {
|
||||
session_queue_item?: components['schemas']['SessionQueueItem'] | null;
|
||||
label: string;
|
||||
}) => {
|
||||
return (
|
||||
<Flex
|
||||
layerStyle="second"
|
||||
borderRadius="base"
|
||||
w="full"
|
||||
p={2}
|
||||
flexDir="column"
|
||||
gap={1}
|
||||
>
|
||||
<Flex justifyContent="space-between" alignItems="flex-start">
|
||||
<Heading size="md">{label}</Heading>
|
||||
{session_queue_item && (
|
||||
<Tooltip label="Batch ID" placement="top" hasArrow>
|
||||
<Text fontSize="xs">{session_queue_item.batch_id}</Text>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Flex>
|
||||
{session_queue_item && (
|
||||
<ScrollableContent>
|
||||
<Text>Batch Values: </Text>
|
||||
{session_queue_item.field_values &&
|
||||
session_queue_item.field_values
|
||||
.filter((v) => v.node_path !== 'metadata_accumulator')
|
||||
.map(({ node_path, field_name, value }) => (
|
||||
<Text
|
||||
key={`${session_queue_item.item_id}.${node_path}.${field_name}.${value}`}
|
||||
>
|
||||
<Text as="span" fontWeight={600}>
|
||||
{node_path}.{field_name}
|
||||
</Text>
|
||||
: {value}
|
||||
</Text>
|
||||
))}
|
||||
</ScrollableContent>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(QueueItemCard);
|
@ -1,28 +0,0 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { SessionQueueItemStatus } from 'services/api/endpoints/queue';
|
||||
|
||||
const STATUSES = {
|
||||
pending: { colorScheme: 'cyan', translationKey: 'queue.pending' },
|
||||
in_progress: { colorScheme: 'yellow', translationKey: 'queue.in_progress' },
|
||||
completed: { colorScheme: 'green', translationKey: 'queue.completed' },
|
||||
failed: { colorScheme: 'red', translationKey: 'queue.failed' },
|
||||
canceled: { colorScheme: 'orange', translationKey: 'queue.canceled' },
|
||||
};
|
||||
|
||||
const QueueStatusDot = ({ status }: { status: SessionQueueItemStatus }) => {
|
||||
const sx = useMemo(
|
||||
() => ({
|
||||
w: 2,
|
||||
h: 2,
|
||||
bg: `${STATUSES[status].colorScheme}.${500}`,
|
||||
_dark: {
|
||||
bg: `${STATUSES[status].colorScheme}.${400}`,
|
||||
},
|
||||
borderRadius: '100%',
|
||||
}),
|
||||
[status]
|
||||
);
|
||||
return <Box sx={sx} />;
|
||||
};
|
||||
export default memo(QueueStatusDot);
|
@ -1,4 +0,0 @@
|
||||
export const formatNumberShort = (num: number) =>
|
||||
Intl.NumberFormat('en-US', {
|
||||
notation: 'standard',
|
||||
}).format(num);
|
@ -1,82 +0,0 @@
|
||||
import {
|
||||
Flex,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react';
|
||||
import { LOCALSTORAGE_KEYS, LOCALSTORAGE_PREFIX } from 'app/store/constants';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import { memo, useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
type Props = {
|
||||
onSettingsModalClose: () => void;
|
||||
};
|
||||
|
||||
const ResetWebUIButton = ({ onSettingsModalClose }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const [countdown, setCountdown] = useState(5);
|
||||
|
||||
const {
|
||||
isOpen: isRefreshModalOpen,
|
||||
onOpen: onRefreshModalOpen,
|
||||
onClose: onRefreshModalClose,
|
||||
} = useDisclosure();
|
||||
|
||||
const handleClickResetWebUI = useCallback(() => {
|
||||
// Only remove our keys
|
||||
Object.keys(window.localStorage).forEach((key) => {
|
||||
if (
|
||||
LOCALSTORAGE_KEYS.includes(key) ||
|
||||
key.startsWith(LOCALSTORAGE_PREFIX)
|
||||
) {
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
});
|
||||
onSettingsModalClose();
|
||||
onRefreshModalOpen();
|
||||
setInterval(() => setCountdown((prev) => prev - 1), 1000);
|
||||
}, [onSettingsModalClose, onRefreshModalOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (countdown <= 0) {
|
||||
window.location.reload();
|
||||
}
|
||||
}, [countdown]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<IAIButton colorScheme="error" onClick={handleClickResetWebUI}>
|
||||
{t('settings.resetWebUI')}
|
||||
</IAIButton>
|
||||
<Modal
|
||||
closeOnOverlayClick={false}
|
||||
isOpen={isRefreshModalOpen}
|
||||
onClose={onRefreshModalClose}
|
||||
isCentered
|
||||
closeOnEsc={false}
|
||||
>
|
||||
<ModalOverlay backdropFilter="blur(40px)" />
|
||||
<ModalContent>
|
||||
<ModalHeader />
|
||||
<ModalBody>
|
||||
<Flex justifyContent="center">
|
||||
<Text fontSize="lg">
|
||||
<Text>{t('settings.resetComplete')}</Text>
|
||||
<Text>Reloading in {countdown}...</Text>
|
||||
</Text>
|
||||
</Flex>
|
||||
</ModalBody>
|
||||
<ModalFooter />
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(ResetWebUIButton);
|
@ -1,3 +0,0 @@
|
||||
import { createAction } from '@reduxjs/toolkit';
|
||||
|
||||
export const sessionReadyToInvoke = createAction('system/sessionReadyToInvoke');
|
@ -1,62 +0,0 @@
|
||||
// TODO: split system slice inot this
|
||||
|
||||
// import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
// import { createSlice } from '@reduxjs/toolkit';
|
||||
// import { socketSubscribed, socketUnsubscribed } from 'services/events/actions';
|
||||
|
||||
// export type SessionState = {
|
||||
// /**
|
||||
// * The current socket session id
|
||||
// */
|
||||
// sessionId: string;
|
||||
// /**
|
||||
// * Whether the current session is a canvas session. Needed to manage the staging area.
|
||||
// */
|
||||
// isCanvasSession: boolean;
|
||||
// /**
|
||||
// * When a session is canceled, its ID is stored here until a new session is created.
|
||||
// */
|
||||
// canceledSessionId: string;
|
||||
// };
|
||||
|
||||
// export const initialSessionState: SessionState = {
|
||||
// sessionId: '',
|
||||
// isCanvasSession: false,
|
||||
// canceledSessionId: '',
|
||||
// };
|
||||
|
||||
// export const sessionSlice = createSlice({
|
||||
// name: 'session',
|
||||
// initialState: initialSessionState,
|
||||
// reducers: {
|
||||
// sessionIdChanged: (state, action: PayloadAction<string>) => {
|
||||
// state.sessionId = action.payload;
|
||||
// },
|
||||
// isCanvasSessionChanged: (state, action: PayloadAction<boolean>) => {
|
||||
// state.isCanvasSession = action.payload;
|
||||
// },
|
||||
// },
|
||||
// extraReducers: (builder) => {
|
||||
// /**
|
||||
// * Socket Subscribed
|
||||
// */
|
||||
// builder.addCase(socketSubscribed, (state, action) => {
|
||||
// state.sessionId = action.payload.sessionId;
|
||||
// state.canceledSessionId = '';
|
||||
// });
|
||||
|
||||
// /**
|
||||
// * Socket Unsubscribed
|
||||
// */
|
||||
// builder.addCase(socketUnsubscribed, (state) => {
|
||||
// state.sessionId = '';
|
||||
// });
|
||||
// },
|
||||
// });
|
||||
|
||||
// export const { sessionIdChanged, isCanvasSessionChanged } =
|
||||
// sessionSlice.actions;
|
||||
|
||||
// export default sessionSlice.reducer;
|
||||
|
||||
export default {};
|
@ -1,22 +0,0 @@
|
||||
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
|
||||
import { PropsWithChildren, memo } from 'react';
|
||||
const OverlayScrollable = (props: PropsWithChildren) => {
|
||||
return (
|
||||
<OverlayScrollbarsComponent
|
||||
defer
|
||||
style={{ height: '100%', width: '100%' }}
|
||||
options={{
|
||||
scrollbars: {
|
||||
visibility: 'auto',
|
||||
autoHide: 'move',
|
||||
autoHideDelay: 1300,
|
||||
theme: 'os-theme-dark',
|
||||
},
|
||||
overflow: { x: 'hidden' },
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</OverlayScrollbarsComponent>
|
||||
);
|
||||
};
|
||||
export default memo(OverlayScrollable);
|
@ -1,3 +0,0 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
|
||||
export const modelmanagerSelector = (state: RootState) => state.modelmanager;
|
@ -1,12 +0,0 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import UnifiedCanvasBrushSettings from './UnifiedCanvasBrushSettings';
|
||||
import UnifiedCanvasLimitStrokesToBox from './UnifiedCanvasLimitStrokesToBox';
|
||||
|
||||
export default function UnifiedCanvasBaseBrushSettings() {
|
||||
return (
|
||||
<Flex gap={4} alignItems="center">
|
||||
<UnifiedCanvasBrushSettings />
|
||||
<UnifiedCanvasLimitStrokesToBox />
|
||||
</Flex>
|
||||
);
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import UnifiedCanvasBrushSize from './UnifiedCanvasBrushSize';
|
||||
import UnifiedCanvasColorPicker from './UnifiedCanvasColorPicker';
|
||||
|
||||
export default function UnifiedCanvasBrushSettings() {
|
||||
return (
|
||||
<Flex columnGap={4} alignItems="center">
|
||||
<UnifiedCanvasBrushSize />
|
||||
<UnifiedCanvasColorPicker />
|
||||
</Flex>
|
||||
);
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { setBrushSize } from 'features/canvas/store/canvasSlice';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function UnifiedCanvasBrushSize() {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const brushSize = useAppSelector(
|
||||
(state: RootState) => state.canvas.brushSize
|
||||
);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
|
||||
useHotkeys(
|
||||
['BracketLeft'],
|
||||
() => {
|
||||
dispatch(setBrushSize(Math.max(brushSize - 5, 5)));
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[brushSize]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['BracketRight'],
|
||||
() => {
|
||||
dispatch(setBrushSize(Math.min(brushSize + 5, 500)));
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[brushSize]
|
||||
);
|
||||
|
||||
return (
|
||||
<IAISlider
|
||||
label={t('unifiedCanvas.brushSize')}
|
||||
value={brushSize}
|
||||
withInput
|
||||
onChange={(newSize) => dispatch(setBrushSize(newSize))}
|
||||
sliderNumberInputProps={{ max: 500 }}
|
||||
isCompact
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
|
||||
import { clearMask } from 'features/canvas/store/canvasSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { FaTrash } from 'react-icons/fa';
|
||||
|
||||
export default function UnifiedCanvasClearMask() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleClearMask = () => dispatch(clearMask());
|
||||
|
||||
return (
|
||||
<IAIButton
|
||||
size="sm"
|
||||
leftIcon={<FaTrash />}
|
||||
onClick={handleClearMask}
|
||||
tooltip={`${t('unifiedCanvas.clearMask')} (Shift+C)`}
|
||||
>
|
||||
{t('unifiedCanvas.betaClear')}
|
||||
</IAIButton>
|
||||
);
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIColorPicker from 'common/components/IAIColorPicker';
|
||||
import IAIPopover from 'common/components/IAIPopover';
|
||||
import {
|
||||
canvasSelector,
|
||||
isStagingSelector,
|
||||
} from 'features/canvas/store/canvasSelectors';
|
||||
import { setBrushColor, setMaskColor } from 'features/canvas/store/canvasSlice';
|
||||
import { clamp, isEqual } from 'lodash-es';
|
||||
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
const selector = createSelector(
|
||||
[canvasSelector, isStagingSelector],
|
||||
(canvas, isStaging) => {
|
||||
const { brushColor, maskColor, layer } = canvas;
|
||||
return {
|
||||
brushColor,
|
||||
maskColor,
|
||||
layer,
|
||||
isStaging,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default function UnifiedCanvasColorPicker() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { brushColor, maskColor, layer, isStaging } = useAppSelector(selector);
|
||||
|
||||
const currentColorDisplay = () => {
|
||||
if (layer === 'base') {
|
||||
return `rgba(${brushColor.r},${brushColor.g},${brushColor.b},${brushColor.a})`;
|
||||
}
|
||||
if (layer === 'mask') {
|
||||
return `rgba(${maskColor.r},${maskColor.g},${maskColor.b},${maskColor.a})`;
|
||||
}
|
||||
};
|
||||
|
||||
useHotkeys(
|
||||
['shift+BracketLeft'],
|
||||
() => {
|
||||
dispatch(
|
||||
setBrushColor({
|
||||
...brushColor,
|
||||
a: clamp(brushColor.a - 0.05, 0.05, 1),
|
||||
})
|
||||
);
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[brushColor]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['shift+BracketRight'],
|
||||
() => {
|
||||
dispatch(
|
||||
setBrushColor({
|
||||
...brushColor,
|
||||
a: clamp(brushColor.a + 0.05, 0.05, 1),
|
||||
})
|
||||
);
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[brushColor]
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIPopover
|
||||
triggerComponent={
|
||||
<Box
|
||||
sx={{
|
||||
width: 7,
|
||||
height: 7,
|
||||
minWidth: 7,
|
||||
minHeight: 7,
|
||||
borderRadius: 'full',
|
||||
bg: currentColorDisplay(),
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Flex minWidth={60} direction="column" gap={4} width="100%">
|
||||
{layer === 'base' && (
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
paddingTop: 2,
|
||||
paddingBottom: 2,
|
||||
}}
|
||||
>
|
||||
<IAIColorPicker
|
||||
color={brushColor}
|
||||
onChange={(newColor) => dispatch(setBrushColor(newColor))}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
{layer === 'mask' && (
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
paddingTop: 2,
|
||||
paddingBottom: 2,
|
||||
}}
|
||||
>
|
||||
<IAIColorPicker
|
||||
color={maskColor}
|
||||
onChange={(newColor) => dispatch(setMaskColor(newColor))}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Flex>
|
||||
</IAIPopover>
|
||||
);
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||
import { setShouldDarkenOutsideBoundingBox } from 'features/canvas/store/canvasSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function UnifiedCanvasDarkenOutsideSelection() {
|
||||
const shouldDarkenOutsideBoundingBox = useAppSelector(
|
||||
(state: RootState) => state.canvas.shouldDarkenOutsideBoundingBox
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<IAISimpleCheckbox
|
||||
label={t('unifiedCanvas.betaDarkenOutside')}
|
||||
isChecked={shouldDarkenOutsideBoundingBox}
|
||||
onChange={(e) =>
|
||||
dispatch(setShouldDarkenOutsideBoundingBox(e.target.checked))
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||
import { setIsMaskEnabled } from 'features/canvas/store/canvasSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function UnifiedCanvasEnableMask() {
|
||||
const isMaskEnabled = useAppSelector(
|
||||
(state: RootState) => state.canvas.isMaskEnabled
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleToggleEnableMask = () =>
|
||||
dispatch(setIsMaskEnabled(!isMaskEnabled));
|
||||
|
||||
return (
|
||||
<IAISimpleCheckbox
|
||||
label={`${t('unifiedCanvas.enableMask')} (H)`}
|
||||
isChecked={isMaskEnabled}
|
||||
onChange={handleToggleEnableMask}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||
import { setShouldRestrictStrokesToBox } from 'features/canvas/store/canvasSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function UnifiedCanvasLimitStrokesToBox() {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const shouldRestrictStrokesToBox = useAppSelector(
|
||||
(state: RootState) => state.canvas.shouldRestrictStrokesToBox
|
||||
);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<IAISimpleCheckbox
|
||||
label={t('unifiedCanvas.betaLimitToBox')}
|
||||
isChecked={shouldRestrictStrokesToBox}
|
||||
onChange={(e) =>
|
||||
dispatch(setShouldRestrictStrokesToBox(e.target.checked))
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import UnifiedCanvasBrushSettings from './UnifiedCanvasBrushSettings';
|
||||
import UnifiedCanvasClearMask from './UnifiedCanvasClearMask';
|
||||
import UnifiedCanvasEnableMask from './UnifiedCanvasEnableMask';
|
||||
import UnifiedCanvasPreserveMask from './UnifiedCanvasPreserveMask';
|
||||
|
||||
export default function UnifiedCanvasMaskBrushSettings() {
|
||||
return (
|
||||
<Flex gap={4} alignItems="center">
|
||||
<UnifiedCanvasBrushSettings />
|
||||
<UnifiedCanvasEnableMask />
|
||||
<UnifiedCanvasPreserveMask />
|
||||
<UnifiedCanvasClearMask />
|
||||
</Flex>
|
||||
);
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
import { Flex } from '@chakra-ui/layout';
|
||||
import UnifiedCanvasDarkenOutsideSelection from './UnifiedCanvasDarkenOutsideSelection';
|
||||
import UnifiedCanvasShowGrid from './UnifiedCanvasShowGrid';
|
||||
import UnifiedCanvasSnapToGrid from './UnifiedCanvasSnapToGrid';
|
||||
|
||||
export default function UnifiedCanvasMoveSettings() {
|
||||
return (
|
||||
<Flex alignItems="center" gap={4}>
|
||||
<UnifiedCanvasShowGrid />
|
||||
<UnifiedCanvasSnapToGrid />
|
||||
<UnifiedCanvasDarkenOutsideSelection />
|
||||
</Flex>
|
||||
);
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||
import { setShouldPreserveMaskedArea } from 'features/canvas/store/canvasSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function UnifiedCanvasPreserveMask() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const shouldPreserveMaskedArea = useAppSelector(
|
||||
(state: RootState) => state.canvas.shouldPreserveMaskedArea
|
||||
);
|
||||
|
||||
return (
|
||||
<IAISimpleCheckbox
|
||||
label={t('unifiedCanvas.betaPreserveMasked')}
|
||||
isChecked={shouldPreserveMaskedArea}
|
||||
onChange={(e) => dispatch(setShouldPreserveMaskedArea(e.target.checked))}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import IAIPopover from 'common/components/IAIPopover';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import {
|
||||
setShouldAntialias,
|
||||
setShouldAutoSave,
|
||||
setShouldCropToBoundingBoxOnSave,
|
||||
setShouldShowCanvasDebugInfo,
|
||||
setShouldShowIntermediates,
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
|
||||
import { FaWrench } from 'react-icons/fa';
|
||||
|
||||
import ClearCanvasHistoryButtonModal from 'features/canvas/components/ClearCanvasHistoryButtonModal';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const canvasControlsSelector = createSelector(
|
||||
[canvasSelector],
|
||||
(canvas) => {
|
||||
const {
|
||||
shouldAutoSave,
|
||||
shouldCropToBoundingBoxOnSave,
|
||||
shouldShowCanvasDebugInfo,
|
||||
shouldShowIntermediates,
|
||||
shouldAntialias,
|
||||
} = canvas;
|
||||
|
||||
return {
|
||||
shouldAutoSave,
|
||||
shouldCropToBoundingBoxOnSave,
|
||||
shouldShowCanvasDebugInfo,
|
||||
shouldShowIntermediates,
|
||||
shouldAntialias,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const UnifiedCanvasSettings = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const {
|
||||
shouldAutoSave,
|
||||
shouldCropToBoundingBoxOnSave,
|
||||
shouldShowCanvasDebugInfo,
|
||||
shouldShowIntermediates,
|
||||
shouldAntialias,
|
||||
} = useAppSelector(canvasControlsSelector);
|
||||
|
||||
return (
|
||||
<IAIPopover
|
||||
isLazy={false}
|
||||
triggerComponent={
|
||||
<IAIIconButton
|
||||
tooltip={t('unifiedCanvas.canvasSettings')}
|
||||
tooltipProps={{
|
||||
placement: 'bottom',
|
||||
}}
|
||||
aria-label={t('unifiedCanvas.canvasSettings')}
|
||||
icon={<FaWrench />}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Flex direction="column" gap={2}>
|
||||
<IAISimpleCheckbox
|
||||
label={t('unifiedCanvas.showIntermediates')}
|
||||
isChecked={shouldShowIntermediates}
|
||||
onChange={(e) =>
|
||||
dispatch(setShouldShowIntermediates(e.target.checked))
|
||||
}
|
||||
/>
|
||||
<IAISimpleCheckbox
|
||||
label={t('unifiedCanvas.autoSaveToGallery')}
|
||||
isChecked={shouldAutoSave}
|
||||
onChange={(e) => dispatch(setShouldAutoSave(e.target.checked))}
|
||||
/>
|
||||
<IAISimpleCheckbox
|
||||
label={t('unifiedCanvas.saveBoxRegionOnly')}
|
||||
isChecked={shouldCropToBoundingBoxOnSave}
|
||||
onChange={(e) =>
|
||||
dispatch(setShouldCropToBoundingBoxOnSave(e.target.checked))
|
||||
}
|
||||
/>
|
||||
<IAISimpleCheckbox
|
||||
label={t('unifiedCanvas.showCanvasDebugInfo')}
|
||||
isChecked={shouldShowCanvasDebugInfo}
|
||||
onChange={(e) =>
|
||||
dispatch(setShouldShowCanvasDebugInfo(e.target.checked))
|
||||
}
|
||||
/>
|
||||
<IAISimpleCheckbox
|
||||
label={t('unifiedCanvas.antialiasing')}
|
||||
isChecked={shouldAntialias}
|
||||
onChange={(e) => dispatch(setShouldAntialias(e.target.checked))}
|
||||
/>
|
||||
<ClearCanvasHistoryButtonModal />
|
||||
</Flex>
|
||||
</IAIPopover>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(UnifiedCanvasSettings);
|
@ -1,22 +0,0 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||
import { setShouldShowGrid } from 'features/canvas/store/canvasSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function UnifiedCanvasShowGrid() {
|
||||
const shouldShowGrid = useAppSelector(
|
||||
(state: RootState) => state.canvas.shouldShowGrid
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<IAISimpleCheckbox
|
||||
label={t('unifiedCanvas.showGrid')}
|
||||
isChecked={shouldShowGrid}
|
||||
onChange={(e) => dispatch(setShouldShowGrid(e.target.checked))}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||
import { setShouldSnapToGrid } from 'features/canvas/store/canvasSlice';
|
||||
import { ChangeEvent } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function UnifiedCanvasSnapToGrid() {
|
||||
const shouldSnapToGrid = useAppSelector(
|
||||
(state: RootState) => state.canvas.shouldSnapToGrid
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChangeShouldSnapToGrid = (e: ChangeEvent<HTMLInputElement>) =>
|
||||
dispatch(setShouldSnapToGrid(e.target.checked));
|
||||
|
||||
return (
|
||||
<IAISimpleCheckbox
|
||||
label={`${t('unifiedCanvas.snapToGrid')} (N)`}
|
||||
isChecked={shouldSnapToGrid}
|
||||
onChange={handleChangeShouldSnapToGrid}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import UnifiedCanvasBaseBrushSettings from './UnifiedCanvasToolSettings/UnifiedCanvasBaseBrushSettings';
|
||||
import UnifiedCanvasMaskBrushSettings from './UnifiedCanvasToolSettings/UnifiedCanvasMaskBrushSettings';
|
||||
import UnifiedCanvasMoveSettings from './UnifiedCanvasToolSettings/UnifiedCanvasMoveSettings';
|
||||
|
||||
const selector = createSelector(
|
||||
[canvasSelector],
|
||||
(canvas) => {
|
||||
const { tool, layer } = canvas;
|
||||
return {
|
||||
tool,
|
||||
layer,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default function UnifiedCanvasToolSettingsBeta() {
|
||||
const { tool, layer } = useAppSelector(selector);
|
||||
|
||||
return (
|
||||
<Flex height={8} minHeight={8} maxHeight={8} alignItems="center">
|
||||
{layer == 'base' && ['brush', 'eraser', 'colorPicker'].includes(tool) && (
|
||||
<UnifiedCanvasBaseBrushSettings />
|
||||
)}
|
||||
{layer == 'mask' && ['brush', 'eraser', 'colorPicker'].includes(tool) && (
|
||||
<UnifiedCanvasMaskBrushSettings />
|
||||
)}
|
||||
{tool == 'move' && <UnifiedCanvasMoveSettings />}
|
||||
</Flex>
|
||||
);
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { canvasCopiedToClipboard } from 'features/canvas/store/actions';
|
||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { useCopyImageToClipboard } from 'features/ui/hooks/useCopyImageToClipboard';
|
||||
import { useCallback } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaCopy } from 'react-icons/fa';
|
||||
|
||||
export default function UnifiedCanvasCopyToClipboard() {
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
const { isClipboardAPIAvailable } = useCopyImageToClipboard();
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
useHotkeys(
|
||||
['meta+c', 'ctrl+c'],
|
||||
() => {
|
||||
handleCopyImageToClipboard();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging && isClipboardAPIAvailable,
|
||||
preventDefault: true,
|
||||
},
|
||||
[isClipboardAPIAvailable]
|
||||
);
|
||||
|
||||
const handleCopyImageToClipboard = useCallback(() => {
|
||||
if (!isClipboardAPIAvailable) {
|
||||
return;
|
||||
}
|
||||
dispatch(canvasCopiedToClipboard());
|
||||
}, [dispatch, isClipboardAPIAvailable]);
|
||||
|
||||
if (!isClipboardAPIAvailable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label={`${t('unifiedCanvas.copyToClipboard')} (Cmd/Ctrl+C)`}
|
||||
tooltip={`${t('unifiedCanvas.copyToClipboard')} (Cmd/Ctrl+C)`}
|
||||
icon={<FaCopy />}
|
||||
onClick={handleCopyImageToClipboard}
|
||||
isDisabled={isStaging}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { canvasDownloadedAsImage } from 'features/canvas/store/actions';
|
||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaDownload } from 'react-icons/fa';
|
||||
|
||||
export default function UnifiedCanvasDownloadImage() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const canvasBaseLayer = getCanvasBaseLayer();
|
||||
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
|
||||
useHotkeys(
|
||||
['shift+d'],
|
||||
() => {
|
||||
handleDownloadAsImage();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[canvasBaseLayer]
|
||||
);
|
||||
|
||||
const handleDownloadAsImage = () => {
|
||||
dispatch(canvasDownloadedAsImage());
|
||||
};
|
||||
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label={`${t('unifiedCanvas.downloadAsImage')} (Shift+D)`}
|
||||
tooltip={`${t('unifiedCanvas.downloadAsImage')} (Shift+D)`}
|
||||
icon={<FaDownload />}
|
||||
onClick={handleDownloadAsImage}
|
||||
isDisabled={isStaging}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { useImageUploadButton } from 'common/hooks/useImageUploadButton';
|
||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaUpload } from 'react-icons/fa';
|
||||
|
||||
export default function UnifiedCanvasFileUploader() {
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
|
||||
const { getUploadButtonProps, getUploadInputProps } = useImageUploadButton({
|
||||
postUploadAction: { type: 'SET_CANVAS_INITIAL_IMAGE' },
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<IAIIconButton
|
||||
aria-label={t('common.upload')}
|
||||
tooltip={t('common.upload')}
|
||||
icon={<FaUpload />}
|
||||
isDisabled={isStaging}
|
||||
{...getUploadButtonProps()}
|
||||
/>
|
||||
<input {...getUploadInputProps()} />
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||
import {
|
||||
canvasSelector,
|
||||
isStagingSelector,
|
||||
} from 'features/canvas/store/canvasSelectors';
|
||||
import { setIsMaskEnabled, setLayer } from 'features/canvas/store/canvasSlice';
|
||||
import {
|
||||
CanvasLayer,
|
||||
LAYER_NAMES_DICT,
|
||||
} from 'features/canvas/store/canvasTypes';
|
||||
import { isEqual } from 'lodash-es';
|
||||
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selector = createSelector(
|
||||
[canvasSelector, isStagingSelector],
|
||||
(canvas, isStaging) => {
|
||||
const { layer, isMaskEnabled } = canvas;
|
||||
return { layer, isMaskEnabled, isStaging };
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default function UnifiedCanvasLayerSelect() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { layer, isMaskEnabled, isStaging } = useAppSelector(selector);
|
||||
|
||||
const handleToggleMaskLayer = () => {
|
||||
dispatch(setLayer(layer === 'mask' ? 'base' : 'mask'));
|
||||
};
|
||||
|
||||
useHotkeys(
|
||||
['q'],
|
||||
() => {
|
||||
handleToggleMaskLayer();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[layer]
|
||||
);
|
||||
|
||||
const handleChangeLayer = (v: string) => {
|
||||
const newLayer = v as CanvasLayer;
|
||||
dispatch(setLayer(newLayer));
|
||||
if (newLayer === 'mask' && !isMaskEnabled) {
|
||||
dispatch(setIsMaskEnabled(true));
|
||||
}
|
||||
};
|
||||
return (
|
||||
<IAIMantineSelect
|
||||
tooltip={`${t('unifiedCanvas.layer')} (Q)`}
|
||||
aria-label={`${t('unifiedCanvas.layer')} (Q)`}
|
||||
value={layer}
|
||||
data={LAYER_NAMES_DICT}
|
||||
onChange={handleChangeLayer}
|
||||
disabled={isStaging}
|
||||
w="full"
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { canvasMerged } from 'features/canvas/store/actions';
|
||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaLayerGroup } from 'react-icons/fa';
|
||||
export default function UnifiedCanvasMergeVisible() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
|
||||
useHotkeys(
|
||||
['shift+m'],
|
||||
() => {
|
||||
handleMergeVisible();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleMergeVisible = () => {
|
||||
dispatch(canvasMerged());
|
||||
};
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label={`${t('unifiedCanvas.mergeVisible')} (Shift+M)`}
|
||||
tooltip={`${t('unifiedCanvas.mergeVisible')} (Shift+M)`}
|
||||
icon={<FaLayerGroup />}
|
||||
onClick={handleMergeVisible}
|
||||
isDisabled={isStaging}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { setTool } from 'features/canvas/store/canvasSlice';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaArrowsAlt } from 'react-icons/fa';
|
||||
|
||||
export default function UnifiedCanvasMoveTool() {
|
||||
const tool = useAppSelector((state: RootState) => state.canvas.tool);
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
useHotkeys(
|
||||
['v'],
|
||||
() => {
|
||||
handleSelectMoveTool();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleSelectMoveTool = () => dispatch(setTool('move'));
|
||||
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label={`${t('unifiedCanvas.move')} (V)`}
|
||||
tooltip={`${t('unifiedCanvas.move')} (V)`}
|
||||
icon={<FaArrowsAlt />}
|
||||
isChecked={tool === 'move' || isStaging}
|
||||
onClick={handleSelectMoveTool}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { resetCanvas } from 'features/canvas/store/canvasSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaTrash } from 'react-icons/fa';
|
||||
|
||||
export default function UnifiedCanvasResetCanvas() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
|
||||
const handleResetCanvas = () => {
|
||||
dispatch(resetCanvas());
|
||||
};
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label={t('unifiedCanvas.clearCanvas')}
|
||||
tooltip={t('unifiedCanvas.clearCanvas')}
|
||||
icon={<FaTrash />}
|
||||
onClick={handleResetCanvas}
|
||||
isDisabled={isStaging}
|
||||
colorScheme="error"
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { useSingleAndDoubleClick } from 'common/hooks/useSingleAndDoubleClick';
|
||||
import { resetCanvasView } from 'features/canvas/store/canvasSlice';
|
||||
import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaCrosshairs } from 'react-icons/fa';
|
||||
|
||||
export default function UnifiedCanvasResetView() {
|
||||
const canvasBaseLayer = getCanvasBaseLayer();
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
useHotkeys(
|
||||
['r'],
|
||||
() => {
|
||||
handleResetCanvasView();
|
||||
},
|
||||
{
|
||||
enabled: () => true,
|
||||
preventDefault: true,
|
||||
},
|
||||
[canvasBaseLayer]
|
||||
);
|
||||
|
||||
const handleClickResetCanvasView = useSingleAndDoubleClick(
|
||||
() => handleResetCanvasView(false),
|
||||
() => handleResetCanvasView(true)
|
||||
);
|
||||
|
||||
const handleResetCanvasView = (shouldScaleTo1 = false) => {
|
||||
const canvasBaseLayer = getCanvasBaseLayer();
|
||||
if (!canvasBaseLayer) {
|
||||
return;
|
||||
}
|
||||
const clientRect = canvasBaseLayer.getClientRect({
|
||||
skipTransform: true,
|
||||
});
|
||||
dispatch(
|
||||
resetCanvasView({
|
||||
contentRect: clientRect,
|
||||
shouldScaleTo1,
|
||||
})
|
||||
);
|
||||
};
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label={`${t('unifiedCanvas.resetView')} (R)`}
|
||||
tooltip={`${t('unifiedCanvas.resetView')} (R)`}
|
||||
icon={<FaCrosshairs />}
|
||||
onClick={handleClickResetCanvasView}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { canvasSavedToGallery } from 'features/canvas/store/actions';
|
||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaSave } from 'react-icons/fa';
|
||||
|
||||
export default function UnifiedCanvasSaveToGallery() {
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
useHotkeys(
|
||||
['shift+s'],
|
||||
() => {
|
||||
handleSaveToGallery();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleSaveToGallery = () => {
|
||||
dispatch(canvasSavedToGallery());
|
||||
};
|
||||
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label={`${t('unifiedCanvas.saveToGallery')} (Shift+S)`}
|
||||
tooltip={`${t('unifiedCanvas.saveToGallery')} (Shift+S)`}
|
||||
icon={<FaSave />}
|
||||
onClick={handleSaveToGallery}
|
||||
isDisabled={isStaging}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,172 +0,0 @@
|
||||
import { ButtonGroup, Flex } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import {
|
||||
addEraseRect,
|
||||
addFillRect,
|
||||
setTool,
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { memo, useCallback } from 'react';
|
||||
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
FaEraser,
|
||||
FaEyeDropper,
|
||||
FaFillDrip,
|
||||
FaPaintBrush,
|
||||
FaPlus,
|
||||
} from 'react-icons/fa';
|
||||
|
||||
export const selector = createSelector(
|
||||
[stateSelector, isStagingSelector],
|
||||
({ canvas }, isStaging) => {
|
||||
const { tool } = canvas;
|
||||
|
||||
return {
|
||||
tool,
|
||||
isStaging,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const UnifiedCanvasToolSelect = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const { tool, isStaging } = useAppSelector(selector);
|
||||
|
||||
useHotkeys(
|
||||
['b'],
|
||||
() => {
|
||||
handleSelectBrushTool();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['e'],
|
||||
() => {
|
||||
handleSelectEraserTool();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[tool]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['c'],
|
||||
() => {
|
||||
handleSelectColorPickerTool();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
},
|
||||
[tool]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['shift+f'],
|
||||
() => {
|
||||
handleFillRect();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
}
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['delete', 'backspace'],
|
||||
() => {
|
||||
handleEraseBoundingBox();
|
||||
},
|
||||
{
|
||||
enabled: () => !isStaging,
|
||||
preventDefault: true,
|
||||
}
|
||||
);
|
||||
|
||||
const handleSelectBrushTool = useCallback(
|
||||
() => dispatch(setTool('brush')),
|
||||
[dispatch]
|
||||
);
|
||||
const handleSelectEraserTool = useCallback(
|
||||
() => dispatch(setTool('eraser')),
|
||||
[dispatch]
|
||||
);
|
||||
const handleSelectColorPickerTool = useCallback(
|
||||
() => dispatch(setTool('colorPicker')),
|
||||
[dispatch]
|
||||
);
|
||||
const handleFillRect = useCallback(() => dispatch(addFillRect()), [dispatch]);
|
||||
const handleEraseBoundingBox = useCallback(
|
||||
() => dispatch(addEraseRect()),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex flexDirection="column" gap={2}>
|
||||
<ButtonGroup>
|
||||
<IAIIconButton
|
||||
aria-label={`${t('unifiedCanvas.brush')} (B)`}
|
||||
tooltip={`${t('unifiedCanvas.brush')} (B)`}
|
||||
icon={<FaPaintBrush />}
|
||||
isChecked={tool === 'brush' && !isStaging}
|
||||
onClick={handleSelectBrushTool}
|
||||
isDisabled={isStaging}
|
||||
/>
|
||||
<IAIIconButton
|
||||
aria-label={`${t('unifiedCanvas.eraser')} (E)`}
|
||||
tooltip={`${t('unifiedCanvas.eraser')} (B)`}
|
||||
icon={<FaEraser />}
|
||||
isChecked={tool === 'eraser' && !isStaging}
|
||||
isDisabled={isStaging}
|
||||
onClick={handleSelectEraserTool}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup>
|
||||
<IAIIconButton
|
||||
aria-label={`${t('unifiedCanvas.fillBoundingBox')} (Shift+F)`}
|
||||
tooltip={`${t('unifiedCanvas.fillBoundingBox')} (Shift+F)`}
|
||||
icon={<FaFillDrip />}
|
||||
isDisabled={isStaging}
|
||||
onClick={handleFillRect}
|
||||
/>
|
||||
<IAIIconButton
|
||||
aria-label={`${t('unifiedCanvas.eraseBoundingBox')} (Del/Backspace)`}
|
||||
tooltip={`${t('unifiedCanvas.eraseBoundingBox')} (Del/Backspace)`}
|
||||
icon={<FaPlus style={{ transform: 'rotate(45deg)' }} />}
|
||||
isDisabled={isStaging}
|
||||
onClick={handleEraseBoundingBox}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
<IAIIconButton
|
||||
aria-label={`${t('unifiedCanvas.colorPicker')} (C)`}
|
||||
tooltip={`${t('unifiedCanvas.colorPicker')} (C)`}
|
||||
icon={<FaEyeDropper />}
|
||||
isChecked={tool === 'colorPicker' && !isStaging}
|
||||
isDisabled={isStaging}
|
||||
onClick={handleSelectColorPickerTool}
|
||||
width="max-content"
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(UnifiedCanvasToolSelect);
|
@ -1,53 +0,0 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
|
||||
import IAICanvasRedoButton from 'features/canvas/components/IAICanvasToolbar/IAICanvasRedoButton';
|
||||
import IAICanvasUndoButton from 'features/canvas/components/IAICanvasToolbar/IAICanvasUndoButton';
|
||||
import { memo } from 'react';
|
||||
import UnifiedCanvasSettings from './UnifiedCanvasToolSettings/UnifiedCanvasSettings';
|
||||
import UnifiedCanvasCopyToClipboard from './UnifiedCanvasToolbar/UnifiedCanvasCopyToClipboard';
|
||||
import UnifiedCanvasDownloadImage from './UnifiedCanvasToolbar/UnifiedCanvasDownloadImage';
|
||||
import UnifiedCanvasFileUploader from './UnifiedCanvasToolbar/UnifiedCanvasFileUploader';
|
||||
import UnifiedCanvasLayerSelect from './UnifiedCanvasToolbar/UnifiedCanvasLayerSelect';
|
||||
import UnifiedCanvasMergeVisible from './UnifiedCanvasToolbar/UnifiedCanvasMergeVisible';
|
||||
import UnifiedCanvasMoveTool from './UnifiedCanvasToolbar/UnifiedCanvasMoveTool';
|
||||
import UnifiedCanvasResetCanvas from './UnifiedCanvasToolbar/UnifiedCanvasResetCanvas';
|
||||
import UnifiedCanvasResetView from './UnifiedCanvasToolbar/UnifiedCanvasResetView';
|
||||
import UnifiedCanvasSaveToGallery from './UnifiedCanvasToolbar/UnifiedCanvasSaveToGallery';
|
||||
import UnifiedCanvasToolSelect from './UnifiedCanvasToolbar/UnifiedCanvasToolSelect';
|
||||
|
||||
const UnifiedCanvasToolbarBeta = () => {
|
||||
return (
|
||||
<Flex flexDirection="column" rowGap={2} width="min-content">
|
||||
<UnifiedCanvasLayerSelect />
|
||||
<UnifiedCanvasToolSelect />
|
||||
|
||||
<Flex gap={2}>
|
||||
<UnifiedCanvasMoveTool />
|
||||
<UnifiedCanvasResetView />
|
||||
</Flex>
|
||||
|
||||
<Flex columnGap={2}>
|
||||
<UnifiedCanvasMergeVisible />
|
||||
<UnifiedCanvasSaveToGallery />
|
||||
</Flex>
|
||||
<Flex columnGap={2}>
|
||||
<UnifiedCanvasCopyToClipboard />
|
||||
<UnifiedCanvasDownloadImage />
|
||||
</Flex>
|
||||
|
||||
<Flex gap={2}>
|
||||
<IAICanvasUndoButton />
|
||||
<IAICanvasRedoButton />
|
||||
</Flex>
|
||||
|
||||
<Flex gap={2}>
|
||||
<UnifiedCanvasFileUploader />
|
||||
<UnifiedCanvasResetCanvas />
|
||||
</Flex>
|
||||
|
||||
<UnifiedCanvasSettings />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(UnifiedCanvasToolbarBeta);
|
Loading…
Reference in New Issue
Block a user