feat(ui): wip img2img ui

This commit is contained in:
psychedelicious 2023-04-24 14:48:46 +10:00
parent 38474fa9d4
commit 568f0aad71
23 changed files with 569 additions and 170 deletions

View File

@ -97,7 +97,8 @@
"statusMergedModels": "Models Merged",
"pinOptionsPanel": "Pin Options Panel",
"loading": "Loading",
"loadingInvokeAI": "Loading Invoke AI"
"loadingInvokeAI": "Loading Invoke AI",
"random": "Random"
},
"gallery": {
"generations": "Generations",

View File

@ -34,10 +34,9 @@ const IAISwitch = (props: Props) => {
display="flex"
gap={4}
alignItems="center"
justifyContent="space-between"
{...formControlProps}
>
<FormLabel my={1} {...formLabelProps}>
<FormLabel my={1} flexGrow={1} {...formLabelProps}>
{label}
</FormLabel>
<Switch {...rest} />

View File

@ -37,28 +37,6 @@ const ImageToImageOverlay = ({
position: 'absolute',
}}
>
<ButtonGroup
sx={{
position: 'absolute',
top: 0,
right: 0,
p: 2,
}}
>
<IAIIconButton
size="sm"
isDisabled={!isImageToImageEnabled}
icon={<FaUndo />}
aria-label={t('accessibility.reset')}
onClick={handleResetInitialImage}
/>
<IAIIconButton
size="sm"
isDisabled={!isImageToImageEnabled}
icon={<FaUpload />}
aria-label={t('common.upload')}
/>
</ButtonGroup>
<Flex
sx={{
position: 'absolute',

View File

@ -0,0 +1,62 @@
import {
Box,
ButtonGroup,
Collapse,
Flex,
Heading,
HStack,
Image,
Spacer,
Text,
useDisclosure,
VStack,
} from '@chakra-ui/react';
import { motion } from 'framer-motion';
import IAIButton from 'common/components/IAIButton';
import ImageFit from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageFit';
import ImageToImageStrength from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageStrength';
import IAIIconButton from 'common/components/IAIIconButton';
import { useTranslation } from 'react-i18next';
import { FaUndo, FaUpload } from 'react-icons/fa';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { RootState } from 'app/store';
import { useCallback } from 'react';
import { clearInitialImage } from 'features/parameters/store/generationSlice';
const ImageToImageSettingsHeader = () => {
const isImageToImageEnabled = useAppSelector(
(state: RootState) => state.generation.isImageToImageEnabled
);
const dispatch = useAppDispatch();
const { t } = useTranslation();
const handleResetInitialImage = useCallback(() => {
dispatch(clearInitialImage());
}, [dispatch]);
return (
<Flex w="full" alignItems="center">
<Text size="sm" fontWeight={500} color="base.300">
Image to Image
</Text>
<Spacer />
<ButtonGroup>
<IAIIconButton
size="sm"
icon={<FaUndo />}
aria-label={t('accessibility.reset')}
onClick={handleResetInitialImage}
/>
<IAIIconButton
size="sm"
icon={<FaUpload />}
aria-label={t('common.upload')}
/>
</ButtonGroup>
</Flex>
);
};
export default ImageToImageSettingsHeader;

View File

@ -3,7 +3,17 @@ import { FaImage } from 'react-icons/fa';
const SelectImagePlaceholder = () => {
return (
<Flex sx={{ h: 36, alignItems: 'center', justifyContent: 'center' }}>
<Flex
sx={{
w: 'full',
h: 'full',
bg: 'base.800',
borderRadius: 'base',
alignItems: 'center',
justifyContent: 'center',
aspectRatio: '1/1',
}}
>
<Icon color="base.400" boxSize={32} as={FaImage}></Icon>
</Flex>
);

View File

@ -1,7 +1,14 @@
import { createSelector } from '@reduxjs/toolkit';
import { isEqual } from 'lodash';
import { ButtonGroup, Flex, FlexProps, Link, useToast } from '@chakra-ui/react';
import {
ButtonGroup,
Flex,
FlexProps,
FormControl,
Link,
useToast,
} from '@chakra-ui/react';
import { runESRGAN, runFacetool } from 'app/socketio/actions';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAIButton from 'common/components/IAIButton';
@ -446,6 +453,7 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
<IAIPopover
triggerComponent={
<IAIIconButton
isDisabled={!selectedImage}
aria-label={`${t('parameters.sendTo')}...`}
icon={<FaShareAlt />}
/>
@ -487,7 +495,7 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
{t('parameters.copyImageToLink')}
</IAIButton>
<Link download={true} href={getUrl(selectedImage?.url)}>
<Link download={true} href={getUrl(selectedImage?.url ?? '')}>
<IAIButton leftIcon={<FaDownload />} size="sm" w="100%">
{t('parameters.downloadImage')}
</IAIButton>

View File

@ -1,8 +1,17 @@
import { Flex, Icon } from '@chakra-ui/react';
import { Box, Collapse, Flex, Icon, useDisclosure } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/storeHooks';
import { systemSelector } from 'features/system/store/systemSelectors';
import IAIButton from 'common/components/IAIButton';
import ImageToImageSettings from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageSettings';
import { isEqual } from 'lodash';
import { useState } from 'react';
import {
AnimatePresence,
motion,
useMotionValue,
useTransform,
} from 'framer-motion';
import { MdPhoto } from 'react-icons/md';
import {
@ -33,31 +42,38 @@ export const currentImageDisplaySelector = createSelector(
*/
const CurrentImageDisplay = () => {
const { hasAnImageToDisplay } = useAppSelector(currentImageDisplaySelector);
const [shouldHideImageToImage, setShouldHideImageToImage] = useState(false);
const w = useMotionValue(0);
const width = useTransform(w, [0, 100], [`0px`, `100px`]);
return (
<Flex
sx={{
position: 'relative',
flexDirection: 'column',
height: '100%',
width: '100%',
rowGap: 4,
borderRadius: 'base',
alignItems: 'center',
justifyContent: 'center',
}}
>
{hasAnImageToDisplay ? (
<>
<CurrentImageButtons />
<Box sx={{ position: 'absolute', top: 0 }}>
<CurrentImageButtons />
</Box>
<Flex
sx={{
w: 'full',
h: 'full',
alignItems: 'center',
justifyContent: 'center',
gap: 4,
}}
>
{hasAnImageToDisplay ? (
<CurrentImagePreview />
</>
) : (
<Flex
sx={{
alignItems: 'center',
justifyContent: 'center',
width: '100%',
height: '100%',
}}
>
) : (
<Icon
as={MdPhoto}
sx={{
@ -65,8 +81,8 @@ const CurrentImageDisplay = () => {
color: 'base.500',
}}
/>
</Flex>
)}
)}
</Flex>
</Flex>
);
};

View File

@ -27,7 +27,7 @@ export default function InvokeAccordionItem({
{header}
</Box>
{additionalHeaderComponents}
{feature && <GuideIcon feature={feature} />}
{/* {feature && <GuideIcon feature={feature} />} */}
<AccordionIcon />
</Flex>
</AccordionButton>

View File

@ -12,10 +12,6 @@ export default function ImageFit() {
(state: RootState) => state.generation.shouldFitToWidthHeight
);
const isImageToImageEnabled = useAppSelector(
(state: RootState) => state.generation.isImageToImageEnabled
);
const handleChangeFit = (e: ChangeEvent<HTMLInputElement>) =>
dispatch(setShouldFitToWidthHeight(e.target.checked));
@ -23,7 +19,6 @@ export default function ImageFit() {
return (
<IAISwitch
isDisabled={!isImageToImageEnabled}
label={t('parameters.imageFit')}
isChecked={shouldFitToWidthHeight}
onChange={handleChangeFit}

View File

@ -1,14 +1,33 @@
import { Flex, Image, VStack } from '@chakra-ui/react';
import {
Box,
ButtonGroup,
Collapse,
Flex,
Heading,
HStack,
Image,
Spacer,
useDisclosure,
VStack,
} from '@chakra-ui/react';
import { motion } from 'framer-motion';
import IAIButton from 'common/components/IAIButton';
import ImageFit from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageFit';
import ImageToImageStrength from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageStrength';
import IAIIconButton from 'common/components/IAIIconButton';
import { useTranslation } from 'react-i18next';
import InitialImagePreview from './InitialImagePreview';
import { useState } from 'react';
import { FaUndo, FaUpload } from 'react-icons/fa';
import ImageToImageSettingsHeader from 'common/components/ImageToImageSettingsHeader';
export default function ImageToImageSettings() {
const { t } = useTranslation();
return (
<VStack gap={2} alignItems="stretch">
<VStack gap={2} w="full" alignItems="stretch">
<ImageToImageSettingsHeader />
<InitialImagePreview />
<ImageToImageStrength label={t('parameters.img2imgStrength')} />
<ImageFit />

View File

@ -3,12 +3,15 @@ import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISwitch from 'common/components/IAISwitch';
import { isImageToImageEnabledChanged } from 'features/parameters/store/generationSlice';
import { ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next';
export default function ImageToImageToggle() {
const isImageToImageEnabled = useAppSelector(
(state: RootState) => state.generation.isImageToImageEnabled
);
const { t } = useTranslation();
const dispatch = useAppDispatch();
const handleChange = (e: ChangeEvent<HTMLInputElement>) =>
@ -16,6 +19,7 @@ export default function ImageToImageToggle() {
return (
<IAISwitch
label={t('common.img2img')}
isChecked={isImageToImageEnabled}
width="auto"
onChange={handleChange}

View File

@ -87,47 +87,44 @@ const InitialImagePreview = () => {
}}
onDrop={handleDrop}
>
<Box
sx={{
height: 'full',
width: 'full',
opacity: isImageToImageEnabled ? 1 : 0.5,
filter: isImageToImageEnabled ? 'none' : 'auto',
blur: '5px',
position: 'relative',
}}
>
{initialImage?.url && (
<>
<Image
sx={{
fit: 'contain',
borderRadius: 'base',
}}
src={getUrl(initialImage?.url)}
onError={onError}
onLoad={() => {
setIsLoaded(true);
}}
fallback={
<Flex
sx={{ h: 36, alignItems: 'center', justifyContent: 'center' }}
>
<Spinner color="grey" w="5rem" h="5rem" />
</Flex>
}
{initialImage?.url && (
<Box
sx={{
height: 'full',
width: 'full',
opacity: isImageToImageEnabled ? 1 : 0.5,
filter: isImageToImageEnabled ? 'none' : 'auto',
blur: '5px',
position: 'relative',
}}
>
<Image
sx={{
fit: 'contain',
borderRadius: 'base',
}}
src={getUrl(initialImage?.url)}
onError={onError}
onLoad={() => {
setIsLoaded(true);
}}
fallback={
<Flex
sx={{ h: 36, alignItems: 'center', justifyContent: 'center' }}
>
<Spinner color="grey" w="5rem" h="5rem" />
</Flex>
}
/>
{isLoaded && (
<ImageToImageOverlay
setIsLoaded={setIsLoaded}
image={initialImage}
/>
{isLoaded && (
<ImageToImageOverlay
setIsLoaded={setIsLoaded}
image={initialImage}
/>
)}
</>
)}
{!initialImage?.url && <SelectImagePlaceholder />}
</Box>
)}
</Box>
)}
{!initialImage?.url && <SelectImagePlaceholder />}
{!isImageToImageEnabled && (
<Flex
sx={{

View File

@ -5,6 +5,7 @@ import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISwitch from 'common/components/IAISwitch';
import { setShouldRandomizeSeed } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next';
import { Switch } from '@chakra-ui/react';
export default function RandomizeSeed() {
const dispatch = useAppDispatch();
@ -18,8 +19,8 @@ export default function RandomizeSeed() {
dispatch(setShouldRandomizeSeed(e.target.checked));
return (
<IAISwitch
label={t('parameters.randomizeSeed')}
<Switch
aria-label={t('parameters.randomizeSeed')}
isChecked={shouldRandomizeSeed}
onChange={handleChangeShouldRandomizeSeed}
/>

View File

@ -10,7 +10,6 @@ import Threshold from './Threshold';
const SeedSettings = () => {
return (
<VStack gap={2} alignItems="stretch">
<RandomizeSeed />
<Seed />
<Threshold />
<Perlin />

View File

@ -3,8 +3,10 @@ import { NUMPY_RAND_MAX, NUMPY_RAND_MIN } from 'app/constants';
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import randomInt from 'common/util/randomInt';
import { IAIIconButton } from 'exports';
import { setSeed } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next';
import { FaRandom } from 'react-icons/fa';
export default function ShuffleSeed() {
const dispatch = useAppDispatch();
@ -17,13 +19,20 @@ export default function ShuffleSeed() {
dispatch(setSeed(randomInt(NUMPY_RAND_MIN, NUMPY_RAND_MAX)));
return (
<Button
<IAIIconButton
size="sm"
isDisabled={shouldRandomizeSeed}
aria-label={t('parameters.shuffle')}
tooltip={t('parameters.shuffle')}
icon={<FaRandom />}
onClick={handleClickRandomizeSeed}
padding="0 1.5rem"
>
<p>{t('parameters.shuffle')}</p>
</Button>
/>
// <Button
// size="sm"
// onClick={handleClickRandomizeSeed}
// padding="0 1.5rem"
// >
// <p>{t('parameters.shuffle')}</p>
// </Button>
);
}

View File

@ -0,0 +1,32 @@
import { memo, useState } from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import ImageToImageSettings from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageSettings';
import { useAppSelector } from 'app/storeHooks';
import { RootState } from 'app/store';
import { Box } from '@chakra-ui/react';
const AnimatedImageToImagePanel = () => {
const isImageToImageEnabled = useAppSelector(
(state: RootState) => state.generation.isImageToImageEnabled
);
return (
<AnimatePresence>
{isImageToImageEnabled && (
<motion.div
initial={{ opacity: 0, scaleX: 0, width: 0 }}
animate={{ opacity: 1, scaleX: 1, width: '28rem' }}
exit={{ opacity: 0, scaleX: 0, width: 0 }}
transition={{ type: 'spring', bounce: 0, duration: 0.35 }}
>
<Box sx={{ h: 'full', w: 'full', pl: 4 }}>
<ImageToImageSettings />
</Box>
</motion.div>
)}
</AnimatePresence>
);
};
export default memo(AnimatedImageToImagePanel);

View File

@ -23,7 +23,7 @@ import { useTranslation } from 'react-i18next';
import { ResourceKey } from 'i18next';
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
import NodeEditor from 'features/nodes/components/NodeEditor';
import LinearWorkarea from './tabs/Linear/LinearWorkarea';
import LinearWorkspace from './tabs/Linear/LinearWorkspace';
import { FaImage } from 'react-icons/fa';
export interface InvokeTabInfo {
@ -41,7 +41,7 @@ const buildTabs = (disabledTabs: InvokeTabName[]): InvokeTabInfo[] => {
{
id: 'linear',
icon: <Icon as={FaImage} sx={tabIconStyles} />,
workarea: <LinearWorkarea />,
workarea: <LinearWorkspace />,
},
{
id: 'unifiedCanvas',

View File

@ -0,0 +1,143 @@
import { Box, Flex, useOutsideClick } from '@chakra-ui/react';
import { Slide } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { uiSelector } from 'features/ui/store/uiSelectors';
import { isEqual } from 'lodash';
import { memo, PropsWithChildren, useRef } from 'react';
import PinParametersPanelButton from 'features/ui/components/PinParametersPanelButton';
import {
setShouldShowParametersPanel,
toggleParametersPanel,
togglePinParametersPanel,
} from 'features/ui/store/uiSlice';
import InvokeAILogoComponent from 'features/system/components/InvokeAILogoComponent';
import Scrollable from 'features/ui/components/common/Scrollable';
import { useLangDirection } from 'features/ui/hooks/useDirection';
import { useHotkeys } from 'react-hotkeys-hook';
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
import { generationSelector } from 'features/parameters/store/generationSelectors';
import AnimatedImageToImagePanel from 'features/parameters/components/AnimatedImageToImagePanel';
const parametersSlideSelector = createSelector(
[uiSelector, generationSelector],
(ui, generation) => {
const { shouldPinParametersPanel, shouldShowParametersPanel } = ui;
const { isImageToImageEnabled } = generation;
return {
shouldPinParametersPanel,
shouldShowParametersPanel,
isImageToImageEnabled,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
type ParametersSlideProps = PropsWithChildren;
const ParametersSlide = (props: ParametersSlideProps) => {
const dispatch = useAppDispatch();
const {
shouldShowParametersPanel,
isImageToImageEnabled,
shouldPinParametersPanel,
} = useAppSelector(parametersSlideSelector);
const langDirection = useLangDirection();
const outsideClickRef = useRef<HTMLDivElement>(null);
const closeParametersPanel = () => {
dispatch(setShouldShowParametersPanel(false));
};
useOutsideClick({
ref: outsideClickRef,
handler: () => {
closeParametersPanel();
},
enabled: shouldShowParametersPanel && !shouldPinParametersPanel,
});
useHotkeys(
'o',
() => {
dispatch(toggleParametersPanel());
shouldPinParametersPanel && dispatch(requestCanvasRescale());
},
[shouldPinParametersPanel]
);
useHotkeys(
'esc',
() => {
dispatch(setShouldShowParametersPanel(false));
},
{
enabled: () => !shouldPinParametersPanel,
preventDefault: true,
},
[shouldPinParametersPanel]
);
useHotkeys(
'shift+o',
() => {
dispatch(togglePinParametersPanel());
dispatch(requestCanvasRescale());
},
[]
);
return (
<Slide
direction={langDirection === 'rtl' ? 'right' : 'left'}
in={shouldShowParametersPanel}
motionProps={{ initial: false }}
style={{ zIndex: 99 }}
>
<Flex
sx={{
boxShadow: '0 0 4rem 0 rgba(0, 0, 0, 0.8)',
pl: 4,
py: 4,
h: 'full',
w: 'min',
bg: 'base.900',
borderInlineEndWidth: 4,
borderInlineEndColor: 'base.800',
}}
>
<Flex ref={outsideClickRef} position="relative" height="full" pr={4}>
<Flex
sx={{
flexDirection: 'column',
width: '28rem',
flexShrink: 0,
}}
>
<Flex
paddingTop={1.5}
paddingBottom={4}
justifyContent="space-between"
alignItems="center"
>
<InvokeAILogoComponent />
<PinParametersPanelButton />
</Flex>
<Scrollable>{props.children}</Scrollable>
</Flex>
<AnimatedImageToImagePanel />
</Flex>
</Flex>
</Slide>
);
};
export default memo(ParametersSlide);

View File

@ -1,10 +1,12 @@
import { Flex } from '@chakra-ui/react';
import { Feature } from 'app/features';
import IAISwitch from 'common/components/IAISwitch';
import ImageToImageSettings from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageSettings';
import ImageToImageToggle from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageToggle';
import OutputSettings from 'features/parameters/components/AdvancedParameters/Output/OutputSettings';
import SymmetrySettings from 'features/parameters/components/AdvancedParameters/Output/SymmetrySettings';
import SymmetryToggle from 'features/parameters/components/AdvancedParameters/Output/SymmetryToggle';
import RandomizeSeed from 'features/parameters/components/AdvancedParameters/Seed/RandomizeSeed';
import SeedSettings from 'features/parameters/components/AdvancedParameters/Seed/SeedSettings';
import GenerateVariationsToggle from 'features/parameters/components/AdvancedParameters/Variations/GenerateVariations';
import VariationsSettings from 'features/parameters/components/AdvancedParameters/Variations/VariationsSettings';
@ -15,58 +17,75 @@ import ParametersAccordion, {
import ProcessButtons from 'features/parameters/components/ProcessButtons/ProcessButtons';
import NegativePromptInput from 'features/parameters/components/PromptInput/NegativePromptInput';
import PromptInput from 'features/parameters/components/PromptInput/PromptInput';
import { memo } from 'react';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { PARAMETERS_PANEL_WIDTH } from 'theme/util/constants';
const LinearParameters = () => {
const { t } = useTranslation();
const linearAccordions: ParametersAccordionItems = {
general: {
name: 'general',
header: `${t('parameters.general')}`,
feature: undefined,
content: <MainSettings />,
},
seed: {
name: 'seed',
header: `${t('parameters.seed')}`,
feature: Feature.SEED,
content: <SeedSettings />,
},
imageToImage: {
name: 'imageToImage',
header: `${t('parameters.imageToImage')}`,
feature: undefined,
content: <ImageToImageSettings />,
additionalHeaderComponents: <ImageToImageToggle />,
},
variations: {
name: 'variations',
header: `${t('parameters.variations')}`,
feature: Feature.VARIATIONS,
content: <VariationsSettings />,
additionalHeaderComponents: <GenerateVariationsToggle />,
},
symmetry: {
name: 'symmetry',
header: `${t('parameters.symmetry')}`,
content: <SymmetrySettings />,
additionalHeaderComponents: <SymmetryToggle />,
},
other: {
name: 'other',
header: `${t('parameters.otherOptions')}`,
feature: Feature.OTHER,
content: <OutputSettings />,
},
};
const linearAccordions: ParametersAccordionItems = useMemo(
() => ({
// general: {
// name: 'general',
// header: `${t('parameters.general')}`,
// feature: undefined,
// content: <MainSettings />,
// },
seed: {
name: 'seed',
header: `${t('parameters.seed')}`,
feature: Feature.SEED,
content: <SeedSettings />,
additionalHeaderComponents: <RandomizeSeed />,
},
// imageToImage: {
// name: 'imageToImage',
// header: `${t('parameters.imageToImage')}`,
// feature: undefined,
// content: <ImageToImageSettings />,
// additionalHeaderComponents: <ImageToImageToggle />,
// },
variations: {
name: 'variations',
header: `${t('parameters.variations')}`,
feature: Feature.VARIATIONS,
content: <VariationsSettings />,
additionalHeaderComponents: <GenerateVariationsToggle />,
},
symmetry: {
name: 'symmetry',
header: `${t('parameters.symmetry')}`,
content: <SymmetrySettings />,
additionalHeaderComponents: <SymmetryToggle />,
},
other: {
name: 'other',
header: `${t('parameters.otherOptions')}`,
feature: Feature.OTHER,
content: <OutputSettings />,
},
}),
[t]
);
return (
<Flex flexDir="column" gap={2}>
<PromptInput />
<NegativePromptInput />
<ProcessButtons />
<Flex
sx={{
flexDirection: 'column',
gap: 2,
bg: 'base.800',
p: 4,
borderRadius: 'base',
}}
>
<MainSettings />
<ImageToImageToggle />
</Flex>
<ParametersAccordion accordionItems={linearAccordions} />
</Flex>
);

View File

@ -1,11 +0,0 @@
import InvokeWorkarea from 'features/ui/components/InvokeWorkarea';
import LinearContent from './LinearContent';
import LinearParameters from './LinearParameters';
export default function LinearWorkarea() {
return (
<InvokeWorkarea parametersPanelContent={<LinearParameters />}>
<LinearContent />
</InvokeWorkarea>
);
}

View File

@ -0,0 +1,53 @@
import { Box, Flex } from '@chakra-ui/react';
import { useAppSelector } from 'app/storeHooks';
import { memo } from 'react';
import LinearContent from './LinearContent';
import LinearParameters from './LinearParameters';
import PinParametersPanelButton from '../../PinParametersPanelButton';
import { RootState } from 'app/store';
import Scrollable from '../../common/Scrollable';
import ParametersSlide from '../../common/ParametersSlide';
import AnimatedImageToImagePanel from 'features/parameters/components/AnimatedImageToImagePanel';
const LinearWorkspace = () => {
const shouldPinParametersPanel = useAppSelector(
(state: RootState) => state.ui.shouldPinParametersPanel
);
return (
<Flex
flexDirection={{ base: 'column-reverse', xl: 'row' }}
w="full"
h="full"
gap={4}
>
{shouldPinParametersPanel ? (
<Flex sx={{ flexDirection: 'row-reverse' }}>
<AnimatedImageToImagePanel />
<Flex
sx={{
flexDirection: 'column',
width: '28rem',
flexShrink: 0,
position: 'relative',
}}
>
<Scrollable>
<LinearParameters />
</Scrollable>
<PinParametersPanelButton
sx={{ position: 'absolute', top: 0, insetInlineEnd: 0 }}
/>
</Flex>
</Flex>
) : (
<ParametersSlide>
<LinearParameters />
</ParametersSlide>
)}
<LinearContent />
</Flex>
);
};
export default memo(LinearWorkspace);

View File

@ -1,26 +1,77 @@
import { RootState } from 'app/store';
// import { RootState } from 'app/store';
// import { useAppSelector } from 'app/storeHooks';
// import InvokeWorkarea from 'features/ui/components/InvokeWorkarea';
// import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
// import UnifiedCanvasContentBeta from './UnifiedCanvasBeta/UnifiedCanvasContentBeta';
// import UnifiedCanvasContent from './UnifiedCanvasContent';
// import UnifiedCanvasParameters from './UnifiedCanvasParameters';
// export default function UnifiedCanvasWorkarea() {
// const shouldUseCanvasBetaLayout = useAppSelector(
// (state: RootState) => state.ui.shouldUseCanvasBetaLayout
// );
// const activeTabName = useAppSelector(activeTabNameSelector);
// return (
// <InvokeWorkarea parametersPanelContent={<UnifiedCanvasParameters />}>
// {activeTabName === 'unifiedCanvas' &&
// (shouldUseCanvasBetaLayout ? (
// <UnifiedCanvasContentBeta />
// ) : (
// <UnifiedCanvasContent />
// ))}
// </InvokeWorkarea>
// );
// }
import { Box, Flex } from '@chakra-ui/react';
import { useAppSelector } from 'app/storeHooks';
import InvokeWorkarea from 'features/ui/components/InvokeWorkarea';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { memo } from 'react';
import PinParametersPanelButton from '../../PinParametersPanelButton';
import { RootState } from 'app/store';
import Scrollable from '../../common/Scrollable';
import ParametersSlide from '../../common/ParametersSlide';
import UnifiedCanvasParameters from './UnifiedCanvasParameters';
import UnifiedCanvasContentBeta from './UnifiedCanvasBeta/UnifiedCanvasContentBeta';
import UnifiedCanvasContent from './UnifiedCanvasContent';
import UnifiedCanvasParameters from './UnifiedCanvasParameters';
export default function UnifiedCanvasWorkarea() {
const CanvasWorkspace = () => {
const shouldPinParametersPanel = useAppSelector(
(state: RootState) => state.ui.shouldPinParametersPanel
);
const shouldUseCanvasBetaLayout = useAppSelector(
(state: RootState) => state.ui.shouldUseCanvasBetaLayout
);
const activeTabName = useAppSelector(activeTabNameSelector);
return (
<InvokeWorkarea parametersPanelContent={<UnifiedCanvasParameters />}>
{activeTabName === 'unifiedCanvas' &&
(shouldUseCanvasBetaLayout ? (
<UnifiedCanvasContentBeta />
) : (
<UnifiedCanvasContent />
))}
</InvokeWorkarea>
<Flex
flexDirection={{ base: 'column-reverse', xl: 'row' }}
w="full"
h="full"
gap={4}
>
{shouldPinParametersPanel ? (
<Box width="28rem" flexShrink={0} position="relative">
<Scrollable>
<UnifiedCanvasParameters />
</Scrollable>
<PinParametersPanelButton
sx={{ position: 'absolute', top: 0, insetInlineEnd: 0 }}
/>
</Box>
) : (
<ParametersSlide>
<UnifiedCanvasParameters />
</ParametersSlide>
)}
{shouldUseCanvasBetaLayout ? (
<UnifiedCanvasContentBeta />
) : (
<UnifiedCanvasContent />
)}
</Flex>
);
}
};
export default memo(CanvasWorkspace);

View File

@ -0,0 +1,14 @@
import { useTheme } from '@chakra-ui/react';
import { useMemo } from 'react';
import { LangDirection } from '../components/common/ResizableDrawer/types';
export const useLangDirection = () => {
const theme = useTheme();
const langDirection = useMemo(
() => theme.direction as LangDirection,
[theme.direction]
);
return langDirection;
};