feat(ui): iterate on layer actions

- Add lock toggle
- Tweak lock and enabled styles
- Update entity list action bar w/ delete & delete all
- Move add layer menu to action bar
- Adjust opacity slider style
This commit is contained in:
psychedelicious 2024-08-27 19:08:57 +10:00
parent e91c7c5a30
commit bc0b5335ff
20 changed files with 190 additions and 73 deletions

View File

@ -1654,7 +1654,6 @@
"autoSave": "Auto-save to Gallery",
"resetCanvas": "Reset Canvas",
"resetAll": "Reset All",
"deleteAll": "Delete All",
"clearCaches": "Clear Caches",
"recalculateRects": "Recalculate Rects",
"clipToBbox": "Clip Strokes to Bbox",
@ -1731,6 +1730,10 @@
"showingType": "Showing {{type}}",
"dynamicGrid": "Dynamic Grid",
"logDebugInfo": "Log Debug Info",
"locked": "Locked",
"unlocked": "Unlocked",
"deleteSelected": "Delete Selected",
"deleteAll": "Delete All",
"fill": {
"fillStyle": "Fill Style",
"solid": "Solid",

View File

@ -1,6 +1,5 @@
import { Flex } from '@invoke-ai/ui-library';
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
import { CanvasEntityOpacity } from 'features/controlLayers/components/common/CanvasEntityOpacity';
import { ControlLayerEntityList } from 'features/controlLayers/components/ControlLayer/ControlLayerEntityList';
import { InpaintMaskList } from 'features/controlLayers/components/InpaintMask/InpaintMaskList';
import { IPAdapterList } from 'features/controlLayers/components/IPAdapter/IPAdapterList';
@ -11,8 +10,7 @@ import { memo } from 'react';
export const CanvasEntityList = memo(() => {
return (
<ScrollableContent>
<Flex flexDir="column" gap={2} pt={2} data-testid="control-layers-layer-list" w="full" h="full">
<CanvasEntityOpacity />
<Flex flexDir="column" gap={2} data-testid="control-layers-layer-list" w="full" h="full">
<InpaintMaskList />
<RegionalGuidanceEntityList />
<IPAdapterList />

View File

@ -0,0 +1,18 @@
import { Flex, Spacer } from '@invoke-ai/ui-library';
import { EntityListActionBarAddLayerButton } from 'features/controlLayers/components/CanvasEntityList/EntityListActionBarAddLayerMenuButton';
import { EntityListActionBarDeleteButton } from 'features/controlLayers/components/CanvasEntityList/EntityListActionBarDeleteButton';
import { SelectedEntityOpacity } from 'features/controlLayers/components/CanvasEntityList/EntityListActionBarSelectedEntityOpacity';
import { memo } from 'react';
export const EntityListActionBar = memo(() => {
return (
<Flex w="full" py={1} px={1} gap={2}>
<SelectedEntityOpacity />
<Spacer />
<EntityListActionBarAddLayerButton />
<EntityListActionBarDeleteButton />
</Flex>
);
});
EntityListActionBar.displayName = 'EntityListActionBar';

View File

@ -1,21 +1,22 @@
import { IconButton, Menu, MenuButton, MenuList } from '@invoke-ai/ui-library';
import { CanvasEntityListMenuItems } from 'features/controlLayers/components/CanvasEntityList/CanvasEntityListMenuItems';
import { CanvasEntityListMenuItems } from 'features/controlLayers/components/CanvasEntityList/EntityListActionBarAddLayerMenuItems';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiDotsThreeOutlineFill } from 'react-icons/pi';
import { PiPlusBold } from 'react-icons/pi';
export const CanvasEntityListMenuButton = memo(() => {
export const EntityListActionBarAddLayerButton = memo(() => {
const { t } = useTranslation();
return (
<Menu>
<MenuButton
as={IconButton}
aria-label={t('accessibility.menu')}
icon={<PiDotsThreeOutlineFill />}
variant="link"
size="sm"
tooltip={t('controlLayers.addLayer')}
aria-label={t('controlLayers.addLayer')}
icon={<PiPlusBold />}
variant="ghost"
data-testid="control-layers-add-layer-menu-button"
alignSelf="stretch"
/>
<MenuList>
<CanvasEntityListMenuItems />
@ -24,4 +25,4 @@ export const CanvasEntityListMenuButton = memo(() => {
);
});
CanvasEntityListMenuButton.displayName = 'CanvasEntityListMenuButton';
EntityListActionBarAddLayerButton.displayName = 'EntityListActionBarAddLayerButton';

View File

@ -1,22 +1,19 @@
import { MenuDivider, MenuItem } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { MenuItem } from '@invoke-ai/ui-library';
import { useAppDispatch } from 'app/store/storeHooks';
import {
allEntitiesDeleted,
controlLayerAdded,
inpaintMaskAdded,
ipaAdded,
rasterLayerAdded,
rgAdded,
} from 'features/controlLayers/store/canvasSlice';
import { selectHasEntities } from 'features/controlLayers/store/selectors';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiPlusBold, PiTrashSimpleBold } from 'react-icons/pi';
import { PiPlusBold } from 'react-icons/pi';
export const CanvasEntityListMenuItems = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const hasEntities = useAppSelector(selectHasEntities);
const addInpaintMask = useCallback(() => {
dispatch(inpaintMaskAdded({ isSelected: true }));
}, [dispatch]);
@ -32,9 +29,6 @@ export const CanvasEntityListMenuItems = memo(() => {
const addIPAdapter = useCallback(() => {
dispatch(ipaAdded({ isSelected: true }));
}, [dispatch]);
const deleteAll = useCallback(() => {
dispatch(allEntitiesDeleted());
}, [dispatch]);
return (
<>
@ -53,10 +47,6 @@ export const CanvasEntityListMenuItems = memo(() => {
<MenuItem icon={<PiPlusBold />} onClick={addIPAdapter}>
{t('controlLayers.ipAdapter', { count: 1 })}
</MenuItem>
<MenuDivider />
<MenuItem onClick={deleteAll} icon={<PiTrashSimpleBold />} color="error.300" isDisabled={!hasEntities}>
{t('controlLayers.deleteAll', { count: 1 })}
</MenuItem>
</>
);
});

View File

@ -0,0 +1,39 @@
import { IconButton, useShiftModifier } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { allEntitiesDeleted, entityDeleted } from 'features/controlLayers/store/canvasSlice';
import { selectEntityCount, selectSelectedEntityIdentifier } from 'features/controlLayers/store/selectors';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiTrashSimpleFill } from 'react-icons/pi';
export const EntityListActionBarDeleteButton = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const selectedEntityIdentifier = useAppSelector(selectSelectedEntityIdentifier);
const entityCount = useAppSelector(selectEntityCount);
const shift = useShiftModifier();
const onClick = useCallback(() => {
if (shift) {
dispatch(allEntitiesDeleted());
return;
}
if (!selectedEntityIdentifier) {
return;
}
dispatch(entityDeleted({ entityIdentifier: selectedEntityIdentifier }));
}, [dispatch, selectedEntityIdentifier, shift]);
return (
<IconButton
onClick={onClick}
isDisabled={shift ? entityCount === 0 : !selectedEntityIdentifier}
size="sm"
variant="ghost"
aria-label={shift ? t('controlLayers.deleteAll') : t('controlLayers.deleteSelected')}
tooltip={shift ? t('controlLayers.deleteAll') : t('controlLayers.deleteSelected')}
icon={<PiTrashSimpleFill />}
/>
);
});
EntityListActionBarDeleteButton.displayName = 'EntityListActionBarDeleteButton';

View File

@ -77,7 +77,7 @@ const selectOpacity = createSelector(selectCanvasSlice, (canvas) => {
return selectedEntity.opacity;
});
export const CanvasEntityOpacity = memo(() => {
export const SelectedEntityOpacity = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const selectedEntityIdentifier = useAppSelector(selectSelectedEntityIdentifier);
@ -151,6 +151,8 @@ export const CanvasEntityOpacity = memo(() => {
defaultValue={1}
onKeyDown={onKeyDown}
clampValueOnBlur={false}
variant="outline"
isDisabled={selectedEntityIdentifier === null || selectedEntityIdentifier.type === 'ip_adapter'}
>
<NumberInputField paddingInlineEnd={7} />
<PopoverTrigger>
@ -186,4 +188,4 @@ export const CanvasEntityOpacity = memo(() => {
);
});
CanvasEntityOpacity.displayName = 'CanvasEntityOpacity';
SelectedEntityOpacity.displayName = 'SelectedEntityOpacity';

View File

@ -1,32 +1,22 @@
import { Box, ContextMenu, MenuList } from '@invoke-ai/ui-library';
import { Divider, Flex } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
import { CanvasAddEntityButtons } from 'features/controlLayers/components/CanvasAddEntityButtons';
import { CanvasEntityList } from 'features/controlLayers/components/CanvasEntityList/CanvasEntityList';
import { CanvasEntityListMenuItems } from 'features/controlLayers/components/CanvasEntityList/CanvasEntityListMenuItems';
import { EntityListActionBar } from 'features/controlLayers/components/CanvasEntityList/EntityListActionBar';
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
import { selectHasEntities } from 'features/controlLayers/store/selectors';
import { memo, useCallback } from 'react';
import { memo } from 'react';
export const CanvasPanelContent = memo(() => {
const hasEntities = useAppSelector(selectHasEntities);
const renderMenu = useCallback(
() => (
<MenuList>
<CanvasEntityListMenuItems />
</MenuList>
),
[]
);
return (
<CanvasManagerProviderGate>
<ContextMenu<HTMLDivElement> renderMenu={renderMenu}>
{(ref) => (
<Box ref={ref} w="full" h="full">
{!hasEntities && <CanvasAddEntityButtons />}
{hasEntities && <CanvasEntityList />}
</Box>
)}
</ContextMenu>
<Flex flexDir="column" gap={2} w="full" h="full">
<EntityListActionBar />
<Divider py={0} />
{!hasEntities && <CanvasAddEntityButtons />}
{hasEntities && <CanvasEntityList />}
</Flex>
</CanvasManagerProviderGate>
);
});

View File

@ -2,6 +2,7 @@ import { Spacer } from '@invoke-ai/ui-library';
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
import { CanvasEntityIsLockedToggle } from 'features/controlLayers/components/common/CanvasEntityIsLockedToggle';
import { CanvasEntityPreviewImage } from 'features/controlLayers/components/common/CanvasEntityPreviewImage';
import { CanvasEntitySettingsWrapper } from 'features/controlLayers/components/common/CanvasEntitySettingsWrapper';
import { CanvasEntityEditableTitle } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
@ -28,6 +29,7 @@ export const ControlLayer = memo(({ id }: Props) => {
<CanvasEntityEditableTitle />
<Spacer />
<ControlLayerBadges />
<CanvasEntityIsLockedToggle />
<CanvasEntityEnabledToggle />
</CanvasEntityHeader>
<CanvasEntitySettingsWrapper>

View File

@ -2,6 +2,7 @@ import { Spacer } from '@invoke-ai/ui-library';
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
import { CanvasEntityIsLockedToggle } from 'features/controlLayers/components/common/CanvasEntityIsLockedToggle';
import { CanvasEntityPreviewImage } from 'features/controlLayers/components/common/CanvasEntityPreviewImage';
import { CanvasEntityEditableTitle } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
import { EntityMaskAdapterGate } from 'features/controlLayers/contexts/EntityAdapterContext';
@ -26,6 +27,7 @@ export const InpaintMask = memo(({ id }: Props) => {
<CanvasEntityPreviewImage />
<CanvasEntityEditableTitle />
<Spacer />
<CanvasEntityIsLockedToggle />
<InpaintMaskMaskFillColorPicker />
<CanvasEntityEnabledToggle />
</CanvasEntityHeader>

View File

@ -2,6 +2,7 @@ import { Spacer } from '@invoke-ai/ui-library';
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
import { CanvasEntityIsLockedToggle } from 'features/controlLayers/components/common/CanvasEntityIsLockedToggle';
import { CanvasEntityPreviewImage } from 'features/controlLayers/components/common/CanvasEntityPreviewImage';
import { CanvasEntityEditableTitle } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
import { EntityLayerAdapterGate } from 'features/controlLayers/contexts/EntityAdapterContext';
@ -24,6 +25,7 @@ export const RasterLayer = memo(({ id }: Props) => {
<CanvasEntityPreviewImage />
<CanvasEntityEditableTitle />
<Spacer />
<CanvasEntityIsLockedToggle />
<CanvasEntityEnabledToggle />
</CanvasEntityHeader>
</CanvasEntityContainer>

View File

@ -2,6 +2,7 @@ import { Spacer } from '@invoke-ai/ui-library';
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
import { CanvasEntityIsLockedToggle } from 'features/controlLayers/components/common/CanvasEntityIsLockedToggle';
import { CanvasEntityPreviewImage } from 'features/controlLayers/components/common/CanvasEntityPreviewImage';
import { CanvasEntityEditableTitle } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
import { RegionalGuidanceBadges } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceBadges';
@ -30,6 +31,7 @@ export const RegionalGuidance = memo(({ id }: Props) => {
<Spacer />
<RegionalGuidanceBadges />
<RegionalGuidanceMaskFillColorPicker />
<CanvasEntityIsLockedToggle />
<CanvasEntityEnabledToggle />
</CanvasEntityHeader>
<RegionalGuidanceSettings />

View File

@ -11,7 +11,7 @@ const selectEntityIds = createMemoizedSelector(selectCanvasSlice, (canvas) => {
return canvas.regions.entities.map(mapId).reverse();
});
const selectIsSelected = createSelector(selectSelectedEntityIdentifier, (selectedEntityIdentifier) => {
return selectedEntityIdentifier?.type === 'raster_layer';
return selectedEntityIdentifier?.type === 'regional_guidance';
});
export const RegionalGuidanceEntityList = memo(() => {

View File

@ -1,32 +1,35 @@
import { IconButton } from '@invoke-ai/ui-library';
import { useAppDispatch } from 'app/store/storeHooks';
import { stopPropagation } from 'common/util/stopPropagation';
import { useBoolean } from 'common/hooks/useBoolean';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import { useEntityIsEnabled } from 'features/controlLayers/hooks/useEntityIsEnabled';
import { entityIsEnabledToggled } from 'features/controlLayers/store/canvasSlice';
import { memo, useCallback } from 'react';
import { memo, useCallback, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { PiCheckBold } from 'react-icons/pi';
export const CanvasEntityEnabledToggle = memo(() => {
const { t } = useTranslation();
const entityIdentifier = useEntityIdentifierContext();
const ref = useRef<HTMLButtonElement>(null);
const isEnabled = useEntityIsEnabled(entityIdentifier);
const dispatch = useAppDispatch();
const onClick = useCallback(() => {
dispatch(entityIsEnabledToggled({ entityIdentifier }));
}, [dispatch, entityIdentifier]);
const isHovered = useBoolean(false);
return (
<IconButton
ref={ref}
size="sm"
onMouseOver={isHovered.setTrue}
onMouseOut={isHovered.setFalse}
aria-label={t(isEnabled ? 'common.enabled' : 'common.disabled')}
tooltip={t(isEnabled ? 'common.enabled' : 'common.disabled')}
variant="outline"
icon={isEnabled ? <PiCheckBold /> : undefined}
variant="ghost"
icon={isEnabled || isHovered.isTrue ? <PiCheckBold /> : undefined}
onClick={onClick}
onDoubleClick={stopPropagation} // double click expands the layer
/>
);
});

View File

@ -38,13 +38,13 @@ export const CanvasEntityGroupList = memo(({ isSelected, type, children }: Props
boxSize={4}
as={PiCaretDownBold}
transform={collapse.isTrue ? undefined : 'rotate(-90deg)'}
fill={isSelected ? 'invokeBlue.300' : 'base.300'}
fill={isSelected ? 'base.200' : 'base.500'}
transitionProperty="common"
transitionDuration="fast"
/>
<Text
fontWeight="semibold"
color={isSelected ? 'invokeBlue.300' : 'base.300'}
color={isSelected ? 'base.200' : 'base.500'}
userSelect="none"
transitionProperty="common"
transitionDuration="fast"

View File

@ -0,0 +1,37 @@
import { IconButton } from '@invoke-ai/ui-library';
import { useAppDispatch } from 'app/store/storeHooks';
import { useBoolean } from 'common/hooks/useBoolean';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import { useEntityIsLocked } from 'features/controlLayers/hooks/useEntityIsLocked';
import { entityIsLockedToggled } from 'features/controlLayers/store/canvasSlice';
import { memo, useCallback, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { PiLockSimpleFill } from 'react-icons/pi';
export const CanvasEntityIsLockedToggle = memo(() => {
const { t } = useTranslation();
const entityIdentifier = useEntityIdentifierContext();
const ref = useRef<HTMLButtonElement>(null);
const isLocked = useEntityIsLocked(entityIdentifier);
const dispatch = useAppDispatch();
const onClick = useCallback(() => {
dispatch(entityIsLockedToggled({ entityIdentifier }));
}, [dispatch, entityIdentifier]);
const isHovered = useBoolean(false);
return (
<IconButton
ref={ref}
size="sm"
onMouseOver={isHovered.setTrue}
onMouseOut={isHovered.setFalse}
aria-label={t(isLocked ? 'controlLayers.locked' : 'controlLayers.unlocked')}
tooltip={t(isLocked ? 'controlLayers.locked' : 'controlLayers.unlocked')}
variant="ghost"
icon={isLocked || isHovered.isTrue ? <PiLockSimpleFill /> : undefined}
onClick={onClick}
/>
);
});
CanvasEntityIsLockedToggle.displayName = 'CanvasEntityIsLockedToggle';

View File

@ -0,0 +1,22 @@
import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks';
import { selectCanvasSlice, selectEntity } from 'features/controlLayers/store/selectors';
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
import { useMemo } from 'react';
export const useEntityIsLocked = (entityIdentifier: CanvasEntityIdentifier) => {
const selectIsLocked = useMemo(
() =>
createSelector(selectCanvasSlice, (canvas) => {
const entity = selectEntity(canvas, entityIdentifier);
if (!entity) {
return false;
} else {
return entity.isLocked;
}
}),
[entityIdentifier]
);
const isLocked = useAppSelector(selectIsLocked);
return isLocked;
};

View File

@ -131,6 +131,7 @@ export const canvasSlice = createSlice({
name: null,
type: 'raster_layer',
isEnabled: true,
isLocked: false,
objects: [],
opacity: 1,
position: { x: 0, y: 0 },
@ -191,6 +192,7 @@ export const canvasSlice = createSlice({
name: null,
type: 'control_layer',
isEnabled: true,
isLocked: false,
withTransparencyEffect: true,
objects: [],
opacity: 1,
@ -332,6 +334,7 @@ export const canvasSlice = createSlice({
id,
type: 'ip_adapter',
name: null,
isLocked: false,
isEnabled: true,
ipAdapter: deepClone(initialIPAdapter),
};
@ -420,6 +423,7 @@ export const canvasSlice = createSlice({
const entity: CanvasRegionalGuidanceState = {
id,
name: null,
isLocked: false,
type: 'regional_guidance',
isEnabled: true,
objects: [],
@ -630,6 +634,7 @@ export const canvasSlice = createSlice({
name: null,
type: 'inpaint_mask',
isEnabled: true,
isLocked: false,
objects: [],
opacity: 1,
position: { x: 0, y: 0 },
@ -849,6 +854,14 @@ export const canvasSlice = createSlice({
}
entity.isEnabled = !entity.isEnabled;
},
entityIsLockedToggled: (state, action: PayloadAction<EntityIdentifierPayload>) => {
const { entityIdentifier } = action.payload;
const entity = selectEntity(state, entityIdentifier);
if (!entity) {
return;
}
entity.isLocked = !entity.isLocked;
},
entityMoved: (state, action: PayloadAction<EntityMovedPayload>) => {
const { entityIdentifier, position } = action.payload;
const entity = selectEntity(state, entityIdentifier);
@ -1074,6 +1087,7 @@ export const {
entityNameChanged,
entityReset,
entityIsEnabledToggled,
entityIsLockedToggled,
entityMoved,
entityDuplicated,
entityRasterized,

View File

@ -529,11 +529,15 @@ const zIPAdapterConfig = z.object({
});
export type IPAdapterConfig = z.infer<typeof zIPAdapterConfig>;
const zCanvasIPAdapterState = z.object({
const zCanvasEntityBase = z.object({
id: zId,
name: zName,
type: z.literal('ip_adapter'),
isEnabled: z.boolean(),
isLocked: z.boolean(),
});
const zCanvasIPAdapterState = zCanvasEntityBase.extend({
type: z.literal('ip_adapter'),
ipAdapter: zIPAdapterConfig,
});
export type CanvasIPAdapterState = z.infer<typeof zCanvasIPAdapterState>;
@ -555,11 +559,8 @@ const zRegionalGuidanceIPAdapterConfig = z.object({
});
export type RegionalGuidanceIPAdapterConfig = z.infer<typeof zRegionalGuidanceIPAdapterConfig>;
const zCanvasRegionalGuidanceState = z.object({
id: zId,
name: zName,
const zCanvasRegionalGuidanceState = zCanvasEntityBase.extend({
type: z.literal('regional_guidance'),
isEnabled: z.boolean(),
position: zCoordinate,
opacity: zOpacity,
objects: z.array(zCanvasObjectState),
@ -571,11 +572,8 @@ const zCanvasRegionalGuidanceState = z.object({
});
export type CanvasRegionalGuidanceState = z.infer<typeof zCanvasRegionalGuidanceState>;
const zCanvasInpaintMaskState = z.object({
id: zId,
name: zName,
const zCanvasInpaintMaskState = zCanvasEntityBase.extend({
type: z.literal('inpaint_mask'),
isEnabled: z.boolean(),
position: zCoordinate,
fill: zFill,
opacity: zOpacity,
@ -600,11 +598,8 @@ const zT2IAdapterConfig = z.object({
});
export type T2IAdapterConfig = z.infer<typeof zT2IAdapterConfig>;
export const zCanvasRasterLayerState = z.object({
id: zId,
name: zName,
export const zCanvasRasterLayerState = zCanvasEntityBase.extend({
type: z.literal('raster_layer'),
isEnabled: z.boolean(),
position: zCoordinate,
opacity: zOpacity,
objects: z.array(zCanvasObjectState),

View File

@ -1,9 +1,8 @@
import type { ChakraProps } from '@invoke-ai/ui-library';
import { Box, Flex, Spacer, Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library';
import { Box, Flex, Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
import { CanvasEntityListMenuButton } from 'features/controlLayers/components/CanvasEntityList/CanvasEntityListMenuButton';
import { CanvasPanelContent } from 'features/controlLayers/components/CanvasPanelContent';
import { selectIsSDXL } from 'features/controlLayers/store/paramsSlice';
import { selectEntityCount } from 'features/controlLayers/store/selectors';
@ -100,8 +99,6 @@ const ParametersPanelTextToImage = () => {
>
{controlLayersTitle}
</Tab>
<Spacer />
<CanvasEntityListMenuButton />
</TabList>
<TabPanels w="full" h="full">
<TabPanel p={0} w="full" h="full">