mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): revise entity menus
This commit is contained in:
parent
1435557d1d
commit
9f2c815e13
@ -1646,7 +1646,7 @@
|
|||||||
"storeNotInitialized": "Store is not initialized"
|
"storeNotInitialized": "Store is not initialized"
|
||||||
},
|
},
|
||||||
"controlLayers": {
|
"controlLayers": {
|
||||||
"deleteAll": "Delete All",
|
"resetAll": "Reset All",
|
||||||
"addLayer": "Add Layer",
|
"addLayer": "Add Layer",
|
||||||
"moveToFront": "Move to Front",
|
"moveToFront": "Move to Front",
|
||||||
"moveToBack": "Move to Back",
|
"moveToBack": "Move to Back",
|
||||||
@ -1694,7 +1694,10 @@
|
|||||||
"layers_other": "Layers",
|
"layers_other": "Layers",
|
||||||
"objects_zero": "empty",
|
"objects_zero": "empty",
|
||||||
"objects_one": "{{count}} object",
|
"objects_one": "{{count}} object",
|
||||||
"objects_other": "{{count}} objects"
|
"objects_other": "{{count}} objects",
|
||||||
|
"filter": "Filter",
|
||||||
|
"convertToControlLayer": "Convert to Control Layer",
|
||||||
|
"convertToRasterLayer": "Convert to Raster Layer"
|
||||||
},
|
},
|
||||||
"upscaling": {
|
"upscaling": {
|
||||||
"upscale": "Upscale",
|
"upscale": "Upscale",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Button, Menu, MenuButton, MenuItem, MenuList } from '@invoke-ai/ui-library';
|
import { Button, Menu, MenuButton, MenuItem, MenuList } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { useAddCALayer, useAddIPALayer } from 'features/controlLayers/hooks/addLayerHooks';
|
import { useDefaultControlAdapter, useDefaultIPAdapter } from 'features/controlLayers/hooks/useLayerControlAdapter';
|
||||||
import { rasterLayerAdded, rgAdded } from 'features/controlLayers/store/canvasV2Slice';
|
import { controlLayerAdded, ipaAdded, rasterLayerAdded, rgAdded } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PiPlusBold } from 'react-icons/pi';
|
import { PiPlusBold } from 'react-icons/pi';
|
||||||
@ -9,14 +9,20 @@ import { PiPlusBold } from 'react-icons/pi';
|
|||||||
export const AddLayerButton = memo(() => {
|
export const AddLayerButton = memo(() => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const [addCALayer, isAddCALayerDisabled] = useAddCALayer();
|
const defaultControlAdapter = useDefaultControlAdapter();
|
||||||
const [addIPALayer, isAddIPALayerDisabled] = useAddIPALayer();
|
const defaultIPAdapter = useDefaultIPAdapter();
|
||||||
const addRGLayer = useCallback(() => {
|
const addRGLayer = useCallback(() => {
|
||||||
dispatch(rgAdded());
|
dispatch(rgAdded());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
const addRasterLayer = useCallback(() => {
|
const addRasterLayer = useCallback(() => {
|
||||||
dispatch(rasterLayerAdded({ isSelected: true }));
|
dispatch(rasterLayerAdded({ isSelected: true }));
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
const addControlLayer = useCallback(() => {
|
||||||
|
dispatch(controlLayerAdded({ isSelected: true, overrides: { controlAdapter: defaultControlAdapter } }));
|
||||||
|
}, [defaultControlAdapter, dispatch]);
|
||||||
|
const addIPAdapter = useCallback(() => {
|
||||||
|
dispatch(ipaAdded({ config: defaultIPAdapter }));
|
||||||
|
}, [defaultIPAdapter, dispatch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu>
|
<Menu>
|
||||||
@ -29,18 +35,10 @@ export const AddLayerButton = memo(() => {
|
|||||||
{t('controlLayers.addLayer')}
|
{t('controlLayers.addLayer')}
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
<MenuList>
|
<MenuList>
|
||||||
<MenuItem icon={<PiPlusBold />} onClick={addRGLayer}>
|
<MenuItem onClick={addRGLayer}>{t('controlLayers.regionalGuidanceLayer')}</MenuItem>
|
||||||
{t('controlLayers.regionalGuidanceLayer')}
|
<MenuItem onClick={addRasterLayer}>{t('controlLayers.rasterLayer')}</MenuItem>
|
||||||
</MenuItem>
|
<MenuItem onClick={addControlLayer}>{t('controlLayers.controlLayer')}</MenuItem>
|
||||||
<MenuItem icon={<PiPlusBold />} onClick={addRasterLayer}>
|
<MenuItem onClick={addIPAdapter}>{t('controlLayers.globalIPAdapterLayer')}</MenuItem>
|
||||||
{t('controlLayers.rasterLayer')}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem icon={<PiPlusBold />} onClick={addCALayer} isDisabled={isAddCALayerDisabled}>
|
|
||||||
{t('controlLayers.globalControlAdapterLayer')}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem icon={<PiPlusBold />} onClick={addIPALayer} isDisabled={isAddIPALayerDisabled}>
|
|
||||||
{t('controlLayers.globalIPAdapterLayer')}
|
|
||||||
</MenuItem>
|
|
||||||
</MenuList>
|
</MenuList>
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { Button, Flex } from '@invoke-ai/ui-library';
|
import { Button, Flex } from '@invoke-ai/ui-library';
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { useAddIPAdapterToRGLayer } from 'features/controlLayers/hooks/addLayerHooks';
|
import { useDefaultIPAdapter } from 'features/controlLayers/hooks/useLayerControlAdapter';
|
||||||
|
import { nanoid } from 'features/controlLayers/konva/util';
|
||||||
import {
|
import {
|
||||||
|
rgIPAdapterAdded,
|
||||||
rgNegativePromptChanged,
|
rgNegativePromptChanged,
|
||||||
rgPositivePromptChanged,
|
rgPositivePromptChanged,
|
||||||
selectCanvasV2Slice,
|
selectCanvasV2Slice,
|
||||||
@ -18,7 +20,7 @@ type AddPromptButtonProps = {
|
|||||||
export const AddPromptButtons = ({ id }: AddPromptButtonProps) => {
|
export const AddPromptButtons = ({ id }: AddPromptButtonProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const [addIPAdapter, isAddIPAdapterDisabled] = useAddIPAdapterToRGLayer(id);
|
const defaultIPAdapter = useDefaultIPAdapter();
|
||||||
const selectValidActions = useMemo(
|
const selectValidActions = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||||
@ -37,6 +39,11 @@ export const AddPromptButtons = ({ id }: AddPromptButtonProps) => {
|
|||||||
const addNegativePrompt = useCallback(() => {
|
const addNegativePrompt = useCallback(() => {
|
||||||
dispatch(rgNegativePromptChanged({ id, prompt: '' }));
|
dispatch(rgNegativePromptChanged({ id, prompt: '' }));
|
||||||
}, [dispatch, id]);
|
}, [dispatch, id]);
|
||||||
|
const addIPAdapter = useCallback(() => {
|
||||||
|
dispatch(
|
||||||
|
rgIPAdapterAdded({ id, ipAdapter: { ...defaultIPAdapter, id: nanoid(), type: 'ip_adapter', isEnabled: true } })
|
||||||
|
);
|
||||||
|
}, [defaultIPAdapter, dispatch, id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex w="full" p={2} justifyContent="space-between">
|
<Flex w="full" p={2} justifyContent="space-between">
|
||||||
@ -58,13 +65,7 @@ export const AddPromptButtons = ({ id }: AddPromptButtonProps) => {
|
|||||||
>
|
>
|
||||||
{t('common.negativePrompt')}
|
{t('common.negativePrompt')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button size="sm" variant="ghost" leftIcon={<PiPlusBold />} onClick={addIPAdapter}>
|
||||||
size="sm"
|
|
||||||
variant="ghost"
|
|
||||||
leftIcon={<PiPlusBold />}
|
|
||||||
onClick={addIPAdapter}
|
|
||||||
isDisabled={isAddIPAdapterDisabled}
|
|
||||||
>
|
|
||||||
{t('common.ipAdapter')}
|
{t('common.ipAdapter')}
|
||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -5,7 +5,6 @@ import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/com
|
|||||||
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
||||||
import { CanvasEntitySettingsWrapper } from 'features/controlLayers/components/common/CanvasEntitySettingsWrapper';
|
import { CanvasEntitySettingsWrapper } from 'features/controlLayers/components/common/CanvasEntitySettingsWrapper';
|
||||||
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
||||||
import { ControlLayerActionsMenu } from 'features/controlLayers/components/ControlLayer/ControlLayerActionsMenu';
|
|
||||||
import { ControlLayerControlAdapter } from 'features/controlLayers/components/ControlLayer/ControlLayerControlAdapter';
|
import { ControlLayerControlAdapter } from 'features/controlLayers/components/ControlLayer/ControlLayerControlAdapter';
|
||||||
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||||
@ -25,7 +24,6 @@ export const ControlLayer = memo(({ id }: Props) => {
|
|||||||
<CanvasEntityEnabledToggle />
|
<CanvasEntityEnabledToggle />
|
||||||
<CanvasEntityTitle />
|
<CanvasEntityTitle />
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<ControlLayerActionsMenu />
|
|
||||||
<CanvasEntityDeleteButton />
|
<CanvasEntityDeleteButton />
|
||||||
</CanvasEntityHeader>
|
</CanvasEntityHeader>
|
||||||
<CanvasEntitySettingsWrapper>
|
<CanvasEntitySettingsWrapper>
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
import { Menu, MenuList } from '@invoke-ai/ui-library';
|
|
||||||
import { CanvasEntityActionMenuItems } from 'features/controlLayers/components/common/CanvasEntityActionMenuItems';
|
|
||||||
import { CanvasEntityMenuButton } from 'features/controlLayers/components/common/CanvasEntityMenuButton';
|
|
||||||
import { memo } from 'react';
|
|
||||||
|
|
||||||
export const ControlLayerActionsMenu = memo(() => {
|
|
||||||
return (
|
|
||||||
<Menu>
|
|
||||||
<CanvasEntityMenuButton />
|
|
||||||
<MenuList>
|
|
||||||
<CanvasEntityActionMenuItems />
|
|
||||||
</MenuList>
|
|
||||||
</Menu>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
ControlLayerActionsMenu.displayName = 'ControlLayerActionsMenu';
|
|
@ -2,8 +2,8 @@ import { Flex } from '@invoke-ai/ui-library';
|
|||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { BeginEndStepPct } from 'features/controlLayers/components/common/BeginEndStepPct';
|
import { BeginEndStepPct } from 'features/controlLayers/components/common/BeginEndStepPct';
|
||||||
import { Weight } from 'features/controlLayers/components/common/Weight';
|
import { Weight } from 'features/controlLayers/components/common/Weight';
|
||||||
import { ControlAdapterControlModeSelect } from 'features/controlLayers/components/ControlAdapter/ControlAdapterControlModeSelect';
|
import { ControlLayerControlAdapterControlMode } from 'features/controlLayers/components/ControlLayer/ControlLayerControlAdapterControlMode';
|
||||||
import { ControlAdapterModel } from 'features/controlLayers/components/ControlAdapter/ControlAdapterModel';
|
import { ControlLayerControlAdapterModel } from 'features/controlLayers/components/ControlLayer/ControlLayerControlAdapterModel';
|
||||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
import { useControlLayerControlAdapter } from 'features/controlLayers/hooks/useLayerControlAdapter';
|
import { useControlLayerControlAdapter } from 'features/controlLayers/hooks/useLayerControlAdapter';
|
||||||
import {
|
import {
|
||||||
@ -51,11 +51,11 @@ export const ControlLayerControlAdapter = memo(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex flexDir="column" gap={3} position="relative" w="full">
|
<Flex flexDir="column" gap={3} position="relative" w="full">
|
||||||
<ControlAdapterModel modelKey={controlAdapter.model?.key ?? null} onChange={onChangeModel} />
|
<ControlLayerControlAdapterModel modelKey={controlAdapter.model?.key ?? null} onChange={onChangeModel} />
|
||||||
<Weight weight={controlAdapter.weight} onChange={onChangeWeight} />
|
<Weight weight={controlAdapter.weight} onChange={onChangeWeight} />
|
||||||
<BeginEndStepPct beginEndStepPct={controlAdapter.beginEndStepPct} onChange={onChangeBeginEndStepPct} />
|
<BeginEndStepPct beginEndStepPct={controlAdapter.beginEndStepPct} onChange={onChangeBeginEndStepPct} />
|
||||||
{controlAdapter.type === 'controlnet' && (
|
{controlAdapter.type === 'controlnet' && (
|
||||||
<ControlAdapterControlModeSelect controlMode={controlAdapter.controlMode} onChange={onChangeControlMode} />
|
<ControlLayerControlAdapterControlMode controlMode={controlAdapter.controlMode} onChange={onChangeControlMode} />
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
@ -12,7 +12,7 @@ type Props = {
|
|||||||
onChange: (controlMode: ControlModeV2) => void;
|
onChange: (controlMode: ControlModeV2) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ControlAdapterControlModeSelect = memo(({ controlMode, onChange }: Props) => {
|
export const ControlLayerControlAdapterControlMode = memo(({ controlMode, onChange }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const CONTROL_MODE_DATA = useMemo(
|
const CONTROL_MODE_DATA = useMemo(
|
||||||
() => [
|
() => [
|
||||||
@ -57,4 +57,4 @@ export const ControlAdapterControlModeSelect = memo(({ controlMode, onChange }:
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
ControlAdapterControlModeSelect.displayName = 'ControlAdapterControlModeSelect';
|
ControlLayerControlAdapterControlMode.displayName = 'ControlLayerControlAdapterControlMode';
|
@ -11,7 +11,7 @@ type Props = {
|
|||||||
onChange: (modelConfig: ControlNetModelConfig | T2IAdapterModelConfig) => void;
|
onChange: (modelConfig: ControlNetModelConfig | T2IAdapterModelConfig) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ControlAdapterModel = memo(({ modelKey, onChange: onChangeModel }: Props) => {
|
export const ControlLayerControlAdapterModel = memo(({ modelKey, onChange: onChangeModel }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const currentBaseModel = useAppSelector((s) => s.canvasV2.params.model?.base);
|
const currentBaseModel = useAppSelector((s) => s.canvasV2.params.model?.base);
|
||||||
const [modelConfigs, { isLoading }] = useControlNetAndT2IAdapterModels();
|
const [modelConfigs, { isLoading }] = useControlNetAndT2IAdapterModels();
|
||||||
@ -60,4 +60,4 @@ export const ControlAdapterModel = memo(({ modelKey, onChange: onChangeModel }:
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
ControlAdapterModel.displayName = 'ControlAdapterModel';
|
ControlLayerControlAdapterModel.displayName = 'ControlLayerControlAdapterModel';
|
@ -0,0 +1,23 @@
|
|||||||
|
import { MenuDivider } from '@invoke-ai/ui-library';
|
||||||
|
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
|
||||||
|
import { CanvasEntityMenuItemsDelete } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDelete';
|
||||||
|
import { CanvasEntityMenuItemsFilter } from 'features/controlLayers/components/common/CanvasEntityMenuItemsFilter';
|
||||||
|
import { CanvasEntityMenuItemsReset } from 'features/controlLayers/components/common/CanvasEntityMenuItemsReset';
|
||||||
|
import { ControlLayerMenuItemsControlToRaster } from 'features/controlLayers/components/ControlLayer/ControlLayerMenuItemsControlToRaster';
|
||||||
|
import { memo } from 'react';
|
||||||
|
|
||||||
|
export const ControlLayerMenuItems = memo(() => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CanvasEntityMenuItemsFilter />
|
||||||
|
<ControlLayerMenuItemsControlToRaster />
|
||||||
|
<MenuDivider />
|
||||||
|
<CanvasEntityMenuItemsArrange />
|
||||||
|
<MenuDivider />
|
||||||
|
<CanvasEntityMenuItemsReset />
|
||||||
|
<CanvasEntityMenuItemsDelete />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
ControlLayerMenuItems.displayName = 'ControlLayerMenuItems';
|
@ -0,0 +1,25 @@
|
|||||||
|
import { MenuItem } from '@invoke-ai/ui-library';
|
||||||
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
|
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
|
import { controlLayerConvertedToRasterLayer } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { PiLightningBold } from 'react-icons/pi';
|
||||||
|
|
||||||
|
export const ControlLayerMenuItemsControlToRaster = memo(() => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const entityIdentifier = useEntityIdentifierContext();
|
||||||
|
|
||||||
|
const convertControlLayerToRasterLayer = useCallback(() => {
|
||||||
|
dispatch(controlLayerConvertedToRasterLayer({ id: entityIdentifier.id }));
|
||||||
|
}, [dispatch, entityIdentifier.id]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuItem onClick={convertControlLayerToRasterLayer} icon={<PiLightningBold />}>
|
||||||
|
{t('controlLayers.convertToRasterLayer')}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
ControlLayerMenuItemsControlToRaster.displayName = 'ControlLayerMenuItemsControlToRaster';
|
@ -3,8 +3,8 @@ import { Flex } from '@invoke-ai/ui-library';
|
|||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
import { AddLayerButton } from 'features/controlLayers/components/AddLayerButton';
|
import { AddLayerButton } from 'features/controlLayers/components/AddLayerButton';
|
||||||
import { CanvasEntityList } from 'features/controlLayers/components/CanvasEntityList';
|
import { CanvasEntityList } from 'features/controlLayers/components/CanvasEntityList';
|
||||||
import { DeleteAllLayersButton } from 'features/controlLayers/components/DeleteAllLayersButton';
|
|
||||||
import { Filter } from 'features/controlLayers/components/Filters/Filter';
|
import { Filter } from 'features/controlLayers/components/Filters/Filter';
|
||||||
|
import { ResetAllEntitiesButton } from 'features/controlLayers/components/ResetAllEntitiesButton';
|
||||||
import { $filteringEntity } from 'features/controlLayers/store/canvasV2Slice';
|
import { $filteringEntity } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import ResizeHandle from 'features/ui/components/tabs/ResizeHandle';
|
import ResizeHandle from 'features/ui/components/tabs/ResizeHandle';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
@ -18,7 +18,7 @@ export const ControlLayersPanelContent = memo(() => {
|
|||||||
<Flex flexDir="column" gap={2} w="full" h="full">
|
<Flex flexDir="column" gap={2} w="full" h="full">
|
||||||
<Flex justifyContent="space-around">
|
<Flex justifyContent="space-around">
|
||||||
<AddLayerButton />
|
<AddLayerButton />
|
||||||
<DeleteAllLayersButton />
|
<ResetAllEntitiesButton />
|
||||||
</Flex>
|
</Flex>
|
||||||
<CanvasEntityList />
|
<CanvasEntityList />
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -5,7 +5,6 @@ import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/com
|
|||||||
import { CanvasEntityGroupTitle } from 'features/controlLayers/components/common/CanvasEntityGroupTitle';
|
import { CanvasEntityGroupTitle } from 'features/controlLayers/components/common/CanvasEntityGroupTitle';
|
||||||
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
||||||
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
||||||
import { InpaintMaskActionsMenu } from 'features/controlLayers/components/InpaintMask/InpaintMaskActionsMenu';
|
|
||||||
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||||
import { memo, useMemo } from 'react';
|
import { memo, useMemo } from 'react';
|
||||||
@ -28,7 +27,6 @@ export const InpaintMask = memo(() => {
|
|||||||
<CanvasEntityTitle />
|
<CanvasEntityTitle />
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<InpaintMaskMaskFillColorPicker />
|
<InpaintMaskMaskFillColorPicker />
|
||||||
<InpaintMaskActionsMenu />
|
|
||||||
</CanvasEntityHeader>
|
</CanvasEntityHeader>
|
||||||
</CanvasEntityContainer>
|
</CanvasEntityContainer>
|
||||||
</EntityIdentifierContext.Provider>
|
</EntityIdentifierContext.Provider>
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
import { Menu, MenuList } from '@invoke-ai/ui-library';
|
|
||||||
import { CanvasEntityActionMenuItems } from 'features/controlLayers/components/common/CanvasEntityActionMenuItems';
|
|
||||||
import { CanvasEntityMenuButton } from 'features/controlLayers/components/common/CanvasEntityMenuButton';
|
|
||||||
import { memo } from 'react';
|
|
||||||
|
|
||||||
export const InpaintMaskActionsMenu = memo(() => {
|
|
||||||
return (
|
|
||||||
<Menu>
|
|
||||||
<CanvasEntityMenuButton />
|
|
||||||
<MenuList>
|
|
||||||
<CanvasEntityActionMenuItems />
|
|
||||||
</MenuList>
|
|
||||||
</Menu>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
InpaintMaskActionsMenu.displayName = 'InpaintMaskActionsMenu';
|
|
@ -0,0 +1,12 @@
|
|||||||
|
import { CanvasEntityMenuItemsReset } from 'features/controlLayers/components/common/CanvasEntityMenuItemsReset';
|
||||||
|
import { memo } from 'react';
|
||||||
|
|
||||||
|
export const InpaintMaskMenuItems = memo(() => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CanvasEntityMenuItemsReset />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
InpaintMaskMenuItems.displayName = 'InpaintMaskMenuItems';
|
@ -4,7 +4,6 @@ import { CanvasEntityDeleteButton } from 'features/controlLayers/components/comm
|
|||||||
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
||||||
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
||||||
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
||||||
import { RasterLayerActionsMenu } from 'features/controlLayers/components/RasterLayer/RasterLayerActionsMenu';
|
|
||||||
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||||
import { memo, useMemo } from 'react';
|
import { memo, useMemo } from 'react';
|
||||||
@ -23,7 +22,6 @@ export const RasterLayer = memo(({ id }: Props) => {
|
|||||||
<CanvasEntityEnabledToggle />
|
<CanvasEntityEnabledToggle />
|
||||||
<CanvasEntityTitle />
|
<CanvasEntityTitle />
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<RasterLayerActionsMenu />
|
|
||||||
<CanvasEntityDeleteButton />
|
<CanvasEntityDeleteButton />
|
||||||
</CanvasEntityHeader>
|
</CanvasEntityHeader>
|
||||||
</CanvasEntityContainer>
|
</CanvasEntityContainer>
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
import { Menu, MenuList } from '@invoke-ai/ui-library';
|
|
||||||
import { CanvasEntityActionMenuItems } from 'features/controlLayers/components/common/CanvasEntityActionMenuItems';
|
|
||||||
import { CanvasEntityMenuButton } from 'features/controlLayers/components/common/CanvasEntityMenuButton';
|
|
||||||
import { memo } from 'react';
|
|
||||||
|
|
||||||
export const RasterLayerActionsMenu = memo(() => {
|
|
||||||
return (
|
|
||||||
<Menu>
|
|
||||||
<CanvasEntityMenuButton />
|
|
||||||
<MenuList>
|
|
||||||
<CanvasEntityActionMenuItems />
|
|
||||||
</MenuList>
|
|
||||||
</Menu>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
RasterLayerActionsMenu.displayName = 'RasterLayerActionsMenu';
|
|
@ -0,0 +1,23 @@
|
|||||||
|
import { MenuDivider } from '@invoke-ai/ui-library';
|
||||||
|
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
|
||||||
|
import { CanvasEntityMenuItemsDelete } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDelete';
|
||||||
|
import { CanvasEntityMenuItemsFilter } from 'features/controlLayers/components/common/CanvasEntityMenuItemsFilter';
|
||||||
|
import { CanvasEntityMenuItemsReset } from 'features/controlLayers/components/common/CanvasEntityMenuItemsReset';
|
||||||
|
import { RasterLayerMenuItemsRasterToControl } from 'features/controlLayers/components/RasterLayer/RasterLayerMenuItemsRasterToControl';
|
||||||
|
import { memo } from 'react';
|
||||||
|
|
||||||
|
export const RasterLayerMenuItems = memo(() => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CanvasEntityMenuItemsFilter />
|
||||||
|
<RasterLayerMenuItemsRasterToControl />
|
||||||
|
<MenuDivider />
|
||||||
|
<CanvasEntityMenuItemsArrange />
|
||||||
|
<MenuDivider />
|
||||||
|
<CanvasEntityMenuItemsReset />
|
||||||
|
<CanvasEntityMenuItemsDelete />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
RasterLayerMenuItems.displayName = 'RasterLayerMenuItems';
|
@ -0,0 +1,28 @@
|
|||||||
|
import { MenuItem } from '@invoke-ai/ui-library';
|
||||||
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
|
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
|
import { useDefaultControlAdapter } from 'features/controlLayers/hooks/useLayerControlAdapter';
|
||||||
|
import { rasterLayerConvertedToControlLayer } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { PiLightningBold } from 'react-icons/pi';
|
||||||
|
|
||||||
|
export const RasterLayerMenuItemsRasterToControl = memo(() => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const entityIdentifier = useEntityIdentifierContext();
|
||||||
|
|
||||||
|
const defaultControlAdapter = useDefaultControlAdapter();
|
||||||
|
|
||||||
|
const convertRasterLayerToControlLayer = useCallback(() => {
|
||||||
|
dispatch(rasterLayerConvertedToControlLayer({ id: entityIdentifier.id, controlAdapter: defaultControlAdapter }));
|
||||||
|
}, [dispatch, defaultControlAdapter, entityIdentifier.id]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuItem onClick={convertRasterLayerToControlLayer} icon={<PiLightningBold />}>
|
||||||
|
{t('controlLayers.convertToControlLayer')}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
RasterLayerMenuItemsRasterToControl.displayName = 'RasterLayerMenuItemsRasterToControl';
|
@ -4,7 +4,6 @@ import { CanvasEntityDeleteButton } from 'features/controlLayers/components/comm
|
|||||||
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
||||||
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
||||||
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
||||||
import { RegionalGuidanceActionsMenu } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceActionsMenu';
|
|
||||||
import { RegionalGuidanceBadges } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceBadges';
|
import { RegionalGuidanceBadges } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceBadges';
|
||||||
import { RegionalGuidanceSettings } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceSettings';
|
import { RegionalGuidanceSettings } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceSettings';
|
||||||
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
@ -30,7 +29,6 @@ export const RegionalGuidance = memo(({ id }: Props) => {
|
|||||||
<RegionalGuidanceBadges />
|
<RegionalGuidanceBadges />
|
||||||
<RegionalGuidanceMaskFillColorPicker />
|
<RegionalGuidanceMaskFillColorPicker />
|
||||||
<RegionalGuidanceSettingsPopover />
|
<RegionalGuidanceSettingsPopover />
|
||||||
<RegionalGuidanceActionsMenu />
|
|
||||||
<CanvasEntityDeleteButton />
|
<CanvasEntityDeleteButton />
|
||||||
</CanvasEntityHeader>
|
</CanvasEntityHeader>
|
||||||
<RegionalGuidanceSettings />
|
<RegionalGuidanceSettings />
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
import { Menu, MenuDivider, MenuItem, MenuList } from '@invoke-ai/ui-library';
|
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|
||||||
import { CanvasEntityActionMenuItems } from 'features/controlLayers/components/common/CanvasEntityActionMenuItems';
|
|
||||||
import { CanvasEntityMenuButton } from 'features/controlLayers/components/common/CanvasEntityMenuButton';
|
|
||||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
|
||||||
import { useAddIPAdapterToRGLayer } from 'features/controlLayers/hooks/addLayerHooks';
|
|
||||||
import {
|
|
||||||
rgNegativePromptChanged,
|
|
||||||
rgPositivePromptChanged,
|
|
||||||
selectCanvasV2Slice,
|
|
||||||
} from 'features/controlLayers/store/canvasV2Slice';
|
|
||||||
import { selectRGOrThrow } from 'features/controlLayers/store/regionsReducers';
|
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { PiPlusBold } from 'react-icons/pi';
|
|
||||||
|
|
||||||
export const RegionalGuidanceActionsMenu = memo(() => {
|
|
||||||
const { id } = useEntityIdentifierContext();
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const [onAddIPAdapter, isAddIPAdapterDisabled] = useAddIPAdapterToRGLayer(id);
|
|
||||||
const selectActionsValidity = useMemo(
|
|
||||||
() =>
|
|
||||||
createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
|
||||||
const rg = selectRGOrThrow(canvasV2, id);
|
|
||||||
return {
|
|
||||||
isAddPositivePromptDisabled: rg.positivePrompt === null,
|
|
||||||
isAddNegativePromptDisabled: rg.negativePrompt === null,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
[id]
|
|
||||||
);
|
|
||||||
const actions = useAppSelector(selectActionsValidity);
|
|
||||||
const onAddPositivePrompt = useCallback(() => {
|
|
||||||
dispatch(rgPositivePromptChanged({ id: id, prompt: '' }));
|
|
||||||
}, [dispatch, id]);
|
|
||||||
const onAddNegativePrompt = useCallback(() => {
|
|
||||||
dispatch(rgNegativePromptChanged({ id: id, prompt: '' }));
|
|
||||||
}, [dispatch, id]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Menu>
|
|
||||||
<CanvasEntityMenuButton />
|
|
||||||
<MenuList>
|
|
||||||
<MenuItem onClick={onAddPositivePrompt} isDisabled={actions.isAddPositivePromptDisabled} icon={<PiPlusBold />}>
|
|
||||||
{t('controlLayers.addPositivePrompt')}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem onClick={onAddNegativePrompt} isDisabled={actions.isAddNegativePromptDisabled} icon={<PiPlusBold />}>
|
|
||||||
{t('controlLayers.addNegativePrompt')}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem onClick={onAddIPAdapter} icon={<PiPlusBold />} isDisabled={isAddIPAdapterDisabled}>
|
|
||||||
{t('controlLayers.addIPAdapter')}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuDivider />
|
|
||||||
<CanvasEntityActionMenuItems />
|
|
||||||
</MenuList>
|
|
||||||
</Menu>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
RegionalGuidanceActionsMenu.displayName = 'RegionalGuidanceActionsMenu';
|
|
@ -0,0 +1,21 @@
|
|||||||
|
import { MenuDivider } from '@invoke-ai/ui-library';
|
||||||
|
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
|
||||||
|
import { CanvasEntityMenuItemsDelete } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDelete';
|
||||||
|
import { CanvasEntityMenuItemsReset } from 'features/controlLayers/components/common/CanvasEntityMenuItemsReset';
|
||||||
|
import { RegionalGuidanceMenuItemsAddPromptsAndIPAdapter } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsAddPromptsAndIPAdapter';
|
||||||
|
import { memo } from 'react';
|
||||||
|
|
||||||
|
export const RegionalGuidanceMenuItems = memo(() => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<RegionalGuidanceMenuItemsAddPromptsAndIPAdapter />
|
||||||
|
<MenuDivider />
|
||||||
|
<CanvasEntityMenuItemsArrange />
|
||||||
|
<MenuDivider />
|
||||||
|
<CanvasEntityMenuItemsReset />
|
||||||
|
<CanvasEntityMenuItemsDelete />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
RegionalGuidanceMenuItems.displayName = 'RegionalGuidanceMenuItems';
|
@ -0,0 +1,58 @@
|
|||||||
|
import { MenuItem } from '@invoke-ai/ui-library';
|
||||||
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
|
import { useDefaultIPAdapter } from 'features/controlLayers/hooks/useLayerControlAdapter';
|
||||||
|
import { nanoid } from 'features/controlLayers/konva/util';
|
||||||
|
import {
|
||||||
|
rgIPAdapterAdded,
|
||||||
|
rgNegativePromptChanged,
|
||||||
|
rgPositivePromptChanged,
|
||||||
|
selectCanvasV2Slice,
|
||||||
|
} from 'features/controlLayers/store/canvasV2Slice';
|
||||||
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
export const RegionalGuidanceMenuItemsAddPromptsAndIPAdapter = memo(() => {
|
||||||
|
const { id } = useEntityIdentifierContext();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const defaultIPAdapter = useDefaultIPAdapter();
|
||||||
|
const selectValidActions = useMemo(
|
||||||
|
() =>
|
||||||
|
createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||||
|
const rg = canvasV2.regions.entities.find((rg) => rg.id === id);
|
||||||
|
return {
|
||||||
|
canAddPositivePrompt: rg?.positivePrompt === null,
|
||||||
|
canAddNegativePrompt: rg?.negativePrompt === null,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
[id]
|
||||||
|
);
|
||||||
|
const validActions = useAppSelector(selectValidActions);
|
||||||
|
const addPositivePrompt = useCallback(() => {
|
||||||
|
dispatch(rgPositivePromptChanged({ id: id, prompt: '' }));
|
||||||
|
}, [dispatch, id]);
|
||||||
|
const addNegativePrompt = useCallback(() => {
|
||||||
|
dispatch(rgNegativePromptChanged({ id: id, prompt: '' }));
|
||||||
|
}, [dispatch, id]);
|
||||||
|
const addIPAdapter = useCallback(() => {
|
||||||
|
dispatch(
|
||||||
|
rgIPAdapterAdded({ id, ipAdapter: { ...defaultIPAdapter, id: nanoid(), type: 'ip_adapter', isEnabled: true } })
|
||||||
|
);
|
||||||
|
}, [defaultIPAdapter, dispatch, id]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<MenuItem onClick={addPositivePrompt} isDisabled={!validActions.canAddPositivePrompt}>
|
||||||
|
{t('controlLayers.addPositivePrompt')}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={addNegativePrompt} isDisabled={!validActions.canAddNegativePrompt}>
|
||||||
|
{t('controlLayers.addNegativePrompt')}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={addIPAdapter}>{t('controlLayers.addIPAdapter')}</MenuItem>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
RegionalGuidanceMenuItemsAddPromptsAndIPAdapter.displayName = 'RegionalGuidanceMenuItemsExtra';
|
@ -1,21 +1,13 @@
|
|||||||
import { Button } from '@invoke-ai/ui-library';
|
import { Button } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { allEntitiesDeleted } from 'features/controlLayers/store/canvasV2Slice';
|
import { allEntitiesDeleted } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PiTrashSimpleBold } from 'react-icons/pi';
|
import { PiTrashSimpleBold } from 'react-icons/pi';
|
||||||
|
|
||||||
export const DeleteAllLayersButton = memo(() => {
|
export const ResetAllEntitiesButton = memo(() => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const entityCount = useAppSelector((s) => {
|
|
||||||
return (
|
|
||||||
s.canvasV2.regions.entities.length +
|
|
||||||
// s.canvasV2.controlAdapters.entities.length +
|
|
||||||
s.canvasV2.ipAdapters.entities.length +
|
|
||||||
s.canvasV2.rasterLayers.entities.length
|
|
||||||
);
|
|
||||||
});
|
|
||||||
const onClick = useCallback(() => {
|
const onClick = useCallback(() => {
|
||||||
dispatch(allEntitiesDeleted());
|
dispatch(allEntitiesDeleted());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
@ -26,12 +18,11 @@ export const DeleteAllLayersButton = memo(() => {
|
|||||||
leftIcon={<PiTrashSimpleBold />}
|
leftIcon={<PiTrashSimpleBold />}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
colorScheme="error"
|
colorScheme="error"
|
||||||
isDisabled={entityCount === 0}
|
|
||||||
data-testid="control-layers-delete-all-layers-button"
|
data-testid="control-layers-delete-all-layers-button"
|
||||||
>
|
>
|
||||||
{t('controlLayers.deleteAll')}
|
{t('controlLayers.resetAll')}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
DeleteAllLayersButton.displayName = 'DeleteAllLayersButton';
|
ResetAllEntitiesButton.displayName = 'ResetAllEntitiesButton';
|
@ -1,200 +0,0 @@
|
|||||||
import { MenuDivider, MenuItem } from '@invoke-ai/ui-library';
|
|
||||||
import { useStore } from '@nanostores/react';
|
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|
||||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
|
||||||
import { useDefaultControlAdapter } from 'features/controlLayers/hooks/useLayerControlAdapter';
|
|
||||||
import { $canvasManager } from 'features/controlLayers/konva/CanvasManager';
|
|
||||||
import {
|
|
||||||
$filteringEntity,
|
|
||||||
controlLayerConvertedToRasterLayer,
|
|
||||||
entityArrangedBackwardOne,
|
|
||||||
entityArrangedForwardOne,
|
|
||||||
entityArrangedToBack,
|
|
||||||
entityArrangedToFront,
|
|
||||||
entityDeleted,
|
|
||||||
entityReset,
|
|
||||||
rasterLayerConvertedToControlLayer,
|
|
||||||
selectCanvasV2Slice,
|
|
||||||
} from 'features/controlLayers/store/canvasV2Slice';
|
|
||||||
import type { CanvasEntityIdentifier, CanvasV2State } from 'features/controlLayers/store/types';
|
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import {
|
|
||||||
PiArrowCounterClockwiseBold,
|
|
||||||
PiArrowDownBold,
|
|
||||||
PiArrowLineDownBold,
|
|
||||||
PiArrowLineUpBold,
|
|
||||||
PiArrowUpBold,
|
|
||||||
PiCheckBold,
|
|
||||||
PiQuestionMarkBold,
|
|
||||||
PiStarHalfBold,
|
|
||||||
PiTrashSimpleBold,
|
|
||||||
} from 'react-icons/pi';
|
|
||||||
|
|
||||||
const getIndexAndCount = (
|
|
||||||
canvasV2: CanvasV2State,
|
|
||||||
{ id, type }: CanvasEntityIdentifier
|
|
||||||
): { index: number; count: number } => {
|
|
||||||
if (type === 'raster_layer') {
|
|
||||||
return {
|
|
||||||
index: canvasV2.rasterLayers.entities.findIndex((entity) => entity.id === id),
|
|
||||||
count: canvasV2.rasterLayers.entities.length,
|
|
||||||
};
|
|
||||||
} else if (type === 'control_layer') {
|
|
||||||
return {
|
|
||||||
index: canvasV2.controlLayers.entities.findIndex((entity) => entity.id === id),
|
|
||||||
count: canvasV2.controlLayers.entities.length,
|
|
||||||
};
|
|
||||||
} else if (type === 'regional_guidance') {
|
|
||||||
return {
|
|
||||||
index: canvasV2.regions.entities.findIndex((entity) => entity.id === id),
|
|
||||||
count: canvasV2.regions.entities.length,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
index: -1,
|
|
||||||
count: 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const CanvasEntityActionMenuItems = memo(() => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const canvasManager = useStore($canvasManager);
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const entityIdentifier = useEntityIdentifierContext();
|
|
||||||
const selectValidActions = useMemo(
|
|
||||||
() =>
|
|
||||||
createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
|
||||||
const { index, count } = getIndexAndCount(canvasV2, entityIdentifier);
|
|
||||||
return {
|
|
||||||
canMoveForwardOne: index < count - 1,
|
|
||||||
canMoveBackwardOne: index > 0,
|
|
||||||
canMoveToFront: index < count - 1,
|
|
||||||
canMoveToBack: index > 0,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
[entityIdentifier]
|
|
||||||
);
|
|
||||||
|
|
||||||
const validActions = useAppSelector(selectValidActions);
|
|
||||||
|
|
||||||
const isArrangeable = useMemo(
|
|
||||||
() =>
|
|
||||||
entityIdentifier.type === 'raster_layer' ||
|
|
||||||
entityIdentifier.type === 'control_layer' ||
|
|
||||||
entityIdentifier.type === 'regional_guidance',
|
|
||||||
[entityIdentifier.type]
|
|
||||||
);
|
|
||||||
|
|
||||||
const isDeleteable = useMemo(
|
|
||||||
() =>
|
|
||||||
entityIdentifier.type === 'raster_layer' ||
|
|
||||||
entityIdentifier.type === 'control_layer' ||
|
|
||||||
entityIdentifier.type === 'regional_guidance',
|
|
||||||
[entityIdentifier.type]
|
|
||||||
);
|
|
||||||
|
|
||||||
const isFilterable = useMemo(
|
|
||||||
() => entityIdentifier.type === 'raster_layer' || entityIdentifier.type === 'control_layer',
|
|
||||||
[entityIdentifier.type]
|
|
||||||
);
|
|
||||||
|
|
||||||
const isRasterLayer = useMemo(() => entityIdentifier.type === 'raster_layer', [entityIdentifier.type]);
|
|
||||||
|
|
||||||
const isControlLayer = useMemo(() => entityIdentifier.type === 'control_layer', [entityIdentifier.type]);
|
|
||||||
|
|
||||||
const defaultControlAdapter = useDefaultControlAdapter();
|
|
||||||
|
|
||||||
const convertRasterLayerToControlLayer = useCallback(() => {
|
|
||||||
dispatch(rasterLayerConvertedToControlLayer({ id: entityIdentifier.id, controlAdapter: defaultControlAdapter }));
|
|
||||||
}, [dispatch, defaultControlAdapter, entityIdentifier.id]);
|
|
||||||
|
|
||||||
const convertControlLayerToRasterLayer = useCallback(() => {
|
|
||||||
dispatch(controlLayerConvertedToRasterLayer({ id: entityIdentifier.id }));
|
|
||||||
}, [dispatch, entityIdentifier.id]);
|
|
||||||
|
|
||||||
const deleteEntity = useCallback(() => {
|
|
||||||
dispatch(entityDeleted({ entityIdentifier }));
|
|
||||||
}, [dispatch, entityIdentifier]);
|
|
||||||
const resetEntity = useCallback(() => {
|
|
||||||
dispatch(entityReset({ entityIdentifier }));
|
|
||||||
}, [dispatch, entityIdentifier]);
|
|
||||||
const moveForwardOne = useCallback(() => {
|
|
||||||
dispatch(entityArrangedForwardOne({ entityIdentifier }));
|
|
||||||
}, [dispatch, entityIdentifier]);
|
|
||||||
const moveToFront = useCallback(() => {
|
|
||||||
dispatch(entityArrangedToFront({ entityIdentifier }));
|
|
||||||
}, [dispatch, entityIdentifier]);
|
|
||||||
const moveBackwardOne = useCallback(() => {
|
|
||||||
dispatch(entityArrangedBackwardOne({ entityIdentifier }));
|
|
||||||
}, [dispatch, entityIdentifier]);
|
|
||||||
const moveToBack = useCallback(() => {
|
|
||||||
dispatch(entityArrangedToBack({ entityIdentifier }));
|
|
||||||
}, [dispatch, entityIdentifier]);
|
|
||||||
const filter = useCallback(() => {
|
|
||||||
$filteringEntity.set(entityIdentifier);
|
|
||||||
}, [entityIdentifier]);
|
|
||||||
const debug = useCallback(() => {
|
|
||||||
if (!canvasManager) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const entity = canvasManager.stateApi.getEntity(entityIdentifier);
|
|
||||||
if (!entity) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.debug(entity);
|
|
||||||
}, [canvasManager, entityIdentifier]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{isArrangeable && (
|
|
||||||
<>
|
|
||||||
<MenuItem onClick={moveToFront} isDisabled={!validActions.canMoveToFront} icon={<PiArrowLineUpBold />}>
|
|
||||||
{t('controlLayers.moveToFront')}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem onClick={moveForwardOne} isDisabled={!validActions.canMoveForwardOne} icon={<PiArrowUpBold />}>
|
|
||||||
{t('controlLayers.moveForward')}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem onClick={moveBackwardOne} isDisabled={!validActions.canMoveBackwardOne} icon={<PiArrowDownBold />}>
|
|
||||||
{t('controlLayers.moveBackward')}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem onClick={moveToBack} isDisabled={!validActions.canMoveToBack} icon={<PiArrowLineDownBold />}>
|
|
||||||
{t('controlLayers.moveToBack')}
|
|
||||||
</MenuItem>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{isFilterable && (
|
|
||||||
<MenuItem onClick={filter} icon={<PiStarHalfBold />}>
|
|
||||||
{t('common.filter')}
|
|
||||||
</MenuItem>
|
|
||||||
)}
|
|
||||||
{isRasterLayer && (
|
|
||||||
<MenuItem onClick={convertRasterLayerToControlLayer} icon={<PiCheckBold />}>
|
|
||||||
{t('common.convertToControlLayer')}
|
|
||||||
</MenuItem>
|
|
||||||
)}
|
|
||||||
{isControlLayer && (
|
|
||||||
<MenuItem onClick={convertControlLayerToRasterLayer} icon={<PiCheckBold />}>
|
|
||||||
{t('common.convertToRasterLayer')}
|
|
||||||
</MenuItem>
|
|
||||||
)}
|
|
||||||
<MenuDivider />
|
|
||||||
<MenuItem onClick={resetEntity} icon={<PiArrowCounterClockwiseBold />}>
|
|
||||||
{t('accessibility.reset')}
|
|
||||||
</MenuItem>
|
|
||||||
{isDeleteable && (
|
|
||||||
<MenuItem onClick={deleteEntity} icon={<PiTrashSimpleBold />} color="error.300">
|
|
||||||
{t('common.delete')}
|
|
||||||
</MenuItem>
|
|
||||||
)}
|
|
||||||
<MenuDivider />
|
|
||||||
<MenuItem onClick={debug} icon={<PiQuestionMarkBold />} color="warn.300">
|
|
||||||
{t('common.debug')}
|
|
||||||
</MenuItem>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
CanvasEntityActionMenuItems.displayName = 'CanvasEntityActionMenuItems';
|
|
@ -1,16 +1,54 @@
|
|||||||
import type { FlexProps } from '@invoke-ai/ui-library';
|
import type { FlexProps } from '@invoke-ai/ui-library';
|
||||||
import { ContextMenu, Flex, MenuList } from '@invoke-ai/ui-library';
|
import { ContextMenu, Flex, MenuList } from '@invoke-ai/ui-library';
|
||||||
import { CanvasEntityActionMenuItems } from 'features/controlLayers/components/common/CanvasEntityActionMenuItems';
|
import { ControlLayerMenuItems } from 'features/controlLayers/components/ControlLayer/ControlLayerMenuItems';
|
||||||
|
import { InpaintMaskMenuItems } from 'features/controlLayers/components/InpaintMask/InpaintMaskMenuItems';
|
||||||
|
import { RasterLayerMenuItems } from 'features/controlLayers/components/RasterLayer/RasterLayerMenuItems';
|
||||||
|
import { RegionalGuidanceMenuItems } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItems';
|
||||||
|
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
export const CanvasEntityHeader = memo(({ children, ...rest }: FlexProps) => {
|
export const CanvasEntityHeader = memo(({ children, ...rest }: FlexProps) => {
|
||||||
|
const entityIdentifier = useEntityIdentifierContext();
|
||||||
const renderMenu = useCallback(() => {
|
const renderMenu = useCallback(() => {
|
||||||
|
if (entityIdentifier.type === 'regional_guidance') {
|
||||||
return (
|
return (
|
||||||
<MenuList>
|
<MenuList>
|
||||||
<CanvasEntityActionMenuItems />
|
<RegionalGuidanceMenuItems />
|
||||||
</MenuList>
|
</MenuList>
|
||||||
);
|
);
|
||||||
}, []);
|
}
|
||||||
|
|
||||||
|
if (entityIdentifier.type === 'inpaint_mask') {
|
||||||
|
return (
|
||||||
|
<MenuList>
|
||||||
|
<InpaintMaskMenuItems />
|
||||||
|
</MenuList>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entityIdentifier.type === 'raster_layer') {
|
||||||
|
return (
|
||||||
|
<MenuList>
|
||||||
|
<RasterLayerMenuItems />
|
||||||
|
</MenuList>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entityIdentifier.type === 'control_layer') {
|
||||||
|
return (
|
||||||
|
<MenuList>
|
||||||
|
<ControlLayerMenuItems />
|
||||||
|
</MenuList>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entityIdentifier.type === 'ip_adapter') {
|
||||||
|
return <MenuList>{/* <ControlLayerMenuItems /> */}</MenuList>;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(false, 'Unhandled entity type');
|
||||||
|
}, [entityIdentifier]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContextMenu renderMenu={renderMenu}>
|
<ContextMenu renderMenu={renderMenu}>
|
||||||
|
@ -0,0 +1,95 @@
|
|||||||
|
import { MenuItem } from '@invoke-ai/ui-library';
|
||||||
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
|
import {
|
||||||
|
entityArrangedBackwardOne,
|
||||||
|
entityArrangedForwardOne,
|
||||||
|
entityArrangedToBack,
|
||||||
|
entityArrangedToFront,
|
||||||
|
selectCanvasV2Slice,
|
||||||
|
} from 'features/controlLayers/store/canvasV2Slice';
|
||||||
|
import type { CanvasEntityIdentifier, CanvasV2State } from 'features/controlLayers/store/types';
|
||||||
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { PiArrowDownBold, PiArrowLineDownBold, PiArrowLineUpBold, PiArrowUpBold } from 'react-icons/pi';
|
||||||
|
|
||||||
|
const getIndexAndCount = (
|
||||||
|
canvasV2: CanvasV2State,
|
||||||
|
{ id, type }: CanvasEntityIdentifier
|
||||||
|
): { index: number; count: number } => {
|
||||||
|
if (type === 'raster_layer') {
|
||||||
|
return {
|
||||||
|
index: canvasV2.rasterLayers.entities.findIndex((entity) => entity.id === id),
|
||||||
|
count: canvasV2.rasterLayers.entities.length,
|
||||||
|
};
|
||||||
|
} else if (type === 'control_layer') {
|
||||||
|
return {
|
||||||
|
index: canvasV2.controlLayers.entities.findIndex((entity) => entity.id === id),
|
||||||
|
count: canvasV2.controlLayers.entities.length,
|
||||||
|
};
|
||||||
|
} else if (type === 'regional_guidance') {
|
||||||
|
return {
|
||||||
|
index: canvasV2.regions.entities.findIndex((entity) => entity.id === id),
|
||||||
|
count: canvasV2.regions.entities.length,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
index: -1,
|
||||||
|
count: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CanvasEntityMenuItemsArrange = memo(() => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const entityIdentifier = useEntityIdentifierContext();
|
||||||
|
const selectValidActions = useMemo(
|
||||||
|
() =>
|
||||||
|
createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||||
|
const { index, count } = getIndexAndCount(canvasV2, entityIdentifier);
|
||||||
|
return {
|
||||||
|
canMoveForwardOne: index < count - 1,
|
||||||
|
canMoveBackwardOne: index > 0,
|
||||||
|
canMoveToFront: index < count - 1,
|
||||||
|
canMoveToBack: index > 0,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
[entityIdentifier]
|
||||||
|
);
|
||||||
|
|
||||||
|
const validActions = useAppSelector(selectValidActions);
|
||||||
|
|
||||||
|
const moveForwardOne = useCallback(() => {
|
||||||
|
dispatch(entityArrangedForwardOne({ entityIdentifier }));
|
||||||
|
}, [dispatch, entityIdentifier]);
|
||||||
|
const moveToFront = useCallback(() => {
|
||||||
|
dispatch(entityArrangedToFront({ entityIdentifier }));
|
||||||
|
}, [dispatch, entityIdentifier]);
|
||||||
|
const moveBackwardOne = useCallback(() => {
|
||||||
|
dispatch(entityArrangedBackwardOne({ entityIdentifier }));
|
||||||
|
}, [dispatch, entityIdentifier]);
|
||||||
|
const moveToBack = useCallback(() => {
|
||||||
|
dispatch(entityArrangedToBack({ entityIdentifier }));
|
||||||
|
}, [dispatch, entityIdentifier]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<MenuItem onClick={moveToFront} isDisabled={!validActions.canMoveToFront} icon={<PiArrowLineUpBold />}>
|
||||||
|
{t('controlLayers.moveToFront')}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={moveForwardOne} isDisabled={!validActions.canMoveForwardOne} icon={<PiArrowUpBold />}>
|
||||||
|
{t('controlLayers.moveForward')}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={moveBackwardOne} isDisabled={!validActions.canMoveBackwardOne} icon={<PiArrowDownBold />}>
|
||||||
|
{t('controlLayers.moveBackward')}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={moveToBack} isDisabled={!validActions.canMoveToBack} icon={<PiArrowLineDownBold />}>
|
||||||
|
{t('controlLayers.moveToBack')}
|
||||||
|
</MenuItem>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
CanvasEntityMenuItemsArrange.displayName = 'CanvasEntityArrangeMenuItems';
|
@ -0,0 +1,25 @@
|
|||||||
|
import { MenuItem } from '@invoke-ai/ui-library';
|
||||||
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
|
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
|
import { entityDeleted } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { PiTrashSimpleBold } from 'react-icons/pi';
|
||||||
|
|
||||||
|
export const CanvasEntityMenuItemsDelete = memo(() => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const entityIdentifier = useEntityIdentifierContext();
|
||||||
|
|
||||||
|
const deleteEntity = useCallback(() => {
|
||||||
|
dispatch(entityDeleted({ entityIdentifier }));
|
||||||
|
}, [dispatch, entityIdentifier]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuItem onClick={deleteEntity} icon={<PiTrashSimpleBold />} color="error.300">
|
||||||
|
{t('common.delete')}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
CanvasEntityMenuItemsDelete.displayName = 'CanvasEntityMenuItemsDelete';
|
@ -0,0 +1,22 @@
|
|||||||
|
import { MenuItem } from '@invoke-ai/ui-library';
|
||||||
|
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
|
import { $filteringEntity } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { PiShootingStarBold } from 'react-icons/pi';
|
||||||
|
|
||||||
|
export const CanvasEntityMenuItemsFilter = memo(() => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const entityIdentifier = useEntityIdentifierContext();
|
||||||
|
const filter = useCallback(() => {
|
||||||
|
$filteringEntity.set(entityIdentifier);
|
||||||
|
}, [entityIdentifier]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuItem onClick={filter} icon={<PiShootingStarBold />}>
|
||||||
|
{t('controlLayers.filter')}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
CanvasEntityMenuItemsFilter.displayName = 'CanvasEntityMenuItemsFilter';
|
@ -0,0 +1,25 @@
|
|||||||
|
import { MenuItem } from '@invoke-ai/ui-library';
|
||||||
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
|
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
|
import { entityReset } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { PiArrowCounterClockwiseBold } from 'react-icons/pi';
|
||||||
|
|
||||||
|
export const CanvasEntityMenuItemsReset = memo(() => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const entityIdentifier = useEntityIdentifierContext();
|
||||||
|
|
||||||
|
const resetEntity = useCallback(() => {
|
||||||
|
dispatch(entityReset({ entityIdentifier }));
|
||||||
|
}, [dispatch, entityIdentifier]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuItem onClick={resetEntity} icon={<PiArrowCounterClockwiseBold />}>
|
||||||
|
{t('accessibility.reset')}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
CanvasEntityMenuItemsReset.displayName = 'CanvasEntityMenuItemsReset';
|
@ -1,89 +0,0 @@
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|
||||||
import { deepClone } from 'common/util/deepClone';
|
|
||||||
import { ipaAdded, rgIPAdapterAdded } from 'features/controlLayers/store/canvasV2Slice';
|
|
||||||
import {
|
|
||||||
IMAGE_FILTERS,
|
|
||||||
initialControlNetV2,
|
|
||||||
initialIPAdapterV2,
|
|
||||||
initialT2IAdapterV2,
|
|
||||||
isFilterType,
|
|
||||||
} from 'features/controlLayers/store/types';
|
|
||||||
import { zModelIdentifierField } from 'features/nodes/types/common';
|
|
||||||
import { useCallback, useMemo } from 'react';
|
|
||||||
import { useControlNetAndT2IAdapterModels, useIPAdapterModels } from 'services/api/hooks/modelsByType';
|
|
||||||
import type { ControlNetModelConfig, IPAdapterModelConfig, T2IAdapterModelConfig } from 'services/api/types';
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
|
|
||||||
export const useAddCALayer = () => {
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const baseModel = useAppSelector((s) => s.canvasV2.params.model?.base);
|
|
||||||
const [modelConfigs] = useControlNetAndT2IAdapterModels();
|
|
||||||
const model: ControlNetModelConfig | T2IAdapterModelConfig | null = useMemo(() => {
|
|
||||||
// prefer to use a model that matches the base model
|
|
||||||
const compatibleModels = modelConfigs.filter((m) => (baseModel ? m.base === baseModel : true));
|
|
||||||
return compatibleModels[0] ?? modelConfigs[0] ?? null;
|
|
||||||
}, [baseModel, modelConfigs]);
|
|
||||||
const isDisabled = useMemo(() => !model, [model]);
|
|
||||||
const addCALayer = useCallback(() => {
|
|
||||||
if (!model) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultPreprocessor = model.default_settings?.preprocessor;
|
|
||||||
const processorConfig = isFilterType(defaultPreprocessor)
|
|
||||||
? IMAGE_FILTERS[defaultPreprocessor].buildDefaults(baseModel)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
const initialConfig = deepClone(model.type === 'controlnet' ? initialControlNetV2 : initialT2IAdapterV2);
|
|
||||||
const config = { ...initialConfig, model: zModelIdentifierField.parse(model), processorConfig };
|
|
||||||
|
|
||||||
// dispatch(caAdded({ config }));
|
|
||||||
}, [dispatch, model, baseModel]);
|
|
||||||
|
|
||||||
return [addCALayer, isDisabled] as const;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useAddIPALayer = () => {
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const baseModel = useAppSelector((s) => s.canvasV2.params.model?.base);
|
|
||||||
const [modelConfigs] = useIPAdapterModels();
|
|
||||||
const model: IPAdapterModelConfig | null = useMemo(() => {
|
|
||||||
// prefer to use a model that matches the base model
|
|
||||||
const compatibleModels = modelConfigs.filter((m) => (baseModel ? m.base === baseModel : true));
|
|
||||||
return compatibleModels[0] ?? modelConfigs[0] ?? null;
|
|
||||||
}, [baseModel, modelConfigs]);
|
|
||||||
const isDisabled = useMemo(() => !model, [model]);
|
|
||||||
const addIPALayer = useCallback(() => {
|
|
||||||
if (!model) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const initialConfig = deepClone(initialIPAdapterV2);
|
|
||||||
const config = { ...initialConfig, model: zModelIdentifierField.parse(model) };
|
|
||||||
dispatch(ipaAdded({ config }));
|
|
||||||
}, [dispatch, model]);
|
|
||||||
|
|
||||||
return [addIPALayer, isDisabled] as const;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useAddIPAdapterToRGLayer = (id: string) => {
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const baseModel = useAppSelector((s) => s.canvasV2.params.model?.base);
|
|
||||||
const [modelConfigs] = useIPAdapterModels();
|
|
||||||
const model: IPAdapterModelConfig | null = useMemo(() => {
|
|
||||||
// prefer to use a model that matches the base model
|
|
||||||
const compatibleModels = modelConfigs.filter((m) => (baseModel ? m.base === baseModel : true));
|
|
||||||
return compatibleModels[0] ?? modelConfigs[0] ?? null;
|
|
||||||
}, [baseModel, modelConfigs]);
|
|
||||||
const isDisabled = useMemo(() => !model, [model]);
|
|
||||||
const addIPAdapter = useCallback(() => {
|
|
||||||
if (!model) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const initialConfig = deepClone(initialIPAdapterV2);
|
|
||||||
const config = { ...initialConfig, model: zModelIdentifierField.parse(model) };
|
|
||||||
dispatch(rgIPAdapterAdded({ id, ipAdapter: { ...config, id: uuidv4(), type: 'ip_adapter', isEnabled: true } }));
|
|
||||||
}, [model, dispatch, id]);
|
|
||||||
|
|
||||||
return [addIPAdapter, isDisabled] as const;
|
|
||||||
};
|
|
@ -4,10 +4,10 @@ import { deepClone } from 'common/util/deepClone';
|
|||||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { selectControlLayerOrThrow } from 'features/controlLayers/store/controlLayersReducers';
|
import { selectControlLayerOrThrow } from 'features/controlLayers/store/controlLayersReducers';
|
||||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||||
import { initialControlNetV2, initialT2IAdapterV2 } from 'features/controlLayers/store/types';
|
import { initialControlNetV2, initialIPAdapterV2, initialT2IAdapterV2 } from 'features/controlLayers/store/types';
|
||||||
import { zModelIdentifierField } from 'features/nodes/types/common';
|
import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useControlNetAndT2IAdapterModels } from 'services/api/hooks/modelsByType';
|
import { useControlNetAndT2IAdapterModels, useIPAdapterModels } from 'services/api/hooks/modelsByType';
|
||||||
|
|
||||||
export const useControlLayerControlAdapter = (entityIdentifier: CanvasEntityIdentifier) => {
|
export const useControlLayerControlAdapter = (entityIdentifier: CanvasEntityIdentifier) => {
|
||||||
const selectControlAdapter = useMemo(
|
const selectControlAdapter = useMemo(
|
||||||
@ -42,3 +42,23 @@ export const useDefaultControlAdapter = () => {
|
|||||||
|
|
||||||
return defaultControlAdapter;
|
return defaultControlAdapter;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useDefaultIPAdapter = () => {
|
||||||
|
const [modelConfigs] = useIPAdapterModels();
|
||||||
|
|
||||||
|
const baseModel = useAppSelector((s) => s.canvasV2.params.model?.base);
|
||||||
|
|
||||||
|
const defaultControlAdapter = useMemo(() => {
|
||||||
|
const compatibleModels = modelConfigs.filter((m) => (baseModel ? m.base === baseModel : true));
|
||||||
|
const model = compatibleModels[0] ?? modelConfigs[0] ?? null;
|
||||||
|
const ipAdapter = deepClone(initialIPAdapterV2);
|
||||||
|
|
||||||
|
if (model) {
|
||||||
|
ipAdapter.model = zModelIdentifierField.parse(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipAdapter;
|
||||||
|
}, [baseModel, modelConfigs]);
|
||||||
|
|
||||||
|
return defaultControlAdapter;
|
||||||
|
};
|
||||||
|
@ -44,7 +44,7 @@ import { IMAGE_FILTERS, isDrawableEntity, RGBA_RED } from './types';
|
|||||||
|
|
||||||
const initialState: CanvasV2State = {
|
const initialState: CanvasV2State = {
|
||||||
_version: 3,
|
_version: 3,
|
||||||
selectedEntityIdentifier: null,
|
selectedEntityIdentifier: { id: 'inpaint_mask', type: 'inpaint_mask' },
|
||||||
rasterLayers: { entities: [], compositeRasterizationCache: [] },
|
rasterLayers: { entities: [], compositeRasterizationCache: [] },
|
||||||
controlLayers: { entities: [] },
|
controlLayers: { entities: [] },
|
||||||
ipAdapters: { entities: [] },
|
ipAdapters: { entities: [] },
|
||||||
@ -385,10 +385,13 @@ export const canvasV2Slice = createSlice({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
allEntitiesDeleted: (state) => {
|
allEntitiesDeleted: (state) => {
|
||||||
state.regions.entities = [];
|
state.ipAdapters = deepClone(initialState.ipAdapters);
|
||||||
state.rasterLayers.entities = [];
|
state.rasterLayers = deepClone(initialState.rasterLayers);
|
||||||
state.rasterLayers.compositeRasterizationCache = [];
|
state.rasterLayers.compositeRasterizationCache = [];
|
||||||
state.ipAdapters.entities = [];
|
state.controlLayers = deepClone(initialState.controlLayers);
|
||||||
|
state.regions = deepClone(initialState.regions);
|
||||||
|
state.inpaintMask = deepClone(initialState.inpaintMask);
|
||||||
|
state.selectedEntityIdentifier = deepClone(initialState.selectedEntityIdentifier);
|
||||||
},
|
},
|
||||||
filterSelected: (state, action: PayloadAction<{ type: FilterConfig['type'] }>) => {
|
filterSelected: (state, action: PayloadAction<{ type: FilterConfig['type'] }>) => {
|
||||||
state.filter.config = IMAGE_FILTERS[action.payload.type].buildDefaults();
|
state.filter.config = IMAGE_FILTERS[action.payload.type].buildDefaults();
|
||||||
@ -423,6 +426,7 @@ export const canvasV2Slice = createSlice({
|
|||||||
|
|
||||||
state.ipAdapters = deepClone(initialState.ipAdapters);
|
state.ipAdapters = deepClone(initialState.ipAdapters);
|
||||||
state.rasterLayers = deepClone(initialState.rasterLayers);
|
state.rasterLayers = deepClone(initialState.rasterLayers);
|
||||||
|
state.rasterLayers.compositeRasterizationCache = [];
|
||||||
state.controlLayers = deepClone(initialState.controlLayers);
|
state.controlLayers = deepClone(initialState.controlLayers);
|
||||||
state.regions = deepClone(initialState.regions);
|
state.regions = deepClone(initialState.regions);
|
||||||
state.selectedEntityIdentifier = deepClone(initialState.selectedEntityIdentifier);
|
state.selectedEntityIdentifier = deepClone(initialState.selectedEntityIdentifier);
|
||||||
|
Loading…
Reference in New Issue
Block a user