feat(ui): use primitive style props or memoized sx objects

This commit is contained in:
psychedelicious 2023-12-29 11:37:17 +11:00 committed by Kent Keirsey
parent 83049a3a5b
commit 4f2930412e
121 changed files with 1237 additions and 1825 deletions

View File

@ -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>

View File

@ -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}

View File

@ -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>

View File

@ -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}
/> />
); );

View File

@ -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>

View File

@ -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>
); );

View File

@ -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>

View File

@ -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"
/> />

View File

@ -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}

View File

@ -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>

View File

@ -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',

View File

@ -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,
}}
/> />
); );
}; };

View File

@ -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>

View File

@ -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,
}}
/> />
); );
}; };

View File

@ -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}

View File

@ -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>
)} )}

View File

@ -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}

View File

@ -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}

View File

@ -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,

View File

@ -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>

View File

@ -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>

View File

@ -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 />}

View File

@ -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>
); );

View File

@ -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>
)} )}

View File

@ -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>

View File

@ -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>
); );

View File

@ -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>

View File

@ -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 />}

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>
</> </>

View File

@ -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

View File

@ -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 />

View File

@ -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',
};

View File

@ -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',
}}
/> />
); );
}; };

View File

@ -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>
); );

View File

@ -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}
> >

View File

@ -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}
> >

View File

@ -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"
> >

View File

@ -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',
}}
/> />
)} )}
</> </>

View File

@ -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}

View File

@ -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}

View File

@ -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%',
};

View File

@ -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>

View File

@ -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>
)} )}

View File

@ -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>
); );

View File

@ -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>

View File

@ -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')}
/> />
) : ( ) : (

View File

@ -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>

View File

@ -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>

View File

@ -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">

View File

@ -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>

View File

@ -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>

View File

@ -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">

View File

@ -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>

View File

@ -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>
)} )}

View File

@ -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>

View File

@ -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">

View File

@ -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>

View File

@ -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')}

View File

@ -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>

View File

@ -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>

View File

@ -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 && (

View File

@ -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

View File

@ -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>
); );

View File

@ -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} />}

View File

@ -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} />

View File

@ -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}

View File

@ -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;
}); });

View File

@ -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">

View File

@ -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(() => {

View File

@ -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')}
> >

View File

@ -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>

View File

@ -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>
)} )}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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}

View File

@ -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}

View File

@ -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}

View File

@ -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}

View File

@ -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>

View File

@ -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}

View File

@ -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>

View File

@ -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',
}}
/> />
} }
/> />

View File

@ -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,
},
};

View File

@ -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} />

View File

@ -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>

View File

@ -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)"
/> />

View File

@ -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 />

View File

@ -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>

View File

@ -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')}

View File

@ -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}

View File

@ -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) => (

View File

@ -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>

View File

@ -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>

View File

@ -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>
); );

View File

@ -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