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">
<ImageUploader>
<Grid
sx={{
gap: 4,
p: 4,
gridAutoRows: 'min-content auto',
w: 'full',
h: 'full',
}}
>
<Grid gap={4} p={4} gridAutoRows="min-content auto" w="full" h="full">
{headerComponent || <SiteHeader />}
<Flex
sx={{
gap: 4,
w: 'full',
h: 'full',
}}
>
<Flex gap={4} w="full" h="full">
<InvokeTabs />
</Flex>
</Grid>

View File

@ -38,41 +38,35 @@ const AppErrorBoundaryFallback = ({ error, resetErrorBoundary }: Props) => {
return (
<Flex
layerStyle="body"
sx={{
w: '100vw',
h: '100vh',
alignItems: 'center',
justifyContent: 'center',
p: 4,
}}
w="100vw"
h="100vh"
alignItems="center"
justifyContent="center"
p={4}
>
<Flex
layerStyle="first"
sx={{
flexDir: 'column',
borderRadius: 'base',
justifyContent: 'center',
gap: 8,
p: 16,
}}
flexDir="column"
borderRadius="base"
justifyContent="center"
gap={8}
p={16}
>
<Heading>{t('common.somethingWentWrong')}</Heading>
<Flex
layerStyle="second"
sx={{
px: 8,
py: 4,
borderRadius: 'base',
gap: 4,
justifyContent: 'space-between',
alignItems: 'center',
}}
px={8}
py={4}
gap={4}
borderRadius="base"
justifyContent="space-between"
alignItems="center"
>
<InvText fontWeight="semibold" color="error.400">
{error.name}: {error.message}
</InvText>
</Flex>
<Flex sx={{ gap: 4 }}>
<Flex gap={4}>
<InvButton
leftIcon={<FaArrowRotateLeft />}
onClick={resetErrorBoundary}

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 {
IAILoadingImageFallback,
@ -17,7 +21,7 @@ import type {
ReactNode,
SyntheticEvent,
} from 'react';
import { memo, useCallback, useState } from 'react';
import { memo, useCallback, useMemo, useState } from 'react';
import { FaImage, FaUpload } from 'react-icons/fa';
import type { ImageDTO, PostUploadAction } from 'services/api/types';
@ -25,14 +29,7 @@ import IAIDraggable from './IAIDraggable';
import IAIDroppable from './IAIDroppable';
import SelectionOverlay from './SelectionOverlay';
const defaultUploadElement = (
<Icon
as={FaUpload}
sx={{
boxSize: 16,
}}
/>
);
const defaultUploadElement = <Icon as={FaUpload} boxSize={16} />;
const defaultNoContentFallback = <IAINoContentFallback icon={FaImage} />;
@ -115,16 +112,29 @@ const IAIDndImage = (props: IAIDndImageProps) => {
isDisabled: isUploadDisabled,
});
const uploadButtonStyles = isUploadDisabled
? {}
: {
const uploadButtonStyles = useMemo(() => {
const styles: SystemStyleObject = {
minH: minSize,
w: 'full',
h: 'full',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 'base',
transitionProperty: 'common',
transitionDuration: '0.1s',
color: 'base.500',
};
if (!isUploadDisabled) {
Object.assign(styles, {
cursor: 'pointer',
bg: 'base.700',
_hover: {
bg: 'base.650',
color: 'base.300',
},
};
});
}
}, [isUploadDisabled, minSize]);
return (
<ImageContextMenu imageDTO={imageDTO}>
@ -133,27 +143,23 @@ const IAIDndImage = (props: IAIDndImageProps) => {
ref={ref}
onMouseOver={handleMouseOver}
onMouseOut={handleMouseOut}
sx={{
width: 'full',
height: 'full',
alignItems: 'center',
justifyContent: 'center',
position: 'relative',
minW: minSize ? minSize : undefined,
minH: minSize ? minSize : undefined,
userSelect: 'none',
cursor: isDragDisabled || !imageDTO ? 'default' : 'pointer',
}}
width="full"
height="full"
alignItems="center"
justifyContent="center"
position="relative"
minW={minSize ? minSize : undefined}
minH={minSize ? minSize : undefined}
userSelect="none"
cursor={isDragDisabled || !imageDTO ? 'default' : 'pointer'}
>
{imageDTO && (
<Flex
sx={{
w: 'full',
h: 'full',
position: fitContainer ? 'absolute' : 'relative',
alignItems: 'center',
justifyContent: 'center',
}}
w="full"
h="full"
position={fitContainer ? 'absolute' : 'relative'}
alignItems="center"
justifyContent="center"
>
<Image
src={thumbnail ? imageDTO.thumbnail_url : imageDTO.image_url}
@ -168,14 +174,12 @@ const IAIDndImage = (props: IAIDndImageProps) => {
}
onError={onError}
draggable={false}
sx={{
w: imageDTO.width,
objectFit: 'contain',
maxW: 'full',
maxH: 'full',
borderRadius: 'base',
...imageSx,
}}
w={imageDTO.width}
objectFit="contain"
maxW="full"
maxH="full"
borderRadius="base"
sx={imageSx}
data-testid={dataTestId}
/>
{withMetadataOverlay && (
@ -189,21 +193,7 @@ const IAIDndImage = (props: IAIDndImageProps) => {
)}
{!imageDTO && !isUploadDisabled && (
<>
<Flex
sx={{
minH: minSize,
w: 'full',
h: 'full',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 'base',
transitionProperty: 'common',
transitionDuration: '0.1s',
color: 'base.500',
...uploadButtonStyles,
}}
{...getUploadButtonProps()}
>
<Flex sx={uploadButtonStyles} {...getUploadButtonProps()}>
<input {...getUploadInputProps()} />
{uploadElement}
</Flex>

View File

@ -1,6 +1,6 @@
import type { SystemStyleObject } from '@chakra-ui/react';
import type { MouseEvent, ReactElement } from 'react';
import { memo } from 'react';
import { memo, useMemo } from 'react';
import { InvIconButton } from './InvIconButton/InvIconButton';
@ -14,6 +14,25 @@ type Props = {
const IAIDndImageIcon = (props: Props) => {
const { onClick, tooltip, icon, styleOverrides } = props;
const sx = useMemo(
() => ({
position: 'absolute',
top: 1,
insetInlineEnd: 1,
p: 0,
minW: 0,
svg: {
transitionProperty: 'common',
transitionDuration: 'normal',
fill: 'base.100',
_hover: { fill: 'base.50' },
filter: 'drop-shadow(0px 0px 0.1rem var(--invokeai-colors-base-800))',
},
...styleOverrides,
}),
[styleOverrides]
);
return (
<InvIconButton
onClick={onClick}
@ -22,21 +41,7 @@ const IAIDndImageIcon = (props: Props) => {
icon={icon}
size="sm"
variant="link"
sx={{
position: 'absolute',
top: 1,
insetInlineEnd: 1,
p: 0,
minW: 0,
svg: {
transitionProperty: 'common',
transitionDuration: 'normal',
fill: 'base.100',
_hover: { fill: 'base.50' },
filter: 'drop-shadow(0px 0px 0.1rem var(--invokeai-colors-base-800))',
},
...styleOverrides,
}}
sx={sx}
data-testid={tooltip}
/>
);

View File

@ -27,59 +27,45 @@ export const IAIDropOverlay = (props: Props) => {
transition: { duration: 0.1 },
}}
>
<Flex
sx={{
position: 'absolute',
top: 0,
insetInlineStart: 0,
w: 'full',
h: 'full',
}}
>
<Flex position="absolute" top={0} insetInlineStart={0} w="full" h="full">
<Flex
sx={{
position: 'absolute',
top: 0,
insetInlineStart: 0,
w: 'full',
h: 'full',
bg: 'base.900',
opacity: 0.7,
borderRadius: 'base',
alignItems: 'center',
justifyContent: 'center',
transitionProperty: 'common',
transitionDuration: '0.1s',
}}
position="absolute"
top={0}
insetInlineStart={0}
w="full"
h="full"
bg="base.900"
opacity={0.7}
borderRadius="base"
alignItems="center"
justifyContent="center"
transitionProperty="common"
transitionDuration="0.1s"
/>
<Flex
sx={{
position: 'absolute',
top: 0.5,
insetInlineStart: 0.5,
insetInlineEnd: 0.5,
bottom: 0.5,
opacity: 1,
borderWidth: 2,
borderColor: isOver ? 'base.50' : 'base.300',
borderRadius: 'lg',
borderStyle: 'dashed',
transitionProperty: 'common',
transitionDuration: '0.1s',
alignItems: 'center',
justifyContent: 'center',
}}
position="absolute"
top={0.5}
insetInlineStart={0.5}
insetInlineEnd={0.5}
bottom={0.5}
opacity={1}
borderWidth={2}
borderColor={isOver ? 'base.50' : 'base.300'}
borderRadius="lg"
borderStyle="dashed"
transitionProperty="common"
transitionDuration="0.1s"
alignItems="center"
justifyContent="center"
>
<Box
sx={{
fontSize: '2xl',
fontWeight: 'semibold',
transform: isOver ? 'scale(1.1)' : 'scale(1)',
color: isOver ? 'base.50' : 'base.300',
transitionProperty: 'common',
transitionDuration: '0.1s',
}}
fontSize="2xl"
fontWeight="semibold"
transform={isOver ? 'scale(1.1)' : 'scale(1)'}
color={isOver ? 'base.50' : 'base.300'}
transitionProperty="common"
transitionDuration="0.1s"
>
{label}
</Box>

View File

@ -1,28 +1,27 @@
import type { SystemStyleObject } from '@chakra-ui/react';
import { Box, Skeleton } from '@chakra-ui/react';
import { memo } from 'react';
const skeletonStyles: SystemStyleObject = {
position: 'relative',
height: 'full',
width: 'full',
'::before': {
content: "''",
display: 'block',
pt: '100%',
},
};
const IAIFillSkeleton = () => {
return (
<Skeleton
sx={{
position: 'relative',
height: 'full',
width: 'full',
'::before': {
content: "''",
display: 'block',
pt: '100%',
},
}}
>
<Skeleton sx={skeletonStyles}>
<Box
sx={{
position: 'absolute',
top: 0,
insetInlineStart: 0,
height: 'full',
width: 'full',
}}
position="absolute"
top={0}
insetInlineStart={0}
height="full"
width="full"
/>
</Skeleton>
);

View File

@ -1,5 +1,6 @@
import type { As, FlexProps, StyleProps } from '@chakra-ui/react';
import { Flex, Icon, Skeleton, Spinner } from '@chakra-ui/react';
import { useMemo } from 'react';
import { FaImage } from 'react-icons/fa';
import type { ImageDTO } from 'services/api/types';
@ -11,27 +12,23 @@ export const IAILoadingImageFallback = (props: Props) => {
if (props.image) {
return (
<Skeleton
sx={{
w: `${props.image.width}px`,
h: 'auto',
objectFit: 'contain',
aspectRatio: `${props.image.width}/${props.image.height}`,
}}
w={`${props.image.width}px`}
h="auto"
objectFit="contain"
aspectRatio={`${props.image.width}/${props.image.height}`}
/>
);
}
return (
<Flex
sx={{
opacity: 0.7,
w: 'full',
h: 'full',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 'base',
bg: 'base.900',
}}
opacity={0.7}
w="full"
h="full"
alignItems="center"
justifyContent="center"
borderRadius="base"
bg="base.900"
>
<Spinner size="xl" />
</Flex>
@ -47,23 +44,25 @@ type IAINoImageFallbackProps = FlexProps & {
export const IAINoContentFallback = (props: IAINoImageFallbackProps) => {
const { icon = FaImage, boxSize = 16, sx, ...rest } = props;
const styles = useMemo(
() => ({
w: 'full',
h: 'full',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 'base',
flexDir: 'column',
gap: 2,
userSelect: 'none',
opacity: 0.7,
color: 'base.500',
...sx,
}),
[sx]
);
return (
<Flex
sx={{
w: 'full',
h: 'full',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 'base',
flexDir: 'column',
gap: 2,
userSelect: 'none',
opacity: 0.7,
color: 'base.500',
...sx,
}}
{...rest}
>
<Flex sx={styles} {...rest}>
{icon && <Icon as={icon} boxSize={boxSize} opacity={0.7} />}
{props.label && <InvText textAlign="center">{props.label}</InvText>}
</Flex>
@ -78,24 +77,25 @@ export const IAINoContentFallbackWithSpinner = (
props: IAINoImageFallbackWithSpinnerProps
) => {
const { sx, ...rest } = props;
const styles = useMemo(
() => ({
w: 'full',
h: 'full',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 'base',
flexDir: 'column',
gap: 2,
userSelect: 'none',
opacity: 0.7,
color: 'base.500',
...sx,
}),
[sx]
);
return (
<Flex
sx={{
w: 'full',
h: 'full',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 'base',
flexDir: 'column',
gap: 2,
userSelect: 'none',
opacity: 0.7,
color: 'base.500',
...sx,
}}
{...rest}
>
<Flex sx={styles} {...rest}>
<Spinner size="xl" />
{props.label && <InvText textAlign="center">{props.label}</InvText>}
</Flex>

View File

@ -103,13 +103,7 @@ const PopoverContent = ({ data, feature }: PopoverContentProps) => {
<InvPopoverContent w={96}>
<InvPopoverCloseButton />
<InvPopoverBody>
<Flex
sx={{
gap: 2,
flexDirection: 'column',
alignItems: 'flex-start',
}}
>
<Flex gap={2} flexDirection="column" alignItems="flex-start">
{heading && (
<>
<InvHeading size="sm">{heading}</InvHeading>
@ -119,12 +113,10 @@ const PopoverContent = ({ data, feature }: PopoverContentProps) => {
{data?.image && (
<>
<Image
sx={{
objectFit: 'contain',
maxW: '60%',
maxH: '60%',
backgroundColor: 'white',
}}
objectFit="contain"
maxW="60%"
maxH="60%"
backgroundColor="white"
src={data.image}
alt="Optional Image"
/>

View File

@ -9,16 +9,14 @@ type ImageMetadataOverlayProps = {
const ImageMetadataOverlay = ({ imageDTO }: ImageMetadataOverlayProps) => {
return (
<Flex
sx={{
pointerEvents: 'none',
flexDirection: 'column',
position: 'absolute',
top: 0,
insetInlineStart: 0,
p: 2,
alignItems: 'flex-start',
gap: 2,
}}
pointerEvents="none"
flexDirection="column"
position="absolute"
top={0}
insetInlineStart={0}
p={2}
alignItems="flex-start"
gap={2}
>
<Badge variant="solid" colorScheme="base">
{imageDTO.width} × {imageDTO.height}

View File

@ -23,57 +23,49 @@ const ImageUploadOverlay = (props: ImageUploadOverlayProps) => {
return (
<Box
sx={{
position: 'absolute',
top: 0,
insetInlineStart: 0,
width: '100vw',
height: '100vh',
zIndex: 999,
backdropFilter: 'blur(20px)',
}}
position="absolute"
top={0}
insetInlineStart={0}
width="100vw"
height="100vh"
zIndex={999}
backdropFilter="blur(20px)"
>
<Flex
sx={{
position: 'absolute',
top: 0,
insetInlineStart: 0,
w: 'full',
h: 'full',
bg: 'base.900',
opacity: 0.7,
alignItems: 'center',
justifyContent: 'center',
transitionProperty: 'common',
transitionDuration: '0.1s',
}}
position="absolute"
top={0}
insetInlineStart={0}
w="full"
h="full"
bg="base.900"
opacity={0.7}
alignItems="center"
justifyContent="center"
transitionProperty="common"
transitionDuration="0.1s"
/>
<Flex
sx={{
position: 'absolute',
top: 0,
insetInlineStart: 0,
width: 'full',
height: 'full',
alignItems: 'center',
justifyContent: 'center',
p: 4,
}}
position="absolute"
top={0}
insetInlineStart={0}
width="full"
height="full"
alignItems="center"
justifyContent="center"
p={4}
>
<Flex
sx={{
width: 'full',
height: 'full',
alignItems: 'center',
justifyContent: 'center',
flexDir: 'column',
gap: 4,
borderWidth: 3,
borderRadius: 'xl',
borderStyle: 'dashed',
color: 'base.100',
borderColor: 'base.200',
}}
width="full"
height="full"
alignItems="center"
justifyContent="center"
flexDir="column"
gap={4}
borderWidth={3}
borderRadius="xl"
borderStyle="dashed"
color="base.100"
borderColor="base.200"
>
{isDragAccept ? (
<Heading size="lg">{t('gallery.dropToUpload')}</Heading>

View File

@ -20,7 +20,7 @@ const baseStyleInput = defineStyle(() => ({
transitionProperty: 'common',
transitionDuration: 'normal',
width: 'full',
_focusVisible: { boxShadow: 'outline' },
_focusVisible: { boxShadow: 'none' },
_placeholder: { opacity: 0.6 },
'::selection': {
color: 'blue.50',

View File

@ -21,19 +21,17 @@ const SelectionOverlay = ({ isSelected, isHovered }: Props) => {
return (
<Box
className="selection-box"
sx={{
position: 'absolute',
top: 0,
insetInlineEnd: 0,
bottom: 0,
insetInlineStart: 0,
borderRadius: 'base',
opacity: isSelected || isHovered ? 1 : 0.5,
transitionProperty: 'common',
transitionDuration: '0.1s',
pointerEvents: 'none',
shadow,
}}
position="absolute"
top={0}
insetInlineEnd={0}
bottom={0}
insetInlineStart={0}
borderRadius="base"
opacity={isSelected || isHovered ? 1 : 0.5}
transitionProperty="common"
transitionDuration="0.1s"
pointerEvents="none"
shadow={shadow}
/>
);
};

View File

@ -1,46 +1,24 @@
import type { StyleProps } from '@chakra-ui/react';
import { Box, Flex } from '@chakra-ui/react';
import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
import type { PropsWithChildren } from 'react';
import type { CSSProperties, PropsWithChildren } from 'react';
import { memo } from 'react';
type Props = PropsWithChildren & {
maxHeight?: StyleProps['maxHeight'];
};
const styles: CSSProperties = { height: '100%', width: '100%' };
const ScrollableContent = ({ children, maxHeight }: Props) => {
return (
<Flex
sx={{
w: 'full',
h: 'full',
maxHeight,
position: 'relative',
}}
>
<Box
sx={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
>
<Flex w="full" h="full" maxHeight={maxHeight} position="relative">
<Box position="absolute" top={0} left={0} right={0} bottom={0}>
<OverlayScrollbarsComponent
defer
style={{ height: '100%', width: '100%' }}
options={{
scrollbars: {
visibility: 'auto',
autoHide: 'scroll',
autoHideDelay: 1300,
theme: 'os-theme-dark',
},
overflow: {
x: 'hidden',
},
}}
style={styles}
options={overlayScrollbarsParams.options}
>
{children}
</OverlayScrollbarsComponent>

View File

@ -1,33 +1,37 @@
import { Box } from '@chakra-ui/react';
import { memo } from 'react';
import { memo, useMemo } from 'react';
type Props = {
isSelected: boolean;
isHovered: boolean;
};
const SelectionOverlay = ({ isSelected, isHovered }: Props) => {
const shadow = useMemo(() => {
if (isSelected && isHovered) {
return 'hoverSelected';
}
if (isSelected && !isHovered) {
return 'selected';
}
if (!isSelected && isHovered) {
return 'hoverUnselected';
}
return undefined;
}, [isHovered, isSelected]);
return (
<Box
className="selection-box"
sx={{
position: 'absolute',
top: 0,
insetInlineEnd: 0,
bottom: 0,
insetInlineStart: 0,
borderRadius: 'base',
opacity: isSelected ? 1 : 0.7,
transitionProperty: 'common',
transitionDuration: '0.1s',
pointerEvents: 'none',
shadow: isSelected
? isHovered
? 'hoverSelected'
: 'selected'
: isHovered
? 'hoverUnselected'
: undefined,
}}
position="absolute"
top={0}
insetInlineEnd={0}
bottom={0}
insetInlineStart={0}
borderRadius="base"
opacity={isSelected ? 1 : 0.7}
transitionProperty="common"
transitionDuration="0.1s"
pointerEvents="none"
shadow={shadow}
/>
);
};

View File

@ -18,7 +18,7 @@ import {
import type Konva from 'konva';
import type { KonvaEventObject } from 'konva/lib/Node';
import type { Vector2d } from 'konva/lib/types';
import { memo, useCallback, useEffect, useRef } from 'react';
import { memo, useCallback, useEffect, useMemo, useRef } from 'react';
import { Layer, Stage } from 'react-konva';
import IAICanvasBoundingBoxOverlay from './IAICanvasBoundingBoxOverlay';
@ -163,35 +163,32 @@ const IAICanvas = () => {
};
}, [dispatch]);
const stageStyles = useMemo(
() => ({
outline: 'none',
overflow: 'hidden',
cursor: stageCursor ? stageCursor : undefined,
canvas: {
outline: 'none',
},
}),
[stageCursor]
);
return (
<Flex
id="canvas-container"
ref={containerRef}
sx={{
position: 'relative',
height: '100%',
width: '100%',
borderRadius: 'base',
}}
position="relative"
height="100%"
width="100%"
borderRadius="base"
>
<Box
sx={{
position: 'absolute',
// top: 0,
// insetInlineStart: 0,
}}
>
<Box position="absolute">
<ChakraStage
tabIndex={-1}
ref={canvasStageRefCallback}
sx={{
outline: 'none',
overflow: 'hidden',
cursor: stageCursor ? stageCursor : undefined,
canvas: {
outline: 'none',
},
}}
sx={stageStyles}
x={stageCoordinates.x}
y={stageCoordinates.y}
width={stageDimensions.width}

View File

@ -83,56 +83,38 @@ const IAICanvasStatusText = () => {
return (
<Flex
sx={{
flexDirection: 'column',
position: 'absolute',
top: 0,
insetInlineStart: 0,
opacity: 0.65,
display: 'flex',
fontSize: 'sm',
padding: 1,
px: 2,
minWidth: 48,
margin: 1,
borderRadius: 'base',
pointerEvents: 'none',
bg: 'base.800',
}}
flexDirection="column"
position="absolute"
top={0}
insetInlineStart={0}
opacity={0.65}
display="flex"
fontSize="sm"
padding={1}
px={2}
minWidth={48}
margin={1}
borderRadius="base"
pointerEvents="none"
bg="base.800"
>
<GenerationModeStatusText />
<Box
style={{
color: activeLayerColor,
}}
>{`${t('unifiedCanvas.activeLayer')}: ${t(
<Box color={activeLayerColor}>{`${t('unifiedCanvas.activeLayer')}: ${t(
`unifiedCanvas.${layer}`
)}`}</Box>
<Box>{`${t('unifiedCanvas.canvasScale')}: ${canvasScaleString}%`}</Box>
{shouldPreserveMaskedArea && (
<Box
style={{
color: warningColor,
}}
>
<Box color={warningColor}>
{t('unifiedCanvas.preserveMaskedArea')}: {t('common.on')}
</Box>
)}
{shouldShowBoundingBox && (
<Box
style={{
color: boundingBoxColor,
}}
>{`${t(
<Box color={boundingBoxColor}>{`${t(
'unifiedCanvas.boundingBox'
)}: ${boundingBoxDimensionsString}`}</Box>
)}
{shouldShowScaledBoundingBox && (
<Box
style={{
color: boundingBoxColor,
}}
>{`${t(
<Box color={boundingBoxColor}>{`${t(
'unifiedCanvas.scaledBoundingBox'
)}: ${scaledBoundingBoxDimensionsString}`}</Box>
)}

View File

@ -150,7 +150,7 @@ const IAICanvasMaskOptions = () => {
onChange={handleChangePreserveMaskedArea}
/>
</InvControl>
<Box sx={{ paddingTop: 2, paddingBottom: 2 }}>
<Box pt={2} pb={2}>
<IAIColorPicker
color={maskColor}
onChange={handleChangeMaskColor}

View File

@ -272,13 +272,7 @@ const IAICanvasToolChooserOptions = () => {
/>
</InvControl>
</Flex>
<Box
sx={{
width: '100%',
paddingTop: 2,
paddingBottom: 2,
}}
>
<Box w="full" pt={2} pb={2}>
<IAIColorPicker
withNumberInput={true}
color={brushColor}

View File

@ -101,7 +101,7 @@ const ChangeBoardModal = () => {
acceptButtonText={t('boards.move')}
cancelButtonText={t('boards.cancel')}
>
<Flex sx={{ flexDir: 'column', gap: 4 }}>
<Flex flexDir="column" gap={4}>
<InvText>
{t('boards.movingImagesToBoard', {
count: imagesToChange.length,

View File

@ -65,18 +65,14 @@ const ControlAdapterConfig = (props: { id: string; number: number }) => {
return (
<Flex
sx={{
flexDir: 'column',
gap: 3,
p: 2,
borderRadius: 'base',
position: 'relative',
bg: 'base.750',
}}
flexDir="column"
gap={3}
p={2}
borderRadius="base"
position="relative"
bg="base.750"
>
<Flex
sx={{ gap: 2, alignItems: 'center', justifyContent: 'space-between' }}
>
<Flex gap={2} alignItems="center" justifyContent="space-between">
<InvControl label={t(`controlnet.${controlAdapterType}`, { number })}>
<InvSwitch
aria-label={t('controlnet.toggleControlNet')}
@ -85,14 +81,12 @@ const ControlAdapterConfig = (props: { id: string; number: number }) => {
/>
</InvControl>
</Flex>
<Flex sx={{ gap: 2, alignItems: 'center' }}>
<Flex gap={2} alignItems="center">
<Box
sx={{
w: 'full',
minW: 0,
transitionProperty: 'common',
transitionDuration: '0.1s',
}}
minW={0}
w="full"
transitionProperty="common"
transitionDuration="0.1s"
>
<ParamControlAdapterModel id={id} />
</Box>
@ -128,57 +122,46 @@ const ControlAdapterConfig = (props: { id: string; number: number }) => {
}
onClick={toggleIsExpanded}
variant="ghost"
sx={{
_hover: {
bg: 'none',
},
}}
icon={
<ChevronUpIcon
sx={{
boxSize: 4,
color: 'base.300',
transform: isExpanded ? 'rotate(0deg)' : 'rotate(180deg)',
transitionProperty: 'common',
transitionDuration: 'normal',
}}
boxSize={4}
color="base.300"
transform={isExpanded ? 'rotate(0deg)' : 'rotate(180deg)'}
transitionProperty="common"
transitionDuration="normal"
/>
}
/>
</Flex>
<Flex sx={{ w: 'full', flexDirection: 'column', gap: 3 }}>
<Flex sx={{ gap: 4, w: 'full', alignItems: 'center' }}>
<Flex w="full" flexDir="column" gap={3}>
<Flex gap={4} w="full" alignItems="center">
<Flex
sx={{
flexDir: 'column',
gap: 3,
h: 28,
w: 'full',
paddingInlineStart: 1,
paddingInlineEnd: isExpanded ? 1 : 0,
pb: 2,
justifyContent: 'space-between',
}}
flexDir="column"
gap={3}
h={28}
w="full"
paddingInlineStart={1}
paddingInlineEnd={isExpanded ? 1 : 0}
pb={2}
justifyContent="space-between"
>
<ParamControlAdapterWeight id={id} />
<ParamControlAdapterBeginEnd id={id} />
</Flex>
{!isExpanded && (
<Flex
sx={{
alignItems: 'center',
justifyContent: 'center',
h: 28,
w: 28,
aspectRatio: '1/1',
}}
alignItems="center"
justifyContent="center"
h={28}
w={28}
aspectRatio="1/1"
>
<ControlAdapterImagePreview id={id} isSmall />
</Flex>
)}
</Flex>
<Flex sx={{ gap: 2 }}>
<Flex gap={2}>
<ParamControlAdapterControlMode id={id} />
<ParamControlAdapterResizeMode id={id} />
</Flex>

View File

@ -177,13 +177,11 @@ const ControlAdapterImagePreview = ({ isSmall, id }: Props) => {
<Flex
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
sx={{
position: 'relative',
w: 'full',
h: isSmall ? 28 : 366, // magic no touch
alignItems: 'center',
justifyContent: 'center',
}}
position="relative"
w="full"
h={isSmall ? 28 : 366} // magic no touch
alignItems="center"
justifyContent="center"
>
<IAIDndImage
draggableData={draggableData}
@ -194,17 +192,15 @@ const ControlAdapterImagePreview = ({ isSmall, id }: Props) => {
/>
<Box
sx={{
position: 'absolute',
top: 0,
insetInlineStart: 0,
w: 'full',
h: 'full',
opacity: shouldShowProcessedImage ? 1 : 0,
transitionProperty: 'common',
transitionDuration: 'normal',
pointerEvents: 'none',
}}
position="absolute"
top={0}
insetInlineStart={0}
w="full"
h="full"
opacity={shouldShowProcessedImage ? 1 : 0}
transitionProperty="common"
transitionDuration="normal"
pointerEvents="none"
>
<IAIDndImage
draggableData={draggableData}
@ -236,20 +232,18 @@ const ControlAdapterImagePreview = ({ isSmall, id }: Props) => {
{pendingControlImages.includes(id) && (
<Flex
sx={{
position: 'absolute',
top: 0,
insetInlineStart: 0,
w: 'full',
h: 'full',
alignItems: 'center',
justifyContent: 'center',
opacity: 0.8,
borderRadius: 'base',
bg: 'base.900',
}}
position="absolute"
top={0}
insetInlineStart={0}
w="full"
h="full"
alignItems="center"
justifyContent="center"
opacity={0.8}
borderRadius="base"
bg="base.900"
>
<Spinner size="xl" sx={{ color: 'base.400' }} />
<Spinner size="xl" color="base.400" />
</Flex>
)}
</Flex>

View File

@ -29,11 +29,7 @@ const ControlNetCanvasImageImports = (
}, [id, dispatch]);
return (
<Flex
sx={{
gap: 2,
}}
>
<Flex gap={2}>
<InvIconButton
size="sm"
icon={<FaImage />}

View File

@ -5,7 +5,7 @@ type Props = PropsWithChildren;
export default function ProcessorWrapper(props: Props) {
return (
<Flex sx={{ flexDirection: 'column', gap: 2, pb: 2 }}>
<Flex flexDir="column" gap={2} pb={2}>
{props.children}
</Flex>
);

View File

@ -29,7 +29,7 @@ const ImageUsageMessage = (props: Props) => {
return (
<>
<InvText>{topMessage}</InvText>
<UnorderedList sx={{ paddingInlineStart: 6 }}>
<UnorderedList paddingInlineStart={6}>
{imageUsage.isInitialImage && (
<ListItem>{t('common.img2img')}</ListItem>
)}

View File

@ -11,7 +11,7 @@ type OverlayDragImageProps = {
const BOX_SIZE = 28;
const STYLES: ChakraProps['sx'] = {
const imageStyles: ChakraProps['sx'] = {
w: BOX_SIZE,
h: BOX_SIZE,
maxW: BOX_SIZE,
@ -24,6 +24,14 @@ const STYLES: ChakraProps['sx'] = {
color: 'base.100',
};
const multiImageStyles: ChakraProps['sx'] = {
position: 'relative',
alignItems: 'center',
justifyContent: 'center',
flexDir: 'column',
...imageStyles,
};
const DragPreview = (props: OverlayDragImageProps) => {
const { t } = useTranslation();
if (!props.dragData) {
@ -34,17 +42,15 @@ const DragPreview = (props: OverlayDragImageProps) => {
const { field, fieldTemplate } = props.dragData.payload;
return (
<Box
sx={{
position: 'relative',
p: 2,
px: 3,
opacity: 0.7,
bg: 'base.300',
borderRadius: 'base',
boxShadow: 'dark-lg',
whiteSpace: 'nowrap',
fontSize: 'sm',
}}
position="relative"
p={2}
px={3}
opacity={0.7}
bg="base.300"
borderRadius="base"
boxShadow="dark-lg"
whiteSpace="nowrap"
fontSize="sm"
>
<InvText>{field.label || fieldTemplate.title}</InvText>
</Box>
@ -55,19 +61,15 @@ const DragPreview = (props: OverlayDragImageProps) => {
const { thumbnail_url, width, height } = props.dragData.payload.imageDTO;
return (
<Box
sx={{
position: 'relative',
width: 'full',
height: 'full',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
position="relative"
width="full"
height="full"
display="flex"
alignItems="center"
justifyContent="center"
>
<Image
sx={{
...STYLES,
}}
sx={imageStyles}
objectFit="contain"
src={thumbnail_url}
width={width}
@ -79,15 +81,7 @@ const DragPreview = (props: OverlayDragImageProps) => {
if (props.dragData.payloadType === 'IMAGE_DTOS') {
return (
<Flex
sx={{
position: 'relative',
alignItems: 'center',
justifyContent: 'center',
flexDir: 'column',
...STYLES,
}}
>
<Flex sx={multiImageStyles}>
<Heading>{props.dragData.payload.imageDTOs.length}</Heading>
<Heading size="sm">{t('parameters.images')}</Heading>
</Flex>

View File

@ -68,7 +68,7 @@ export const EmbeddingSelect = ({
onChange={onChange}
onMenuClose={onClose}
data-testid="add-embedding"
sx={{ w: 'full' }}
w="full"
/>
</InvControl>
);

View File

@ -5,15 +5,8 @@ import { useTranslation } from 'react-i18next';
const AutoAddIcon = () => {
const { t } = useTranslation();
return (
<Flex
sx={{
position: 'absolute',
insetInlineEnd: 0,
top: 0,
p: 1,
}}
>
<Badge variant="solid" sx={{ bg: 'blue.500' }}>
<Flex position="absolute" insetInlineEnd={0} top={0} p={1}>
<Badge variant="solid" bg="blue.500">
{t('common.auto')}
</Badge>
</Flex>

View File

@ -93,10 +93,7 @@ const BoardContextMenu = ({
const renderMenuFunc = useCallback(
() => (
<InvMenuList
sx={{ visibility: 'visible !important' }}
onContextMenu={skipEvent}
>
<InvMenuList visibility="visible !important" onContextMenu={skipEvent}>
<InvMenuGroup title={boardName}>
<InvMenuItem
icon={<FaPlus />}

View File

@ -5,6 +5,7 @@ import { useAppSelector } from 'app/store/storeHooks';
import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
import DeleteBoardModal from 'features/gallery/components/Boards/DeleteBoardModal';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
import type { CSSProperties} from 'react';
import { memo, useState } from 'react';
import { useListAllBoardsQuery } from 'services/api/endpoints/boards';
import type { BoardDTO } from 'services/api/types';
@ -14,6 +15,11 @@ import BoardsSearch from './BoardsSearch';
import GalleryBoard from './GalleryBoard';
import NoBoardBoard from './NoBoardBoard';
const overlayScrollbarsStyles: CSSProperties = {
height: '100%',
width: '100%',
};
const selector = createMemoizedSelector([stateSelector], ({ gallery }) => {
const { selectedBoardId, boardSearchText } = gallery;
return { selectedBoardId, boardSearchText };
@ -39,39 +45,35 @@ const BoardsList = (props: Props) => {
<Collapse in={isOpen} animateOpacity>
<Flex
layerStyle="first"
sx={{
flexDir: 'column',
gap: 2,
p: 2,
mt: 2,
borderRadius: 'base',
}}
flexDir="column"
gap={2}
p={2}
mt={2}
borderRadius="base"
>
<Flex sx={{ gap: 2, alignItems: 'center' }}>
<Flex gap={2} alignItems="center">
<BoardsSearch />
<AddBoardButton />
</Flex>
<OverlayScrollbarsComponent
defer
style={{ height: '100%', width: '100%' }}
style={overlayScrollbarsStyles}
options={overlayScrollbarsParams.options}
>
<Grid
className="list-container"
data-testid="boards-list"
sx={{
gridTemplateColumns: `repeat(auto-fill, minmax(108px, 1fr));`,
maxH: 346,
}}
gridTemplateColumns="repeat(auto-fill, minmax(108px, 1fr))"
maxH={346}
>
<GridItem sx={{ p: 1.5 }} data-testid="no-board">
<GridItem p={1.5} data-testid="no-board">
<NoBoardBoard isSelected={selectedBoardId === 'none'} />
</GridItem>
{filteredBoards &&
filteredBoards.map((board, index) => (
<GridItem
key={board.board_id}
sx={{ p: 1.5 }}
p={1.5}
data-testid={`board-${index}`}
>
<GalleryBoard

View File

@ -1,3 +1,4 @@
import type { SystemStyleObject } from '@chakra-ui/react';
import {
Box,
Editable,
@ -33,6 +34,14 @@ import {
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
import type { BoardDTO } from 'services/api/types';
const editableInputStyles: SystemStyleObject = {
p: 0,
_focusVisible: {
p: 0,
textAlign: 'center',
},
};
interface GalleryBoardProps {
board: BoardDTO;
isSelected: boolean;
@ -140,18 +149,16 @@ const GalleryBoard = ({
}, []);
const { t } = useTranslation();
return (
<Box sx={{ w: 'full', h: 'full', touchAction: 'none', userSelect: 'none' }}>
<Box w="full" h="full" userSelect="none">
<Flex
onMouseOver={handleMouseOver}
onMouseOut={handleMouseOut}
sx={{
position: 'relative',
justifyContent: 'center',
alignItems: 'center',
aspectRatio: '1/1',
w: 'full',
h: 'full',
}}
position="relative"
justifyContent="center"
alignItems="center"
aspectRatio="1/1"
w="full"
h="full"
>
<BoardContextMenu
board={board}
@ -163,47 +170,39 @@ const GalleryBoard = ({
<Flex
ref={ref}
onClick={handleSelectBoard}
sx={{
w: 'full',
h: 'full',
position: 'relative',
justifyContent: 'center',
alignItems: 'center',
borderRadius: 'base',
cursor: 'pointer',
bg: 'base.800',
}}
w="full"
h="full"
position="relative"
justifyContent="center"
alignItems="center"
borderRadius="base"
cursor="pointer"
bg="base.800"
>
{coverImage?.thumbnail_url ? (
<Image
src={coverImage?.thumbnail_url}
draggable={false}
sx={{
objectFit: 'cover',
w: 'full',
h: 'full',
maxH: 'full',
borderRadius: 'base',
borderBottomRadius: 'lg',
}}
objectFit="cover"
w="full"
h="full"
maxH="full"
borderRadius="base"
borderBottomRadius="lg"
/>
) : (
<Flex
sx={{
w: 'full',
h: 'full',
justifyContent: 'center',
alignItems: 'center',
}}
w="full"
h="full"
justifyContent="center"
alignItems="center"
>
<Icon
boxSize={12}
as={FaUser}
sx={{
mt: -6,
opacity: 0.7,
color: 'base.500',
}}
mt={-6}
opacity={0.7}
color="base.500"
/>
</Flex>
)}
@ -213,21 +212,19 @@ const GalleryBoard = ({
isHovered={isHovered}
/>
<Flex
sx={{
position: 'absolute',
bottom: 0,
left: 0,
p: 1,
justifyContent: 'center',
alignItems: 'center',
w: 'full',
maxW: 'full',
borderBottomRadius: 'base',
bg: isSelected ? 'blue.500' : 'base.600',
color: isSelected ? 'base.50' : 'base.100',
lineHeight: 'short',
fontSize: 'xs',
}}
position="absolute"
bottom={0}
left={0}
p={1}
justifyContent="center"
alignItems="center"
w="full"
maxW="full"
borderBottomRadius="base"
bg={isSelected ? 'blue.500' : 'base.600'}
color={isSelected ? 'base.50' : 'base.100'}
lineHeight="short"
fontSize="xs"
>
<Editable
value={localBoardName}
@ -235,31 +232,17 @@ const GalleryBoard = ({
submitOnBlur={true}
onChange={handleChange}
onSubmit={handleSubmit}
sx={{
w: 'full',
}}
w="full"
>
<EditablePreview
sx={{
p: 0,
fontWeight: isSelected ? 'bold' : 'normal',
textAlign: 'center',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
p={0}
fontWeight={isSelected ? 'bold' : 'normal'}
textAlign="center"
overflow="hidden"
textOverflow="ellipsis"
noOfLines={1}
/>
<EditableInput
sx={{
p: 0,
_focusVisible: {
p: 0,
textAlign: 'center',
// get rid of the edit border
boxShadow: 'none',
},
}}
/>
<EditableInput sx={editableInputStyles} />
</Editable>
</Flex>

View File

@ -71,19 +71,17 @@ const NoBoardBoard = memo(({ isSelected }: Props) => {
);
const { t } = useTranslation();
return (
<Box sx={{ w: 'full', h: 'full', touchAction: 'none', userSelect: 'none' }}>
<Box w="full" h="full" userSelect="none">
<Flex
onMouseOver={handleMouseOver}
onMouseOut={handleMouseOut}
sx={{
position: 'relative',
justifyContent: 'center',
alignItems: 'center',
aspectRatio: '1/1',
borderRadius: 'base',
w: 'full',
h: 'full',
}}
position="relative"
justifyContent="center"
alignItems="center"
aspectRatio="1/1"
borderRadius="base"
w="full"
h="full"
>
<BoardContextMenu board_id="none">
{(ref) => (
@ -91,58 +89,50 @@ const NoBoardBoard = memo(({ isSelected }: Props) => {
<Flex
ref={ref}
onClick={handleSelectBoard}
sx={{
w: 'full',
h: 'full',
position: 'relative',
justifyContent: 'center',
alignItems: 'center',
borderRadius: 'base',
cursor: 'pointer',
bg: 'base.800',
}}
w="full"
h="full"
position="relative"
justifyContent="center"
alignItems="center"
borderRadius="base"
cursor="pointer"
bg="base.800"
>
<Flex
sx={{
w: 'full',
h: 'full',
justifyContent: 'center',
alignItems: 'center',
}}
w="full"
h="full"
justifyContent="center"
alignItems="center"
>
<Image
src={InvokeAILogoImage}
alt="invoke-ai-logo"
sx={{
opacity: 0.4,
filter: 'grayscale(1)',
mt: -6,
w: 16,
h: 16,
minW: 16,
minH: 16,
userSelect: 'none',
}}
opacity={0.4}
filter="grayscale(1)"
mt={-6}
w={16}
h={16}
minW={16}
minH={16}
userSelect="none"
/>
</Flex>
{autoAddBoardId === 'none' && <AutoAddIcon />}
<Flex
sx={{
position: 'absolute',
bottom: 0,
left: 0,
p: 1,
justifyContent: 'center',
alignItems: 'center',
w: 'full',
maxW: 'full',
borderBottomRadius: 'base',
bg: isSelected ? 'blue.500' : 'base.600',
color: isSelected ? 'base.50' : 'base.100',
lineHeight: 'short',
fontSize: 'xs',
fontWeight: isSelected ? 'bold' : 'normal',
}}
position="absolute"
bottom={0}
left={0}
p={1}
justifyContent="center"
alignItems="center"
w="full"
maxW="full"
borderBottomRadius="base"
bg={isSelected ? 'blue.500' : 'base.600'}
color={isSelected ? 'base.50' : 'base.100'}
lineHeight="short"
fontSize="xs"
fontWeight={isSelected ? 'bold' : 'normal'}
>
{boardName}
</Flex>

View File

@ -139,9 +139,7 @@ const DeleteBoardModal = (props: Props) => {
</Flex>
</InvAlertDialogBody>
<InvAlertDialogFooter>
<Flex
sx={{ justifyContent: 'space-between', width: 'full', gap: 2 }}
>
<Flex w="full" gap={2} justifyContent="space-between">
<InvButton ref={cancelRef} onClick={handleClose}>
{t('boards.cancel')}
</InvButton>

View File

@ -20,11 +20,7 @@ const GalleryBoardContextMenuItems = ({ board, setBoardToDelete }: Props) => {
return (
<>
<InvMenuItem
sx={{ color: 'error.300' }}
icon={<FaTrash />}
onClick={handleDelete}
>
<InvMenuItem color="error.300" icon={<FaTrash />} onClick={handleDelete}>
{t('boards.deleteBoard')}
</InvMenuItem>
</>

View File

@ -217,14 +217,7 @@ const CurrentImageButtons = () => {
return (
<>
<Flex
sx={{
flexWrap: 'wrap',
justifyContent: 'center',
alignItems: 'center',
gap: 2,
}}
>
<Flex flexWrap="wrap" justifyContent="center" alignItems="center" gap={2}>
<InvButtonGroup isDisabled={shouldDisableToolbarButtons}>
<InvMenu isLazy>
<InvMenuButton

View File

@ -7,15 +7,13 @@ import CurrentImagePreview from './CurrentImagePreview';
const CurrentImageDisplay = () => {
return (
<Flex
sx={{
position: 'relative',
flexDirection: 'column',
height: '100%',
width: '100%',
rowGap: 4,
alignItems: 'center',
justifyContent: 'center',
}}
position="relative"
flexDirection="column"
height="100%"
width="100%"
rowGap={4}
alignItems="center"
justifyContent="center"
>
<CurrentImageButtons />
<CurrentImagePreview />

View File

@ -14,8 +14,17 @@ import ImageMetadataViewer from 'features/gallery/components/ImageMetadataViewer
import NextPrevImageButtons from 'features/gallery/components/NextPrevImageButtons';
import { useNextPrevImage } from 'features/gallery/hooks/useNextPrevImage';
import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors';
import type { AnimationProps} from 'framer-motion';
import { AnimatePresence, motion } from 'framer-motion';
import { memo, useCallback, useMemo, useRef, useState } from 'react';
import type {
CSSProperties} from 'react';
import {
memo,
useCallback,
useMemo,
useRef,
useState,
} from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import { FaImage } from 'react-icons/fa';
@ -128,13 +137,11 @@ const CurrentImagePreview = () => {
<Flex
onMouseOver={handleMouseOver}
onMouseOut={handleMouseOut}
sx={{
width: 'full',
height: 'full',
alignItems: 'center',
justifyContent: 'center',
position: 'relative',
}}
width="full"
height="full"
alignItems="center"
justifyContent="center"
position="relative"
>
{hasDenoiseProgress && shouldShowProgressInViewer ? (
<ProgressImage />
@ -158,13 +165,11 @@ const CurrentImagePreview = () => {
)}
{shouldShowImageDetails && imageDTO && (
<Box
sx={{
position: 'absolute',
top: '0',
width: 'full',
height: 'full',
borderRadius: 'base',
}}
position="absolute"
top="0"
width="full"
height="full"
borderRadius="base"
>
<ImageMetadataViewer image={imageDTO} />
</Box>
@ -173,24 +178,10 @@ const CurrentImagePreview = () => {
{!shouldShowImageDetails && imageDTO && shouldShowNextPrevButtons && (
<motion.div
key="nextPrevButtons"
initial={{
opacity: 0,
}}
animate={{
opacity: 1,
transition: { duration: 0.1 },
}}
exit={{
opacity: 0,
transition: { duration: 0.1 },
}}
style={{
position: 'absolute',
top: '0',
width: '100%',
height: '100%',
pointerEvents: 'none',
}}
initial={initial}
animate={animate}
exit={exit}
style={motionStyles}
>
<NextPrevImageButtons />
</motion.div>
@ -201,3 +192,22 @@ const CurrentImagePreview = () => {
};
export default memo(CurrentImagePreview);
const initial: AnimationProps['initial'] = {
opacity: 0,
};
const animate: AnimationProps['animate'] = {
opacity: 1,
transition: { duration: 0.1 },
};
const exit: AnimationProps['exit'] = {
opacity: 0,
transition: { duration: 0.1 },
};
const motionStyles: CSSProperties = {
position: 'absolute',
top: '0',
width: '100%',
height: '100%',
pointerEvents: 'none',
};

View File

@ -1,6 +1,7 @@
import type { SystemStyleObject } from '@chakra-ui/react';
import { Image } from '@chakra-ui/react';
import { useAppSelector } from 'app/store/storeHooks';
import { memo } from 'react';
import { memo, useMemo } from 'react';
const CurrentImagePreview = () => {
const progress_image = useAppSelector(
@ -10,6 +11,13 @@ const CurrentImagePreview = () => {
(state) => state.system.shouldAntialiasProgressImage
);
const sx = useMemo<SystemStyleObject>(
() => ({
imageRendering: shouldAntialiasProgressImage ? 'auto' : 'pixelated',
}),
[shouldAntialiasProgressImage]
);
if (!progress_image) {
return null;
}
@ -21,15 +29,12 @@ const CurrentImagePreview = () => {
height={progress_image.height}
draggable={false}
data-testid="progress-image"
sx={{
objectFit: 'contain',
maxWidth: 'full',
maxHeight: 'full',
height: 'auto',
position: 'absolute',
borderRadius: 'base',
imageRendering: shouldAntialiasProgressImage ? 'auto' : 'pixelated',
}}
objectFit="contain"
maxWidth="full"
maxHeight="full"
position="absolute"
borderRadius="base"
sx={sx}
/>
);
};

View File

@ -36,20 +36,14 @@ const ImageContextMenu = ({ imageDTO, children }: Props) => {
if (selectionCount > 1) {
return (
<InvMenuList
sx={{ visibility: 'visible !important' }}
onContextMenu={skipEvent}
>
<InvMenuList visibility="visible" onContextMenu={skipEvent}>
<MultipleSelectionMenuItems />
</InvMenuList>
);
}
return (
<InvMenuList
sx={{ visibility: 'visible !important' }}
onContextMenu={skipEvent}
>
<InvMenuList visibility="visible" onContextMenu={skipEvent}>
<SingleSelectionMenuItems imageDTO={imageDTO} />
</InvMenuList>
);

View File

@ -113,7 +113,7 @@ const MultipleSelectionMenuItems = () => {
{t('boards.changeBoard')}
</InvMenuItem>
<InvMenuItem
sx={{ color: 'error.300' }}
color="error.300"
icon={<FaTrash />}
onClickCapture={handleDeleteSelection}
>

View File

@ -248,7 +248,7 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
</InvMenuItem>
)}
<InvMenuItem
sx={{ color: 'error.300' }}
color="error.300"
icon={<FaTrash />}
onClickCapture={handleDelete}
>

View File

@ -50,22 +50,18 @@ const ImageGalleryContent = () => {
return (
<VStack
layerStyle="first"
sx={{
flexDirection: 'column',
h: 'full',
w: 'full',
borderRadius: 'base',
p: 2,
}}
flexDirection="column"
h="full"
w="full"
borderRadius="base"
p={2}
>
<Box sx={{ w: 'full' }}>
<Box w="full">
<Flex
ref={resizeObserverRef}
sx={{
alignItems: 'center',
justifyContent: 'space-between',
gap: 2,
}}
alignItems="center"
justifyContent="space-between"
gap={2}
>
<GalleryBoardName
isOpen={isBoardListOpen}
@ -78,18 +74,12 @@ const ImageGalleryContent = () => {
</Box>
</Box>
<Flex ref={galleryGridRef} direction="column" gap={2} h="full" w="full">
<Flex
sx={{
alignItems: 'center',
justifyContent: 'space-between',
gap: 2,
}}
>
<Flex alignItems="center" justifyContent="space-between" gap={2}>
<Tabs
index={galleryView === 'images' ? 0 : 1}
variant="unstyled"
size="sm"
sx={{ w: 'full' }}
w="full"
>
<TabList>
<InvButtonGroup w="full">
@ -98,9 +88,7 @@ const ImageGalleryContent = () => {
size="sm"
isChecked={galleryView === 'images'}
onClick={handleClickImages}
sx={{
w: 'full',
}}
w="full"
leftIcon={<FaImages />}
data-testid="images-tab"
>
@ -111,9 +99,7 @@ const ImageGalleryContent = () => {
size="sm"
isChecked={galleryView === 'assets'}
onClick={handleClickAssets}
sx={{
w: 'full',
}}
w="full"
leftIcon={<FaServer />}
data-testid="assets-tab"
>

View File

@ -1,3 +1,4 @@
import type { SystemStyleObject } from '@chakra-ui/react';
import { Box, Flex } from '@chakra-ui/react';
import { useStore } from '@nanostores/react';
import { $customStarUI } from 'app/store/nanostores/customStarUI';
@ -26,6 +27,11 @@ import {
useUnstarImagesMutation,
} from 'services/api/endpoints/images';
const imageSx: SystemStyleObject = { w: 'full', h: 'full' };
const imageIconStyleOverrides: SystemStyleObject = {
bottom: 2,
top: 'auto',
};
interface HoverableImageProps {
imageName: string;
index: number;
@ -130,19 +136,14 @@ const GalleryImage = (props: HoverableImageProps) => {
}
return (
<Box
sx={{ w: 'full', h: 'full', touchAction: 'none' }}
data-testid={`image-${imageDTO.image_name}`}
>
<Box w="full" h="full" data-testid={`image-${imageDTO.image_name}`}>
<Flex
ref={imageContainerRef}
userSelect="none"
sx={{
position: 'relative',
justifyContent: 'center',
alignItems: 'center',
aspectRatio: '1/1',
}}
position="relative"
justifyContent="center"
alignItems="center"
aspectRatio="1/1"
>
<IAIDndImage
onClick={handleClick}
@ -150,7 +151,7 @@ const GalleryImage = (props: HoverableImageProps) => {
draggableData={draggableData}
isSelected={isSelected}
minSize={0}
imageSx={{ w: 'full', h: 'full' }}
imageSx={imageSx}
isDropDisabled={true}
isUploadDisabled={true}
thumbnail={true}
@ -170,10 +171,7 @@ const GalleryImage = (props: HoverableImageProps) => {
onClick={handleDelete}
icon={<FaTrash />}
tooltip={t('gallery.deleteImage')}
styleOverrides={{
bottom: 2,
top: 'auto',
}}
styleOverrides={imageIconStyleOverrides}
/>
)}
</>

View File

@ -9,10 +9,12 @@ import { $useNextPrevImageState } from 'features/gallery/hooks/useNextPrevImage'
import { selectListImagesBaseQueryArgs } from 'features/gallery/store/gallerySelectors';
import { IMAGE_LIMIT } from 'features/gallery/store/types';
import { useOverlayScrollbars } from 'overlayscrollbars-react';
import type { CSSProperties } from 'react';
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { FaExclamationCircle, FaImage } from 'react-icons/fa';
import type {
GridComponents,
ItemContent,
ListRange,
VirtuosoGridHandle,
@ -28,6 +30,13 @@ import GalleryImage from './GalleryImage';
import ImageGridItemContainer from './ImageGridItemContainer';
import ImageGridListContainer from './ImageGridListContainer';
const components: GridComponents = {
Item: ImageGridItemContainer,
List: ImageGridListContainer,
};
const virtuosoStyles: CSSProperties = { height: '100%' };
const GalleryImageGrid = () => {
const { t } = useTranslation();
const rootRef = useRef<HTMLDivElement>(null);
@ -115,14 +124,7 @@ const GalleryImageGrid = () => {
if (!currentData) {
return (
<Flex
sx={{
w: 'full',
h: 'full',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Flex w="full" h="full" alignItems="center" justifyContent="center">
<IAINoContentFallback label={t('gallery.loading')} icon={FaImage} />
</Flex>
);
@ -130,14 +132,7 @@ const GalleryImageGrid = () => {
if (isSuccess && currentData?.ids.length === 0) {
return (
<Flex
sx={{
w: 'full',
h: 'full',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Flex w="full" h="full" alignItems="center" justifyContent="center">
<IAINoContentFallback
label={t('gallery.noImagesInGallery')}
icon={FaImage}
@ -151,13 +146,10 @@ const GalleryImageGrid = () => {
<>
<Box ref={rootRef} data-overlayscrollbars="" h="100%">
<VirtuosoGrid
style={{ height: '100%' }}
style={virtuosoStyles}
data={currentData.ids}
endReached={handleLoadMoreImages}
components={{
Item: ImageGridItemContainer,
List: ImageGridListContainer,
}}
components={components}
scrollerRef={setScroller}
itemContent={itemContentFunc}
ref={virtuosoRef}
@ -181,7 +173,7 @@ const GalleryImageGrid = () => {
if (isError) {
return (
<Box sx={{ w: 'full', h: 'full' }}>
<Box w="full" h="full">
<IAINoContentFallback
label={t('gallery.unableToLoad')}
icon={FaExclamationCircle}

View File

@ -16,9 +16,7 @@ const ListContainer = forwardRef((props: ListContainerProps, ref) => {
{...props}
className="list-container"
ref={ref}
sx={{
gridTemplateColumns: `repeat(auto-fill, minmax(${galleryImageMinimumWidth}px, 1fr));`,
}}
gridTemplateColumns={`repeat(auto-fill, minmax(${galleryImageMinimumWidth}px, 1fr))`}
data-testid="image-list-container"
>
{props.children}

View File

@ -4,6 +4,7 @@ import { InvTooltip } from 'common/components/InvTooltip/InvTooltip';
import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
import { isString } from 'lodash-es';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
import type { CSSProperties} from 'react';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { FaCopy, FaDownload } from 'react-icons/fa';
@ -42,35 +43,31 @@ const DataViewer = (props: Props) => {
return (
<Flex
layerStyle="second"
sx={{
borderRadius: 'base',
flexGrow: 1,
w: 'full',
h: 'full',
position: 'relative',
}}
borderRadius="base"
flexGrow={1}
w="full"
h="full"
position="relative"
>
<Box
sx={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
overflow: 'auto',
p: 4,
fontSize: 'sm',
}}
position="absolute"
top={0}
left={0}
right={0}
bottom={0}
overflow="auto"
p={4}
fontSize="sm"
>
<OverlayScrollbarsComponent
defer
style={{ height: '100%', width: '100%' }}
style={overlayScrollbarsStyles}
options={overlayScrollbarsParams.options}
>
<pre>{dataString}</pre>
</OverlayScrollbarsComponent>
</Box>
<Flex sx={{ position: 'absolute', top: 0, insetInlineEnd: 0, p: 2 }}>
<Flex position="absolute" top={0} insetInlineEnd={0} p={2}>
{withDownload && (
<InvTooltip label={`${t('gallery.download')} ${label} JSON`}>
<InvIconButton
@ -99,3 +96,8 @@ const DataViewer = (props: Props) => {
};
export default memo(DataViewer);
const overlayScrollbarsStyles: CSSProperties = {
height: '100%',
width: '100%',
};

View File

@ -37,16 +37,14 @@ const ImageMetadataViewer = ({ image }: ImageMetadataViewerProps) => {
return (
<Flex
layerStyle="first"
sx={{
padding: 4,
gap: 1,
flexDirection: 'column',
width: 'full',
height: 'full',
borderRadius: 'base',
position: 'absolute',
overflow: 'hidden',
}}
padding={4}
gap={1}
flexDirection="column"
width="full"
height="full"
borderRadius="base"
position="absolute"
overflow="hidden"
>
<Flex gap={2}>
<InvText fontWeight="semibold">{t('common.file')}:</InvText>
@ -58,13 +56,11 @@ const ImageMetadataViewer = ({ image }: ImageMetadataViewerProps) => {
<Tabs
variant="line"
sx={{
display: 'flex',
flexDir: 'column',
w: 'full',
h: 'full',
}}
isLazy={true}
display="flex"
flexDir="column"
w="full"
h="full"
>
<TabList>
<Tab>{t('metadata.recallParameters')}</Tab>

View File

@ -25,20 +25,12 @@ const NextPrevImageButtons = () => {
} = useNextPrevImage();
return (
<Box
sx={{
position: 'relative',
height: '100%',
width: '100%',
}}
>
<Box pos="relative" h="full" w="full">
<Box
sx={{
pos: 'absolute',
top: '50%',
transform: 'translate(0, -50%)',
insetInlineStart: 0,
}}
pos="absolute"
top="50%"
transform="translate(0, -50%)"
insetInlineStart={0}
>
{!isOnFirstImage && (
<InvIconButton
@ -52,12 +44,10 @@ const NextPrevImageButtons = () => {
)}
</Box>
<Box
sx={{
pos: 'absolute',
top: '50%',
transform: 'translate(0, -50%)',
insetInlineEnd: 0,
}}
pos="absolute"
top="50%"
transform="translate(0, -50%)"
insetInlineEnd={0}
>
{!isOnLastImage && (
<InvIconButton
@ -80,14 +70,7 @@ const NextPrevImageButtons = () => {
/>
)}
{isOnLastImage && areMoreImagesAvailable && isFetching && (
<Flex
sx={{
w: 16,
h: 16,
alignItems: 'center',
justifyContent: 'center',
}}
>
<Flex w={16} h={16} alignItems="center" justifyContent="center">
<Spinner opacity={0.5} size="xl" />
</Flex>
)}

View File

@ -69,7 +69,7 @@ const LoRASelect = () => {
noOptionsMessage={noOptionsMessage}
onChange={onChange}
data-testid="add-lora"
sx={{ w: 'full' }}
w="full"
/>
</InvControl>
);

View File

@ -41,13 +41,7 @@ export default function AddModels() {
{t('common.advanced')}
</InvButton>
</InvButtonGroup>
<Flex
sx={{
p: 4,
borderRadius: 4,
background: 'base.800',
}}
>
<Flex p={4} borderRadius={4} bg="base.800">
{addModelMode === 'simple' && <SimpleAddModels />}
{addModelMode === 'advanced' && <AdvancedAddModels />}
</Flex>

View File

@ -148,7 +148,7 @@ export default function AdvancedAddCheckpoint(
{!useCustomConfig ? (
<CheckpointConfigsSelect
required
sx={{ w: 'full' }}
w="full"
{...advancedAddCheckpointForm.getInputProps('config')}
/>
) : (

View File

@ -48,13 +48,7 @@ export default function AdvancedAddModels() {
<InvSelect value={value} options={options} onChange={handleChange} />
</InvControl>
<Flex
sx={{
p: 4,
borderRadius: 4,
bg: 'base.850',
}}
>
<Flex p={4} borderRadius={4} bg="base.850">
{advancedAddMode === 'diffusers' && <AdvancedAddDiffusers />}
{advancedAddMode === 'checkpoint' && <AdvancedAddCheckpoint />}
</Flex>

View File

@ -110,25 +110,18 @@ export default function FoundModelsList() {
return models.map((model) => {
return (
<Flex
sx={{
p: 4,
gap: 4,
alignItems: 'center',
borderRadius: 4,
bg: 'base.800',
}}
key={model}
p={4}
gap={4}
alignItems="center"
borderRadius={4}
bg="base.800"
>
<Flex w="100%" sx={{ flexDirection: 'column', minW: '25%' }}>
<InvText sx={{ fontWeight: 'semibold' }}>
<Flex w="full" minW="25%" flexDir="column">
<InvText fontWeight="semibold">
{model.split('\\').slice(-1)[0]}
</InvText>
<InvText
sx={{
fontSize: 'sm',
color: 'base.400',
}}
>
<InvText fontSize="sm" color="base.400">
{model}
</InvText>
</Flex>
@ -150,13 +143,11 @@ export default function FoundModelsList() {
</Flex>
) : (
<InvText
sx={{
fontWeight: 'semibold',
p: 2,
borderRadius: 4,
color: 'blue.100',
bg: 'blue.600',
}}
fontWeight="semibold"
p={2}
borderRadius={4}
color="blue.100"
bg="blue.600"
>
{t('common.installed')}
</InvText>
@ -174,15 +165,13 @@ export default function FoundModelsList() {
if (!foundModels || foundModels.length === 0) {
return (
<Flex
sx={{
w: 'full',
h: 'full',
justifyContent: 'center',
alignItems: 'center',
height: 96,
userSelect: 'none',
bg: 'base.900',
}}
w="full"
h="full"
justifyContent="center"
alignItems="center"
height={96}
userSelect="none"
bg="base.900"
>
<InvText variant="subtext">{t('modelManager.noModels')}</InvText>
</Flex>
@ -190,27 +179,15 @@ export default function FoundModelsList() {
}
return (
<Flex
sx={{
flexDirection: 'column',
gap: 2,
w: '100%',
minW: '50%',
}}
>
<Flex flexDirection="column" gap={2} w="100%" minW="50%">
<InvControl label={t('modelManager.search')}>
<InvInput onChange={handleSearchFilter} />
</InvControl>
<Flex p={2} gap={2}>
<InvText sx={{ fontWeight: 'semibold' }}>
<InvText fontWeight="semibold">
{t('modelManager.modelsFound')}: {foundModels.length}
</InvText>
<InvText
sx={{
fontWeight: 'semibold',
color: 'blue.200',
}}
>
<InvText fontWeight="semibold" color="blue.200">
{t('common.notInstalled')}: {filteredModels.length}
</InvText>
</Flex>

View File

@ -10,7 +10,6 @@ import type {
} from 'common/components/InvSelect/types';
import { InvText } from 'common/components/InvText/wrapper';
import { setAdvancedAddScanModel } from 'features/modelManager/store/modelManagerSlice';
import { motion } from 'framer-motion';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { FaTimes } from 'react-icons/fa';
@ -79,20 +78,15 @@ export default function ScanAdvancedAddModels() {
return (
<Box
as={motion.div}
initial={{ x: -100, opacity: 0 }}
animate={{ x: 0, opacity: 1, transition: { duration: 0.2 } }}
sx={{
display: 'flex',
flexDirection: 'column',
minWidth: '40%',
maxHeight: window.innerHeight - 300,
overflow: 'scroll',
p: 4,
gap: 4,
borderRadius: 4,
bg: 'base.800',
}}
display="flex"
flexDirection="column"
minWidth="40%"
maxHeight="calc(100vh - 300px)"
overflow="scroll"
p={4}
gap={4}
borderRadius={4}
bg="base.800"
>
<Flex justifyContent="space-between" alignItems="center">
<InvText size="xl" fontWeight="semibold">

View File

@ -10,12 +10,10 @@ export default function ScanModels() {
<SearchFolderForm />
<Flex gap={4}>
<Flex
sx={{
maxHeight: window.innerHeight - 300,
overflow: 'scroll',
gap: 4,
w: '100%',
}}
maxHeight="calc(100vh - 300px)"
overflow="scroll"
gap={4}
w="100%"
>
<FoundModelsList />
</Flex>

View File

@ -59,22 +59,13 @@ function SearchFolderForm() {
)}
style={{ width: '100%' }}
>
<Flex
sx={{
w: '100%',
gap: 2,
borderRadius: 4,
alignItems: 'center',
}}
>
<Flex w="100%" gap={2} borderRadius={4} alignItems="center">
<Flex w="100%" alignItems="center" gap={4} minH={12}>
<InvText
sx={{
fontSize: 'sm',
fontWeight: 'semibold',
color: 'base.300',
minW: 'max-content',
}}
fontSize="sm"
fontWeight="semibold"
color="base.300"
minW="max-content"
>
{t('common.folder')}
</InvText>
@ -86,15 +77,13 @@ function SearchFolderForm() {
/>
) : (
<Flex
sx={{
w: '100%',
p: 2,
px: 4,
bg: 'base.700',
borderRadius: 4,
fontSize: 'sm',
fontWeight: 'bold',
}}
w="100%"
p={2}
px={4}
bg="base.700"
borderRadius={4}
fontSize="sm"
fontWeight="bold"
>
{searchFolder}
</Flex>

View File

@ -259,13 +259,8 @@ export default function MergeModelsPanel() {
]);
return (
<Flex flexDirection="column" rowGap={4}>
<Flex
sx={{
flexDirection: 'column',
rowGap: 1,
}}
>
<Flex flexDir="column" gap={4}>
<Flex flexDir="column" gap={1}>
<InvText>{t('modelManager.modelMergeHeaderHelp1')}</InvText>
<InvText fontSize="sm" variant="subtext">
{t('modelManager.modelMergeHeaderHelp2')}
@ -273,28 +268,28 @@ export default function MergeModelsPanel() {
</Flex>
<Flex columnGap={4}>
<InvControl label={t('modelManager.modelType')} sx={{ w: 'full' }}>
<InvControl label={t('modelManager.modelType')} w="full">
<InvSelect
options={baseModelTypeSelectOptions}
value={valueBaseModel}
onChange={onChangeBaseModel}
/>
</InvControl>
<InvControl label={t('modelManager.modelOne')} sx={{ w: 'full' }}>
<InvControl label={t('modelManager.modelOne')} w="full">
<InvSelect
options={optionsModelOne}
value={valueModelOne}
onChange={onChangeModelOne}
/>
</InvControl>
<InvControl label={t('modelManager.modelTwo')} sx={{ w: 'full' }}>
<InvControl label={t('modelManager.modelTwo')} w="full">
<InvSelect
options={optionsModelTwo}
value={valueModelTwo}
onChange={onChangeModelTwo}
/>
</InvControl>
<InvControl label={t('modelManager.modelThree')} sx={{ w: 'full' }}>
<InvControl label={t('modelManager.modelThree')} w="full">
<InvSelect
options={optionsModelThree}
value={valueModelThree}
@ -312,13 +307,11 @@ export default function MergeModelsPanel() {
</InvControl>
<Flex
sx={{
flexDirection: 'column',
padding: 4,
borderRadius: 'base',
gap: 4,
bg: 'base.800',
}}
flexDirection="column"
padding={4}
borderRadius="base"
gap={4}
bg="base.800"
>
<InvControl
label={t('modelManager.alpha')}
@ -337,14 +330,7 @@ export default function MergeModelsPanel() {
</InvControl>
</Flex>
<Flex
sx={{
padding: 4,
borderRadius: 'base',
gap: 4,
bg: 'base.800',
}}
>
<Flex padding={4} gap={4} borderRadius="base" bg="base.800">
<InvText fontSize="sm" variant="subtext">
{t('modelManager.interpolationType')}
</InvText>
@ -382,13 +368,11 @@ export default function MergeModelsPanel() {
</Flex>
<Flex
sx={{
flexDirection: 'column',
padding: 4,
borderRadius: 'base',
gap: 4,
bg: 'base.900',
}}
flexDirection="column"
padding={4}
borderRadius="base"
gap={4}
bg="base.900"
>
<Flex columnGap={4}>
<InvText fontSize="sm" variant="subtext">

View File

@ -34,7 +34,7 @@ export default function ModelManagerPanel() {
const model = mainModel ? mainModel : loraModel;
return (
<Flex sx={{ gap: 8, w: 'full', h: 'full' }}>
<Flex gap={8} w="full" h="full">
<ModelList
selectedModelId={selectedModelId}
setSelectedModelId={setSelectedModelId}
@ -71,14 +71,12 @@ const ModelEdit = (props: ModelEditProps) => {
return (
<Flex
sx={{
w: 'full',
h: 'full',
justifyContent: 'center',
alignItems: 'center',
maxH: 96,
userSelect: 'none',
}}
w="full"
h="full"
justifyContent="center"
alignItems="center"
maxH={96}
userSelect="none"
>
<InvText variant="subtext">{t('modelManager.noModelSelected')}</InvText>
</Flex>

View File

@ -123,13 +123,7 @@ export default function CheckpointModelEdit(props: CheckpointModelEditProps) {
{![''].includes(model.base_model) ? (
<ModelConvert model={model} />
) : (
<Badge
sx={{
p: 2,
borderRadius: 4,
bg: 'error.400',
}}
>
<Badge p={2} borderRadius={4} bg="error.400">
{t('modelManager.conversionNotSupported')}
</Badge>
)}

View File

@ -272,15 +272,7 @@ const modelsFilter = <
const StyledModelContainer = memo((props: PropsWithChildren) => {
return (
<Flex
flexDirection="column"
gap={4}
borderRadius={4}
p={4}
sx={{
bg: 'base.800',
}}
>
<Flex flexDirection="column" gap={4} borderRadius={4} p={4} bg="base.800">
{props.children}
</Flex>
);
@ -301,7 +293,7 @@ const ModelListWrapper = memo((props: ModelListWrapperProps) => {
const { title, modelList, selected } = props;
return (
<StyledModelContainer>
<Flex sx={{ gap: 2, flexDir: 'column' }}>
<Flex gap={2} flexDir="column">
<InvText variant="subtext" fontSize="sm">
{title}
</InvText>

View File

@ -84,23 +84,16 @@ export default function ModelListItem(props: ModelListItemProps) {
]);
return (
<Flex sx={{ gap: 2, alignItems: 'center', w: 'full' }}>
<Flex gap={2} alignItems="center" w="full">
<Flex
as={InvButton}
isChecked={isSelected}
sx={{
justifyContent: 'start',
p: 2,
borderRadius: 'base',
w: 'full',
alignItems: 'center',
color: isSelected ? 'base.50' : 'base.100',
bg: isSelected ? 'blue.600' : 'base.850',
_hover: {
color: isSelected ? 'base.50' : 'base.100',
bg: isSelected ? 'blue.550' : 'base.700',
},
}}
variant={isSelected ? 'solid' : 'ghost'}
justifyContent="start"
p={2}
borderRadius="base"
w="full"
alignItems="center"
onClick={handleSelectModel}
>
<Flex gap={4} alignItems="center">

View File

@ -8,26 +8,17 @@ export default function SyncModels() {
return (
<Flex
sx={{
w: 'full',
p: 4,
borderRadius: 4,
gap: 4,
justifyContent: 'space-between',
alignItems: 'center',
bg: 'base.800',
}}
w="full"
p={4}
borderRadius={4}
gap={4}
justifyContent="space-between"
alignItems="center"
bg="base.800"
>
<Flex
sx={{
flexDirection: 'column',
gap: 2,
}}
>
<InvText sx={{ fontWeight: 'semibold' }}>
{t('modelManager.syncModels')}
</InvText>
<InvText fontSize="sm" sx={{ color: 'base.400' }}>
<Flex flexDirection="column" gap={2}>
<InvText fontWeight="semibold">{t('modelManager.syncModels')}</InvText>
<InvText fontSize="sm" variant="subtext">
{t('modelManager.syncModelsDesc')}
</InvText>
</Flex>

View File

@ -20,14 +20,12 @@ const NodeEditor = () => {
return (
<Flex
layerStyle="first"
sx={{
position: 'relative',
width: 'full',
height: 'full',
borderRadius: 'base',
alignItems: 'center',
justifyContent: 'center',
}}
position="relative"
width="full"
height="full"
borderRadius="base"
alignItems="center"
justifyContent="center"
>
<AnimatePresence>
{isReady && (
@ -71,15 +69,13 @@ const NodeEditor = () => {
>
<Flex
layerStyle="first"
sx={{
position: 'relative',
width: 'full',
height: 'full',
borderRadius: 'base',
alignItems: 'center',
justifyContent: 'center',
pointerEvents: 'none',
}}
position="relative"
width="full"
height="full"
borderRadius="base"
alignItems="center"
justifyContent="center"
pointerEvents="none"
>
<IAINoContentFallback
label={t('nodes.loadingNodes')}

View File

@ -230,9 +230,6 @@ const AddNodePopover = () => {
onChange={onChange}
onMenuClose={onClose}
onKeyDown={onKeyDown}
sx={{
width: 'full',
}}
/>
</InvPopoverBody>
</InvPopoverContent>

View File

@ -65,19 +65,15 @@ const InvocationCollapsedEdge = ({
{data?.count && data.count > 1 && (
<EdgeLabelRenderer>
<Flex
sx={{
position: 'absolute',
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
}}
position="absolute"
transform={`translate(-50%, -50%) translate(${labelX}px,${labelY}px)`}
className="nodrag nopan"
>
<Badge
variant="solid"
sx={{
bg: 'base.500',
opacity: isSelected ? 0.8 : 0.5,
boxShadow: 'base',
}}
bg="base.500"
opacity={isSelected ? 0.8 : 0.5}
boxShadow="base"
>
{data.count}
</Badge>

View File

@ -34,12 +34,10 @@ const CurrentImageNode = (props: NodeProps) => {
<Wrapper nodeProps={props}>
<Image
src={progressImage.dataURL}
sx={{
w: 'full',
h: 'full',
objectFit: 'contain',
borderRadius: 'base',
}}
w="full"
h="full"
objectFit="contain"
borderRadius="base"
/>
</Wrapper>
);
@ -83,38 +81,26 @@ const Wrapper = (props: PropsWithChildren<{ nodeProps: NodeProps }>) => {
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
className={DRAG_HANDLE_CLASSNAME}
sx={{
position: 'relative',
flexDirection: 'column',
}}
position="relative"
flexDirection="column"
>
<Flex
layerStyle="nodeHeader"
sx={{
borderTopRadius: 'base',
alignItems: 'center',
justifyContent: 'center',
h: 8,
}}
borderTopRadius="base"
alignItems="center"
justifyContent="center"
h={8}
>
<InvText
sx={{
fontSize: 'sm',
fontWeight: 'semibold',
color: 'base.200',
}}
>
<InvText fontSize="sm" fontWeight="semibold" color="base.200">
{t('nodes.currentImage')}
</InvText>
</Flex>
<Flex
layerStyle="nodeBody"
sx={{
w: 'full',
h: 'full',
borderBottomRadius: 'base',
p: 2,
}}
w="full"
h="full"
borderBottomRadius="base"
p={2}
>
{props.children}
{isHovering && (

View File

@ -38,16 +38,14 @@ const InvocationNode = ({ nodeId, isOpen, label, type, selected }: Props) => {
<>
<Flex
layerStyle="nodeBody"
sx={{
flexDirection: 'column',
w: 'full',
h: 'full',
py: 2,
gap: 1,
borderBottomRadius: withFooter ? 0 : 'base',
}}
flexDirection="column"
w="full"
h="full"
py={2}
gap={1}
borderBottomRadius={withFooter ? 0 : 'base'}
>
<Flex sx={{ flexDir: 'column', px: 2, w: 'full', h: 'full' }}>
<Flex flexDir="column" px={2} w="full" h="full">
<Grid gridTemplateColumns="1fr auto" gridAutoRows="1fr">
{inputConnectionFieldNames.map((fieldName, i) => (
<GridItem

View File

@ -26,11 +26,9 @@ const InvocationNodeClassificationIcon = ({ nodeId }: Props) => {
>
<Icon
as={getIcon(classification)}
sx={{
display: 'block',
boxSize: 4,
color: 'base.400',
}}
display="block"
boxSize={4}
color="base.400"
/>
</InvTooltip>
);

View File

@ -18,14 +18,12 @@ const InvocationNodeFooter = ({ nodeId }: Props) => {
<Flex
className={DRAG_HANDLE_CLASSNAME}
layerStyle="nodeFooter"
sx={{
w: 'full',
borderBottomRadius: 'base',
px: 2,
py: 0,
h: 8,
justifyContent: 'space-between',
}}
w="full"
borderBottomRadius="base"
px={2}
py={0}
h={8}
justifyContent="space-between"
>
{isCacheEnabled && <UseCacheCheckbox nodeId={nodeId} />}
{hasImageOutput && <SaveToGalleryCheckbox nodeId={nodeId} />}

View File

@ -20,15 +20,13 @@ const InvocationNodeHeader = ({ nodeId, isOpen }: Props) => {
return (
<Flex
layerStyle="nodeHeader"
sx={{
borderTopRadius: 'base',
borderBottomRadius: isOpen ? 0 : 'base',
alignItems: 'center',
justifyContent: 'space-between',
h: 8,
textAlign: 'center',
color: 'base.200',
}}
borderTopRadius="base"
borderBottomRadius={isOpen ? 0 : 'base'}
alignItems="center"
justifyContent="space-between"
h={8}
textAlign="center"
color="base.200"
>
<NodeCollapseButton nodeId={nodeId} isOpen={isOpen} />
<InvocationNodeClassificationIcon nodeId={nodeId} />

View File

@ -25,12 +25,10 @@ const InvocationNodeInfoIcon = ({ nodeId }: Props) => {
>
<Icon
as={FaInfoCircle}
sx={{
display: 'block',
boxSize: 4,
w: 8,
color: needsUpdate ? 'error.400' : 'base.400',
}}
display="block"
boxSize={4}
w={8}
color={needsUpdate ? 'error.400' : 'base.400'}
/>
</InvTooltip>
);
@ -66,7 +64,7 @@ const TooltipContent = memo(({ nodeId }: { nodeId: string }) => {
if (!data.version) {
return (
<InvText as="span" sx={{ color: 'error.500' }}>
<InvText as="span" color="error.500">
{t('nodes.versionUnknown')}
</InvText>
);
@ -74,7 +72,7 @@ const TooltipContent = memo(({ nodeId }: { nodeId: string }) => {
if (!nodeTemplate.version) {
return (
<InvText as="span" sx={{ color: 'error.500' }}>
<InvText as="span" color="error.500">
{t('nodes.version')} {data.version} ({t('nodes.unknownTemplate')})
</InvText>
);
@ -82,7 +80,7 @@ const TooltipContent = memo(({ nodeId }: { nodeId: string }) => {
if (compare(data.version, nodeTemplate.version, '<')) {
return (
<InvText as="span" sx={{ color: 'error.500' }}>
<InvText as="span" color="error.500">
{t('nodes.version')} {data.version} ({t('nodes.updateNode')})
</InvText>
);
@ -90,7 +88,7 @@ const TooltipContent = memo(({ nodeId }: { nodeId: string }) => {
if (compare(data.version, nodeTemplate.version, '>')) {
return (
<InvText as="span" sx={{ color: 'error.500' }}>
<InvText as="span" color="error.500">
{t('nodes.version')} {data.version} ({t('nodes.updateApp')})
</InvText>
);
@ -104,16 +102,12 @@ const TooltipContent = memo(({ nodeId }: { nodeId: string }) => {
}, [data, nodeTemplate, t]);
if (!isInvocationNodeData(data)) {
return (
<InvText sx={{ fontWeight: 'semibold' }}>
{t('nodes.unknownNode')}
</InvText>
);
return <InvText fontWeight="semibold">{t('nodes.unknownNode')}</InvText>;
}
return (
<Flex sx={{ flexDir: 'column' }}>
<InvText as="span" sx={{ fontWeight: 'semibold' }}>
<Flex flexDir="column">
<InvText as="span" fontWeight="semibold">
{title}
</InvText>
{nodeTemplate?.nodePack && (
@ -121,7 +115,7 @@ const TooltipContent = memo(({ nodeId }: { nodeId: string }) => {
{t('nodes.nodePack')}: {nodeTemplate.nodePack}
</InvText>
)}
<InvText sx={{ opacity: 0.7, fontStyle: 'oblique 5deg' }}>
<InvText opacity={0.7} fontStyle="oblique 5deg">
{nodeTemplate?.description}
</InvText>
{versionComponent}

View File

@ -1,3 +1,4 @@
import type { SystemStyleObject } from '@chakra-ui/react';
import { Badge, CircularProgress, Flex, Icon, Image } from '@chakra-ui/react';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
@ -16,7 +17,7 @@ type Props = {
};
const iconBoxSize = 3;
const circleStyles = {
const circleStyles: SystemStyleObject = {
circle: {
transitionProperty: 'none',
transitionDuration: '0s',
@ -47,12 +48,10 @@ const InvocationNodeStatusIndicator = ({ nodeId }: Props) => {
>
<Flex
className={DRAG_HANDLE_CLASSNAME}
sx={{
w: 5,
h: 'full',
alignItems: 'center',
justifyContent: 'flex-end',
}}
w={5}
h="full"
alignItems="center"
justifyContent="flex-end"
>
<StatusIcon nodeExecutionState={nodeExecutionState} />
</Flex>
@ -75,16 +74,16 @@ const TooltipLabel = memo(({ nodeExecutionState }: TooltipLabelProps) => {
if (status === zNodeStatus.enum.IN_PROGRESS) {
if (progressImage) {
return (
<Flex sx={{ pos: 'relative', pt: 1.5, pb: 0.5 }}>
<Flex pos="relative" pt={1.5} pb={0.5}>
<Image
src={progressImage.dataURL}
sx={{ w: 32, h: 32, borderRadius: 'base', objectFit: 'contain' }}
w={32}
h={32}
borderRadius="base"
objectFit="contain"
/>
{progress !== null && (
<Badge
variant="solid"
sx={{ pos: 'absolute', top: 2.5, insetInlineEnd: 1 }}
>
<Badge variant="solid" pos="absolute" top={2.5} insetInlineEnd={1}>
{Math.round(progress * 100)}%
</Badge>
)}
@ -123,15 +122,7 @@ type StatusIconProps = {
const StatusIcon = memo((props: StatusIconProps) => {
const { progress, status } = props.nodeExecutionState;
if (status === zNodeStatus.enum.PENDING) {
return (
<Icon
as={FaEllipsisH}
sx={{
boxSize: iconBoxSize,
color: 'base.300',
}}
/>
);
return <Icon as={FaEllipsisH} boxSize={iconBoxSize} color="base.300" />;
}
if (status === zNodeStatus.enum.IN_PROGRESS) {
return progress === null ? (
@ -153,26 +144,10 @@ const StatusIcon = memo((props: StatusIconProps) => {
);
}
if (status === zNodeStatus.enum.COMPLETED) {
return (
<Icon
as={FaCheck}
sx={{
boxSize: iconBoxSize,
color: 'ok.300',
}}
/>
);
return <Icon as={FaCheck} boxSize={iconBoxSize} color="ok.300" />;
}
if (status === zNodeStatus.enum.FAILED) {
return (
<Icon
as={FaExclamation}
sx={{
boxSize: iconBoxSize,
color: 'error.300',
}}
/>
);
return <Icon as={FaExclamation} boxSize={iconBoxSize} color="error.300" />;
}
return null;
});

View File

@ -29,40 +29,29 @@ const InvocationNodeUnknownFallback = ({
<Flex
className={DRAG_HANDLE_CLASSNAME}
layerStyle="nodeHeader"
sx={{
borderTopRadius: 'base',
borderBottomRadius: isOpen ? 0 : 'base',
alignItems: 'center',
h: 8,
fontWeight: 'semibold',
fontSize: 'sm',
}}
borderTopRadius="base"
borderBottomRadius={isOpen ? 0 : 'base'}
alignItems="center"
h={8}
fontWeight="semibold"
fontSize="sm"
>
<NodeCollapseButton nodeId={nodeId} isOpen={isOpen} />
<InvText
sx={{
w: 'full',
textAlign: 'center',
pe: 8,
color: 'error.300',
}}
>
<InvText w="full" textAlign="center" pe={8} color="error.300">
{label ? `${label} (${type})` : type}
</InvText>
</Flex>
{isOpen && (
<Flex
layerStyle="nodeBody"
sx={{
userSelect: 'auto',
flexDirection: 'column',
w: 'full',
h: 'full',
p: 4,
gap: 1,
borderBottomRadius: 'base',
fontSize: 'sm',
}}
userSelect="auto"
flexDirection="column"
w="full"
h="full"
p={4}
gap={1}
borderBottomRadius="base"
fontSize="sm"
>
<Flex gap={2} flexDir="column">
<InvText as="span">

View File

@ -1,3 +1,4 @@
import type { SystemStyleObject } from '@chakra-ui/react';
import {
Editable,
EditableInput,
@ -78,51 +79,28 @@ const EditableFieldTitle = forwardRef((props: Props, ref) => {
>
<Flex
ref={ref}
sx={{
position: 'relative',
overflow: 'hidden',
alignItems: 'center',
justifyContent: 'flex-start',
gap: 1,
h: 'full',
}}
position="relative"
overflow="hidden"
alignItems="center"
justifyContent="flex-start"
gap={1}
h="full"
>
<Editable
value={localTitle}
onChange={handleChange}
onSubmit={handleSubmit}
as={Flex}
sx={{
position: 'relative',
alignItems: 'center',
h: 'full',
}}
position="relative"
alignItems="center"
h="full"
>
<EditablePreview
sx={{
p: 0,
fontWeight: isMissingInput ? 'bold' : 'normal',
textAlign: 'left',
_hover: {
fontWeight: 'semibold !important',
},
}}
fontWeight={isMissingInput ? 'bold' : 'normal'}
sx={editablePreviewStyles}
noOfLines={1}
/>
<EditableInput
className="nodrag"
sx={{
p: 0,
w: 'full',
fontWeight: 'semibold',
color: 'base.100',
_focusVisible: {
p: 0,
textAlign: 'left',
boxShadow: 'none',
},
}}
/>
<EditableInput className="nodrag" sx={editableInputStyles} />
<EditableControls />
</Editable>
</Flex>
@ -130,6 +108,25 @@ const EditableFieldTitle = forwardRef((props: Props, ref) => {
);
});
const editableInputStyles: SystemStyleObject = {
p: 0,
w: 'full',
fontWeight: 'semibold',
color: 'base.100',
_focusVisible: {
p: 0,
textAlign: 'left',
boxShadow: 'none',
},
};
const editablePreviewStyles: SystemStyleObject = {
p: 0,
textAlign: 'left',
_hover: {
fontWeight: 'semibold !important',
},
};
export default memo(EditableFieldTitle);
const EditableControls = memo(() => {

View File

@ -103,10 +103,7 @@ const FieldContextMenu = ({ nodeId, fieldName, kind, children }: Props) => {
const renderMenuFunc = useCallback(
() =>
!menuItems.length ? null : (
<InvMenuList
sx={{ visibility: 'visible !important' }}
onContextMenu={skipEvent}
>
<InvMenuList visibility="visible" onContextMenu={skipEvent}>
<InvMenuGroup
title={label || fieldTemplateTitle || t('nodes.unknownField')}
>

View File

@ -19,17 +19,15 @@ const FieldTitle = forwardRef((props: Props, ref) => {
return (
<Flex
ref={ref}
sx={{
position: 'relative',
overflow: 'hidden',
alignItems: 'center',
justifyContent: 'flex-start',
gap: 1,
h: 'full',
w: 'full',
}}
position="relative"
overflow="hidden"
alignItems="center"
justifyContent="flex-start"
gap={1}
h="full"
w="full"
>
<InvText sx={{ fontWeight: isMissingInput ? 'bold' : 'normal' }}>
<InvText fontWeight={isMissingInput ? 'bold' : 'normal'}>
{label || fieldTemplateTitle}
</InvText>
</Flex>

View File

@ -43,10 +43,10 @@ const FieldTooltipContent = ({ nodeId, fieldName, kind }: Props) => {
}, [field, fieldTemplate, t]);
return (
<Flex sx={{ flexDir: 'column' }}>
<InvText sx={{ fontWeight: 'semibold' }}>{fieldTitle}</InvText>
<Flex flexDir="column">
<InvText fontWeight="semibold">{fieldTitle}</InvText>
{fieldTemplate && (
<InvText sx={{ opacity: 0.7, fontStyle: 'oblique 5deg' }}>
<InvText opacity={0.7} fontStyle="oblique 5deg">
{fieldTemplate.description}
</InvText>
)}

View File

@ -292,13 +292,7 @@ const InputFieldRenderer = ({ nodeId, fieldName }: InputFieldProps) => {
return (
<Box p={1}>
<InvText
sx={{
fontSize: 'sm',
fontWeight: 'semibold',
color: 'error.300',
}}
>
<InvText fontSize="sm" fontWeight="semibold" color="error.300">
{t('nodes.unknownFieldType', { type: fieldInstance?.type.name })}
</InvText>
</Box>

View File

@ -97,16 +97,14 @@ type OutputFieldWrapperProps = PropsWithChildren<{
const OutputFieldWrapper = memo(
({ shouldDim, children }: OutputFieldWrapperProps) => (
<Flex
sx={{
position: 'relative',
minH: 8,
py: 0.5,
alignItems: 'center',
opacity: shouldDim ? 0.5 : 1,
transitionProperty: 'opacity',
transitionDuration: '0.1s',
justifyContent: 'flex-end',
}}
position="relative"
minH={8}
py={0.5}
alignItems="center"
opacity={shouldDim ? 0.5 : 1}
transitionProperty="opacity"
transitionDuration="0.1s"
justifyContent="flex-end"
>
{children}
</Flex>

View File

@ -56,7 +56,6 @@ const IPAdapterModelFieldInputComponent = (
placeholder="Pick one"
options={options}
onChange={onChange}
sx={{ width: '100%' }}
/>
</InvControl>
</InvTooltip>

View File

@ -78,12 +78,10 @@ const ImageFieldInputComponent = (
return (
<Flex
className="nodrag"
sx={{
w: 'full',
h: 'full',
alignItems: 'center',
justifyContent: 'center',
}}
w="full"
h="full"
alignItems="center"
justifyContent="center"
>
<IAIDndImage
imageDTO={imageDTO}

View File

@ -48,7 +48,7 @@ const MainModelFieldInputComponent = (props: Props) => {
});
return (
<Flex sx={{ w: 'full', alignItems: 'center', gap: 2 }}>
<Flex w="full" alignItems="center" gap={2}>
<InvControl
className="nowheel nodrag"
isDisabled={!options.length}

View File

@ -48,7 +48,7 @@ const RefinerModelFieldInputComponent = (props: Props) => {
});
return (
<Flex sx={{ w: 'full', alignItems: 'center', gap: 2 }}>
<Flex w="full" alignItems="center" gap={2}>
<InvControl
className="nowheel nodrag"
isDisabled={!options.length}

View File

@ -48,7 +48,7 @@ const SDXLMainModelFieldInputComponent = (props: Props) => {
});
return (
<Flex sx={{ w: 'full', alignItems: 'center', gap: 2 }}>
<Flex w="full" alignItems="center" gap={2}>
<InvControl
className="nowheel nodrag"
isDisabled={!options.length}

View File

@ -57,7 +57,6 @@ const T2IAdapterModelFieldInputComponent = (
placeholder="Pick one"
options={options}
onChange={onChange}
sx={{ width: '100%' }}
/>
</InvControl>
</InvTooltip>

View File

@ -48,7 +48,7 @@ const VAEModelFieldInputComponent = (props: Props) => {
});
return (
<Flex sx={{ w: 'full', alignItems: 'center', gap: 2 }}>
<Flex w="full" alignItems="center" gap={2}>
<InvControl
className="nowheel nodrag"
isDisabled={!options.length}

View File

@ -25,13 +25,11 @@ const NotesNode = (props: NodeProps<NotesNodeData>) => {
<NodeWrapper nodeId={nodeId} selected={selected}>
<Flex
layerStyle="nodeHeader"
sx={{
borderTopRadius: 'base',
borderBottomRadius: isOpen ? 0 : 'base',
alignItems: 'center',
justifyContent: 'space-between',
h: 8,
}}
borderTopRadius="base"
borderBottomRadius={isOpen ? 0 : 'base'}
alignItems="center"
justifyContent="space-between"
h={8}
>
<NodeCollapseButton nodeId={nodeId} isOpen={isOpen} />
<NodeTitle nodeId={nodeId} title="Notes" />
@ -42,26 +40,21 @@ const NotesNode = (props: NodeProps<NotesNodeData>) => {
<Flex
layerStyle="nodeBody"
className="nopan"
sx={{
cursor: 'auto',
flexDirection: 'column',
borderBottomRadius: 'base',
w: 'full',
h: 'full',
p: 2,
gap: 1,
}}
cursor="auto"
flexDirection="column"
borderBottomRadius="base"
w="full"
h="full"
p={2}
gap={1}
>
<Flex
className="nopan"
sx={{ flexDir: 'column', w: 'full', h: 'full' }}
>
<Flex className="nopan" w="full" h="full" flexDir="column">
<InvTextarea
value={notes}
onChange={handleChange}
rows={8}
resize="none"
sx={{ fontSize: 'xs' }}
fontSize="sm"
/>
</Flex>
</Flex>

View File

@ -24,23 +24,15 @@ const NodeCollapseButton = ({ nodeId, isOpen }: Props) => {
className="nodrag"
onClick={handleClick}
aria-label="Minimize"
sx={{
minW: 8,
w: 8,
h: 8,
color: 'base.500',
_hover: {
color: 'base.300',
},
}}
minW={8}
w={8}
h={8}
variant="link"
icon={
<ChevronUpIcon
sx={{
transform: isOpen ? 'rotate(0deg)' : 'rotate(180deg)',
transitionProperty: 'common',
transitionDuration: 'normal',
}}
transform={isOpen ? 'rotate(0deg)' : 'rotate(180deg)'}
transitionProperty="common"
transitionDuration="normal"
/>
}
/>

View File

@ -1,3 +1,4 @@
import type { SystemStyleObject } from '@chakra-ui/react';
import {
Box,
Editable,
@ -50,46 +51,28 @@ const NodeTitle = ({ nodeId, title }: Props) => {
return (
<Flex
sx={{
overflow: 'hidden',
w: 'full',
h: 'full',
alignItems: 'center',
justifyContent: 'center',
cursor: 'text',
}}
overflow="hidden"
w="full"
h="full"
alignItems="center"
justifyContent="center"
cursor="text"
>
<Editable
as={Flex}
value={localTitle}
onChange={handleChange}
onSubmit={handleSubmit}
sx={{
alignItems: 'center',
position: 'relative',
w: 'full',
h: 'full',
}}
alignItems="center"
position="relative"
w="full"
h="full"
>
<EditablePreview
fontSize="sm"
sx={{
p: 0,
w: 'full',
}}
noOfLines={1}
/>
<EditablePreview fontSize="sm" p={0} w="full" noOfLines={1} />
<EditableInput
className="nodrag"
fontSize="sm"
sx={{
p: 0,
fontWeight: 'bold',
_focusVisible: {
p: 0,
boxShadow: 'none',
},
}}
sx={editableInputStyles}
/>
<EditableControls />
</Editable>
@ -120,13 +103,19 @@ function EditableControls() {
<Box
className={DRAG_HANDLE_CLASSNAME}
onDoubleClick={handleDoubleClick}
sx={{
position: 'absolute',
w: 'full',
h: 'full',
top: 0,
cursor: 'grab',
}}
position="absolute"
w="full"
h="full"
top={0}
cursor="grab"
/>
);
}
const editableInputStyles: SystemStyleObject = {
p: 0,
fontWeight: 'bold',
_focusVisible: {
p: 0,
},
};

View File

@ -18,7 +18,7 @@ import { memo, useCallback, useMemo } from 'react';
type NodeWrapperProps = PropsWithChildren & {
nodeId: string;
selected: boolean;
width?: NonNullable<ChakraProps['sx']>['w'];
width?: ChakraProps['w'];
};
const NodeWrapper = (props: NodeWrapperProps) => {
@ -65,45 +65,39 @@ const NodeWrapper = (props: NodeWrapperProps) => {
onMouseEnter={handleMouseOver}
onMouseLeave={handleMouseOut}
className={DRAG_HANDLE_CLASSNAME}
sx={{
h: 'full',
position: 'relative',
borderRadius: 'base',
w: width ?? NODE_WIDTH,
transitionProperty: 'common',
transitionDuration: '0.1s',
cursor: 'grab',
opacity,
}}
h="full"
position="relative"
borderRadius="base"
w={width ? width : NODE_WIDTH}
transitionProperty="common"
transitionDuration="0.1s"
cursor="grab"
opacity={opacity}
>
<Box
sx={{
position: 'absolute',
top: 0,
insetInlineEnd: 0,
bottom: 0,
insetInlineStart: 0,
borderRadius: 'base',
pointerEvents: 'none',
shadow: `${shadowsXl}, ${shadowsBase}, ${shadowsBase}`,
zIndex: -1,
}}
position="absolute"
top={0}
insetInlineEnd={0}
bottom={0}
insetInlineStart={0}
borderRadius="base"
pointerEvents="none"
shadow={`${shadowsXl}, ${shadowsBase}, ${shadowsBase}`}
zIndex={-1}
/>
<Box
sx={{
position: 'absolute',
top: 0,
insetInlineEnd: 0,
bottom: 0,
insetInlineStart: 0,
borderRadius: 'md',
pointerEvents: 'none',
transitionProperty: 'common',
transitionDuration: '0.1s',
opacity: 0.7,
shadow: isInProgress ? nodeInProgress : undefined,
zIndex: -1,
}}
position="absolute"
top={0}
insetInlineEnd={0}
bottom={0}
insetInlineStart={0}
borderRadius="md"
pointerEvents="none"
transitionProperty="common"
transitionDuration="0.1s"
opacity={0.7}
shadow={isInProgress ? nodeInProgress : undefined}
zIndex={-1}
/>
{children}
<NodeSelectionOverlay isSelected={selected} isHovered={isMouseOverNode} />

View File

@ -5,7 +5,7 @@ import NodeOpacitySlider from './NodeOpacitySlider';
import ViewportControls from './ViewportControls';
const BottomLeftPanel = () => (
<Flex sx={{ gap: 2, position: 'absolute', bottom: 2, insetInlineStart: 2 }}>
<Flex gap={2} position="absolute" bottom={2} insetInlineStart={2}>
<ViewportControls />
<NodeOpacitySlider />
</Flex>

View File

@ -1,3 +1,4 @@
import type { SystemStyleObject } from '@chakra-ui/react';
import { chakra, Flex } from '@chakra-ui/react';
import type { RootState } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
@ -6,26 +7,28 @@ import { MiniMap } from 'reactflow';
const ChakraMiniMap = chakra(MiniMap);
const minimapStyles: SystemStyleObject = {
m: '0 !important',
borderRadius: 'base',
backgroundColor: 'base.500 !important',
svg: {
borderRadius: 'inherit',
},
};
const MinimapPanel = () => {
const shouldShowMinimapPanel = useAppSelector(
(state: RootState) => state.nodes.shouldShowMinimapPanel
);
return (
<Flex sx={{ gap: 2, position: 'absolute', bottom: 2, insetInlineEnd: 2 }}>
<Flex gap={2} position="absolute" bottom={2} insetInlineEnd={2}>
{shouldShowMinimapPanel && (
<ChakraMiniMap
pannable
zoomable
nodeBorderRadius={15}
sx={{
m: '0 !important',
borderRadius: 'base',
backgroundColor: 'base.500 !important',
svg: {
borderRadius: 'inherit',
},
}}
sx={minimapStyles}
nodeColor="var(--invokeai-colors-blue-600)"
maskColor="var(--invokeai-colors-blackAlpha-600)"
/>

View File

@ -13,15 +13,13 @@ const TopCenterPanel = () => {
return (
<Flex
sx={{
gap: 2,
top: 2,
left: 2,
right: 2,
position: 'absolute',
alignItems: 'center',
pointerEvents: 'none',
}}
gap={2}
top={2}
left={2}
right={2}
position="absolute"
alignItems="center"
pointerEvents="none"
>
<AddNodeButton />
<UpdateNodesButton />

View File

@ -9,7 +9,7 @@ const TopRightPanel = () => {
useFeatureStatus('workflowLibrary').isFeatureEnabled;
return (
<Flex sx={{ gap: 2, position: 'absolute', top: 2, insetInlineEnd: 2 }}>
<Flex gap={2} position="absolute" top={2} insetInlineEnd={2}>
{isWorkflowLibraryEnabled && <WorkflowLibraryButton />}
<WorkflowLibraryMenu />
</Flex>

View File

@ -104,13 +104,7 @@ const WorkflowEditorSettings = ({ children }: Props) => {
<InvModalHeader>{t('nodes.workflowSettings')}</InvModalHeader>
<InvModalCloseButton />
<InvModalBody>
<Flex
sx={{
flexDirection: 'column',
gap: 4,
py: 4,
}}
>
<Flex flexDirection="column" gap={4} py={4}>
<Heading size="sm">{t('parameters.general')}</Heading>
<InvControl
label={t('nodes.animatedEdges')}

View File

@ -24,7 +24,7 @@ const NodeEditorPanelGroup = () => {
}, []);
return (
<Flex sx={{ flexDir: 'column', gap: 2, height: '100%', width: '100%' }}>
<Flex w="full" h="full" gap={2} flexDir="column">
<QueueControls />
<PanelGroup
ref={panelGroupRef}

View File

@ -52,24 +52,16 @@ const InspectorOutputsTab = () => {
}
return (
<Box
sx={{
position: 'relative',
w: 'full',
h: 'full',
}}
>
<Box position="relative" w="full" h="full">
<ScrollableContent>
<Flex
sx={{
position: 'relative',
flexDir: 'column',
alignItems: 'flex-start',
p: 1,
gap: 2,
h: 'full',
w: 'full',
}}
position="relative"
flexDir="column"
alignItems="flex-start"
p={1}
gap={2}
h="full"
w="full"
>
{template?.outputType === 'image_output' ? (
nes.outputs.map((result, i) => (

View File

@ -19,19 +19,14 @@ const InspectorPanel = () => {
return (
<Flex
layerStyle="first"
sx={{
flexDir: 'column',
w: 'full',
h: 'full',
borderRadius: 'base',
p: 2,
gap: 2,
}}
flexDir="column"
w="full"
h="full"
borderRadius="base"
p={2}
gap={2}
>
<Tabs
variant="line"
sx={{ display: 'flex', flexDir: 'column', w: 'full', h: 'full' }}
>
<Tabs variant="line" display="flex" flexDir="column" w="full" h="full">
<TabList>
<Tab>{t('common.details')}</Tab>
<Tab>{t('common.outputs')}</Tab>

View File

@ -87,16 +87,9 @@ const WorkflowGeneralTab = () => {
return (
<ScrollableContent>
<Flex
sx={{
flexDir: 'column',
alignItems: 'flex-start',
gap: 2,
h: 'full',
}}
>
<Flex flexDir="column" alignItems="flex-start" gap={2} h="full">
<InvControlGroup orientation="vertical">
<Flex sx={{ gap: 2, w: 'full' }}>
<Flex gap={2} w="full">
<InvControl label={t('nodes.workflowName')}>
<InvInput value={name} onChange={handleChangeName} />
</InvControl>
@ -104,7 +97,7 @@ const WorkflowGeneralTab = () => {
<InvInput value={version} onChange={handleChangeVersion} />
</InvControl>
</Flex>
<Flex sx={{ gap: 2, w: 'full' }}>
<Flex gap={2} w="full">
<InvControl label={t('nodes.workflowAuthor')}>
<InvInput value={author} onChange={handleChangeAuthor} />
</InvControl>

View File

@ -9,14 +9,7 @@ const WorkflowJSONTab = () => {
const { t } = useTranslation();
return (
<Flex
sx={{
flexDir: 'column',
alignItems: 'flex-start',
gap: 2,
h: 'full',
}}
>
<Flex flexDir="column" alignItems="flex-start" gap={2} h="full">
<DataViewer data={workflow} label={t('nodes.workflow')} />
</Flex>
);

View File

@ -19,24 +19,16 @@ const WorkflowLinearTab = () => {
const { t } = useTranslation();
return (
<Box
sx={{
position: 'relative',
w: 'full',
h: 'full',
}}
>
<Box position="relative" w="full" h="full">
<ScrollableContent>
<Flex
sx={{
position: 'relative',
flexDir: 'column',
alignItems: 'flex-start',
p: 1,
gap: 2,
h: 'full',
w: 'full',
}}
position="relative"
flexDir="column"
alignItems="flex-start"
p={1}
gap={2}
h="full"
w="full"
>
{fields.length ? (
fields.map(({ nodeId, fieldName }) => (

Some files were not shown because too many files have changed in this diff Show More