mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
clean up image implementation
This commit is contained in:
parent
cc96dcf0ed
commit
0b0abfbe8f
@ -8,6 +8,7 @@ from PIL import Image
|
|||||||
|
|
||||||
from invokeai.app.api.dependencies import ApiDependencies
|
from invokeai.app.api.dependencies import ApiDependencies
|
||||||
from invokeai.app.api.routers.model_manager import IMAGE_MAX_AGE
|
from invokeai.app.api.routers.model_manager import IMAGE_MAX_AGE
|
||||||
|
from invokeai.app.services.style_preset_images.style_preset_images_common import StylePresetImageFileNotFoundException
|
||||||
from invokeai.app.services.style_preset_records.style_preset_records_common import (
|
from invokeai.app.services.style_preset_records.style_preset_records_common import (
|
||||||
PresetData,
|
PresetData,
|
||||||
StylePresetChanges,
|
StylePresetChanges,
|
||||||
@ -91,7 +92,11 @@ async def delete_style_preset(
|
|||||||
style_preset_id: str = Path(description="The style preset to delete"),
|
style_preset_id: str = Path(description="The style preset to delete"),
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Deletes a style preset"""
|
"""Deletes a style preset"""
|
||||||
ApiDependencies.invoker.services.style_preset_images_service.delete(style_preset_id)
|
try:
|
||||||
|
ApiDependencies.invoker.services.style_preset_images_service.delete(style_preset_id)
|
||||||
|
except StylePresetImageFileNotFoundException:
|
||||||
|
pass
|
||||||
|
|
||||||
ApiDependencies.invoker.services.style_preset_records.delete(style_preset_id)
|
ApiDependencies.invoker.services.style_preset_records.delete(style_preset_id)
|
||||||
|
|
||||||
|
|
||||||
|
@ -72,6 +72,8 @@ class StylePresetImageFileStorageDisk(StylePresetImageFileStorageBase):
|
|||||||
|
|
||||||
send2trash(path)
|
send2trash(path)
|
||||||
|
|
||||||
|
except StylePresetImageFileNotFoundException as e:
|
||||||
|
raise StylePresetImageFileNotFoundException from e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise StylePresetImageFileDeleteException from e
|
raise StylePresetImageFileDeleteException from e
|
||||||
|
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import { Flex, IconButton, Text } from '@invoke-ai/ui-library';
|
import { Flex, IconButton, Text, Box, ButtonGroup } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { negativePromptChanged, positivePromptChanged } from 'features/controlLayers/store/controlLayersSlice';
|
import { negativePromptChanged, positivePromptChanged } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { usePresetModifiedPrompts } from 'features/stylePresets/hooks/usePresetModifiedPrompts';
|
import { usePresetModifiedPrompts } from 'features/stylePresets/hooks/usePresetModifiedPrompts';
|
||||||
import { activeStylePresetChanged } from 'features/stylePresets/store/stylePresetSlice';
|
import { activeStylePresetChanged } from 'features/stylePresets/store/stylePresetSlice';
|
||||||
import type { MouseEventHandler } from 'react';
|
import type { MouseEventHandler } from 'react';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { CgPushDown } from 'react-icons/cg';
|
import { PiStackSimpleBold, PiXBold } from 'react-icons/pi';
|
||||||
import { PiXBold } from 'react-icons/pi';
|
|
||||||
import StylePresetImage from './StylePresetImage';
|
import StylePresetImage from './StylePresetImage';
|
||||||
|
|
||||||
export const ActiveStylePreset = () => {
|
export const ActiveStylePreset = () => {
|
||||||
@ -34,18 +33,21 @@ export const ActiveStylePreset = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!activeStylePreset) {
|
if (!activeStylePreset) {
|
||||||
return <>Choose Preset</>;
|
return (
|
||||||
|
<Flex h="25px" alignItems="center">
|
||||||
|
<Text fontSize="sm" fontWeight="semibold" color="base.300">
|
||||||
|
Choose Preset
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Flex justifyContent="space-between" w="full" alignItems="center">
|
<Flex justifyContent="space-between" w="full" alignItems="center">
|
||||||
<Flex gap="2">
|
<Flex gap="2" alignItems="center">
|
||||||
<StylePresetImage presetImageUrl={activeStylePreset.image} />
|
<StylePresetImage imageWidth={25} presetImageUrl={activeStylePreset.image} />
|
||||||
<Flex flexDir="column">
|
<Flex flexDir="column">
|
||||||
<Text variant="subtext" fontSize="xs">
|
<Text fontSize="sm" fontWeight="semibold" color="base.300">
|
||||||
Prompt Style
|
|
||||||
</Text>
|
|
||||||
<Text fontSize="md" fontWeight="semibold">
|
|
||||||
{activeStylePreset.name}
|
{activeStylePreset.name}
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
@ -53,15 +55,15 @@ export const ActiveStylePreset = () => {
|
|||||||
<Flex gap="1">
|
<Flex gap="1">
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={handleFlattenPrompts}
|
onClick={handleFlattenPrompts}
|
||||||
variant="ghost"
|
variant="outline"
|
||||||
size="md"
|
size="sm"
|
||||||
aria-label="Flatten"
|
aria-label="Flatten"
|
||||||
icon={<CgPushDown />}
|
icon={<PiStackSimpleBold />}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={handleClearActiveStylePreset}
|
onClick={handleClearActiveStylePreset}
|
||||||
variant="ghost"
|
variant="outline"
|
||||||
size="md"
|
size="sm"
|
||||||
aria-label="Clear"
|
aria-label="Clear"
|
||||||
icon={<PiXBold />}
|
icon={<PiXBold />}
|
||||||
/>
|
/>
|
||||||
|
@ -5,29 +5,29 @@ import { PiImage } from 'react-icons/pi';
|
|||||||
const IMAGE_THUMBNAIL_SIZE = '40px';
|
const IMAGE_THUMBNAIL_SIZE = '40px';
|
||||||
const FALLBACK_ICON_SIZE = '24px';
|
const FALLBACK_ICON_SIZE = '24px';
|
||||||
|
|
||||||
const StylePresetImage = ({ presetImageUrl }: { presetImageUrl: string | null }) => {
|
const StylePresetImage = ({ presetImageUrl, imageWidth }: { presetImageUrl: string | null; imageWidth?: number }) => {
|
||||||
return (
|
return (
|
||||||
<Image
|
<Image
|
||||||
src={presetImageUrl || ''}
|
src={presetImageUrl || ''}
|
||||||
fallbackStrategy="beforeLoadOrError"
|
fallbackStrategy="beforeLoadOrError"
|
||||||
fallback={
|
fallback={
|
||||||
<Flex
|
<Flex
|
||||||
height={IMAGE_THUMBNAIL_SIZE}
|
height={imageWidth || IMAGE_THUMBNAIL_SIZE}
|
||||||
minWidth={IMAGE_THUMBNAIL_SIZE}
|
minWidth={imageWidth || IMAGE_THUMBNAIL_SIZE}
|
||||||
bg="base.650"
|
bg="base.650"
|
||||||
borderRadius="base"
|
borderRadius="base"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
>
|
>
|
||||||
<Icon color="base.500" as={PiImage} boxSize={FALLBACK_ICON_SIZE} />
|
<Icon color="base.500" as={PiImage} boxSize={imageWidth ? imageWidth / 2 : FALLBACK_ICON_SIZE} />
|
||||||
</Flex>
|
</Flex>
|
||||||
}
|
}
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
objectPosition="50% 50%"
|
objectPosition="50% 50%"
|
||||||
height={IMAGE_THUMBNAIL_SIZE}
|
height={imageWidth || IMAGE_THUMBNAIL_SIZE}
|
||||||
width={IMAGE_THUMBNAIL_SIZE}
|
width={imageWidth || IMAGE_THUMBNAIL_SIZE}
|
||||||
minHeight={IMAGE_THUMBNAIL_SIZE}
|
minHeight={imageWidth || IMAGE_THUMBNAIL_SIZE}
|
||||||
minWidth={IMAGE_THUMBNAIL_SIZE}
|
minWidth={imageWidth || IMAGE_THUMBNAIL_SIZE}
|
||||||
borderRadius="base"
|
borderRadius="base"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Badge, Flex, IconButton, Text } from '@invoke-ai/ui-library';
|
import { Badge, ConfirmationAlertDialog, Flex, IconButton, Text, useDisclosure } from '@invoke-ai/ui-library';
|
||||||
import type { MouseEvent } from 'react';
|
import type { MouseEvent } from 'react';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import ModelImage from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelImage';
|
import ModelImage from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelImage';
|
||||||
@ -14,6 +14,7 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithI
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const [deleteStylePreset] = useDeleteStylePresetMutation();
|
const [deleteStylePreset] = useDeleteStylePresetMutation();
|
||||||
const activeStylePreset = useAppSelector((s) => s.stylePreset.activeStylePreset);
|
const activeStylePreset = useAppSelector((s) => s.stylePreset.activeStylePreset);
|
||||||
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
|
|
||||||
const handleClickEdit = useCallback(
|
const handleClickEdit = useCallback(
|
||||||
(e: MouseEvent<HTMLButtonElement>) => {
|
(e: MouseEvent<HTMLButtonElement>) => {
|
||||||
@ -29,72 +30,94 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithI
|
|||||||
dispatch(isMenuOpenChanged(false));
|
dispatch(isMenuOpenChanged(false));
|
||||||
}, [dispatch, preset]);
|
}, [dispatch, preset]);
|
||||||
|
|
||||||
const handleDeletePreset = useCallback(
|
const handleClickDelete = useCallback(
|
||||||
async (e: MouseEvent<HTMLButtonElement>) => {
|
(e: MouseEvent<HTMLButtonElement>) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
try {
|
onOpen();
|
||||||
await deleteStylePreset(preset.id);
|
|
||||||
} catch (error) {}
|
|
||||||
},
|
},
|
||||||
[preset]
|
[dispatch, preset]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleDeletePreset = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
await deleteStylePreset(preset.id);
|
||||||
|
} catch (error) {}
|
||||||
|
}, [preset]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<>
|
||||||
gap="4"
|
<Flex
|
||||||
onClick={handleClickApply}
|
gap="4"
|
||||||
cursor="pointer"
|
onClick={handleClickApply}
|
||||||
_hover={{ backgroundColor: 'base.750' }}
|
cursor="pointer"
|
||||||
padding="10px"
|
_hover={{ backgroundColor: 'base.750' }}
|
||||||
borderRadius="base"
|
padding="10px"
|
||||||
alignItems="center"
|
borderRadius="base"
|
||||||
w="full"
|
alignItems="center"
|
||||||
>
|
w="full"
|
||||||
<StylePresetImage presetImageUrl={preset.image} />
|
>
|
||||||
<Flex flexDir="column" w="full">
|
<StylePresetImage presetImageUrl={preset.image} />
|
||||||
<Flex w="full" justifyContent="space-between">
|
<Flex flexDir="column" w="full">
|
||||||
<Flex alignItems="center" gap="2">
|
<Flex w="full" justifyContent="space-between">
|
||||||
<Text fontSize="md">{preset.name}</Text>
|
<Flex alignItems="center" gap="2">
|
||||||
{activeStylePreset && activeStylePreset.id === preset.id && (
|
<Text fontSize="md">{preset.name}</Text>
|
||||||
<Badge
|
{activeStylePreset && activeStylePreset.id === preset.id && (
|
||||||
color="invokeBlue.400"
|
<Badge
|
||||||
borderColor="invokeBlue.700"
|
color="invokeBlue.400"
|
||||||
borderWidth={1}
|
borderColor="invokeBlue.700"
|
||||||
bg="transparent"
|
borderWidth={1}
|
||||||
flexShrink={0}
|
bg="transparent"
|
||||||
>
|
flexShrink={0}
|
||||||
Active
|
>
|
||||||
</Badge>
|
Active
|
||||||
)}
|
</Badge>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<Flex alignItems="center" gap="1">
|
||||||
|
<IconButton
|
||||||
|
size="sm"
|
||||||
|
variant="ghost"
|
||||||
|
aria-label="Edit"
|
||||||
|
onClick={handleClickEdit}
|
||||||
|
icon={<PiPencilBold />}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
size="sm"
|
||||||
|
variant="ghost"
|
||||||
|
aria-label="Delete"
|
||||||
|
onClick={handleClickDelete}
|
||||||
|
icon={<PiTrashBold />}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex alignItems="center" gap="1">
|
<Flex flexDir="column">
|
||||||
<IconButton size="sm" variant="ghost" aria-label="Edit" onClick={handleClickEdit} icon={<PiPencilBold />} />
|
<Text fontSize="xs">
|
||||||
<IconButton
|
<Text as="span" fontWeight="semibold">
|
||||||
size="sm"
|
Positive prompt:
|
||||||
variant="ghost"
|
</Text>{' '}
|
||||||
aria-label="Delete"
|
{preset.preset_data.positive_prompt}
|
||||||
onClick={handleDeletePreset}
|
</Text>
|
||||||
icon={<PiTrashBold />}
|
<Text fontSize="xs">
|
||||||
/>
|
<Text as="span" fontWeight="semibold">
|
||||||
|
Negative prompt:
|
||||||
|
</Text>{' '}
|
||||||
|
{preset.preset_data.negative_prompt}
|
||||||
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex flexDir="column">
|
|
||||||
<Text fontSize="xs">
|
|
||||||
<Text as="span" fontWeight="semibold">
|
|
||||||
Positive prompt:
|
|
||||||
</Text>{' '}
|
|
||||||
{preset.preset_data.positive_prompt}
|
|
||||||
</Text>
|
|
||||||
<Text fontSize="xs">
|
|
||||||
<Text as="span" fontWeight="semibold">
|
|
||||||
Negative prompt:
|
|
||||||
</Text>{' '}
|
|
||||||
{preset.preset_data.negative_prompt}
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
<ConfirmationAlertDialog
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={onClose}
|
||||||
|
title={'Delete preset'}
|
||||||
|
acceptCallback={handleDeletePreset}
|
||||||
|
acceptButtonText={'Delete'}
|
||||||
|
>
|
||||||
|
<p>{'Delete Preset?'}</p>
|
||||||
|
<br />
|
||||||
|
</ConfirmationAlertDialog>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -16,16 +16,18 @@ export const StylePresetMenuTrigger = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
|
as="button"
|
||||||
onClick={handleToggle}
|
onClick={handleToggle}
|
||||||
backgroundColor="base.800"
|
backgroundColor="base.800"
|
||||||
justifyContent="space-between"
|
justifyContent="space-between"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
padding="5px 10px"
|
padding="5px 10px"
|
||||||
borderRadius="base"
|
borderRadius="base"
|
||||||
|
gap="2"
|
||||||
>
|
>
|
||||||
<ActiveStylePreset />
|
<ActiveStylePreset />
|
||||||
|
|
||||||
<Icon as={PiCaretDownBold} />
|
<Icon boxSize="15px" as={PiCaretDownBold} color="base.300" />
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -24,8 +24,8 @@ export const useStylePresetFields = (preset: StylePresetRecordWithImage | null)
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
name: preset.name,
|
name: preset.name,
|
||||||
positivePrompt: preset.preset_data.positive_prompt,
|
positivePrompt: preset.preset_data.positive_prompt || "",
|
||||||
negativePrompt: preset.preset_data.negative_prompt,
|
negativePrompt: preset.preset_data.negative_prompt || "",
|
||||||
image: file
|
image: file
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { ChakraProps } from '@invoke-ai/ui-library';
|
import type { ChakraProps } from '@invoke-ai/ui-library';
|
||||||
import { Box, Flex, Portal,Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library';
|
import { Box, Flex, Portal, Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
|
import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
|
||||||
import { ControlLayersPanelContent } from 'features/controlLayers/components/ControlLayersPanelContent';
|
import { ControlLayersPanelContent } from 'features/controlLayers/components/ControlLayersPanelContent';
|
||||||
@ -71,63 +71,64 @@ const ParametersPanelTextToImage = () => {
|
|||||||
<Box position="absolute" top={0} left={0} right={0} bottom={0} ref={ref}>
|
<Box position="absolute" top={0} left={0} right={0} bottom={0} ref={ref}>
|
||||||
<Portal containerRef={ref}>
|
<Portal containerRef={ref}>
|
||||||
{isMenuOpen && (
|
{isMenuOpen && (
|
||||||
<Box position="absolute" top={0} left={0} right={0} bottom={0}>
|
<Box position="absolute" top={0} left={0} right={0} bottom={0} layerStyle="second">
|
||||||
<OverlayScrollbarsComponent
|
<OverlayScrollbarsComponent
|
||||||
defer
|
defer
|
||||||
style={overlayScrollbarsStyles}
|
style={overlayScrollbarsStyles}
|
||||||
options={overlayScrollbarsParams.options}
|
options={overlayScrollbarsParams.options}
|
||||||
// backgroundColor="rgba(0,0,0,0.5)"
|
|
||||||
>
|
>
|
||||||
<Flex gap={2} flexDirection="column" h="full" w="full" layerStyle="second">
|
<Flex gap={2} flexDirection="column" h="full" w="full">
|
||||||
<StylePresetMenu />
|
<StylePresetMenu />
|
||||||
</Flex>
|
</Flex>
|
||||||
</OverlayScrollbarsComponent>
|
</OverlayScrollbarsComponent>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Portal>
|
</Portal>
|
||||||
<OverlayScrollbarsComponent defer style={overlayScrollbarsStyles} options={overlayScrollbarsParams.options}>
|
{!isMenuOpen && (
|
||||||
<Flex gap={2} flexDirection="column" h="full" w="full">
|
<OverlayScrollbarsComponent defer style={overlayScrollbarsStyles} options={overlayScrollbarsParams.options}>
|
||||||
<Prompts />
|
<Flex gap={2} flexDirection="column" h="full" w="full">
|
||||||
<Tabs
|
<Prompts />
|
||||||
defaultIndex={0}
|
<Tabs
|
||||||
variant="enclosed"
|
defaultIndex={0}
|
||||||
display="flex"
|
variant="enclosed"
|
||||||
flexDir="column"
|
display="flex"
|
||||||
w="full"
|
flexDir="column"
|
||||||
h="full"
|
w="full"
|
||||||
gap={2}
|
h="full"
|
||||||
onChange={onChangeTabs}
|
gap={2}
|
||||||
>
|
onChange={onChangeTabs}
|
||||||
<TabList gap={2} fontSize="sm" borderColor="base.800">
|
>
|
||||||
<Tab sx={baseStyles} _selected={selectedStyles} data-testid="generation-tab-settings-tab-button">
|
<TabList gap={2} fontSize="sm" borderColor="base.800">
|
||||||
{t('common.settingsLabel')}
|
<Tab sx={baseStyles} _selected={selectedStyles} data-testid="generation-tab-settings-tab-button">
|
||||||
</Tab>
|
{t('common.settingsLabel')}
|
||||||
<Tab
|
</Tab>
|
||||||
sx={baseStyles}
|
<Tab
|
||||||
_selected={selectedStyles}
|
sx={baseStyles}
|
||||||
data-testid="generation-tab-control-layers-tab-button"
|
_selected={selectedStyles}
|
||||||
>
|
data-testid="generation-tab-control-layers-tab-button"
|
||||||
{controlLayersTitle}
|
>
|
||||||
</Tab>
|
{controlLayersTitle}
|
||||||
</TabList>
|
</Tab>
|
||||||
<TabPanels w="full" h="full">
|
</TabList>
|
||||||
<TabPanel p={0} w="full" h="full">
|
<TabPanels w="full" h="full">
|
||||||
<Flex gap={2} flexDirection="column" h="full" w="full">
|
<TabPanel p={0} w="full" h="full">
|
||||||
<ImageSettingsAccordion />
|
<Flex gap={2} flexDirection="column" h="full" w="full">
|
||||||
<GenerationSettingsAccordion />
|
<ImageSettingsAccordion />
|
||||||
{activeTabName !== 'generation' && <ControlSettingsAccordion />}
|
<GenerationSettingsAccordion />
|
||||||
{activeTabName === 'canvas' && <CompositingSettingsAccordion />}
|
{activeTabName !== 'generation' && <ControlSettingsAccordion />}
|
||||||
{isSDXL && <RefinerSettingsAccordion />}
|
{activeTabName === 'canvas' && <CompositingSettingsAccordion />}
|
||||||
<AdvancedSettingsAccordion />
|
{isSDXL && <RefinerSettingsAccordion />}
|
||||||
</Flex>
|
<AdvancedSettingsAccordion />
|
||||||
</TabPanel>
|
</Flex>
|
||||||
<TabPanel p={0} w="full" h="full">
|
</TabPanel>
|
||||||
<ControlLayersPanelContent />
|
<TabPanel p={0} w="full" h="full">
|
||||||
</TabPanel>
|
<ControlLayersPanelContent />
|
||||||
</TabPanels>
|
</TabPanel>
|
||||||
</Tabs>
|
</TabPanels>
|
||||||
</Flex>
|
</Tabs>
|
||||||
</OverlayScrollbarsComponent>
|
</Flex>
|
||||||
|
</OverlayScrollbarsComponent>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
Loading…
Reference in New Issue
Block a user