mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): use primitive style props or memoized sx objects
This commit is contained in:
parent
83049a3a5b
commit
4f2930412e
@ -75,23 +75,9 @@ const App = ({ config = DEFAULT_CONFIG, selectedImage }: Props) => {
|
||||
>
|
||||
<Grid w="100vw" h="100vh" position="relative" overflow="hidden">
|
||||
<ImageUploader>
|
||||
<Grid
|
||||
sx={{
|
||||
gap: 4,
|
||||
p: 4,
|
||||
gridAutoRows: 'min-content auto',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
}}
|
||||
>
|
||||
<Grid gap={4} p={4} gridAutoRows="min-content auto" w="full" h="full">
|
||||
{headerComponent || <SiteHeader />}
|
||||
<Flex
|
||||
sx={{
|
||||
gap: 4,
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
}}
|
||||
>
|
||||
<Flex gap={4} w="full" h="full">
|
||||
<InvokeTabs />
|
||||
</Flex>
|
||||
</Grid>
|
||||
|
@ -38,41 +38,35 @@ const AppErrorBoundaryFallback = ({ error, resetErrorBoundary }: Props) => {
|
||||
return (
|
||||
<Flex
|
||||
layerStyle="body"
|
||||
sx={{
|
||||
w: '100vw',
|
||||
h: '100vh',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
p: 4,
|
||||
}}
|
||||
w="100vw"
|
||||
h="100vh"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
p={4}
|
||||
>
|
||||
<Flex
|
||||
layerStyle="first"
|
||||
sx={{
|
||||
flexDir: 'column',
|
||||
borderRadius: 'base',
|
||||
justifyContent: 'center',
|
||||
gap: 8,
|
||||
p: 16,
|
||||
}}
|
||||
flexDir="column"
|
||||
borderRadius="base"
|
||||
justifyContent="center"
|
||||
gap={8}
|
||||
p={16}
|
||||
>
|
||||
<Heading>{t('common.somethingWentWrong')}</Heading>
|
||||
<Flex
|
||||
layerStyle="second"
|
||||
sx={{
|
||||
px: 8,
|
||||
py: 4,
|
||||
borderRadius: 'base',
|
||||
gap: 4,
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
px={8}
|
||||
py={4}
|
||||
gap={4}
|
||||
borderRadius="base"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
>
|
||||
<InvText fontWeight="semibold" color="error.400">
|
||||
{error.name}: {error.message}
|
||||
</InvText>
|
||||
</Flex>
|
||||
<Flex sx={{ gap: 4 }}>
|
||||
<Flex gap={4}>
|
||||
<InvButton
|
||||
leftIcon={<FaArrowRotateLeft />}
|
||||
onClick={resetErrorBoundary}
|
||||
|
@ -1,4 +1,8 @@
|
||||
import type { ChakraProps, FlexProps } from '@chakra-ui/react';
|
||||
import type {
|
||||
ChakraProps,
|
||||
FlexProps,
|
||||
SystemStyleObject,
|
||||
} from '@chakra-ui/react';
|
||||
import { Flex, Icon, Image } from '@chakra-ui/react';
|
||||
import {
|
||||
IAILoadingImageFallback,
|
||||
@ -17,7 +21,7 @@ import type {
|
||||
ReactNode,
|
||||
SyntheticEvent,
|
||||
} from 'react';
|
||||
import { memo, useCallback, useState } from 'react';
|
||||
import { memo, useCallback, useMemo, useState } from 'react';
|
||||
import { FaImage, FaUpload } from 'react-icons/fa';
|
||||
import type { ImageDTO, PostUploadAction } from 'services/api/types';
|
||||
|
||||
@ -25,14 +29,7 @@ import IAIDraggable from './IAIDraggable';
|
||||
import IAIDroppable from './IAIDroppable';
|
||||
import SelectionOverlay from './SelectionOverlay';
|
||||
|
||||
const defaultUploadElement = (
|
||||
<Icon
|
||||
as={FaUpload}
|
||||
sx={{
|
||||
boxSize: 16,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
const defaultUploadElement = <Icon as={FaUpload} boxSize={16} />;
|
||||
|
||||
const defaultNoContentFallback = <IAINoContentFallback icon={FaImage} />;
|
||||
|
||||
@ -115,16 +112,29 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
||||
isDisabled: isUploadDisabled,
|
||||
});
|
||||
|
||||
const uploadButtonStyles = isUploadDisabled
|
||||
? {}
|
||||
: {
|
||||
const uploadButtonStyles = useMemo(() => {
|
||||
const styles: SystemStyleObject = {
|
||||
minH: minSize,
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderRadius: 'base',
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: '0.1s',
|
||||
color: 'base.500',
|
||||
};
|
||||
if (!isUploadDisabled) {
|
||||
Object.assign(styles, {
|
||||
cursor: 'pointer',
|
||||
bg: 'base.700',
|
||||
_hover: {
|
||||
bg: 'base.650',
|
||||
color: 'base.300',
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
}, [isUploadDisabled, minSize]);
|
||||
|
||||
return (
|
||||
<ImageContextMenu imageDTO={imageDTO}>
|
||||
@ -133,27 +143,23 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
||||
ref={ref}
|
||||
onMouseOver={handleMouseOver}
|
||||
onMouseOut={handleMouseOut}
|
||||
sx={{
|
||||
width: 'full',
|
||||
height: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'relative',
|
||||
minW: minSize ? minSize : undefined,
|
||||
minH: minSize ? minSize : undefined,
|
||||
userSelect: 'none',
|
||||
cursor: isDragDisabled || !imageDTO ? 'default' : 'pointer',
|
||||
}}
|
||||
width="full"
|
||||
height="full"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
position="relative"
|
||||
minW={minSize ? minSize : undefined}
|
||||
minH={minSize ? minSize : undefined}
|
||||
userSelect="none"
|
||||
cursor={isDragDisabled || !imageDTO ? 'default' : 'pointer'}
|
||||
>
|
||||
{imageDTO && (
|
||||
<Flex
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
position: fitContainer ? 'absolute' : 'relative',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
w="full"
|
||||
h="full"
|
||||
position={fitContainer ? 'absolute' : 'relative'}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<Image
|
||||
src={thumbnail ? imageDTO.thumbnail_url : imageDTO.image_url}
|
||||
@ -168,14 +174,12 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
||||
}
|
||||
onError={onError}
|
||||
draggable={false}
|
||||
sx={{
|
||||
w: imageDTO.width,
|
||||
objectFit: 'contain',
|
||||
maxW: 'full',
|
||||
maxH: 'full',
|
||||
borderRadius: 'base',
|
||||
...imageSx,
|
||||
}}
|
||||
w={imageDTO.width}
|
||||
objectFit="contain"
|
||||
maxW="full"
|
||||
maxH="full"
|
||||
borderRadius="base"
|
||||
sx={imageSx}
|
||||
data-testid={dataTestId}
|
||||
/>
|
||||
{withMetadataOverlay && (
|
||||
@ -189,21 +193,7 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
||||
)}
|
||||
{!imageDTO && !isUploadDisabled && (
|
||||
<>
|
||||
<Flex
|
||||
sx={{
|
||||
minH: minSize,
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderRadius: 'base',
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: '0.1s',
|
||||
color: 'base.500',
|
||||
...uploadButtonStyles,
|
||||
}}
|
||||
{...getUploadButtonProps()}
|
||||
>
|
||||
<Flex sx={uploadButtonStyles} {...getUploadButtonProps()}>
|
||||
<input {...getUploadInputProps()} />
|
||||
{uploadElement}
|
||||
</Flex>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { SystemStyleObject } from '@chakra-ui/react';
|
||||
import type { MouseEvent, ReactElement } from 'react';
|
||||
import { memo } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
|
||||
import { InvIconButton } from './InvIconButton/InvIconButton';
|
||||
|
||||
@ -14,6 +14,25 @@ type Props = {
|
||||
const IAIDndImageIcon = (props: Props) => {
|
||||
const { onClick, tooltip, icon, styleOverrides } = props;
|
||||
|
||||
const sx = useMemo(
|
||||
() => ({
|
||||
position: 'absolute',
|
||||
top: 1,
|
||||
insetInlineEnd: 1,
|
||||
p: 0,
|
||||
minW: 0,
|
||||
svg: {
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: 'normal',
|
||||
fill: 'base.100',
|
||||
_hover: { fill: 'base.50' },
|
||||
filter: 'drop-shadow(0px 0px 0.1rem var(--invokeai-colors-base-800))',
|
||||
},
|
||||
...styleOverrides,
|
||||
}),
|
||||
[styleOverrides]
|
||||
);
|
||||
|
||||
return (
|
||||
<InvIconButton
|
||||
onClick={onClick}
|
||||
@ -22,21 +41,7 @@ const IAIDndImageIcon = (props: Props) => {
|
||||
icon={icon}
|
||||
size="sm"
|
||||
variant="link"
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 1,
|
||||
insetInlineEnd: 1,
|
||||
p: 0,
|
||||
minW: 0,
|
||||
svg: {
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: 'normal',
|
||||
fill: 'base.100',
|
||||
_hover: { fill: 'base.50' },
|
||||
filter: 'drop-shadow(0px 0px 0.1rem var(--invokeai-colors-base-800))',
|
||||
},
|
||||
...styleOverrides,
|
||||
}}
|
||||
sx={sx}
|
||||
data-testid={tooltip}
|
||||
/>
|
||||
);
|
||||
|
@ -27,59 +27,45 @@ export const IAIDropOverlay = (props: Props) => {
|
||||
transition: { duration: 0.1 },
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineStart: 0,
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
}}
|
||||
>
|
||||
<Flex position="absolute" top={0} insetInlineStart={0} w="full" h="full">
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineStart: 0,
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
bg: 'base.900',
|
||||
opacity: 0.7,
|
||||
borderRadius: 'base',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: '0.1s',
|
||||
}}
|
||||
position="absolute"
|
||||
top={0}
|
||||
insetInlineStart={0}
|
||||
w="full"
|
||||
h="full"
|
||||
bg="base.900"
|
||||
opacity={0.7}
|
||||
borderRadius="base"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
transitionProperty="common"
|
||||
transitionDuration="0.1s"
|
||||
/>
|
||||
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0.5,
|
||||
insetInlineStart: 0.5,
|
||||
insetInlineEnd: 0.5,
|
||||
bottom: 0.5,
|
||||
opacity: 1,
|
||||
borderWidth: 2,
|
||||
borderColor: isOver ? 'base.50' : 'base.300',
|
||||
borderRadius: 'lg',
|
||||
borderStyle: 'dashed',
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: '0.1s',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
position="absolute"
|
||||
top={0.5}
|
||||
insetInlineStart={0.5}
|
||||
insetInlineEnd={0.5}
|
||||
bottom={0.5}
|
||||
opacity={1}
|
||||
borderWidth={2}
|
||||
borderColor={isOver ? 'base.50' : 'base.300'}
|
||||
borderRadius="lg"
|
||||
borderStyle="dashed"
|
||||
transitionProperty="common"
|
||||
transitionDuration="0.1s"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
fontSize: '2xl',
|
||||
fontWeight: 'semibold',
|
||||
transform: isOver ? 'scale(1.1)' : 'scale(1)',
|
||||
color: isOver ? 'base.50' : 'base.300',
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: '0.1s',
|
||||
}}
|
||||
fontSize="2xl"
|
||||
fontWeight="semibold"
|
||||
transform={isOver ? 'scale(1.1)' : 'scale(1)'}
|
||||
color={isOver ? 'base.50' : 'base.300'}
|
||||
transitionProperty="common"
|
||||
transitionDuration="0.1s"
|
||||
>
|
||||
{label}
|
||||
</Box>
|
||||
|
@ -1,28 +1,27 @@
|
||||
import type { SystemStyleObject } from '@chakra-ui/react';
|
||||
import { Box, Skeleton } from '@chakra-ui/react';
|
||||
import { memo } from 'react';
|
||||
|
||||
const skeletonStyles: SystemStyleObject = {
|
||||
position: 'relative',
|
||||
height: 'full',
|
||||
width: 'full',
|
||||
'::before': {
|
||||
content: "''",
|
||||
display: 'block',
|
||||
pt: '100%',
|
||||
},
|
||||
};
|
||||
|
||||
const IAIFillSkeleton = () => {
|
||||
return (
|
||||
<Skeleton
|
||||
sx={{
|
||||
position: 'relative',
|
||||
height: 'full',
|
||||
width: 'full',
|
||||
'::before': {
|
||||
content: "''",
|
||||
display: 'block',
|
||||
pt: '100%',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Skeleton sx={skeletonStyles}>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineStart: 0,
|
||||
height: 'full',
|
||||
width: 'full',
|
||||
}}
|
||||
position="absolute"
|
||||
top={0}
|
||||
insetInlineStart={0}
|
||||
height="full"
|
||||
width="full"
|
||||
/>
|
||||
</Skeleton>
|
||||
);
|
||||
|
@ -1,5 +1,6 @@
|
||||
import type { As, FlexProps, StyleProps } from '@chakra-ui/react';
|
||||
import { Flex, Icon, Skeleton, Spinner } from '@chakra-ui/react';
|
||||
import { useMemo } from 'react';
|
||||
import { FaImage } from 'react-icons/fa';
|
||||
import type { ImageDTO } from 'services/api/types';
|
||||
|
||||
@ -11,27 +12,23 @@ export const IAILoadingImageFallback = (props: Props) => {
|
||||
if (props.image) {
|
||||
return (
|
||||
<Skeleton
|
||||
sx={{
|
||||
w: `${props.image.width}px`,
|
||||
h: 'auto',
|
||||
objectFit: 'contain',
|
||||
aspectRatio: `${props.image.width}/${props.image.height}`,
|
||||
}}
|
||||
w={`${props.image.width}px`}
|
||||
h="auto"
|
||||
objectFit="contain"
|
||||
aspectRatio={`${props.image.width}/${props.image.height}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
opacity: 0.7,
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderRadius: 'base',
|
||||
bg: 'base.900',
|
||||
}}
|
||||
opacity={0.7}
|
||||
w="full"
|
||||
h="full"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
borderRadius="base"
|
||||
bg="base.900"
|
||||
>
|
||||
<Spinner size="xl" />
|
||||
</Flex>
|
||||
@ -47,23 +44,25 @@ type IAINoImageFallbackProps = FlexProps & {
|
||||
export const IAINoContentFallback = (props: IAINoImageFallbackProps) => {
|
||||
const { icon = FaImage, boxSize = 16, sx, ...rest } = props;
|
||||
|
||||
const styles = useMemo(
|
||||
() => ({
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderRadius: 'base',
|
||||
flexDir: 'column',
|
||||
gap: 2,
|
||||
userSelect: 'none',
|
||||
opacity: 0.7,
|
||||
color: 'base.500',
|
||||
...sx,
|
||||
}),
|
||||
[sx]
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderRadius: 'base',
|
||||
flexDir: 'column',
|
||||
gap: 2,
|
||||
userSelect: 'none',
|
||||
opacity: 0.7,
|
||||
color: 'base.500',
|
||||
...sx,
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
<Flex sx={styles} {...rest}>
|
||||
{icon && <Icon as={icon} boxSize={boxSize} opacity={0.7} />}
|
||||
{props.label && <InvText textAlign="center">{props.label}</InvText>}
|
||||
</Flex>
|
||||
@ -78,24 +77,25 @@ export const IAINoContentFallbackWithSpinner = (
|
||||
props: IAINoImageFallbackWithSpinnerProps
|
||||
) => {
|
||||
const { sx, ...rest } = props;
|
||||
const styles = useMemo(
|
||||
() => ({
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderRadius: 'base',
|
||||
flexDir: 'column',
|
||||
gap: 2,
|
||||
userSelect: 'none',
|
||||
opacity: 0.7,
|
||||
color: 'base.500',
|
||||
...sx,
|
||||
}),
|
||||
[sx]
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderRadius: 'base',
|
||||
flexDir: 'column',
|
||||
gap: 2,
|
||||
userSelect: 'none',
|
||||
opacity: 0.7,
|
||||
color: 'base.500',
|
||||
...sx,
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
<Flex sx={styles} {...rest}>
|
||||
<Spinner size="xl" />
|
||||
{props.label && <InvText textAlign="center">{props.label}</InvText>}
|
||||
</Flex>
|
||||
|
@ -103,13 +103,7 @@ const PopoverContent = ({ data, feature }: PopoverContentProps) => {
|
||||
<InvPopoverContent w={96}>
|
||||
<InvPopoverCloseButton />
|
||||
<InvPopoverBody>
|
||||
<Flex
|
||||
sx={{
|
||||
gap: 2,
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
}}
|
||||
>
|
||||
<Flex gap={2} flexDirection="column" alignItems="flex-start">
|
||||
{heading && (
|
||||
<>
|
||||
<InvHeading size="sm">{heading}</InvHeading>
|
||||
@ -119,12 +113,10 @@ const PopoverContent = ({ data, feature }: PopoverContentProps) => {
|
||||
{data?.image && (
|
||||
<>
|
||||
<Image
|
||||
sx={{
|
||||
objectFit: 'contain',
|
||||
maxW: '60%',
|
||||
maxH: '60%',
|
||||
backgroundColor: 'white',
|
||||
}}
|
||||
objectFit="contain"
|
||||
maxW="60%"
|
||||
maxH="60%"
|
||||
backgroundColor="white"
|
||||
src={data.image}
|
||||
alt="Optional Image"
|
||||
/>
|
||||
|
@ -9,16 +9,14 @@ type ImageMetadataOverlayProps = {
|
||||
const ImageMetadataOverlay = ({ imageDTO }: ImageMetadataOverlayProps) => {
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
pointerEvents: 'none',
|
||||
flexDirection: 'column',
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineStart: 0,
|
||||
p: 2,
|
||||
alignItems: 'flex-start',
|
||||
gap: 2,
|
||||
}}
|
||||
pointerEvents="none"
|
||||
flexDirection="column"
|
||||
position="absolute"
|
||||
top={0}
|
||||
insetInlineStart={0}
|
||||
p={2}
|
||||
alignItems="flex-start"
|
||||
gap={2}
|
||||
>
|
||||
<Badge variant="solid" colorScheme="base">
|
||||
{imageDTO.width} × {imageDTO.height}
|
||||
|
@ -23,57 +23,49 @@ const ImageUploadOverlay = (props: ImageUploadOverlayProps) => {
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineStart: 0,
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
zIndex: 999,
|
||||
backdropFilter: 'blur(20px)',
|
||||
}}
|
||||
position="absolute"
|
||||
top={0}
|
||||
insetInlineStart={0}
|
||||
width="100vw"
|
||||
height="100vh"
|
||||
zIndex={999}
|
||||
backdropFilter="blur(20px)"
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineStart: 0,
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
bg: 'base.900',
|
||||
opacity: 0.7,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: '0.1s',
|
||||
}}
|
||||
position="absolute"
|
||||
top={0}
|
||||
insetInlineStart={0}
|
||||
w="full"
|
||||
h="full"
|
||||
bg="base.900"
|
||||
opacity={0.7}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
transitionProperty="common"
|
||||
transitionDuration="0.1s"
|
||||
/>
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineStart: 0,
|
||||
width: 'full',
|
||||
height: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
p: 4,
|
||||
}}
|
||||
position="absolute"
|
||||
top={0}
|
||||
insetInlineStart={0}
|
||||
width="full"
|
||||
height="full"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
p={4}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
width: 'full',
|
||||
height: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexDir: 'column',
|
||||
gap: 4,
|
||||
borderWidth: 3,
|
||||
borderRadius: 'xl',
|
||||
borderStyle: 'dashed',
|
||||
color: 'base.100',
|
||||
borderColor: 'base.200',
|
||||
}}
|
||||
width="full"
|
||||
height="full"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
flexDir="column"
|
||||
gap={4}
|
||||
borderWidth={3}
|
||||
borderRadius="xl"
|
||||
borderStyle="dashed"
|
||||
color="base.100"
|
||||
borderColor="base.200"
|
||||
>
|
||||
{isDragAccept ? (
|
||||
<Heading size="lg">{t('gallery.dropToUpload')}</Heading>
|
||||
|
@ -20,7 +20,7 @@ const baseStyleInput = defineStyle(() => ({
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: 'normal',
|
||||
width: 'full',
|
||||
_focusVisible: { boxShadow: 'outline' },
|
||||
_focusVisible: { boxShadow: 'none' },
|
||||
_placeholder: { opacity: 0.6 },
|
||||
'::selection': {
|
||||
color: 'blue.50',
|
||||
|
@ -21,19 +21,17 @@ const SelectionOverlay = ({ isSelected, isHovered }: Props) => {
|
||||
return (
|
||||
<Box
|
||||
className="selection-box"
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineEnd: 0,
|
||||
bottom: 0,
|
||||
insetInlineStart: 0,
|
||||
borderRadius: 'base',
|
||||
opacity: isSelected || isHovered ? 1 : 0.5,
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: '0.1s',
|
||||
pointerEvents: 'none',
|
||||
shadow,
|
||||
}}
|
||||
position="absolute"
|
||||
top={0}
|
||||
insetInlineEnd={0}
|
||||
bottom={0}
|
||||
insetInlineStart={0}
|
||||
borderRadius="base"
|
||||
opacity={isSelected || isHovered ? 1 : 0.5}
|
||||
transitionProperty="common"
|
||||
transitionDuration="0.1s"
|
||||
pointerEvents="none"
|
||||
shadow={shadow}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -1,46 +1,24 @@
|
||||
import type { StyleProps } from '@chakra-ui/react';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
|
||||
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import type { CSSProperties, PropsWithChildren } from 'react';
|
||||
import { memo } from 'react';
|
||||
|
||||
type Props = PropsWithChildren & {
|
||||
maxHeight?: StyleProps['maxHeight'];
|
||||
};
|
||||
|
||||
const styles: CSSProperties = { height: '100%', width: '100%' };
|
||||
|
||||
const ScrollableContent = ({ children, maxHeight }: Props) => {
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
maxHeight,
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
}}
|
||||
>
|
||||
<Flex w="full" h="full" maxHeight={maxHeight} position="relative">
|
||||
<Box position="absolute" top={0} left={0} right={0} bottom={0}>
|
||||
<OverlayScrollbarsComponent
|
||||
defer
|
||||
style={{ height: '100%', width: '100%' }}
|
||||
options={{
|
||||
scrollbars: {
|
||||
visibility: 'auto',
|
||||
autoHide: 'scroll',
|
||||
autoHideDelay: 1300,
|
||||
theme: 'os-theme-dark',
|
||||
},
|
||||
overflow: {
|
||||
x: 'hidden',
|
||||
},
|
||||
}}
|
||||
style={styles}
|
||||
options={overlayScrollbarsParams.options}
|
||||
>
|
||||
{children}
|
||||
</OverlayScrollbarsComponent>
|
||||
|
@ -1,33 +1,37 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { memo } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
|
||||
type Props = {
|
||||
isSelected: boolean;
|
||||
isHovered: boolean;
|
||||
};
|
||||
const SelectionOverlay = ({ isSelected, isHovered }: Props) => {
|
||||
const shadow = useMemo(() => {
|
||||
if (isSelected && isHovered) {
|
||||
return 'hoverSelected';
|
||||
}
|
||||
if (isSelected && !isHovered) {
|
||||
return 'selected';
|
||||
}
|
||||
if (!isSelected && isHovered) {
|
||||
return 'hoverUnselected';
|
||||
}
|
||||
return undefined;
|
||||
}, [isHovered, isSelected]);
|
||||
return (
|
||||
<Box
|
||||
className="selection-box"
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineEnd: 0,
|
||||
bottom: 0,
|
||||
insetInlineStart: 0,
|
||||
borderRadius: 'base',
|
||||
opacity: isSelected ? 1 : 0.7,
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: '0.1s',
|
||||
pointerEvents: 'none',
|
||||
shadow: isSelected
|
||||
? isHovered
|
||||
? 'hoverSelected'
|
||||
: 'selected'
|
||||
: isHovered
|
||||
? 'hoverUnselected'
|
||||
: undefined,
|
||||
}}
|
||||
position="absolute"
|
||||
top={0}
|
||||
insetInlineEnd={0}
|
||||
bottom={0}
|
||||
insetInlineStart={0}
|
||||
borderRadius="base"
|
||||
opacity={isSelected ? 1 : 0.7}
|
||||
transitionProperty="common"
|
||||
transitionDuration="0.1s"
|
||||
pointerEvents="none"
|
||||
shadow={shadow}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -18,7 +18,7 @@ import {
|
||||
import type Konva from 'konva';
|
||||
import type { KonvaEventObject } from 'konva/lib/Node';
|
||||
import type { Vector2d } from 'konva/lib/types';
|
||||
import { memo, useCallback, useEffect, useRef } from 'react';
|
||||
import { memo, useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import { Layer, Stage } from 'react-konva';
|
||||
|
||||
import IAICanvasBoundingBoxOverlay from './IAICanvasBoundingBoxOverlay';
|
||||
@ -163,35 +163,32 @@ const IAICanvas = () => {
|
||||
};
|
||||
}, [dispatch]);
|
||||
|
||||
const stageStyles = useMemo(
|
||||
() => ({
|
||||
outline: 'none',
|
||||
overflow: 'hidden',
|
||||
cursor: stageCursor ? stageCursor : undefined,
|
||||
canvas: {
|
||||
outline: 'none',
|
||||
},
|
||||
}),
|
||||
[stageCursor]
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
id="canvas-container"
|
||||
ref={containerRef}
|
||||
sx={{
|
||||
position: 'relative',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
borderRadius: 'base',
|
||||
}}
|
||||
position="relative"
|
||||
height="100%"
|
||||
width="100%"
|
||||
borderRadius="base"
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
// top: 0,
|
||||
// insetInlineStart: 0,
|
||||
}}
|
||||
>
|
||||
<Box position="absolute">
|
||||
<ChakraStage
|
||||
tabIndex={-1}
|
||||
ref={canvasStageRefCallback}
|
||||
sx={{
|
||||
outline: 'none',
|
||||
overflow: 'hidden',
|
||||
cursor: stageCursor ? stageCursor : undefined,
|
||||
canvas: {
|
||||
outline: 'none',
|
||||
},
|
||||
}}
|
||||
sx={stageStyles}
|
||||
x={stageCoordinates.x}
|
||||
y={stageCoordinates.y}
|
||||
width={stageDimensions.width}
|
||||
|
@ -83,56 +83,38 @@ const IAICanvasStatusText = () => {
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineStart: 0,
|
||||
opacity: 0.65,
|
||||
display: 'flex',
|
||||
fontSize: 'sm',
|
||||
padding: 1,
|
||||
px: 2,
|
||||
minWidth: 48,
|
||||
margin: 1,
|
||||
borderRadius: 'base',
|
||||
pointerEvents: 'none',
|
||||
bg: 'base.800',
|
||||
}}
|
||||
flexDirection="column"
|
||||
position="absolute"
|
||||
top={0}
|
||||
insetInlineStart={0}
|
||||
opacity={0.65}
|
||||
display="flex"
|
||||
fontSize="sm"
|
||||
padding={1}
|
||||
px={2}
|
||||
minWidth={48}
|
||||
margin={1}
|
||||
borderRadius="base"
|
||||
pointerEvents="none"
|
||||
bg="base.800"
|
||||
>
|
||||
<GenerationModeStatusText />
|
||||
<Box
|
||||
style={{
|
||||
color: activeLayerColor,
|
||||
}}
|
||||
>{`${t('unifiedCanvas.activeLayer')}: ${t(
|
||||
<Box color={activeLayerColor}>{`${t('unifiedCanvas.activeLayer')}: ${t(
|
||||
`unifiedCanvas.${layer}`
|
||||
)}`}</Box>
|
||||
<Box>{`${t('unifiedCanvas.canvasScale')}: ${canvasScaleString}%`}</Box>
|
||||
{shouldPreserveMaskedArea && (
|
||||
<Box
|
||||
style={{
|
||||
color: warningColor,
|
||||
}}
|
||||
>
|
||||
<Box color={warningColor}>
|
||||
{t('unifiedCanvas.preserveMaskedArea')}: {t('common.on')}
|
||||
</Box>
|
||||
)}
|
||||
{shouldShowBoundingBox && (
|
||||
<Box
|
||||
style={{
|
||||
color: boundingBoxColor,
|
||||
}}
|
||||
>{`${t(
|
||||
<Box color={boundingBoxColor}>{`${t(
|
||||
'unifiedCanvas.boundingBox'
|
||||
)}: ${boundingBoxDimensionsString}`}</Box>
|
||||
)}
|
||||
{shouldShowScaledBoundingBox && (
|
||||
<Box
|
||||
style={{
|
||||
color: boundingBoxColor,
|
||||
}}
|
||||
>{`${t(
|
||||
<Box color={boundingBoxColor}>{`${t(
|
||||
'unifiedCanvas.scaledBoundingBox'
|
||||
)}: ${scaledBoundingBoxDimensionsString}`}</Box>
|
||||
)}
|
||||
|
@ -150,7 +150,7 @@ const IAICanvasMaskOptions = () => {
|
||||
onChange={handleChangePreserveMaskedArea}
|
||||
/>
|
||||
</InvControl>
|
||||
<Box sx={{ paddingTop: 2, paddingBottom: 2 }}>
|
||||
<Box pt={2} pb={2}>
|
||||
<IAIColorPicker
|
||||
color={maskColor}
|
||||
onChange={handleChangeMaskColor}
|
||||
|
@ -272,13 +272,7 @@ const IAICanvasToolChooserOptions = () => {
|
||||
/>
|
||||
</InvControl>
|
||||
</Flex>
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
paddingTop: 2,
|
||||
paddingBottom: 2,
|
||||
}}
|
||||
>
|
||||
<Box w="full" pt={2} pb={2}>
|
||||
<IAIColorPicker
|
||||
withNumberInput={true}
|
||||
color={brushColor}
|
||||
|
@ -101,7 +101,7 @@ const ChangeBoardModal = () => {
|
||||
acceptButtonText={t('boards.move')}
|
||||
cancelButtonText={t('boards.cancel')}
|
||||
>
|
||||
<Flex sx={{ flexDir: 'column', gap: 4 }}>
|
||||
<Flex flexDir="column" gap={4}>
|
||||
<InvText>
|
||||
{t('boards.movingImagesToBoard', {
|
||||
count: imagesToChange.length,
|
||||
|
@ -65,18 +65,14 @@ const ControlAdapterConfig = (props: { id: string; number: number }) => {
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
flexDir: 'column',
|
||||
gap: 3,
|
||||
p: 2,
|
||||
borderRadius: 'base',
|
||||
position: 'relative',
|
||||
bg: 'base.750',
|
||||
}}
|
||||
flexDir="column"
|
||||
gap={3}
|
||||
p={2}
|
||||
borderRadius="base"
|
||||
position="relative"
|
||||
bg="base.750"
|
||||
>
|
||||
<Flex
|
||||
sx={{ gap: 2, alignItems: 'center', justifyContent: 'space-between' }}
|
||||
>
|
||||
<Flex gap={2} alignItems="center" justifyContent="space-between">
|
||||
<InvControl label={t(`controlnet.${controlAdapterType}`, { number })}>
|
||||
<InvSwitch
|
||||
aria-label={t('controlnet.toggleControlNet')}
|
||||
@ -85,14 +81,12 @@ const ControlAdapterConfig = (props: { id: string; number: number }) => {
|
||||
/>
|
||||
</InvControl>
|
||||
</Flex>
|
||||
<Flex sx={{ gap: 2, alignItems: 'center' }}>
|
||||
<Flex gap={2} alignItems="center">
|
||||
<Box
|
||||
sx={{
|
||||
w: 'full',
|
||||
minW: 0,
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: '0.1s',
|
||||
}}
|
||||
minW={0}
|
||||
w="full"
|
||||
transitionProperty="common"
|
||||
transitionDuration="0.1s"
|
||||
>
|
||||
<ParamControlAdapterModel id={id} />
|
||||
</Box>
|
||||
@ -128,57 +122,46 @@ const ControlAdapterConfig = (props: { id: string; number: number }) => {
|
||||
}
|
||||
onClick={toggleIsExpanded}
|
||||
variant="ghost"
|
||||
sx={{
|
||||
_hover: {
|
||||
bg: 'none',
|
||||
},
|
||||
}}
|
||||
icon={
|
||||
<ChevronUpIcon
|
||||
sx={{
|
||||
boxSize: 4,
|
||||
color: 'base.300',
|
||||
transform: isExpanded ? 'rotate(0deg)' : 'rotate(180deg)',
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: 'normal',
|
||||
}}
|
||||
boxSize={4}
|
||||
color="base.300"
|
||||
transform={isExpanded ? 'rotate(0deg)' : 'rotate(180deg)'}
|
||||
transitionProperty="common"
|
||||
transitionDuration="normal"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
<Flex sx={{ w: 'full', flexDirection: 'column', gap: 3 }}>
|
||||
<Flex sx={{ gap: 4, w: 'full', alignItems: 'center' }}>
|
||||
<Flex w="full" flexDir="column" gap={3}>
|
||||
<Flex gap={4} w="full" alignItems="center">
|
||||
<Flex
|
||||
sx={{
|
||||
flexDir: 'column',
|
||||
gap: 3,
|
||||
h: 28,
|
||||
w: 'full',
|
||||
paddingInlineStart: 1,
|
||||
paddingInlineEnd: isExpanded ? 1 : 0,
|
||||
pb: 2,
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
flexDir="column"
|
||||
gap={3}
|
||||
h={28}
|
||||
w="full"
|
||||
paddingInlineStart={1}
|
||||
paddingInlineEnd={isExpanded ? 1 : 0}
|
||||
pb={2}
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<ParamControlAdapterWeight id={id} />
|
||||
<ParamControlAdapterBeginEnd id={id} />
|
||||
</Flex>
|
||||
{!isExpanded && (
|
||||
<Flex
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
h: 28,
|
||||
w: 28,
|
||||
aspectRatio: '1/1',
|
||||
}}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
h={28}
|
||||
w={28}
|
||||
aspectRatio="1/1"
|
||||
>
|
||||
<ControlAdapterImagePreview id={id} isSmall />
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
<Flex sx={{ gap: 2 }}>
|
||||
<Flex gap={2}>
|
||||
<ParamControlAdapterControlMode id={id} />
|
||||
<ParamControlAdapterResizeMode id={id} />
|
||||
</Flex>
|
||||
|
@ -177,13 +177,11 @@ const ControlAdapterImagePreview = ({ isSmall, id }: Props) => {
|
||||
<Flex
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
sx={{
|
||||
position: 'relative',
|
||||
w: 'full',
|
||||
h: isSmall ? 28 : 366, // magic no touch
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
position="relative"
|
||||
w="full"
|
||||
h={isSmall ? 28 : 366} // magic no touch
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<IAIDndImage
|
||||
draggableData={draggableData}
|
||||
@ -194,17 +192,15 @@ const ControlAdapterImagePreview = ({ isSmall, id }: Props) => {
|
||||
/>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineStart: 0,
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
opacity: shouldShowProcessedImage ? 1 : 0,
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: 'normal',
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
position="absolute"
|
||||
top={0}
|
||||
insetInlineStart={0}
|
||||
w="full"
|
||||
h="full"
|
||||
opacity={shouldShowProcessedImage ? 1 : 0}
|
||||
transitionProperty="common"
|
||||
transitionDuration="normal"
|
||||
pointerEvents="none"
|
||||
>
|
||||
<IAIDndImage
|
||||
draggableData={draggableData}
|
||||
@ -236,20 +232,18 @@ const ControlAdapterImagePreview = ({ isSmall, id }: Props) => {
|
||||
|
||||
{pendingControlImages.includes(id) && (
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineStart: 0,
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
opacity: 0.8,
|
||||
borderRadius: 'base',
|
||||
bg: 'base.900',
|
||||
}}
|
||||
position="absolute"
|
||||
top={0}
|
||||
insetInlineStart={0}
|
||||
w="full"
|
||||
h="full"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
opacity={0.8}
|
||||
borderRadius="base"
|
||||
bg="base.900"
|
||||
>
|
||||
<Spinner size="xl" sx={{ color: 'base.400' }} />
|
||||
<Spinner size="xl" color="base.400" />
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
|
@ -29,11 +29,7 @@ const ControlNetCanvasImageImports = (
|
||||
}, [id, dispatch]);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<Flex gap={2}>
|
||||
<InvIconButton
|
||||
size="sm"
|
||||
icon={<FaImage />}
|
||||
|
@ -5,7 +5,7 @@ type Props = PropsWithChildren;
|
||||
|
||||
export default function ProcessorWrapper(props: Props) {
|
||||
return (
|
||||
<Flex sx={{ flexDirection: 'column', gap: 2, pb: 2 }}>
|
||||
<Flex flexDir="column" gap={2} pb={2}>
|
||||
{props.children}
|
||||
</Flex>
|
||||
);
|
||||
|
@ -29,7 +29,7 @@ const ImageUsageMessage = (props: Props) => {
|
||||
return (
|
||||
<>
|
||||
<InvText>{topMessage}</InvText>
|
||||
<UnorderedList sx={{ paddingInlineStart: 6 }}>
|
||||
<UnorderedList paddingInlineStart={6}>
|
||||
{imageUsage.isInitialImage && (
|
||||
<ListItem>{t('common.img2img')}</ListItem>
|
||||
)}
|
||||
|
@ -11,7 +11,7 @@ type OverlayDragImageProps = {
|
||||
|
||||
const BOX_SIZE = 28;
|
||||
|
||||
const STYLES: ChakraProps['sx'] = {
|
||||
const imageStyles: ChakraProps['sx'] = {
|
||||
w: BOX_SIZE,
|
||||
h: BOX_SIZE,
|
||||
maxW: BOX_SIZE,
|
||||
@ -24,6 +24,14 @@ const STYLES: ChakraProps['sx'] = {
|
||||
color: 'base.100',
|
||||
};
|
||||
|
||||
const multiImageStyles: ChakraProps['sx'] = {
|
||||
position: 'relative',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexDir: 'column',
|
||||
...imageStyles,
|
||||
};
|
||||
|
||||
const DragPreview = (props: OverlayDragImageProps) => {
|
||||
const { t } = useTranslation();
|
||||
if (!props.dragData) {
|
||||
@ -34,17 +42,15 @@ const DragPreview = (props: OverlayDragImageProps) => {
|
||||
const { field, fieldTemplate } = props.dragData.payload;
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'relative',
|
||||
p: 2,
|
||||
px: 3,
|
||||
opacity: 0.7,
|
||||
bg: 'base.300',
|
||||
borderRadius: 'base',
|
||||
boxShadow: 'dark-lg',
|
||||
whiteSpace: 'nowrap',
|
||||
fontSize: 'sm',
|
||||
}}
|
||||
position="relative"
|
||||
p={2}
|
||||
px={3}
|
||||
opacity={0.7}
|
||||
bg="base.300"
|
||||
borderRadius="base"
|
||||
boxShadow="dark-lg"
|
||||
whiteSpace="nowrap"
|
||||
fontSize="sm"
|
||||
>
|
||||
<InvText>{field.label || fieldTemplate.title}</InvText>
|
||||
</Box>
|
||||
@ -55,19 +61,15 @@ const DragPreview = (props: OverlayDragImageProps) => {
|
||||
const { thumbnail_url, width, height } = props.dragData.payload.imageDTO;
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'relative',
|
||||
width: 'full',
|
||||
height: 'full',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
position="relative"
|
||||
width="full"
|
||||
height="full"
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<Image
|
||||
sx={{
|
||||
...STYLES,
|
||||
}}
|
||||
sx={imageStyles}
|
||||
objectFit="contain"
|
||||
src={thumbnail_url}
|
||||
width={width}
|
||||
@ -79,15 +81,7 @@ const DragPreview = (props: OverlayDragImageProps) => {
|
||||
|
||||
if (props.dragData.payloadType === 'IMAGE_DTOS') {
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'relative',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexDir: 'column',
|
||||
...STYLES,
|
||||
}}
|
||||
>
|
||||
<Flex sx={multiImageStyles}>
|
||||
<Heading>{props.dragData.payload.imageDTOs.length}</Heading>
|
||||
<Heading size="sm">{t('parameters.images')}</Heading>
|
||||
</Flex>
|
||||
|
@ -68,7 +68,7 @@ export const EmbeddingSelect = ({
|
||||
onChange={onChange}
|
||||
onMenuClose={onClose}
|
||||
data-testid="add-embedding"
|
||||
sx={{ w: 'full' }}
|
||||
w="full"
|
||||
/>
|
||||
</InvControl>
|
||||
);
|
||||
|
@ -5,15 +5,8 @@ import { useTranslation } from 'react-i18next';
|
||||
const AutoAddIcon = () => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
insetInlineEnd: 0,
|
||||
top: 0,
|
||||
p: 1,
|
||||
}}
|
||||
>
|
||||
<Badge variant="solid" sx={{ bg: 'blue.500' }}>
|
||||
<Flex position="absolute" insetInlineEnd={0} top={0} p={1}>
|
||||
<Badge variant="solid" bg="blue.500">
|
||||
{t('common.auto')}
|
||||
</Badge>
|
||||
</Flex>
|
||||
|
@ -93,10 +93,7 @@ const BoardContextMenu = ({
|
||||
|
||||
const renderMenuFunc = useCallback(
|
||||
() => (
|
||||
<InvMenuList
|
||||
sx={{ visibility: 'visible !important' }}
|
||||
onContextMenu={skipEvent}
|
||||
>
|
||||
<InvMenuList visibility="visible !important" onContextMenu={skipEvent}>
|
||||
<InvMenuGroup title={boardName}>
|
||||
<InvMenuItem
|
||||
icon={<FaPlus />}
|
||||
|
@ -5,6 +5,7 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
|
||||
import DeleteBoardModal from 'features/gallery/components/Boards/DeleteBoardModal';
|
||||
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
|
||||
import type { CSSProperties} from 'react';
|
||||
import { memo, useState } from 'react';
|
||||
import { useListAllBoardsQuery } from 'services/api/endpoints/boards';
|
||||
import type { BoardDTO } from 'services/api/types';
|
||||
@ -14,6 +15,11 @@ import BoardsSearch from './BoardsSearch';
|
||||
import GalleryBoard from './GalleryBoard';
|
||||
import NoBoardBoard from './NoBoardBoard';
|
||||
|
||||
const overlayScrollbarsStyles: CSSProperties = {
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
};
|
||||
|
||||
const selector = createMemoizedSelector([stateSelector], ({ gallery }) => {
|
||||
const { selectedBoardId, boardSearchText } = gallery;
|
||||
return { selectedBoardId, boardSearchText };
|
||||
@ -39,39 +45,35 @@ const BoardsList = (props: Props) => {
|
||||
<Collapse in={isOpen} animateOpacity>
|
||||
<Flex
|
||||
layerStyle="first"
|
||||
sx={{
|
||||
flexDir: 'column',
|
||||
gap: 2,
|
||||
p: 2,
|
||||
mt: 2,
|
||||
borderRadius: 'base',
|
||||
}}
|
||||
flexDir="column"
|
||||
gap={2}
|
||||
p={2}
|
||||
mt={2}
|
||||
borderRadius="base"
|
||||
>
|
||||
<Flex sx={{ gap: 2, alignItems: 'center' }}>
|
||||
<Flex gap={2} alignItems="center">
|
||||
<BoardsSearch />
|
||||
<AddBoardButton />
|
||||
</Flex>
|
||||
<OverlayScrollbarsComponent
|
||||
defer
|
||||
style={{ height: '100%', width: '100%' }}
|
||||
style={overlayScrollbarsStyles}
|
||||
options={overlayScrollbarsParams.options}
|
||||
>
|
||||
<Grid
|
||||
className="list-container"
|
||||
data-testid="boards-list"
|
||||
sx={{
|
||||
gridTemplateColumns: `repeat(auto-fill, minmax(108px, 1fr));`,
|
||||
maxH: 346,
|
||||
}}
|
||||
gridTemplateColumns="repeat(auto-fill, minmax(108px, 1fr))"
|
||||
maxH={346}
|
||||
>
|
||||
<GridItem sx={{ p: 1.5 }} data-testid="no-board">
|
||||
<GridItem p={1.5} data-testid="no-board">
|
||||
<NoBoardBoard isSelected={selectedBoardId === 'none'} />
|
||||
</GridItem>
|
||||
{filteredBoards &&
|
||||
filteredBoards.map((board, index) => (
|
||||
<GridItem
|
||||
key={board.board_id}
|
||||
sx={{ p: 1.5 }}
|
||||
p={1.5}
|
||||
data-testid={`board-${index}`}
|
||||
>
|
||||
<GalleryBoard
|
||||
|
@ -1,3 +1,4 @@
|
||||
import type { SystemStyleObject } from '@chakra-ui/react';
|
||||
import {
|
||||
Box,
|
||||
Editable,
|
||||
@ -33,6 +34,14 @@ import {
|
||||
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
|
||||
import type { BoardDTO } from 'services/api/types';
|
||||
|
||||
const editableInputStyles: SystemStyleObject = {
|
||||
p: 0,
|
||||
_focusVisible: {
|
||||
p: 0,
|
||||
textAlign: 'center',
|
||||
},
|
||||
};
|
||||
|
||||
interface GalleryBoardProps {
|
||||
board: BoardDTO;
|
||||
isSelected: boolean;
|
||||
@ -140,18 +149,16 @@ const GalleryBoard = ({
|
||||
}, []);
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Box sx={{ w: 'full', h: 'full', touchAction: 'none', userSelect: 'none' }}>
|
||||
<Box w="full" h="full" userSelect="none">
|
||||
<Flex
|
||||
onMouseOver={handleMouseOver}
|
||||
onMouseOut={handleMouseOut}
|
||||
sx={{
|
||||
position: 'relative',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
aspectRatio: '1/1',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
}}
|
||||
position="relative"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
aspectRatio="1/1"
|
||||
w="full"
|
||||
h="full"
|
||||
>
|
||||
<BoardContextMenu
|
||||
board={board}
|
||||
@ -163,47 +170,39 @@ const GalleryBoard = ({
|
||||
<Flex
|
||||
ref={ref}
|
||||
onClick={handleSelectBoard}
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
position: 'relative',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
borderRadius: 'base',
|
||||
cursor: 'pointer',
|
||||
bg: 'base.800',
|
||||
}}
|
||||
w="full"
|
||||
h="full"
|
||||
position="relative"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
borderRadius="base"
|
||||
cursor="pointer"
|
||||
bg="base.800"
|
||||
>
|
||||
{coverImage?.thumbnail_url ? (
|
||||
<Image
|
||||
src={coverImage?.thumbnail_url}
|
||||
draggable={false}
|
||||
sx={{
|
||||
objectFit: 'cover',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
maxH: 'full',
|
||||
borderRadius: 'base',
|
||||
borderBottomRadius: 'lg',
|
||||
}}
|
||||
objectFit="cover"
|
||||
w="full"
|
||||
h="full"
|
||||
maxH="full"
|
||||
borderRadius="base"
|
||||
borderBottomRadius="lg"
|
||||
/>
|
||||
) : (
|
||||
<Flex
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
w="full"
|
||||
h="full"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
<Icon
|
||||
boxSize={12}
|
||||
as={FaUser}
|
||||
sx={{
|
||||
mt: -6,
|
||||
opacity: 0.7,
|
||||
color: 'base.500',
|
||||
}}
|
||||
mt={-6}
|
||||
opacity={0.7}
|
||||
color="base.500"
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
@ -213,21 +212,19 @@ const GalleryBoard = ({
|
||||
isHovered={isHovered}
|
||||
/>
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
p: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
w: 'full',
|
||||
maxW: 'full',
|
||||
borderBottomRadius: 'base',
|
||||
bg: isSelected ? 'blue.500' : 'base.600',
|
||||
color: isSelected ? 'base.50' : 'base.100',
|
||||
lineHeight: 'short',
|
||||
fontSize: 'xs',
|
||||
}}
|
||||
position="absolute"
|
||||
bottom={0}
|
||||
left={0}
|
||||
p={1}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
w="full"
|
||||
maxW="full"
|
||||
borderBottomRadius="base"
|
||||
bg={isSelected ? 'blue.500' : 'base.600'}
|
||||
color={isSelected ? 'base.50' : 'base.100'}
|
||||
lineHeight="short"
|
||||
fontSize="xs"
|
||||
>
|
||||
<Editable
|
||||
value={localBoardName}
|
||||
@ -235,31 +232,17 @@ const GalleryBoard = ({
|
||||
submitOnBlur={true}
|
||||
onChange={handleChange}
|
||||
onSubmit={handleSubmit}
|
||||
sx={{
|
||||
w: 'full',
|
||||
}}
|
||||
w="full"
|
||||
>
|
||||
<EditablePreview
|
||||
sx={{
|
||||
p: 0,
|
||||
fontWeight: isSelected ? 'bold' : 'normal',
|
||||
textAlign: 'center',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
}}
|
||||
p={0}
|
||||
fontWeight={isSelected ? 'bold' : 'normal'}
|
||||
textAlign="center"
|
||||
overflow="hidden"
|
||||
textOverflow="ellipsis"
|
||||
noOfLines={1}
|
||||
/>
|
||||
<EditableInput
|
||||
sx={{
|
||||
p: 0,
|
||||
_focusVisible: {
|
||||
p: 0,
|
||||
textAlign: 'center',
|
||||
// get rid of the edit border
|
||||
boxShadow: 'none',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<EditableInput sx={editableInputStyles} />
|
||||
</Editable>
|
||||
</Flex>
|
||||
|
||||
|
@ -71,19 +71,17 @@ const NoBoardBoard = memo(({ isSelected }: Props) => {
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Box sx={{ w: 'full', h: 'full', touchAction: 'none', userSelect: 'none' }}>
|
||||
<Box w="full" h="full" userSelect="none">
|
||||
<Flex
|
||||
onMouseOver={handleMouseOver}
|
||||
onMouseOut={handleMouseOut}
|
||||
sx={{
|
||||
position: 'relative',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
aspectRatio: '1/1',
|
||||
borderRadius: 'base',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
}}
|
||||
position="relative"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
aspectRatio="1/1"
|
||||
borderRadius="base"
|
||||
w="full"
|
||||
h="full"
|
||||
>
|
||||
<BoardContextMenu board_id="none">
|
||||
{(ref) => (
|
||||
@ -91,58 +89,50 @@ const NoBoardBoard = memo(({ isSelected }: Props) => {
|
||||
<Flex
|
||||
ref={ref}
|
||||
onClick={handleSelectBoard}
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
position: 'relative',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
borderRadius: 'base',
|
||||
cursor: 'pointer',
|
||||
bg: 'base.800',
|
||||
}}
|
||||
w="full"
|
||||
h="full"
|
||||
position="relative"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
borderRadius="base"
|
||||
cursor="pointer"
|
||||
bg="base.800"
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
w="full"
|
||||
h="full"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
<Image
|
||||
src={InvokeAILogoImage}
|
||||
alt="invoke-ai-logo"
|
||||
sx={{
|
||||
opacity: 0.4,
|
||||
filter: 'grayscale(1)',
|
||||
mt: -6,
|
||||
w: 16,
|
||||
h: 16,
|
||||
minW: 16,
|
||||
minH: 16,
|
||||
userSelect: 'none',
|
||||
}}
|
||||
opacity={0.4}
|
||||
filter="grayscale(1)"
|
||||
mt={-6}
|
||||
w={16}
|
||||
h={16}
|
||||
minW={16}
|
||||
minH={16}
|
||||
userSelect="none"
|
||||
/>
|
||||
</Flex>
|
||||
{autoAddBoardId === 'none' && <AutoAddIcon />}
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
p: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
w: 'full',
|
||||
maxW: 'full',
|
||||
borderBottomRadius: 'base',
|
||||
bg: isSelected ? 'blue.500' : 'base.600',
|
||||
color: isSelected ? 'base.50' : 'base.100',
|
||||
lineHeight: 'short',
|
||||
fontSize: 'xs',
|
||||
fontWeight: isSelected ? 'bold' : 'normal',
|
||||
}}
|
||||
position="absolute"
|
||||
bottom={0}
|
||||
left={0}
|
||||
p={1}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
w="full"
|
||||
maxW="full"
|
||||
borderBottomRadius="base"
|
||||
bg={isSelected ? 'blue.500' : 'base.600'}
|
||||
color={isSelected ? 'base.50' : 'base.100'}
|
||||
lineHeight="short"
|
||||
fontSize="xs"
|
||||
fontWeight={isSelected ? 'bold' : 'normal'}
|
||||
>
|
||||
{boardName}
|
||||
</Flex>
|
||||
|
@ -139,9 +139,7 @@ const DeleteBoardModal = (props: Props) => {
|
||||
</Flex>
|
||||
</InvAlertDialogBody>
|
||||
<InvAlertDialogFooter>
|
||||
<Flex
|
||||
sx={{ justifyContent: 'space-between', width: 'full', gap: 2 }}
|
||||
>
|
||||
<Flex w="full" gap={2} justifyContent="space-between">
|
||||
<InvButton ref={cancelRef} onClick={handleClose}>
|
||||
{t('boards.cancel')}
|
||||
</InvButton>
|
||||
|
@ -20,11 +20,7 @@ const GalleryBoardContextMenuItems = ({ board, setBoardToDelete }: Props) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<InvMenuItem
|
||||
sx={{ color: 'error.300' }}
|
||||
icon={<FaTrash />}
|
||||
onClick={handleDelete}
|
||||
>
|
||||
<InvMenuItem color="error.300" icon={<FaTrash />} onClick={handleDelete}>
|
||||
{t('boards.deleteBoard')}
|
||||
</InvMenuItem>
|
||||
</>
|
||||
|
@ -217,14 +217,7 @@ const CurrentImageButtons = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
sx={{
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<Flex flexWrap="wrap" justifyContent="center" alignItems="center" gap={2}>
|
||||
<InvButtonGroup isDisabled={shouldDisableToolbarButtons}>
|
||||
<InvMenu isLazy>
|
||||
<InvMenuButton
|
||||
|
@ -7,15 +7,13 @@ import CurrentImagePreview from './CurrentImagePreview';
|
||||
const CurrentImageDisplay = () => {
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'relative',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
rowGap: 4,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
position="relative"
|
||||
flexDirection="column"
|
||||
height="100%"
|
||||
width="100%"
|
||||
rowGap={4}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<CurrentImageButtons />
|
||||
<CurrentImagePreview />
|
||||
|
@ -14,8 +14,17 @@ import ImageMetadataViewer from 'features/gallery/components/ImageMetadataViewer
|
||||
import NextPrevImageButtons from 'features/gallery/components/NextPrevImageButtons';
|
||||
import { useNextPrevImage } from 'features/gallery/hooks/useNextPrevImage';
|
||||
import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors';
|
||||
import type { AnimationProps} from 'framer-motion';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { memo, useCallback, useMemo, useRef, useState } from 'react';
|
||||
import type {
|
||||
CSSProperties} from 'react';
|
||||
import {
|
||||
memo,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaImage } from 'react-icons/fa';
|
||||
@ -128,13 +137,11 @@ const CurrentImagePreview = () => {
|
||||
<Flex
|
||||
onMouseOver={handleMouseOver}
|
||||
onMouseOut={handleMouseOut}
|
||||
sx={{
|
||||
width: 'full',
|
||||
height: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'relative',
|
||||
}}
|
||||
width="full"
|
||||
height="full"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
position="relative"
|
||||
>
|
||||
{hasDenoiseProgress && shouldShowProgressInViewer ? (
|
||||
<ProgressImage />
|
||||
@ -158,13 +165,11 @@ const CurrentImagePreview = () => {
|
||||
)}
|
||||
{shouldShowImageDetails && imageDTO && (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
width: 'full',
|
||||
height: 'full',
|
||||
borderRadius: 'base',
|
||||
}}
|
||||
position="absolute"
|
||||
top="0"
|
||||
width="full"
|
||||
height="full"
|
||||
borderRadius="base"
|
||||
>
|
||||
<ImageMetadataViewer image={imageDTO} />
|
||||
</Box>
|
||||
@ -173,24 +178,10 @@ const CurrentImagePreview = () => {
|
||||
{!shouldShowImageDetails && imageDTO && shouldShowNextPrevButtons && (
|
||||
<motion.div
|
||||
key="nextPrevButtons"
|
||||
initial={{
|
||||
opacity: 0,
|
||||
}}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
transition: { duration: 0.1 },
|
||||
}}
|
||||
exit={{
|
||||
opacity: 0,
|
||||
transition: { duration: 0.1 },
|
||||
}}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
initial={initial}
|
||||
animate={animate}
|
||||
exit={exit}
|
||||
style={motionStyles}
|
||||
>
|
||||
<NextPrevImageButtons />
|
||||
</motion.div>
|
||||
@ -201,3 +192,22 @@ const CurrentImagePreview = () => {
|
||||
};
|
||||
|
||||
export default memo(CurrentImagePreview);
|
||||
|
||||
const initial: AnimationProps['initial'] = {
|
||||
opacity: 0,
|
||||
};
|
||||
const animate: AnimationProps['animate'] = {
|
||||
opacity: 1,
|
||||
transition: { duration: 0.1 },
|
||||
};
|
||||
const exit: AnimationProps['exit'] = {
|
||||
opacity: 0,
|
||||
transition: { duration: 0.1 },
|
||||
};
|
||||
const motionStyles: CSSProperties = {
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
pointerEvents: 'none',
|
||||
};
|
||||
|
@ -1,6 +1,7 @@
|
||||
import type { SystemStyleObject } from '@chakra-ui/react';
|
||||
import { Image } from '@chakra-ui/react';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { memo } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
|
||||
const CurrentImagePreview = () => {
|
||||
const progress_image = useAppSelector(
|
||||
@ -10,6 +11,13 @@ const CurrentImagePreview = () => {
|
||||
(state) => state.system.shouldAntialiasProgressImage
|
||||
);
|
||||
|
||||
const sx = useMemo<SystemStyleObject>(
|
||||
() => ({
|
||||
imageRendering: shouldAntialiasProgressImage ? 'auto' : 'pixelated',
|
||||
}),
|
||||
[shouldAntialiasProgressImage]
|
||||
);
|
||||
|
||||
if (!progress_image) {
|
||||
return null;
|
||||
}
|
||||
@ -21,15 +29,12 @@ const CurrentImagePreview = () => {
|
||||
height={progress_image.height}
|
||||
draggable={false}
|
||||
data-testid="progress-image"
|
||||
sx={{
|
||||
objectFit: 'contain',
|
||||
maxWidth: 'full',
|
||||
maxHeight: 'full',
|
||||
height: 'auto',
|
||||
position: 'absolute',
|
||||
borderRadius: 'base',
|
||||
imageRendering: shouldAntialiasProgressImage ? 'auto' : 'pixelated',
|
||||
}}
|
||||
objectFit="contain"
|
||||
maxWidth="full"
|
||||
maxHeight="full"
|
||||
position="absolute"
|
||||
borderRadius="base"
|
||||
sx={sx}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -36,20 +36,14 @@ const ImageContextMenu = ({ imageDTO, children }: Props) => {
|
||||
|
||||
if (selectionCount > 1) {
|
||||
return (
|
||||
<InvMenuList
|
||||
sx={{ visibility: 'visible !important' }}
|
||||
onContextMenu={skipEvent}
|
||||
>
|
||||
<InvMenuList visibility="visible" onContextMenu={skipEvent}>
|
||||
<MultipleSelectionMenuItems />
|
||||
</InvMenuList>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<InvMenuList
|
||||
sx={{ visibility: 'visible !important' }}
|
||||
onContextMenu={skipEvent}
|
||||
>
|
||||
<InvMenuList visibility="visible" onContextMenu={skipEvent}>
|
||||
<SingleSelectionMenuItems imageDTO={imageDTO} />
|
||||
</InvMenuList>
|
||||
);
|
||||
|
@ -113,7 +113,7 @@ const MultipleSelectionMenuItems = () => {
|
||||
{t('boards.changeBoard')}
|
||||
</InvMenuItem>
|
||||
<InvMenuItem
|
||||
sx={{ color: 'error.300' }}
|
||||
color="error.300"
|
||||
icon={<FaTrash />}
|
||||
onClickCapture={handleDeleteSelection}
|
||||
>
|
||||
|
@ -248,7 +248,7 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
|
||||
</InvMenuItem>
|
||||
)}
|
||||
<InvMenuItem
|
||||
sx={{ color: 'error.300' }}
|
||||
color="error.300"
|
||||
icon={<FaTrash />}
|
||||
onClickCapture={handleDelete}
|
||||
>
|
||||
|
@ -50,22 +50,18 @@ const ImageGalleryContent = () => {
|
||||
return (
|
||||
<VStack
|
||||
layerStyle="first"
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
h: 'full',
|
||||
w: 'full',
|
||||
borderRadius: 'base',
|
||||
p: 2,
|
||||
}}
|
||||
flexDirection="column"
|
||||
h="full"
|
||||
w="full"
|
||||
borderRadius="base"
|
||||
p={2}
|
||||
>
|
||||
<Box sx={{ w: 'full' }}>
|
||||
<Box w="full">
|
||||
<Flex
|
||||
ref={resizeObserverRef}
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
gap: 2,
|
||||
}}
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
gap={2}
|
||||
>
|
||||
<GalleryBoardName
|
||||
isOpen={isBoardListOpen}
|
||||
@ -78,18 +74,12 @@ const ImageGalleryContent = () => {
|
||||
</Box>
|
||||
</Box>
|
||||
<Flex ref={galleryGridRef} direction="column" gap={2} h="full" w="full">
|
||||
<Flex
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<Flex alignItems="center" justifyContent="space-between" gap={2}>
|
||||
<Tabs
|
||||
index={galleryView === 'images' ? 0 : 1}
|
||||
variant="unstyled"
|
||||
size="sm"
|
||||
sx={{ w: 'full' }}
|
||||
w="full"
|
||||
>
|
||||
<TabList>
|
||||
<InvButtonGroup w="full">
|
||||
@ -98,9 +88,7 @@ const ImageGalleryContent = () => {
|
||||
size="sm"
|
||||
isChecked={galleryView === 'images'}
|
||||
onClick={handleClickImages}
|
||||
sx={{
|
||||
w: 'full',
|
||||
}}
|
||||
w="full"
|
||||
leftIcon={<FaImages />}
|
||||
data-testid="images-tab"
|
||||
>
|
||||
@ -111,9 +99,7 @@ const ImageGalleryContent = () => {
|
||||
size="sm"
|
||||
isChecked={galleryView === 'assets'}
|
||||
onClick={handleClickAssets}
|
||||
sx={{
|
||||
w: 'full',
|
||||
}}
|
||||
w="full"
|
||||
leftIcon={<FaServer />}
|
||||
data-testid="assets-tab"
|
||||
>
|
||||
|
@ -1,3 +1,4 @@
|
||||
import type { SystemStyleObject } from '@chakra-ui/react';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { $customStarUI } from 'app/store/nanostores/customStarUI';
|
||||
@ -26,6 +27,11 @@ import {
|
||||
useUnstarImagesMutation,
|
||||
} from 'services/api/endpoints/images';
|
||||
|
||||
const imageSx: SystemStyleObject = { w: 'full', h: 'full' };
|
||||
const imageIconStyleOverrides: SystemStyleObject = {
|
||||
bottom: 2,
|
||||
top: 'auto',
|
||||
};
|
||||
interface HoverableImageProps {
|
||||
imageName: string;
|
||||
index: number;
|
||||
@ -130,19 +136,14 @@ const GalleryImage = (props: HoverableImageProps) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{ w: 'full', h: 'full', touchAction: 'none' }}
|
||||
data-testid={`image-${imageDTO.image_name}`}
|
||||
>
|
||||
<Box w="full" h="full" data-testid={`image-${imageDTO.image_name}`}>
|
||||
<Flex
|
||||
ref={imageContainerRef}
|
||||
userSelect="none"
|
||||
sx={{
|
||||
position: 'relative',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
aspectRatio: '1/1',
|
||||
}}
|
||||
position="relative"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
aspectRatio="1/1"
|
||||
>
|
||||
<IAIDndImage
|
||||
onClick={handleClick}
|
||||
@ -150,7 +151,7 @@ const GalleryImage = (props: HoverableImageProps) => {
|
||||
draggableData={draggableData}
|
||||
isSelected={isSelected}
|
||||
minSize={0}
|
||||
imageSx={{ w: 'full', h: 'full' }}
|
||||
imageSx={imageSx}
|
||||
isDropDisabled={true}
|
||||
isUploadDisabled={true}
|
||||
thumbnail={true}
|
||||
@ -170,10 +171,7 @@ const GalleryImage = (props: HoverableImageProps) => {
|
||||
onClick={handleDelete}
|
||||
icon={<FaTrash />}
|
||||
tooltip={t('gallery.deleteImage')}
|
||||
styleOverrides={{
|
||||
bottom: 2,
|
||||
top: 'auto',
|
||||
}}
|
||||
styleOverrides={imageIconStyleOverrides}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
@ -9,10 +9,12 @@ import { $useNextPrevImageState } from 'features/gallery/hooks/useNextPrevImage'
|
||||
import { selectListImagesBaseQueryArgs } from 'features/gallery/store/gallerySelectors';
|
||||
import { IMAGE_LIMIT } from 'features/gallery/store/types';
|
||||
import { useOverlayScrollbars } from 'overlayscrollbars-react';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaExclamationCircle, FaImage } from 'react-icons/fa';
|
||||
import type {
|
||||
GridComponents,
|
||||
ItemContent,
|
||||
ListRange,
|
||||
VirtuosoGridHandle,
|
||||
@ -28,6 +30,13 @@ import GalleryImage from './GalleryImage';
|
||||
import ImageGridItemContainer from './ImageGridItemContainer';
|
||||
import ImageGridListContainer from './ImageGridListContainer';
|
||||
|
||||
const components: GridComponents = {
|
||||
Item: ImageGridItemContainer,
|
||||
List: ImageGridListContainer,
|
||||
};
|
||||
|
||||
const virtuosoStyles: CSSProperties = { height: '100%' };
|
||||
|
||||
const GalleryImageGrid = () => {
|
||||
const { t } = useTranslation();
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
@ -115,14 +124,7 @@ const GalleryImageGrid = () => {
|
||||
|
||||
if (!currentData) {
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Flex w="full" h="full" alignItems="center" justifyContent="center">
|
||||
<IAINoContentFallback label={t('gallery.loading')} icon={FaImage} />
|
||||
</Flex>
|
||||
);
|
||||
@ -130,14 +132,7 @@ const GalleryImageGrid = () => {
|
||||
|
||||
if (isSuccess && currentData?.ids.length === 0) {
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Flex w="full" h="full" alignItems="center" justifyContent="center">
|
||||
<IAINoContentFallback
|
||||
label={t('gallery.noImagesInGallery')}
|
||||
icon={FaImage}
|
||||
@ -151,13 +146,10 @@ const GalleryImageGrid = () => {
|
||||
<>
|
||||
<Box ref={rootRef} data-overlayscrollbars="" h="100%">
|
||||
<VirtuosoGrid
|
||||
style={{ height: '100%' }}
|
||||
style={virtuosoStyles}
|
||||
data={currentData.ids}
|
||||
endReached={handleLoadMoreImages}
|
||||
components={{
|
||||
Item: ImageGridItemContainer,
|
||||
List: ImageGridListContainer,
|
||||
}}
|
||||
components={components}
|
||||
scrollerRef={setScroller}
|
||||
itemContent={itemContentFunc}
|
||||
ref={virtuosoRef}
|
||||
@ -181,7 +173,7 @@ const GalleryImageGrid = () => {
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<Box sx={{ w: 'full', h: 'full' }}>
|
||||
<Box w="full" h="full">
|
||||
<IAINoContentFallback
|
||||
label={t('gallery.unableToLoad')}
|
||||
icon={FaExclamationCircle}
|
||||
|
@ -16,9 +16,7 @@ const ListContainer = forwardRef((props: ListContainerProps, ref) => {
|
||||
{...props}
|
||||
className="list-container"
|
||||
ref={ref}
|
||||
sx={{
|
||||
gridTemplateColumns: `repeat(auto-fill, minmax(${galleryImageMinimumWidth}px, 1fr));`,
|
||||
}}
|
||||
gridTemplateColumns={`repeat(auto-fill, minmax(${galleryImageMinimumWidth}px, 1fr))`}
|
||||
data-testid="image-list-container"
|
||||
>
|
||||
{props.children}
|
||||
|
@ -4,6 +4,7 @@ import { InvTooltip } from 'common/components/InvTooltip/InvTooltip';
|
||||
import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
|
||||
import { isString } from 'lodash-es';
|
||||
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
|
||||
import type { CSSProperties} from 'react';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaCopy, FaDownload } from 'react-icons/fa';
|
||||
@ -42,35 +43,31 @@ const DataViewer = (props: Props) => {
|
||||
return (
|
||||
<Flex
|
||||
layerStyle="second"
|
||||
sx={{
|
||||
borderRadius: 'base',
|
||||
flexGrow: 1,
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
position: 'relative',
|
||||
}}
|
||||
borderRadius="base"
|
||||
flexGrow={1}
|
||||
w="full"
|
||||
h="full"
|
||||
position="relative"
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
overflow: 'auto',
|
||||
p: 4,
|
||||
fontSize: 'sm',
|
||||
}}
|
||||
position="absolute"
|
||||
top={0}
|
||||
left={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
overflow="auto"
|
||||
p={4}
|
||||
fontSize="sm"
|
||||
>
|
||||
<OverlayScrollbarsComponent
|
||||
defer
|
||||
style={{ height: '100%', width: '100%' }}
|
||||
style={overlayScrollbarsStyles}
|
||||
options={overlayScrollbarsParams.options}
|
||||
>
|
||||
<pre>{dataString}</pre>
|
||||
</OverlayScrollbarsComponent>
|
||||
</Box>
|
||||
<Flex sx={{ position: 'absolute', top: 0, insetInlineEnd: 0, p: 2 }}>
|
||||
<Flex position="absolute" top={0} insetInlineEnd={0} p={2}>
|
||||
{withDownload && (
|
||||
<InvTooltip label={`${t('gallery.download')} ${label} JSON`}>
|
||||
<InvIconButton
|
||||
@ -99,3 +96,8 @@ const DataViewer = (props: Props) => {
|
||||
};
|
||||
|
||||
export default memo(DataViewer);
|
||||
|
||||
const overlayScrollbarsStyles: CSSProperties = {
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
};
|
||||
|
@ -37,16 +37,14 @@ const ImageMetadataViewer = ({ image }: ImageMetadataViewerProps) => {
|
||||
return (
|
||||
<Flex
|
||||
layerStyle="first"
|
||||
sx={{
|
||||
padding: 4,
|
||||
gap: 1,
|
||||
flexDirection: 'column',
|
||||
width: 'full',
|
||||
height: 'full',
|
||||
borderRadius: 'base',
|
||||
position: 'absolute',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
padding={4}
|
||||
gap={1}
|
||||
flexDirection="column"
|
||||
width="full"
|
||||
height="full"
|
||||
borderRadius="base"
|
||||
position="absolute"
|
||||
overflow="hidden"
|
||||
>
|
||||
<Flex gap={2}>
|
||||
<InvText fontWeight="semibold">{t('common.file')}:</InvText>
|
||||
@ -58,13 +56,11 @@ const ImageMetadataViewer = ({ image }: ImageMetadataViewerProps) => {
|
||||
|
||||
<Tabs
|
||||
variant="line"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDir: 'column',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
}}
|
||||
isLazy={true}
|
||||
display="flex"
|
||||
flexDir="column"
|
||||
w="full"
|
||||
h="full"
|
||||
>
|
||||
<TabList>
|
||||
<Tab>{t('metadata.recallParameters')}</Tab>
|
||||
|
@ -25,20 +25,12 @@ const NextPrevImageButtons = () => {
|
||||
} = useNextPrevImage();
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'relative',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Box pos="relative" h="full" w="full">
|
||||
<Box
|
||||
sx={{
|
||||
pos: 'absolute',
|
||||
top: '50%',
|
||||
transform: 'translate(0, -50%)',
|
||||
insetInlineStart: 0,
|
||||
}}
|
||||
pos="absolute"
|
||||
top="50%"
|
||||
transform="translate(0, -50%)"
|
||||
insetInlineStart={0}
|
||||
>
|
||||
{!isOnFirstImage && (
|
||||
<InvIconButton
|
||||
@ -52,12 +44,10 @@ const NextPrevImageButtons = () => {
|
||||
)}
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
pos: 'absolute',
|
||||
top: '50%',
|
||||
transform: 'translate(0, -50%)',
|
||||
insetInlineEnd: 0,
|
||||
}}
|
||||
pos="absolute"
|
||||
top="50%"
|
||||
transform="translate(0, -50%)"
|
||||
insetInlineEnd={0}
|
||||
>
|
||||
{!isOnLastImage && (
|
||||
<InvIconButton
|
||||
@ -80,14 +70,7 @@ const NextPrevImageButtons = () => {
|
||||
/>
|
||||
)}
|
||||
{isOnLastImage && areMoreImagesAvailable && isFetching && (
|
||||
<Flex
|
||||
sx={{
|
||||
w: 16,
|
||||
h: 16,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Flex w={16} h={16} alignItems="center" justifyContent="center">
|
||||
<Spinner opacity={0.5} size="xl" />
|
||||
</Flex>
|
||||
)}
|
||||
|
@ -69,7 +69,7 @@ const LoRASelect = () => {
|
||||
noOptionsMessage={noOptionsMessage}
|
||||
onChange={onChange}
|
||||
data-testid="add-lora"
|
||||
sx={{ w: 'full' }}
|
||||
w="full"
|
||||
/>
|
||||
</InvControl>
|
||||
);
|
||||
|
@ -41,13 +41,7 @@ export default function AddModels() {
|
||||
{t('common.advanced')}
|
||||
</InvButton>
|
||||
</InvButtonGroup>
|
||||
<Flex
|
||||
sx={{
|
||||
p: 4,
|
||||
borderRadius: 4,
|
||||
background: 'base.800',
|
||||
}}
|
||||
>
|
||||
<Flex p={4} borderRadius={4} bg="base.800">
|
||||
{addModelMode === 'simple' && <SimpleAddModels />}
|
||||
{addModelMode === 'advanced' && <AdvancedAddModels />}
|
||||
</Flex>
|
||||
|
@ -148,7 +148,7 @@ export default function AdvancedAddCheckpoint(
|
||||
{!useCustomConfig ? (
|
||||
<CheckpointConfigsSelect
|
||||
required
|
||||
sx={{ w: 'full' }}
|
||||
w="full"
|
||||
{...advancedAddCheckpointForm.getInputProps('config')}
|
||||
/>
|
||||
) : (
|
||||
|
@ -48,13 +48,7 @@ export default function AdvancedAddModels() {
|
||||
<InvSelect value={value} options={options} onChange={handleChange} />
|
||||
</InvControl>
|
||||
|
||||
<Flex
|
||||
sx={{
|
||||
p: 4,
|
||||
borderRadius: 4,
|
||||
bg: 'base.850',
|
||||
}}
|
||||
>
|
||||
<Flex p={4} borderRadius={4} bg="base.850">
|
||||
{advancedAddMode === 'diffusers' && <AdvancedAddDiffusers />}
|
||||
{advancedAddMode === 'checkpoint' && <AdvancedAddCheckpoint />}
|
||||
</Flex>
|
||||
|
@ -110,25 +110,18 @@ export default function FoundModelsList() {
|
||||
return models.map((model) => {
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
p: 4,
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
borderRadius: 4,
|
||||
bg: 'base.800',
|
||||
}}
|
||||
key={model}
|
||||
p={4}
|
||||
gap={4}
|
||||
alignItems="center"
|
||||
borderRadius={4}
|
||||
bg="base.800"
|
||||
>
|
||||
<Flex w="100%" sx={{ flexDirection: 'column', minW: '25%' }}>
|
||||
<InvText sx={{ fontWeight: 'semibold' }}>
|
||||
<Flex w="full" minW="25%" flexDir="column">
|
||||
<InvText fontWeight="semibold">
|
||||
{model.split('\\').slice(-1)[0]}
|
||||
</InvText>
|
||||
<InvText
|
||||
sx={{
|
||||
fontSize: 'sm',
|
||||
color: 'base.400',
|
||||
}}
|
||||
>
|
||||
<InvText fontSize="sm" color="base.400">
|
||||
{model}
|
||||
</InvText>
|
||||
</Flex>
|
||||
@ -150,13 +143,11 @@ export default function FoundModelsList() {
|
||||
</Flex>
|
||||
) : (
|
||||
<InvText
|
||||
sx={{
|
||||
fontWeight: 'semibold',
|
||||
p: 2,
|
||||
borderRadius: 4,
|
||||
color: 'blue.100',
|
||||
bg: 'blue.600',
|
||||
}}
|
||||
fontWeight="semibold"
|
||||
p={2}
|
||||
borderRadius={4}
|
||||
color="blue.100"
|
||||
bg="blue.600"
|
||||
>
|
||||
{t('common.installed')}
|
||||
</InvText>
|
||||
@ -174,15 +165,13 @@ export default function FoundModelsList() {
|
||||
if (!foundModels || foundModels.length === 0) {
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: 96,
|
||||
userSelect: 'none',
|
||||
bg: 'base.900',
|
||||
}}
|
||||
w="full"
|
||||
h="full"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
height={96}
|
||||
userSelect="none"
|
||||
bg="base.900"
|
||||
>
|
||||
<InvText variant="subtext">{t('modelManager.noModels')}</InvText>
|
||||
</Flex>
|
||||
@ -190,27 +179,15 @@ export default function FoundModelsList() {
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
gap: 2,
|
||||
w: '100%',
|
||||
minW: '50%',
|
||||
}}
|
||||
>
|
||||
<Flex flexDirection="column" gap={2} w="100%" minW="50%">
|
||||
<InvControl label={t('modelManager.search')}>
|
||||
<InvInput onChange={handleSearchFilter} />
|
||||
</InvControl>
|
||||
<Flex p={2} gap={2}>
|
||||
<InvText sx={{ fontWeight: 'semibold' }}>
|
||||
<InvText fontWeight="semibold">
|
||||
{t('modelManager.modelsFound')}: {foundModels.length}
|
||||
</InvText>
|
||||
<InvText
|
||||
sx={{
|
||||
fontWeight: 'semibold',
|
||||
color: 'blue.200',
|
||||
}}
|
||||
>
|
||||
<InvText fontWeight="semibold" color="blue.200">
|
||||
{t('common.notInstalled')}: {filteredModels.length}
|
||||
</InvText>
|
||||
</Flex>
|
||||
|
@ -10,7 +10,6 @@ import type {
|
||||
} from 'common/components/InvSelect/types';
|
||||
import { InvText } from 'common/components/InvText/wrapper';
|
||||
import { setAdvancedAddScanModel } from 'features/modelManager/store/modelManagerSlice';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaTimes } from 'react-icons/fa';
|
||||
@ -79,20 +78,15 @@ export default function ScanAdvancedAddModels() {
|
||||
|
||||
return (
|
||||
<Box
|
||||
as={motion.div}
|
||||
initial={{ x: -100, opacity: 0 }}
|
||||
animate={{ x: 0, opacity: 1, transition: { duration: 0.2 } }}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
minWidth: '40%',
|
||||
maxHeight: window.innerHeight - 300,
|
||||
overflow: 'scroll',
|
||||
p: 4,
|
||||
gap: 4,
|
||||
borderRadius: 4,
|
||||
bg: 'base.800',
|
||||
}}
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
minWidth="40%"
|
||||
maxHeight="calc(100vh - 300px)"
|
||||
overflow="scroll"
|
||||
p={4}
|
||||
gap={4}
|
||||
borderRadius={4}
|
||||
bg="base.800"
|
||||
>
|
||||
<Flex justifyContent="space-between" alignItems="center">
|
||||
<InvText size="xl" fontWeight="semibold">
|
||||
|
@ -10,12 +10,10 @@ export default function ScanModels() {
|
||||
<SearchFolderForm />
|
||||
<Flex gap={4}>
|
||||
<Flex
|
||||
sx={{
|
||||
maxHeight: window.innerHeight - 300,
|
||||
overflow: 'scroll',
|
||||
gap: 4,
|
||||
w: '100%',
|
||||
}}
|
||||
maxHeight="calc(100vh - 300px)"
|
||||
overflow="scroll"
|
||||
gap={4}
|
||||
w="100%"
|
||||
>
|
||||
<FoundModelsList />
|
||||
</Flex>
|
||||
|
@ -59,22 +59,13 @@ function SearchFolderForm() {
|
||||
)}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
w: '100%',
|
||||
gap: 2,
|
||||
borderRadius: 4,
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Flex w="100%" gap={2} borderRadius={4} alignItems="center">
|
||||
<Flex w="100%" alignItems="center" gap={4} minH={12}>
|
||||
<InvText
|
||||
sx={{
|
||||
fontSize: 'sm',
|
||||
fontWeight: 'semibold',
|
||||
color: 'base.300',
|
||||
minW: 'max-content',
|
||||
}}
|
||||
fontSize="sm"
|
||||
fontWeight="semibold"
|
||||
color="base.300"
|
||||
minW="max-content"
|
||||
>
|
||||
{t('common.folder')}
|
||||
</InvText>
|
||||
@ -86,15 +77,13 @@ function SearchFolderForm() {
|
||||
/>
|
||||
) : (
|
||||
<Flex
|
||||
sx={{
|
||||
w: '100%',
|
||||
p: 2,
|
||||
px: 4,
|
||||
bg: 'base.700',
|
||||
borderRadius: 4,
|
||||
fontSize: 'sm',
|
||||
fontWeight: 'bold',
|
||||
}}
|
||||
w="100%"
|
||||
p={2}
|
||||
px={4}
|
||||
bg="base.700"
|
||||
borderRadius={4}
|
||||
fontSize="sm"
|
||||
fontWeight="bold"
|
||||
>
|
||||
{searchFolder}
|
||||
</Flex>
|
||||
|
@ -259,13 +259,8 @@ export default function MergeModelsPanel() {
|
||||
]);
|
||||
|
||||
return (
|
||||
<Flex flexDirection="column" rowGap={4}>
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
rowGap: 1,
|
||||
}}
|
||||
>
|
||||
<Flex flexDir="column" gap={4}>
|
||||
<Flex flexDir="column" gap={1}>
|
||||
<InvText>{t('modelManager.modelMergeHeaderHelp1')}</InvText>
|
||||
<InvText fontSize="sm" variant="subtext">
|
||||
{t('modelManager.modelMergeHeaderHelp2')}
|
||||
@ -273,28 +268,28 @@ export default function MergeModelsPanel() {
|
||||
</Flex>
|
||||
|
||||
<Flex columnGap={4}>
|
||||
<InvControl label={t('modelManager.modelType')} sx={{ w: 'full' }}>
|
||||
<InvControl label={t('modelManager.modelType')} w="full">
|
||||
<InvSelect
|
||||
options={baseModelTypeSelectOptions}
|
||||
value={valueBaseModel}
|
||||
onChange={onChangeBaseModel}
|
||||
/>
|
||||
</InvControl>
|
||||
<InvControl label={t('modelManager.modelOne')} sx={{ w: 'full' }}>
|
||||
<InvControl label={t('modelManager.modelOne')} w="full">
|
||||
<InvSelect
|
||||
options={optionsModelOne}
|
||||
value={valueModelOne}
|
||||
onChange={onChangeModelOne}
|
||||
/>
|
||||
</InvControl>
|
||||
<InvControl label={t('modelManager.modelTwo')} sx={{ w: 'full' }}>
|
||||
<InvControl label={t('modelManager.modelTwo')} w="full">
|
||||
<InvSelect
|
||||
options={optionsModelTwo}
|
||||
value={valueModelTwo}
|
||||
onChange={onChangeModelTwo}
|
||||
/>
|
||||
</InvControl>
|
||||
<InvControl label={t('modelManager.modelThree')} sx={{ w: 'full' }}>
|
||||
<InvControl label={t('modelManager.modelThree')} w="full">
|
||||
<InvSelect
|
||||
options={optionsModelThree}
|
||||
value={valueModelThree}
|
||||
@ -312,13 +307,11 @@ export default function MergeModelsPanel() {
|
||||
</InvControl>
|
||||
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
padding: 4,
|
||||
borderRadius: 'base',
|
||||
gap: 4,
|
||||
bg: 'base.800',
|
||||
}}
|
||||
flexDirection="column"
|
||||
padding={4}
|
||||
borderRadius="base"
|
||||
gap={4}
|
||||
bg="base.800"
|
||||
>
|
||||
<InvControl
|
||||
label={t('modelManager.alpha')}
|
||||
@ -337,14 +330,7 @@ export default function MergeModelsPanel() {
|
||||
</InvControl>
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
sx={{
|
||||
padding: 4,
|
||||
borderRadius: 'base',
|
||||
gap: 4,
|
||||
bg: 'base.800',
|
||||
}}
|
||||
>
|
||||
<Flex padding={4} gap={4} borderRadius="base" bg="base.800">
|
||||
<InvText fontSize="sm" variant="subtext">
|
||||
{t('modelManager.interpolationType')}
|
||||
</InvText>
|
||||
@ -382,13 +368,11 @@ export default function MergeModelsPanel() {
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
padding: 4,
|
||||
borderRadius: 'base',
|
||||
gap: 4,
|
||||
bg: 'base.900',
|
||||
}}
|
||||
flexDirection="column"
|
||||
padding={4}
|
||||
borderRadius="base"
|
||||
gap={4}
|
||||
bg="base.900"
|
||||
>
|
||||
<Flex columnGap={4}>
|
||||
<InvText fontSize="sm" variant="subtext">
|
||||
|
@ -34,7 +34,7 @@ export default function ModelManagerPanel() {
|
||||
const model = mainModel ? mainModel : loraModel;
|
||||
|
||||
return (
|
||||
<Flex sx={{ gap: 8, w: 'full', h: 'full' }}>
|
||||
<Flex gap={8} w="full" h="full">
|
||||
<ModelList
|
||||
selectedModelId={selectedModelId}
|
||||
setSelectedModelId={setSelectedModelId}
|
||||
@ -71,14 +71,12 @@ const ModelEdit = (props: ModelEditProps) => {
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
maxH: 96,
|
||||
userSelect: 'none',
|
||||
}}
|
||||
w="full"
|
||||
h="full"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
maxH={96}
|
||||
userSelect="none"
|
||||
>
|
||||
<InvText variant="subtext">{t('modelManager.noModelSelected')}</InvText>
|
||||
</Flex>
|
||||
|
@ -123,13 +123,7 @@ export default function CheckpointModelEdit(props: CheckpointModelEditProps) {
|
||||
{![''].includes(model.base_model) ? (
|
||||
<ModelConvert model={model} />
|
||||
) : (
|
||||
<Badge
|
||||
sx={{
|
||||
p: 2,
|
||||
borderRadius: 4,
|
||||
bg: 'error.400',
|
||||
}}
|
||||
>
|
||||
<Badge p={2} borderRadius={4} bg="error.400">
|
||||
{t('modelManager.conversionNotSupported')}
|
||||
</Badge>
|
||||
)}
|
||||
|
@ -272,15 +272,7 @@ const modelsFilter = <
|
||||
|
||||
const StyledModelContainer = memo((props: PropsWithChildren) => {
|
||||
return (
|
||||
<Flex
|
||||
flexDirection="column"
|
||||
gap={4}
|
||||
borderRadius={4}
|
||||
p={4}
|
||||
sx={{
|
||||
bg: 'base.800',
|
||||
}}
|
||||
>
|
||||
<Flex flexDirection="column" gap={4} borderRadius={4} p={4} bg="base.800">
|
||||
{props.children}
|
||||
</Flex>
|
||||
);
|
||||
@ -301,7 +293,7 @@ const ModelListWrapper = memo((props: ModelListWrapperProps) => {
|
||||
const { title, modelList, selected } = props;
|
||||
return (
|
||||
<StyledModelContainer>
|
||||
<Flex sx={{ gap: 2, flexDir: 'column' }}>
|
||||
<Flex gap={2} flexDir="column">
|
||||
<InvText variant="subtext" fontSize="sm">
|
||||
{title}
|
||||
</InvText>
|
||||
|
@ -84,23 +84,16 @@ export default function ModelListItem(props: ModelListItemProps) {
|
||||
]);
|
||||
|
||||
return (
|
||||
<Flex sx={{ gap: 2, alignItems: 'center', w: 'full' }}>
|
||||
<Flex gap={2} alignItems="center" w="full">
|
||||
<Flex
|
||||
as={InvButton}
|
||||
isChecked={isSelected}
|
||||
sx={{
|
||||
justifyContent: 'start',
|
||||
p: 2,
|
||||
borderRadius: 'base',
|
||||
w: 'full',
|
||||
alignItems: 'center',
|
||||
color: isSelected ? 'base.50' : 'base.100',
|
||||
bg: isSelected ? 'blue.600' : 'base.850',
|
||||
_hover: {
|
||||
color: isSelected ? 'base.50' : 'base.100',
|
||||
bg: isSelected ? 'blue.550' : 'base.700',
|
||||
},
|
||||
}}
|
||||
variant={isSelected ? 'solid' : 'ghost'}
|
||||
justifyContent="start"
|
||||
p={2}
|
||||
borderRadius="base"
|
||||
w="full"
|
||||
alignItems="center"
|
||||
onClick={handleSelectModel}
|
||||
>
|
||||
<Flex gap={4} alignItems="center">
|
||||
|
@ -8,26 +8,17 @@ export default function SyncModels() {
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
w: 'full',
|
||||
p: 4,
|
||||
borderRadius: 4,
|
||||
gap: 4,
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
bg: 'base.800',
|
||||
}}
|
||||
w="full"
|
||||
p={4}
|
||||
borderRadius={4}
|
||||
gap={4}
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
bg="base.800"
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<InvText sx={{ fontWeight: 'semibold' }}>
|
||||
{t('modelManager.syncModels')}
|
||||
</InvText>
|
||||
<InvText fontSize="sm" sx={{ color: 'base.400' }}>
|
||||
<Flex flexDirection="column" gap={2}>
|
||||
<InvText fontWeight="semibold">{t('modelManager.syncModels')}</InvText>
|
||||
<InvText fontSize="sm" variant="subtext">
|
||||
{t('modelManager.syncModelsDesc')}
|
||||
</InvText>
|
||||
</Flex>
|
||||
|
@ -20,14 +20,12 @@ const NodeEditor = () => {
|
||||
return (
|
||||
<Flex
|
||||
layerStyle="first"
|
||||
sx={{
|
||||
position: 'relative',
|
||||
width: 'full',
|
||||
height: 'full',
|
||||
borderRadius: 'base',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
position="relative"
|
||||
width="full"
|
||||
height="full"
|
||||
borderRadius="base"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<AnimatePresence>
|
||||
{isReady && (
|
||||
@ -71,15 +69,13 @@ const NodeEditor = () => {
|
||||
>
|
||||
<Flex
|
||||
layerStyle="first"
|
||||
sx={{
|
||||
position: 'relative',
|
||||
width: 'full',
|
||||
height: 'full',
|
||||
borderRadius: 'base',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
position="relative"
|
||||
width="full"
|
||||
height="full"
|
||||
borderRadius="base"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
pointerEvents="none"
|
||||
>
|
||||
<IAINoContentFallback
|
||||
label={t('nodes.loadingNodes')}
|
||||
|
@ -230,9 +230,6 @@ const AddNodePopover = () => {
|
||||
onChange={onChange}
|
||||
onMenuClose={onClose}
|
||||
onKeyDown={onKeyDown}
|
||||
sx={{
|
||||
width: 'full',
|
||||
}}
|
||||
/>
|
||||
</InvPopoverBody>
|
||||
</InvPopoverContent>
|
||||
|
@ -65,19 +65,15 @@ const InvocationCollapsedEdge = ({
|
||||
{data?.count && data.count > 1 && (
|
||||
<EdgeLabelRenderer>
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
|
||||
}}
|
||||
position="absolute"
|
||||
transform={`translate(-50%, -50%) translate(${labelX}px,${labelY}px)`}
|
||||
className="nodrag nopan"
|
||||
>
|
||||
<Badge
|
||||
variant="solid"
|
||||
sx={{
|
||||
bg: 'base.500',
|
||||
opacity: isSelected ? 0.8 : 0.5,
|
||||
boxShadow: 'base',
|
||||
}}
|
||||
bg="base.500"
|
||||
opacity={isSelected ? 0.8 : 0.5}
|
||||
boxShadow="base"
|
||||
>
|
||||
{data.count}
|
||||
</Badge>
|
||||
|
@ -34,12 +34,10 @@ const CurrentImageNode = (props: NodeProps) => {
|
||||
<Wrapper nodeProps={props}>
|
||||
<Image
|
||||
src={progressImage.dataURL}
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
objectFit: 'contain',
|
||||
borderRadius: 'base',
|
||||
}}
|
||||
w="full"
|
||||
h="full"
|
||||
objectFit="contain"
|
||||
borderRadius="base"
|
||||
/>
|
||||
</Wrapper>
|
||||
);
|
||||
@ -83,38 +81,26 @@ const Wrapper = (props: PropsWithChildren<{ nodeProps: NodeProps }>) => {
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
className={DRAG_HANDLE_CLASSNAME}
|
||||
sx={{
|
||||
position: 'relative',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
position="relative"
|
||||
flexDirection="column"
|
||||
>
|
||||
<Flex
|
||||
layerStyle="nodeHeader"
|
||||
sx={{
|
||||
borderTopRadius: 'base',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
h: 8,
|
||||
}}
|
||||
borderTopRadius="base"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
h={8}
|
||||
>
|
||||
<InvText
|
||||
sx={{
|
||||
fontSize: 'sm',
|
||||
fontWeight: 'semibold',
|
||||
color: 'base.200',
|
||||
}}
|
||||
>
|
||||
<InvText fontSize="sm" fontWeight="semibold" color="base.200">
|
||||
{t('nodes.currentImage')}
|
||||
</InvText>
|
||||
</Flex>
|
||||
<Flex
|
||||
layerStyle="nodeBody"
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
borderBottomRadius: 'base',
|
||||
p: 2,
|
||||
}}
|
||||
w="full"
|
||||
h="full"
|
||||
borderBottomRadius="base"
|
||||
p={2}
|
||||
>
|
||||
{props.children}
|
||||
{isHovering && (
|
||||
|
@ -38,16 +38,14 @@ const InvocationNode = ({ nodeId, isOpen, label, type, selected }: Props) => {
|
||||
<>
|
||||
<Flex
|
||||
layerStyle="nodeBody"
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
py: 2,
|
||||
gap: 1,
|
||||
borderBottomRadius: withFooter ? 0 : 'base',
|
||||
}}
|
||||
flexDirection="column"
|
||||
w="full"
|
||||
h="full"
|
||||
py={2}
|
||||
gap={1}
|
||||
borderBottomRadius={withFooter ? 0 : 'base'}
|
||||
>
|
||||
<Flex sx={{ flexDir: 'column', px: 2, w: 'full', h: 'full' }}>
|
||||
<Flex flexDir="column" px={2} w="full" h="full">
|
||||
<Grid gridTemplateColumns="1fr auto" gridAutoRows="1fr">
|
||||
{inputConnectionFieldNames.map((fieldName, i) => (
|
||||
<GridItem
|
||||
|
@ -26,11 +26,9 @@ const InvocationNodeClassificationIcon = ({ nodeId }: Props) => {
|
||||
>
|
||||
<Icon
|
||||
as={getIcon(classification)}
|
||||
sx={{
|
||||
display: 'block',
|
||||
boxSize: 4,
|
||||
color: 'base.400',
|
||||
}}
|
||||
display="block"
|
||||
boxSize={4}
|
||||
color="base.400"
|
||||
/>
|
||||
</InvTooltip>
|
||||
);
|
||||
|
@ -18,14 +18,12 @@ const InvocationNodeFooter = ({ nodeId }: Props) => {
|
||||
<Flex
|
||||
className={DRAG_HANDLE_CLASSNAME}
|
||||
layerStyle="nodeFooter"
|
||||
sx={{
|
||||
w: 'full',
|
||||
borderBottomRadius: 'base',
|
||||
px: 2,
|
||||
py: 0,
|
||||
h: 8,
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
w="full"
|
||||
borderBottomRadius="base"
|
||||
px={2}
|
||||
py={0}
|
||||
h={8}
|
||||
justifyContent="space-between"
|
||||
>
|
||||
{isCacheEnabled && <UseCacheCheckbox nodeId={nodeId} />}
|
||||
{hasImageOutput && <SaveToGalleryCheckbox nodeId={nodeId} />}
|
||||
|
@ -20,15 +20,13 @@ const InvocationNodeHeader = ({ nodeId, isOpen }: Props) => {
|
||||
return (
|
||||
<Flex
|
||||
layerStyle="nodeHeader"
|
||||
sx={{
|
||||
borderTopRadius: 'base',
|
||||
borderBottomRadius: isOpen ? 0 : 'base',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
h: 8,
|
||||
textAlign: 'center',
|
||||
color: 'base.200',
|
||||
}}
|
||||
borderTopRadius="base"
|
||||
borderBottomRadius={isOpen ? 0 : 'base'}
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
h={8}
|
||||
textAlign="center"
|
||||
color="base.200"
|
||||
>
|
||||
<NodeCollapseButton nodeId={nodeId} isOpen={isOpen} />
|
||||
<InvocationNodeClassificationIcon nodeId={nodeId} />
|
||||
|
@ -25,12 +25,10 @@ const InvocationNodeInfoIcon = ({ nodeId }: Props) => {
|
||||
>
|
||||
<Icon
|
||||
as={FaInfoCircle}
|
||||
sx={{
|
||||
display: 'block',
|
||||
boxSize: 4,
|
||||
w: 8,
|
||||
color: needsUpdate ? 'error.400' : 'base.400',
|
||||
}}
|
||||
display="block"
|
||||
boxSize={4}
|
||||
w={8}
|
||||
color={needsUpdate ? 'error.400' : 'base.400'}
|
||||
/>
|
||||
</InvTooltip>
|
||||
);
|
||||
@ -66,7 +64,7 @@ const TooltipContent = memo(({ nodeId }: { nodeId: string }) => {
|
||||
|
||||
if (!data.version) {
|
||||
return (
|
||||
<InvText as="span" sx={{ color: 'error.500' }}>
|
||||
<InvText as="span" color="error.500">
|
||||
{t('nodes.versionUnknown')}
|
||||
</InvText>
|
||||
);
|
||||
@ -74,7 +72,7 @@ const TooltipContent = memo(({ nodeId }: { nodeId: string }) => {
|
||||
|
||||
if (!nodeTemplate.version) {
|
||||
return (
|
||||
<InvText as="span" sx={{ color: 'error.500' }}>
|
||||
<InvText as="span" color="error.500">
|
||||
{t('nodes.version')} {data.version} ({t('nodes.unknownTemplate')})
|
||||
</InvText>
|
||||
);
|
||||
@ -82,7 +80,7 @@ const TooltipContent = memo(({ nodeId }: { nodeId: string }) => {
|
||||
|
||||
if (compare(data.version, nodeTemplate.version, '<')) {
|
||||
return (
|
||||
<InvText as="span" sx={{ color: 'error.500' }}>
|
||||
<InvText as="span" color="error.500">
|
||||
{t('nodes.version')} {data.version} ({t('nodes.updateNode')})
|
||||
</InvText>
|
||||
);
|
||||
@ -90,7 +88,7 @@ const TooltipContent = memo(({ nodeId }: { nodeId: string }) => {
|
||||
|
||||
if (compare(data.version, nodeTemplate.version, '>')) {
|
||||
return (
|
||||
<InvText as="span" sx={{ color: 'error.500' }}>
|
||||
<InvText as="span" color="error.500">
|
||||
{t('nodes.version')} {data.version} ({t('nodes.updateApp')})
|
||||
</InvText>
|
||||
);
|
||||
@ -104,16 +102,12 @@ const TooltipContent = memo(({ nodeId }: { nodeId: string }) => {
|
||||
}, [data, nodeTemplate, t]);
|
||||
|
||||
if (!isInvocationNodeData(data)) {
|
||||
return (
|
||||
<InvText sx={{ fontWeight: 'semibold' }}>
|
||||
{t('nodes.unknownNode')}
|
||||
</InvText>
|
||||
);
|
||||
return <InvText fontWeight="semibold">{t('nodes.unknownNode')}</InvText>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex sx={{ flexDir: 'column' }}>
|
||||
<InvText as="span" sx={{ fontWeight: 'semibold' }}>
|
||||
<Flex flexDir="column">
|
||||
<InvText as="span" fontWeight="semibold">
|
||||
{title}
|
||||
</InvText>
|
||||
{nodeTemplate?.nodePack && (
|
||||
@ -121,7 +115,7 @@ const TooltipContent = memo(({ nodeId }: { nodeId: string }) => {
|
||||
{t('nodes.nodePack')}: {nodeTemplate.nodePack}
|
||||
</InvText>
|
||||
)}
|
||||
<InvText sx={{ opacity: 0.7, fontStyle: 'oblique 5deg' }}>
|
||||
<InvText opacity={0.7} fontStyle="oblique 5deg">
|
||||
{nodeTemplate?.description}
|
||||
</InvText>
|
||||
{versionComponent}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import type { SystemStyleObject } from '@chakra-ui/react';
|
||||
import { Badge, CircularProgress, Flex, Icon, Image } from '@chakra-ui/react';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
@ -16,7 +17,7 @@ type Props = {
|
||||
};
|
||||
|
||||
const iconBoxSize = 3;
|
||||
const circleStyles = {
|
||||
const circleStyles: SystemStyleObject = {
|
||||
circle: {
|
||||
transitionProperty: 'none',
|
||||
transitionDuration: '0s',
|
||||
@ -47,12 +48,10 @@ const InvocationNodeStatusIndicator = ({ nodeId }: Props) => {
|
||||
>
|
||||
<Flex
|
||||
className={DRAG_HANDLE_CLASSNAME}
|
||||
sx={{
|
||||
w: 5,
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
}}
|
||||
w={5}
|
||||
h="full"
|
||||
alignItems="center"
|
||||
justifyContent="flex-end"
|
||||
>
|
||||
<StatusIcon nodeExecutionState={nodeExecutionState} />
|
||||
</Flex>
|
||||
@ -75,16 +74,16 @@ const TooltipLabel = memo(({ nodeExecutionState }: TooltipLabelProps) => {
|
||||
if (status === zNodeStatus.enum.IN_PROGRESS) {
|
||||
if (progressImage) {
|
||||
return (
|
||||
<Flex sx={{ pos: 'relative', pt: 1.5, pb: 0.5 }}>
|
||||
<Flex pos="relative" pt={1.5} pb={0.5}>
|
||||
<Image
|
||||
src={progressImage.dataURL}
|
||||
sx={{ w: 32, h: 32, borderRadius: 'base', objectFit: 'contain' }}
|
||||
w={32}
|
||||
h={32}
|
||||
borderRadius="base"
|
||||
objectFit="contain"
|
||||
/>
|
||||
{progress !== null && (
|
||||
<Badge
|
||||
variant="solid"
|
||||
sx={{ pos: 'absolute', top: 2.5, insetInlineEnd: 1 }}
|
||||
>
|
||||
<Badge variant="solid" pos="absolute" top={2.5} insetInlineEnd={1}>
|
||||
{Math.round(progress * 100)}%
|
||||
</Badge>
|
||||
)}
|
||||
@ -123,15 +122,7 @@ type StatusIconProps = {
|
||||
const StatusIcon = memo((props: StatusIconProps) => {
|
||||
const { progress, status } = props.nodeExecutionState;
|
||||
if (status === zNodeStatus.enum.PENDING) {
|
||||
return (
|
||||
<Icon
|
||||
as={FaEllipsisH}
|
||||
sx={{
|
||||
boxSize: iconBoxSize,
|
||||
color: 'base.300',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
return <Icon as={FaEllipsisH} boxSize={iconBoxSize} color="base.300" />;
|
||||
}
|
||||
if (status === zNodeStatus.enum.IN_PROGRESS) {
|
||||
return progress === null ? (
|
||||
@ -153,26 +144,10 @@ const StatusIcon = memo((props: StatusIconProps) => {
|
||||
);
|
||||
}
|
||||
if (status === zNodeStatus.enum.COMPLETED) {
|
||||
return (
|
||||
<Icon
|
||||
as={FaCheck}
|
||||
sx={{
|
||||
boxSize: iconBoxSize,
|
||||
color: 'ok.300',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
return <Icon as={FaCheck} boxSize={iconBoxSize} color="ok.300" />;
|
||||
}
|
||||
if (status === zNodeStatus.enum.FAILED) {
|
||||
return (
|
||||
<Icon
|
||||
as={FaExclamation}
|
||||
sx={{
|
||||
boxSize: iconBoxSize,
|
||||
color: 'error.300',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
return <Icon as={FaExclamation} boxSize={iconBoxSize} color="error.300" />;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
@ -29,40 +29,29 @@ const InvocationNodeUnknownFallback = ({
|
||||
<Flex
|
||||
className={DRAG_HANDLE_CLASSNAME}
|
||||
layerStyle="nodeHeader"
|
||||
sx={{
|
||||
borderTopRadius: 'base',
|
||||
borderBottomRadius: isOpen ? 0 : 'base',
|
||||
alignItems: 'center',
|
||||
h: 8,
|
||||
fontWeight: 'semibold',
|
||||
fontSize: 'sm',
|
||||
}}
|
||||
borderTopRadius="base"
|
||||
borderBottomRadius={isOpen ? 0 : 'base'}
|
||||
alignItems="center"
|
||||
h={8}
|
||||
fontWeight="semibold"
|
||||
fontSize="sm"
|
||||
>
|
||||
<NodeCollapseButton nodeId={nodeId} isOpen={isOpen} />
|
||||
<InvText
|
||||
sx={{
|
||||
w: 'full',
|
||||
textAlign: 'center',
|
||||
pe: 8,
|
||||
color: 'error.300',
|
||||
}}
|
||||
>
|
||||
<InvText w="full" textAlign="center" pe={8} color="error.300">
|
||||
{label ? `${label} (${type})` : type}
|
||||
</InvText>
|
||||
</Flex>
|
||||
{isOpen && (
|
||||
<Flex
|
||||
layerStyle="nodeBody"
|
||||
sx={{
|
||||
userSelect: 'auto',
|
||||
flexDirection: 'column',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
p: 4,
|
||||
gap: 1,
|
||||
borderBottomRadius: 'base',
|
||||
fontSize: 'sm',
|
||||
}}
|
||||
userSelect="auto"
|
||||
flexDirection="column"
|
||||
w="full"
|
||||
h="full"
|
||||
p={4}
|
||||
gap={1}
|
||||
borderBottomRadius="base"
|
||||
fontSize="sm"
|
||||
>
|
||||
<Flex gap={2} flexDir="column">
|
||||
<InvText as="span">
|
||||
|
@ -1,3 +1,4 @@
|
||||
import type { SystemStyleObject } from '@chakra-ui/react';
|
||||
import {
|
||||
Editable,
|
||||
EditableInput,
|
||||
@ -78,51 +79,28 @@ const EditableFieldTitle = forwardRef((props: Props, ref) => {
|
||||
>
|
||||
<Flex
|
||||
ref={ref}
|
||||
sx={{
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
gap: 1,
|
||||
h: 'full',
|
||||
}}
|
||||
position="relative"
|
||||
overflow="hidden"
|
||||
alignItems="center"
|
||||
justifyContent="flex-start"
|
||||
gap={1}
|
||||
h="full"
|
||||
>
|
||||
<Editable
|
||||
value={localTitle}
|
||||
onChange={handleChange}
|
||||
onSubmit={handleSubmit}
|
||||
as={Flex}
|
||||
sx={{
|
||||
position: 'relative',
|
||||
alignItems: 'center',
|
||||
h: 'full',
|
||||
}}
|
||||
position="relative"
|
||||
alignItems="center"
|
||||
h="full"
|
||||
>
|
||||
<EditablePreview
|
||||
sx={{
|
||||
p: 0,
|
||||
fontWeight: isMissingInput ? 'bold' : 'normal',
|
||||
textAlign: 'left',
|
||||
_hover: {
|
||||
fontWeight: 'semibold !important',
|
||||
},
|
||||
}}
|
||||
fontWeight={isMissingInput ? 'bold' : 'normal'}
|
||||
sx={editablePreviewStyles}
|
||||
noOfLines={1}
|
||||
/>
|
||||
<EditableInput
|
||||
className="nodrag"
|
||||
sx={{
|
||||
p: 0,
|
||||
w: 'full',
|
||||
fontWeight: 'semibold',
|
||||
color: 'base.100',
|
||||
_focusVisible: {
|
||||
p: 0,
|
||||
textAlign: 'left',
|
||||
boxShadow: 'none',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<EditableInput className="nodrag" sx={editableInputStyles} />
|
||||
<EditableControls />
|
||||
</Editable>
|
||||
</Flex>
|
||||
@ -130,6 +108,25 @@ const EditableFieldTitle = forwardRef((props: Props, ref) => {
|
||||
);
|
||||
});
|
||||
|
||||
const editableInputStyles: SystemStyleObject = {
|
||||
p: 0,
|
||||
w: 'full',
|
||||
fontWeight: 'semibold',
|
||||
color: 'base.100',
|
||||
_focusVisible: {
|
||||
p: 0,
|
||||
textAlign: 'left',
|
||||
boxShadow: 'none',
|
||||
},
|
||||
};
|
||||
const editablePreviewStyles: SystemStyleObject = {
|
||||
p: 0,
|
||||
textAlign: 'left',
|
||||
_hover: {
|
||||
fontWeight: 'semibold !important',
|
||||
},
|
||||
};
|
||||
|
||||
export default memo(EditableFieldTitle);
|
||||
|
||||
const EditableControls = memo(() => {
|
||||
|
@ -103,10 +103,7 @@ const FieldContextMenu = ({ nodeId, fieldName, kind, children }: Props) => {
|
||||
const renderMenuFunc = useCallback(
|
||||
() =>
|
||||
!menuItems.length ? null : (
|
||||
<InvMenuList
|
||||
sx={{ visibility: 'visible !important' }}
|
||||
onContextMenu={skipEvent}
|
||||
>
|
||||
<InvMenuList visibility="visible" onContextMenu={skipEvent}>
|
||||
<InvMenuGroup
|
||||
title={label || fieldTemplateTitle || t('nodes.unknownField')}
|
||||
>
|
||||
|
@ -19,17 +19,15 @@ const FieldTitle = forwardRef((props: Props, ref) => {
|
||||
return (
|
||||
<Flex
|
||||
ref={ref}
|
||||
sx={{
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
gap: 1,
|
||||
h: 'full',
|
||||
w: 'full',
|
||||
}}
|
||||
position="relative"
|
||||
overflow="hidden"
|
||||
alignItems="center"
|
||||
justifyContent="flex-start"
|
||||
gap={1}
|
||||
h="full"
|
||||
w="full"
|
||||
>
|
||||
<InvText sx={{ fontWeight: isMissingInput ? 'bold' : 'normal' }}>
|
||||
<InvText fontWeight={isMissingInput ? 'bold' : 'normal'}>
|
||||
{label || fieldTemplateTitle}
|
||||
</InvText>
|
||||
</Flex>
|
||||
|
@ -43,10 +43,10 @@ const FieldTooltipContent = ({ nodeId, fieldName, kind }: Props) => {
|
||||
}, [field, fieldTemplate, t]);
|
||||
|
||||
return (
|
||||
<Flex sx={{ flexDir: 'column' }}>
|
||||
<InvText sx={{ fontWeight: 'semibold' }}>{fieldTitle}</InvText>
|
||||
<Flex flexDir="column">
|
||||
<InvText fontWeight="semibold">{fieldTitle}</InvText>
|
||||
{fieldTemplate && (
|
||||
<InvText sx={{ opacity: 0.7, fontStyle: 'oblique 5deg' }}>
|
||||
<InvText opacity={0.7} fontStyle="oblique 5deg">
|
||||
{fieldTemplate.description}
|
||||
</InvText>
|
||||
)}
|
||||
|
@ -292,13 +292,7 @@ const InputFieldRenderer = ({ nodeId, fieldName }: InputFieldProps) => {
|
||||
|
||||
return (
|
||||
<Box p={1}>
|
||||
<InvText
|
||||
sx={{
|
||||
fontSize: 'sm',
|
||||
fontWeight: 'semibold',
|
||||
color: 'error.300',
|
||||
}}
|
||||
>
|
||||
<InvText fontSize="sm" fontWeight="semibold" color="error.300">
|
||||
{t('nodes.unknownFieldType', { type: fieldInstance?.type.name })}
|
||||
</InvText>
|
||||
</Box>
|
||||
|
@ -97,16 +97,14 @@ type OutputFieldWrapperProps = PropsWithChildren<{
|
||||
const OutputFieldWrapper = memo(
|
||||
({ shouldDim, children }: OutputFieldWrapperProps) => (
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'relative',
|
||||
minH: 8,
|
||||
py: 0.5,
|
||||
alignItems: 'center',
|
||||
opacity: shouldDim ? 0.5 : 1,
|
||||
transitionProperty: 'opacity',
|
||||
transitionDuration: '0.1s',
|
||||
justifyContent: 'flex-end',
|
||||
}}
|
||||
position="relative"
|
||||
minH={8}
|
||||
py={0.5}
|
||||
alignItems="center"
|
||||
opacity={shouldDim ? 0.5 : 1}
|
||||
transitionProperty="opacity"
|
||||
transitionDuration="0.1s"
|
||||
justifyContent="flex-end"
|
||||
>
|
||||
{children}
|
||||
</Flex>
|
||||
|
@ -56,7 +56,6 @@ const IPAdapterModelFieldInputComponent = (
|
||||
placeholder="Pick one"
|
||||
options={options}
|
||||
onChange={onChange}
|
||||
sx={{ width: '100%' }}
|
||||
/>
|
||||
</InvControl>
|
||||
</InvTooltip>
|
||||
|
@ -78,12 +78,10 @@ const ImageFieldInputComponent = (
|
||||
return (
|
||||
<Flex
|
||||
className="nodrag"
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
w="full"
|
||||
h="full"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<IAIDndImage
|
||||
imageDTO={imageDTO}
|
||||
|
@ -48,7 +48,7 @@ const MainModelFieldInputComponent = (props: Props) => {
|
||||
});
|
||||
|
||||
return (
|
||||
<Flex sx={{ w: 'full', alignItems: 'center', gap: 2 }}>
|
||||
<Flex w="full" alignItems="center" gap={2}>
|
||||
<InvControl
|
||||
className="nowheel nodrag"
|
||||
isDisabled={!options.length}
|
||||
|
@ -48,7 +48,7 @@ const RefinerModelFieldInputComponent = (props: Props) => {
|
||||
});
|
||||
|
||||
return (
|
||||
<Flex sx={{ w: 'full', alignItems: 'center', gap: 2 }}>
|
||||
<Flex w="full" alignItems="center" gap={2}>
|
||||
<InvControl
|
||||
className="nowheel nodrag"
|
||||
isDisabled={!options.length}
|
||||
|
@ -48,7 +48,7 @@ const SDXLMainModelFieldInputComponent = (props: Props) => {
|
||||
});
|
||||
|
||||
return (
|
||||
<Flex sx={{ w: 'full', alignItems: 'center', gap: 2 }}>
|
||||
<Flex w="full" alignItems="center" gap={2}>
|
||||
<InvControl
|
||||
className="nowheel nodrag"
|
||||
isDisabled={!options.length}
|
||||
|
@ -57,7 +57,6 @@ const T2IAdapterModelFieldInputComponent = (
|
||||
placeholder="Pick one"
|
||||
options={options}
|
||||
onChange={onChange}
|
||||
sx={{ width: '100%' }}
|
||||
/>
|
||||
</InvControl>
|
||||
</InvTooltip>
|
||||
|
@ -48,7 +48,7 @@ const VAEModelFieldInputComponent = (props: Props) => {
|
||||
});
|
||||
|
||||
return (
|
||||
<Flex sx={{ w: 'full', alignItems: 'center', gap: 2 }}>
|
||||
<Flex w="full" alignItems="center" gap={2}>
|
||||
<InvControl
|
||||
className="nowheel nodrag"
|
||||
isDisabled={!options.length}
|
||||
|
@ -25,13 +25,11 @@ const NotesNode = (props: NodeProps<NotesNodeData>) => {
|
||||
<NodeWrapper nodeId={nodeId} selected={selected}>
|
||||
<Flex
|
||||
layerStyle="nodeHeader"
|
||||
sx={{
|
||||
borderTopRadius: 'base',
|
||||
borderBottomRadius: isOpen ? 0 : 'base',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
h: 8,
|
||||
}}
|
||||
borderTopRadius="base"
|
||||
borderBottomRadius={isOpen ? 0 : 'base'}
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
h={8}
|
||||
>
|
||||
<NodeCollapseButton nodeId={nodeId} isOpen={isOpen} />
|
||||
<NodeTitle nodeId={nodeId} title="Notes" />
|
||||
@ -42,26 +40,21 @@ const NotesNode = (props: NodeProps<NotesNodeData>) => {
|
||||
<Flex
|
||||
layerStyle="nodeBody"
|
||||
className="nopan"
|
||||
sx={{
|
||||
cursor: 'auto',
|
||||
flexDirection: 'column',
|
||||
borderBottomRadius: 'base',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
p: 2,
|
||||
gap: 1,
|
||||
}}
|
||||
cursor="auto"
|
||||
flexDirection="column"
|
||||
borderBottomRadius="base"
|
||||
w="full"
|
||||
h="full"
|
||||
p={2}
|
||||
gap={1}
|
||||
>
|
||||
<Flex
|
||||
className="nopan"
|
||||
sx={{ flexDir: 'column', w: 'full', h: 'full' }}
|
||||
>
|
||||
<Flex className="nopan" w="full" h="full" flexDir="column">
|
||||
<InvTextarea
|
||||
value={notes}
|
||||
onChange={handleChange}
|
||||
rows={8}
|
||||
resize="none"
|
||||
sx={{ fontSize: 'xs' }}
|
||||
fontSize="sm"
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
@ -24,23 +24,15 @@ const NodeCollapseButton = ({ nodeId, isOpen }: Props) => {
|
||||
className="nodrag"
|
||||
onClick={handleClick}
|
||||
aria-label="Minimize"
|
||||
sx={{
|
||||
minW: 8,
|
||||
w: 8,
|
||||
h: 8,
|
||||
color: 'base.500',
|
||||
_hover: {
|
||||
color: 'base.300',
|
||||
},
|
||||
}}
|
||||
minW={8}
|
||||
w={8}
|
||||
h={8}
|
||||
variant="link"
|
||||
icon={
|
||||
<ChevronUpIcon
|
||||
sx={{
|
||||
transform: isOpen ? 'rotate(0deg)' : 'rotate(180deg)',
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: 'normal',
|
||||
}}
|
||||
transform={isOpen ? 'rotate(0deg)' : 'rotate(180deg)'}
|
||||
transitionProperty="common"
|
||||
transitionDuration="normal"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
@ -1,3 +1,4 @@
|
||||
import type { SystemStyleObject } from '@chakra-ui/react';
|
||||
import {
|
||||
Box,
|
||||
Editable,
|
||||
@ -50,46 +51,28 @@ const NodeTitle = ({ nodeId, title }: Props) => {
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
overflow: 'hidden',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
cursor: 'text',
|
||||
}}
|
||||
overflow="hidden"
|
||||
w="full"
|
||||
h="full"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
cursor="text"
|
||||
>
|
||||
<Editable
|
||||
as={Flex}
|
||||
value={localTitle}
|
||||
onChange={handleChange}
|
||||
onSubmit={handleSubmit}
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
position: 'relative',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
}}
|
||||
alignItems="center"
|
||||
position="relative"
|
||||
w="full"
|
||||
h="full"
|
||||
>
|
||||
<EditablePreview
|
||||
fontSize="sm"
|
||||
sx={{
|
||||
p: 0,
|
||||
w: 'full',
|
||||
}}
|
||||
noOfLines={1}
|
||||
/>
|
||||
<EditablePreview fontSize="sm" p={0} w="full" noOfLines={1} />
|
||||
<EditableInput
|
||||
className="nodrag"
|
||||
fontSize="sm"
|
||||
sx={{
|
||||
p: 0,
|
||||
fontWeight: 'bold',
|
||||
_focusVisible: {
|
||||
p: 0,
|
||||
boxShadow: 'none',
|
||||
},
|
||||
}}
|
||||
sx={editableInputStyles}
|
||||
/>
|
||||
<EditableControls />
|
||||
</Editable>
|
||||
@ -120,13 +103,19 @@ function EditableControls() {
|
||||
<Box
|
||||
className={DRAG_HANDLE_CLASSNAME}
|
||||
onDoubleClick={handleDoubleClick}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
top: 0,
|
||||
cursor: 'grab',
|
||||
}}
|
||||
position="absolute"
|
||||
w="full"
|
||||
h="full"
|
||||
top={0}
|
||||
cursor="grab"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const editableInputStyles: SystemStyleObject = {
|
||||
p: 0,
|
||||
fontWeight: 'bold',
|
||||
_focusVisible: {
|
||||
p: 0,
|
||||
},
|
||||
};
|
||||
|
@ -18,7 +18,7 @@ import { memo, useCallback, useMemo } from 'react';
|
||||
type NodeWrapperProps = PropsWithChildren & {
|
||||
nodeId: string;
|
||||
selected: boolean;
|
||||
width?: NonNullable<ChakraProps['sx']>['w'];
|
||||
width?: ChakraProps['w'];
|
||||
};
|
||||
|
||||
const NodeWrapper = (props: NodeWrapperProps) => {
|
||||
@ -65,45 +65,39 @@ const NodeWrapper = (props: NodeWrapperProps) => {
|
||||
onMouseEnter={handleMouseOver}
|
||||
onMouseLeave={handleMouseOut}
|
||||
className={DRAG_HANDLE_CLASSNAME}
|
||||
sx={{
|
||||
h: 'full',
|
||||
position: 'relative',
|
||||
borderRadius: 'base',
|
||||
w: width ?? NODE_WIDTH,
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: '0.1s',
|
||||
cursor: 'grab',
|
||||
opacity,
|
||||
}}
|
||||
h="full"
|
||||
position="relative"
|
||||
borderRadius="base"
|
||||
w={width ? width : NODE_WIDTH}
|
||||
transitionProperty="common"
|
||||
transitionDuration="0.1s"
|
||||
cursor="grab"
|
||||
opacity={opacity}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineEnd: 0,
|
||||
bottom: 0,
|
||||
insetInlineStart: 0,
|
||||
borderRadius: 'base',
|
||||
pointerEvents: 'none',
|
||||
shadow: `${shadowsXl}, ${shadowsBase}, ${shadowsBase}`,
|
||||
zIndex: -1,
|
||||
}}
|
||||
position="absolute"
|
||||
top={0}
|
||||
insetInlineEnd={0}
|
||||
bottom={0}
|
||||
insetInlineStart={0}
|
||||
borderRadius="base"
|
||||
pointerEvents="none"
|
||||
shadow={`${shadowsXl}, ${shadowsBase}, ${shadowsBase}`}
|
||||
zIndex={-1}
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineEnd: 0,
|
||||
bottom: 0,
|
||||
insetInlineStart: 0,
|
||||
borderRadius: 'md',
|
||||
pointerEvents: 'none',
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: '0.1s',
|
||||
opacity: 0.7,
|
||||
shadow: isInProgress ? nodeInProgress : undefined,
|
||||
zIndex: -1,
|
||||
}}
|
||||
position="absolute"
|
||||
top={0}
|
||||
insetInlineEnd={0}
|
||||
bottom={0}
|
||||
insetInlineStart={0}
|
||||
borderRadius="md"
|
||||
pointerEvents="none"
|
||||
transitionProperty="common"
|
||||
transitionDuration="0.1s"
|
||||
opacity={0.7}
|
||||
shadow={isInProgress ? nodeInProgress : undefined}
|
||||
zIndex={-1}
|
||||
/>
|
||||
{children}
|
||||
<NodeSelectionOverlay isSelected={selected} isHovered={isMouseOverNode} />
|
||||
|
@ -5,7 +5,7 @@ import NodeOpacitySlider from './NodeOpacitySlider';
|
||||
import ViewportControls from './ViewportControls';
|
||||
|
||||
const BottomLeftPanel = () => (
|
||||
<Flex sx={{ gap: 2, position: 'absolute', bottom: 2, insetInlineStart: 2 }}>
|
||||
<Flex gap={2} position="absolute" bottom={2} insetInlineStart={2}>
|
||||
<ViewportControls />
|
||||
<NodeOpacitySlider />
|
||||
</Flex>
|
||||
|
@ -1,3 +1,4 @@
|
||||
import type { SystemStyleObject } from '@chakra-ui/react';
|
||||
import { chakra, Flex } from '@chakra-ui/react';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
@ -6,26 +7,28 @@ import { MiniMap } from 'reactflow';
|
||||
|
||||
const ChakraMiniMap = chakra(MiniMap);
|
||||
|
||||
const minimapStyles: SystemStyleObject = {
|
||||
m: '0 !important',
|
||||
borderRadius: 'base',
|
||||
backgroundColor: 'base.500 !important',
|
||||
svg: {
|
||||
borderRadius: 'inherit',
|
||||
},
|
||||
};
|
||||
|
||||
const MinimapPanel = () => {
|
||||
const shouldShowMinimapPanel = useAppSelector(
|
||||
(state: RootState) => state.nodes.shouldShowMinimapPanel
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex sx={{ gap: 2, position: 'absolute', bottom: 2, insetInlineEnd: 2 }}>
|
||||
<Flex gap={2} position="absolute" bottom={2} insetInlineEnd={2}>
|
||||
{shouldShowMinimapPanel && (
|
||||
<ChakraMiniMap
|
||||
pannable
|
||||
zoomable
|
||||
nodeBorderRadius={15}
|
||||
sx={{
|
||||
m: '0 !important',
|
||||
borderRadius: 'base',
|
||||
backgroundColor: 'base.500 !important',
|
||||
svg: {
|
||||
borderRadius: 'inherit',
|
||||
},
|
||||
}}
|
||||
sx={minimapStyles}
|
||||
nodeColor="var(--invokeai-colors-blue-600)"
|
||||
maskColor="var(--invokeai-colors-blackAlpha-600)"
|
||||
/>
|
||||
|
@ -13,15 +13,13 @@ const TopCenterPanel = () => {
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
gap: 2,
|
||||
top: 2,
|
||||
left: 2,
|
||||
right: 2,
|
||||
position: 'absolute',
|
||||
alignItems: 'center',
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
gap={2}
|
||||
top={2}
|
||||
left={2}
|
||||
right={2}
|
||||
position="absolute"
|
||||
alignItems="center"
|
||||
pointerEvents="none"
|
||||
>
|
||||
<AddNodeButton />
|
||||
<UpdateNodesButton />
|
||||
|
@ -9,7 +9,7 @@ const TopRightPanel = () => {
|
||||
useFeatureStatus('workflowLibrary').isFeatureEnabled;
|
||||
|
||||
return (
|
||||
<Flex sx={{ gap: 2, position: 'absolute', top: 2, insetInlineEnd: 2 }}>
|
||||
<Flex gap={2} position="absolute" top={2} insetInlineEnd={2}>
|
||||
{isWorkflowLibraryEnabled && <WorkflowLibraryButton />}
|
||||
<WorkflowLibraryMenu />
|
||||
</Flex>
|
||||
|
@ -104,13 +104,7 @@ const WorkflowEditorSettings = ({ children }: Props) => {
|
||||
<InvModalHeader>{t('nodes.workflowSettings')}</InvModalHeader>
|
||||
<InvModalCloseButton />
|
||||
<InvModalBody>
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
gap: 4,
|
||||
py: 4,
|
||||
}}
|
||||
>
|
||||
<Flex flexDirection="column" gap={4} py={4}>
|
||||
<Heading size="sm">{t('parameters.general')}</Heading>
|
||||
<InvControl
|
||||
label={t('nodes.animatedEdges')}
|
||||
|
@ -24,7 +24,7 @@ const NodeEditorPanelGroup = () => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Flex sx={{ flexDir: 'column', gap: 2, height: '100%', width: '100%' }}>
|
||||
<Flex w="full" h="full" gap={2} flexDir="column">
|
||||
<QueueControls />
|
||||
<PanelGroup
|
||||
ref={panelGroupRef}
|
||||
|
@ -52,24 +52,16 @@ const InspectorOutputsTab = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'relative',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
}}
|
||||
>
|
||||
<Box position="relative" w="full" h="full">
|
||||
<ScrollableContent>
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'relative',
|
||||
flexDir: 'column',
|
||||
alignItems: 'flex-start',
|
||||
p: 1,
|
||||
gap: 2,
|
||||
h: 'full',
|
||||
w: 'full',
|
||||
}}
|
||||
position="relative"
|
||||
flexDir="column"
|
||||
alignItems="flex-start"
|
||||
p={1}
|
||||
gap={2}
|
||||
h="full"
|
||||
w="full"
|
||||
>
|
||||
{template?.outputType === 'image_output' ? (
|
||||
nes.outputs.map((result, i) => (
|
||||
|
@ -19,19 +19,14 @@ const InspectorPanel = () => {
|
||||
return (
|
||||
<Flex
|
||||
layerStyle="first"
|
||||
sx={{
|
||||
flexDir: 'column',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
borderRadius: 'base',
|
||||
p: 2,
|
||||
gap: 2,
|
||||
}}
|
||||
flexDir="column"
|
||||
w="full"
|
||||
h="full"
|
||||
borderRadius="base"
|
||||
p={2}
|
||||
gap={2}
|
||||
>
|
||||
<Tabs
|
||||
variant="line"
|
||||
sx={{ display: 'flex', flexDir: 'column', w: 'full', h: 'full' }}
|
||||
>
|
||||
<Tabs variant="line" display="flex" flexDir="column" w="full" h="full">
|
||||
<TabList>
|
||||
<Tab>{t('common.details')}</Tab>
|
||||
<Tab>{t('common.outputs')}</Tab>
|
||||
|
@ -87,16 +87,9 @@ const WorkflowGeneralTab = () => {
|
||||
|
||||
return (
|
||||
<ScrollableContent>
|
||||
<Flex
|
||||
sx={{
|
||||
flexDir: 'column',
|
||||
alignItems: 'flex-start',
|
||||
gap: 2,
|
||||
h: 'full',
|
||||
}}
|
||||
>
|
||||
<Flex flexDir="column" alignItems="flex-start" gap={2} h="full">
|
||||
<InvControlGroup orientation="vertical">
|
||||
<Flex sx={{ gap: 2, w: 'full' }}>
|
||||
<Flex gap={2} w="full">
|
||||
<InvControl label={t('nodes.workflowName')}>
|
||||
<InvInput value={name} onChange={handleChangeName} />
|
||||
</InvControl>
|
||||
@ -104,7 +97,7 @@ const WorkflowGeneralTab = () => {
|
||||
<InvInput value={version} onChange={handleChangeVersion} />
|
||||
</InvControl>
|
||||
</Flex>
|
||||
<Flex sx={{ gap: 2, w: 'full' }}>
|
||||
<Flex gap={2} w="full">
|
||||
<InvControl label={t('nodes.workflowAuthor')}>
|
||||
<InvInput value={author} onChange={handleChangeAuthor} />
|
||||
</InvControl>
|
||||
|
@ -9,14 +9,7 @@ const WorkflowJSONTab = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
flexDir: 'column',
|
||||
alignItems: 'flex-start',
|
||||
gap: 2,
|
||||
h: 'full',
|
||||
}}
|
||||
>
|
||||
<Flex flexDir="column" alignItems="flex-start" gap={2} h="full">
|
||||
<DataViewer data={workflow} label={t('nodes.workflow')} />
|
||||
</Flex>
|
||||
);
|
||||
|
@ -19,24 +19,16 @@ const WorkflowLinearTab = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'relative',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
}}
|
||||
>
|
||||
<Box position="relative" w="full" h="full">
|
||||
<ScrollableContent>
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'relative',
|
||||
flexDir: 'column',
|
||||
alignItems: 'flex-start',
|
||||
p: 1,
|
||||
gap: 2,
|
||||
h: 'full',
|
||||
w: 'full',
|
||||
}}
|
||||
position="relative"
|
||||
flexDir="column"
|
||||
alignItems="flex-start"
|
||||
p={1}
|
||||
gap={2}
|
||||
h="full"
|
||||
w="full"
|
||||
>
|
||||
{fields.length ? (
|
||||
fields.map(({ nodeId, fieldName }) => (
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user