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