From 568f0aad7186ccb03a822fd805a511276b907723 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 24 Apr 2023 14:48:46 +1000 Subject: [PATCH] feat(ui): wip img2img ui --- invokeai/frontend/web/public/locales/en.json | 3 +- .../web/src/common/components/IAISwitch.tsx | 3 +- .../common/components/ImageToImageOverlay.tsx | 22 --- .../components/ImageToImageSettingsHeader.tsx | 62 ++++++++ .../components/SelectImagePlaceholder.tsx | 12 +- .../components/CurrentImageButtons.tsx | 12 +- .../components/CurrentImageDisplay.tsx | 48 ++++-- .../AccordionItems/InvokeAccordionItem.tsx | 2 +- .../ImageToImage/ImageFit.tsx | 5 - .../ImageToImage/ImageToImageSettings.tsx | 23 ++- .../ImageToImage/ImageToImageToggle.tsx | 4 + .../ImageToImage/InitialImagePreview.tsx | 77 +++++----- .../AdvancedParameters/Seed/RandomizeSeed.tsx | 5 +- .../AdvancedParameters/Seed/SeedSettings.tsx | 1 - .../AdvancedParameters/Seed/ShuffleSeed.tsx | 19 ++- .../components/AnimatedImageToImagePanel.tsx | 32 ++++ .../src/features/ui/components/InvokeTabs.tsx | 4 +- .../ui/components/common/ParametersSlide.tsx | 143 ++++++++++++++++++ .../tabs/Linear/LinearParameters.tsx | 101 ++++++++----- .../components/tabs/Linear/LinearWorkarea.tsx | 11 -- .../tabs/Linear/LinearWorkspace.tsx | 53 +++++++ .../UnifiedCanvas/UnifiedCanvasWorkarea.tsx | 83 ++++++++-- .../web/src/features/ui/hooks/useDirection.ts | 14 ++ 23 files changed, 569 insertions(+), 170 deletions(-) create mode 100644 invokeai/frontend/web/src/common/components/ImageToImageSettingsHeader.tsx create mode 100644 invokeai/frontend/web/src/features/parameters/components/AnimatedImageToImagePanel.tsx create mode 100644 invokeai/frontend/web/src/features/ui/components/common/ParametersSlide.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/Linear/LinearWorkarea.tsx create mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/Linear/LinearWorkspace.tsx create mode 100644 invokeai/frontend/web/src/features/ui/hooks/useDirection.ts diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index 247372e172..6996119de3 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -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", diff --git a/invokeai/frontend/web/src/common/components/IAISwitch.tsx b/invokeai/frontend/web/src/common/components/IAISwitch.tsx index c03fc17d51..e1bddb9f43 100644 --- a/invokeai/frontend/web/src/common/components/IAISwitch.tsx +++ b/invokeai/frontend/web/src/common/components/IAISwitch.tsx @@ -34,10 +34,9 @@ const IAISwitch = (props: Props) => { display="flex" gap={4} alignItems="center" - justifyContent="space-between" {...formControlProps} > - + {label} diff --git a/invokeai/frontend/web/src/common/components/ImageToImageOverlay.tsx b/invokeai/frontend/web/src/common/components/ImageToImageOverlay.tsx index c006cf0c6b..a5bb6ba813 100644 --- a/invokeai/frontend/web/src/common/components/ImageToImageOverlay.tsx +++ b/invokeai/frontend/web/src/common/components/ImageToImageOverlay.tsx @@ -37,28 +37,6 @@ const ImageToImageOverlay = ({ position: 'absolute', }} > - - } - aria-label={t('accessibility.reset')} - onClick={handleResetInitialImage} - /> - } - aria-label={t('common.upload')} - /> - { + const isImageToImageEnabled = useAppSelector( + (state: RootState) => state.generation.isImageToImageEnabled + ); + const dispatch = useAppDispatch(); + const { t } = useTranslation(); + + const handleResetInitialImage = useCallback(() => { + dispatch(clearInitialImage()); + }, [dispatch]); + + return ( + + + Image to Image + + + + } + aria-label={t('accessibility.reset')} + onClick={handleResetInitialImage} + /> + } + aria-label={t('common.upload')} + /> + + + ); +}; + +export default ImageToImageSettingsHeader; diff --git a/invokeai/frontend/web/src/common/components/SelectImagePlaceholder.tsx b/invokeai/frontend/web/src/common/components/SelectImagePlaceholder.tsx index c52cf75f9f..2c0d71ca69 100644 --- a/invokeai/frontend/web/src/common/components/SelectImagePlaceholder.tsx +++ b/invokeai/frontend/web/src/common/components/SelectImagePlaceholder.tsx @@ -3,7 +3,17 @@ import { FaImage } from 'react-icons/fa'; const SelectImagePlaceholder = () => { return ( - + ); diff --git a/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx b/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx index aae104dc41..875a5bcd0d 100644 --- a/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx @@ -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) => { } /> @@ -487,7 +495,7 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => { {t('parameters.copyImageToLink')} - + } size="sm" w="100%"> {t('parameters.downloadImage')} diff --git a/invokeai/frontend/web/src/features/gallery/components/CurrentImageDisplay.tsx b/invokeai/frontend/web/src/features/gallery/components/CurrentImageDisplay.tsx index f9179ca4e2..0364f879cc 100644 --- a/invokeai/frontend/web/src/features/gallery/components/CurrentImageDisplay.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/CurrentImageDisplay.tsx @@ -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 ( - {hasAnImageToDisplay ? ( - <> - + + + + + {hasAnImageToDisplay ? ( - - ) : ( - + ) : ( { color: 'base.500', }} /> - - )} + )} + ); }; diff --git a/invokeai/frontend/web/src/features/parameters/components/AccordionItems/InvokeAccordionItem.tsx b/invokeai/frontend/web/src/features/parameters/components/AccordionItems/InvokeAccordionItem.tsx index a4f9be7918..ccf0a8ed26 100644 --- a/invokeai/frontend/web/src/features/parameters/components/AccordionItems/InvokeAccordionItem.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/AccordionItems/InvokeAccordionItem.tsx @@ -27,7 +27,7 @@ export default function InvokeAccordionItem({ {header} {additionalHeaderComponents} - {feature && } + {/* {feature && } */} diff --git a/invokeai/frontend/web/src/features/parameters/components/AdvancedParameters/ImageToImage/ImageFit.tsx b/invokeai/frontend/web/src/features/parameters/components/AdvancedParameters/ImageToImage/ImageFit.tsx index b0fe389123..f127afeda3 100644 --- a/invokeai/frontend/web/src/features/parameters/components/AdvancedParameters/ImageToImage/ImageFit.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/AdvancedParameters/ImageToImage/ImageFit.tsx @@ -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) => dispatch(setShouldFitToWidthHeight(e.target.checked)); @@ -23,7 +19,6 @@ export default function ImageFit() { return ( + + diff --git a/invokeai/frontend/web/src/features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageToggle.tsx b/invokeai/frontend/web/src/features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageToggle.tsx index ad449a5ff3..dbaaf327c4 100644 --- a/invokeai/frontend/web/src/features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageToggle.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageToggle.tsx @@ -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) => @@ -16,6 +19,7 @@ export default function ImageToImageToggle() { return ( { }} onDrop={handleDrop} > - - {initialImage?.url && ( - <> - { - setIsLoaded(true); - }} - fallback={ - - - - } + {initialImage?.url && ( + + { + setIsLoaded(true); + }} + fallback={ + + + + } + /> + {isLoaded && ( + - {isLoaded && ( - - )} - - )} - - {!initialImage?.url && } - + )} + + )} + {!initialImage?.url && } {!isImageToImageEnabled && ( diff --git a/invokeai/frontend/web/src/features/parameters/components/AdvancedParameters/Seed/SeedSettings.tsx b/invokeai/frontend/web/src/features/parameters/components/AdvancedParameters/Seed/SeedSettings.tsx index 891b528acd..576358d2e1 100644 --- a/invokeai/frontend/web/src/features/parameters/components/AdvancedParameters/Seed/SeedSettings.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/AdvancedParameters/Seed/SeedSettings.tsx @@ -10,7 +10,6 @@ import Threshold from './Threshold'; const SeedSettings = () => { return ( - diff --git a/invokeai/frontend/web/src/features/parameters/components/AdvancedParameters/Seed/ShuffleSeed.tsx b/invokeai/frontend/web/src/features/parameters/components/AdvancedParameters/Seed/ShuffleSeed.tsx index 6d852d91b7..675640050b 100644 --- a/invokeai/frontend/web/src/features/parameters/components/AdvancedParameters/Seed/ShuffleSeed.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/AdvancedParameters/Seed/ShuffleSeed.tsx @@ -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 ( - + /> + // ); } diff --git a/invokeai/frontend/web/src/features/parameters/components/AnimatedImageToImagePanel.tsx b/invokeai/frontend/web/src/features/parameters/components/AnimatedImageToImagePanel.tsx new file mode 100644 index 0000000000..d1e07220ae --- /dev/null +++ b/invokeai/frontend/web/src/features/parameters/components/AnimatedImageToImagePanel.tsx @@ -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 ( + + {isImageToImageEnabled && ( + + + + + + )} + + ); +}; + +export default memo(AnimatedImageToImagePanel); diff --git a/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx b/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx index bb259d242d..11dddc48bf 100644 --- a/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx +++ b/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx @@ -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: , - workarea: , + workarea: , }, { id: 'unifiedCanvas', diff --git a/invokeai/frontend/web/src/features/ui/components/common/ParametersSlide.tsx b/invokeai/frontend/web/src/features/ui/components/common/ParametersSlide.tsx new file mode 100644 index 0000000000..0889e779f1 --- /dev/null +++ b/invokeai/frontend/web/src/features/ui/components/common/ParametersSlide.tsx @@ -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(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 ( + + + + + + + + + {props.children} + + + + + + ); +}; + +export default memo(ParametersSlide); diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/Linear/LinearParameters.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/Linear/LinearParameters.tsx index 67ec737a12..5e6c9f82f3 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/Linear/LinearParameters.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/Linear/LinearParameters.tsx @@ -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: , - }, - seed: { - name: 'seed', - header: `${t('parameters.seed')}`, - feature: Feature.SEED, - content: , - }, - imageToImage: { - name: 'imageToImage', - header: `${t('parameters.imageToImage')}`, - feature: undefined, - content: , - additionalHeaderComponents: , - }, - variations: { - name: 'variations', - header: `${t('parameters.variations')}`, - feature: Feature.VARIATIONS, - content: , - additionalHeaderComponents: , - }, - symmetry: { - name: 'symmetry', - header: `${t('parameters.symmetry')}`, - content: , - additionalHeaderComponents: , - }, - other: { - name: 'other', - header: `${t('parameters.otherOptions')}`, - feature: Feature.OTHER, - content: , - }, - }; + const linearAccordions: ParametersAccordionItems = useMemo( + () => ({ + // general: { + // name: 'general', + // header: `${t('parameters.general')}`, + // feature: undefined, + // content: , + // }, + seed: { + name: 'seed', + header: `${t('parameters.seed')}`, + feature: Feature.SEED, + content: , + additionalHeaderComponents: , + }, + // imageToImage: { + // name: 'imageToImage', + // header: `${t('parameters.imageToImage')}`, + // feature: undefined, + // content: , + // additionalHeaderComponents: , + // }, + variations: { + name: 'variations', + header: `${t('parameters.variations')}`, + feature: Feature.VARIATIONS, + content: , + additionalHeaderComponents: , + }, + symmetry: { + name: 'symmetry', + header: `${t('parameters.symmetry')}`, + content: , + additionalHeaderComponents: , + }, + other: { + name: 'other', + header: `${t('parameters.otherOptions')}`, + feature: Feature.OTHER, + content: , + }, + }), + [t] + ); return ( + + + + ); diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/Linear/LinearWorkarea.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/Linear/LinearWorkarea.tsx deleted file mode 100644 index f75065b6a3..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/Linear/LinearWorkarea.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import InvokeWorkarea from 'features/ui/components/InvokeWorkarea'; -import LinearContent from './LinearContent'; -import LinearParameters from './LinearParameters'; - -export default function LinearWorkarea() { - return ( - }> - - - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/Linear/LinearWorkspace.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/Linear/LinearWorkspace.tsx new file mode 100644 index 0000000000..706cee8247 --- /dev/null +++ b/invokeai/frontend/web/src/features/ui/components/tabs/Linear/LinearWorkspace.tsx @@ -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 ( + + {shouldPinParametersPanel ? ( + + + + + + + + + + ) : ( + + + + )} + + + ); +}; + +export default memo(LinearWorkspace); diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasWorkarea.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasWorkarea.tsx index f7991f6eaa..f1b931f5f1 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasWorkarea.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasWorkarea.tsx @@ -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 ( +// }> +// {activeTabName === 'unifiedCanvas' && +// (shouldUseCanvasBetaLayout ? ( +// +// ) : ( +// +// ))} +// +// ); +// } +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 ( - }> - {activeTabName === 'unifiedCanvas' && - (shouldUseCanvasBetaLayout ? ( - - ) : ( - - ))} - + + {shouldPinParametersPanel ? ( + + + + + + + ) : ( + + + + )} + {shouldUseCanvasBetaLayout ? ( + + ) : ( + + )} + ); -} +}; + +export default memo(CanvasWorkspace); diff --git a/invokeai/frontend/web/src/features/ui/hooks/useDirection.ts b/invokeai/frontend/web/src/features/ui/hooks/useDirection.ts new file mode 100644 index 0000000000..b5fcb499fa --- /dev/null +++ b/invokeai/frontend/web/src/features/ui/hooks/useDirection.ts @@ -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; +};