clean up image implementation

This commit is contained in:
Mary Hipp 2024-08-07 10:36:38 -04:00
parent cc96dcf0ed
commit 0b0abfbe8f
8 changed files with 167 additions and 132 deletions

View File

@ -8,6 +8,7 @@ from PIL import Image
from invokeai.app.api.dependencies import ApiDependencies
from invokeai.app.api.routers.model_manager import IMAGE_MAX_AGE
from invokeai.app.services.style_preset_images.style_preset_images_common import StylePresetImageFileNotFoundException
from invokeai.app.services.style_preset_records.style_preset_records_common import (
PresetData,
StylePresetChanges,
@ -91,7 +92,11 @@ async def delete_style_preset(
style_preset_id: str = Path(description="The style preset to delete"),
) -> None:
"""Deletes a style preset"""
ApiDependencies.invoker.services.style_preset_images_service.delete(style_preset_id)
try:
ApiDependencies.invoker.services.style_preset_images_service.delete(style_preset_id)
except StylePresetImageFileNotFoundException:
pass
ApiDependencies.invoker.services.style_preset_records.delete(style_preset_id)

View File

@ -72,6 +72,8 @@ class StylePresetImageFileStorageDisk(StylePresetImageFileStorageBase):
send2trash(path)
except StylePresetImageFileNotFoundException as e:
raise StylePresetImageFileNotFoundException from e
except Exception as e:
raise StylePresetImageFileDeleteException from e

View File

@ -1,12 +1,11 @@
import { Flex, IconButton, Text } from '@invoke-ai/ui-library';
import { Flex, IconButton, Text, Box, ButtonGroup } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { negativePromptChanged, positivePromptChanged } from 'features/controlLayers/store/controlLayersSlice';
import { usePresetModifiedPrompts } from 'features/stylePresets/hooks/usePresetModifiedPrompts';
import { activeStylePresetChanged } from 'features/stylePresets/store/stylePresetSlice';
import type { MouseEventHandler } from 'react';
import { useCallback } from 'react';
import { CgPushDown } from 'react-icons/cg';
import { PiXBold } from 'react-icons/pi';
import { PiStackSimpleBold, PiXBold } from 'react-icons/pi';
import StylePresetImage from './StylePresetImage';
export const ActiveStylePreset = () => {
@ -34,18 +33,21 @@ export const ActiveStylePreset = () => {
);
if (!activeStylePreset) {
return <>Choose Preset</>;
return (
<Flex h="25px" alignItems="center">
<Text fontSize="sm" fontWeight="semibold" color="base.300">
Choose Preset
</Text>
</Flex>
);
}
return (
<>
<Flex justifyContent="space-between" w="full" alignItems="center">
<Flex gap="2">
<StylePresetImage presetImageUrl={activeStylePreset.image} />
<Flex gap="2" alignItems="center">
<StylePresetImage imageWidth={25} presetImageUrl={activeStylePreset.image} />
<Flex flexDir="column">
<Text variant="subtext" fontSize="xs">
Prompt Style
</Text>
<Text fontSize="md" fontWeight="semibold">
<Text fontSize="sm" fontWeight="semibold" color="base.300">
{activeStylePreset.name}
</Text>
</Flex>
@ -53,15 +55,15 @@ export const ActiveStylePreset = () => {
<Flex gap="1">
<IconButton
onClick={handleFlattenPrompts}
variant="ghost"
size="md"
variant="outline"
size="sm"
aria-label="Flatten"
icon={<CgPushDown />}
icon={<PiStackSimpleBold />}
/>
<IconButton
onClick={handleClearActiveStylePreset}
variant="ghost"
size="md"
variant="outline"
size="sm"
aria-label="Clear"
icon={<PiXBold />}
/>

View File

@ -5,29 +5,29 @@ import { PiImage } from 'react-icons/pi';
const IMAGE_THUMBNAIL_SIZE = '40px';
const FALLBACK_ICON_SIZE = '24px';
const StylePresetImage = ({ presetImageUrl }: { presetImageUrl: string | null }) => {
const StylePresetImage = ({ presetImageUrl, imageWidth }: { presetImageUrl: string | null; imageWidth?: number }) => {
return (
<Image
src={presetImageUrl || ''}
fallbackStrategy="beforeLoadOrError"
fallback={
<Flex
height={IMAGE_THUMBNAIL_SIZE}
minWidth={IMAGE_THUMBNAIL_SIZE}
height={imageWidth || IMAGE_THUMBNAIL_SIZE}
minWidth={imageWidth || IMAGE_THUMBNAIL_SIZE}
bg="base.650"
borderRadius="base"
alignItems="center"
justifyContent="center"
>
<Icon color="base.500" as={PiImage} boxSize={FALLBACK_ICON_SIZE} />
<Icon color="base.500" as={PiImage} boxSize={imageWidth ? imageWidth / 2 : FALLBACK_ICON_SIZE} />
</Flex>
}
objectFit="cover"
objectPosition="50% 50%"
height={IMAGE_THUMBNAIL_SIZE}
width={IMAGE_THUMBNAIL_SIZE}
minHeight={IMAGE_THUMBNAIL_SIZE}
minWidth={IMAGE_THUMBNAIL_SIZE}
height={imageWidth || IMAGE_THUMBNAIL_SIZE}
width={imageWidth || IMAGE_THUMBNAIL_SIZE}
minHeight={imageWidth || IMAGE_THUMBNAIL_SIZE}
minWidth={imageWidth || IMAGE_THUMBNAIL_SIZE}
borderRadius="base"
/>
);

View File

@ -1,4 +1,4 @@
import { Badge, Flex, IconButton, Text } from '@invoke-ai/ui-library';
import { Badge, ConfirmationAlertDialog, Flex, IconButton, Text, useDisclosure } from '@invoke-ai/ui-library';
import type { MouseEvent } from 'react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import ModelImage from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelImage';
@ -14,6 +14,7 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithI
const dispatch = useAppDispatch();
const [deleteStylePreset] = useDeleteStylePresetMutation();
const activeStylePreset = useAppSelector((s) => s.stylePreset.activeStylePreset);
const { isOpen, onOpen, onClose } = useDisclosure();
const handleClickEdit = useCallback(
(e: MouseEvent<HTMLButtonElement>) => {
@ -29,72 +30,94 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithI
dispatch(isMenuOpenChanged(false));
}, [dispatch, preset]);
const handleDeletePreset = useCallback(
async (e: MouseEvent<HTMLButtonElement>) => {
const handleClickDelete = useCallback(
(e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
try {
await deleteStylePreset(preset.id);
} catch (error) {}
onOpen();
},
[preset]
[dispatch, preset]
);
const handleDeletePreset = useCallback(async () => {
try {
await deleteStylePreset(preset.id);
} catch (error) {}
}, [preset]);
return (
<Flex
gap="4"
onClick={handleClickApply}
cursor="pointer"
_hover={{ backgroundColor: 'base.750' }}
padding="10px"
borderRadius="base"
alignItems="center"
w="full"
>
<StylePresetImage presetImageUrl={preset.image} />
<Flex flexDir="column" w="full">
<Flex w="full" justifyContent="space-between">
<Flex alignItems="center" gap="2">
<Text fontSize="md">{preset.name}</Text>
{activeStylePreset && activeStylePreset.id === preset.id && (
<Badge
color="invokeBlue.400"
borderColor="invokeBlue.700"
borderWidth={1}
bg="transparent"
flexShrink={0}
>
Active
</Badge>
)}
<>
<Flex
gap="4"
onClick={handleClickApply}
cursor="pointer"
_hover={{ backgroundColor: 'base.750' }}
padding="10px"
borderRadius="base"
alignItems="center"
w="full"
>
<StylePresetImage presetImageUrl={preset.image} />
<Flex flexDir="column" w="full">
<Flex w="full" justifyContent="space-between">
<Flex alignItems="center" gap="2">
<Text fontSize="md">{preset.name}</Text>
{activeStylePreset && activeStylePreset.id === preset.id && (
<Badge
color="invokeBlue.400"
borderColor="invokeBlue.700"
borderWidth={1}
bg="transparent"
flexShrink={0}
>
Active
</Badge>
)}
</Flex>
<Flex alignItems="center" gap="1">
<IconButton
size="sm"
variant="ghost"
aria-label="Edit"
onClick={handleClickEdit}
icon={<PiPencilBold />}
/>
<IconButton
size="sm"
variant="ghost"
aria-label="Delete"
onClick={handleClickDelete}
icon={<PiTrashBold />}
/>
</Flex>
</Flex>
<Flex alignItems="center" gap="1">
<IconButton size="sm" variant="ghost" aria-label="Edit" onClick={handleClickEdit} icon={<PiPencilBold />} />
<IconButton
size="sm"
variant="ghost"
aria-label="Delete"
onClick={handleDeletePreset}
icon={<PiTrashBold />}
/>
<Flex flexDir="column">
<Text fontSize="xs">
<Text as="span" fontWeight="semibold">
Positive prompt:
</Text>{' '}
{preset.preset_data.positive_prompt}
</Text>
<Text fontSize="xs">
<Text as="span" fontWeight="semibold">
Negative prompt:
</Text>{' '}
{preset.preset_data.negative_prompt}
</Text>
</Flex>
</Flex>
<Flex flexDir="column">
<Text fontSize="xs">
<Text as="span" fontWeight="semibold">
Positive prompt:
</Text>{' '}
{preset.preset_data.positive_prompt}
</Text>
<Text fontSize="xs">
<Text as="span" fontWeight="semibold">
Negative prompt:
</Text>{' '}
{preset.preset_data.negative_prompt}
</Text>
</Flex>
</Flex>
</Flex>
<ConfirmationAlertDialog
isOpen={isOpen}
onClose={onClose}
title={'Delete preset'}
acceptCallback={handleDeletePreset}
acceptButtonText={'Delete'}
>
<p>{'Delete Preset?'}</p>
<br />
</ConfirmationAlertDialog>
</>
);
};

View File

@ -16,16 +16,18 @@ export const StylePresetMenuTrigger = () => {
return (
<Flex
as="button"
onClick={handleToggle}
backgroundColor="base.800"
justifyContent="space-between"
alignItems="center"
padding="5px 10px"
borderRadius="base"
gap="2"
>
<ActiveStylePreset />
<Icon as={PiCaretDownBold} />
<Icon boxSize="15px" as={PiCaretDownBold} color="base.300" />
</Flex>
);
};

View File

@ -24,8 +24,8 @@ export const useStylePresetFields = (preset: StylePresetRecordWithImage | null)
return {
name: preset.name,
positivePrompt: preset.preset_data.positive_prompt,
negativePrompt: preset.preset_data.negative_prompt,
positivePrompt: preset.preset_data.positive_prompt || "",
negativePrompt: preset.preset_data.negative_prompt || "",
image: file
};
}

View File

@ -1,5 +1,5 @@
import type { ChakraProps } from '@invoke-ai/ui-library';
import { Box, Flex, Portal,Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library';
import { Box, Flex, Portal, Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
import { ControlLayersPanelContent } from 'features/controlLayers/components/ControlLayersPanelContent';
@ -71,63 +71,64 @@ const ParametersPanelTextToImage = () => {
<Box position="absolute" top={0} left={0} right={0} bottom={0} ref={ref}>
<Portal containerRef={ref}>
{isMenuOpen && (
<Box position="absolute" top={0} left={0} right={0} bottom={0}>
<Box position="absolute" top={0} left={0} right={0} bottom={0} layerStyle="second">
<OverlayScrollbarsComponent
defer
style={overlayScrollbarsStyles}
options={overlayScrollbarsParams.options}
// backgroundColor="rgba(0,0,0,0.5)"
>
<Flex gap={2} flexDirection="column" h="full" w="full" layerStyle="second">
<Flex gap={2} flexDirection="column" h="full" w="full">
<StylePresetMenu />
</Flex>
</OverlayScrollbarsComponent>
</Box>
)}
</Portal>
<OverlayScrollbarsComponent defer style={overlayScrollbarsStyles} options={overlayScrollbarsParams.options}>
<Flex gap={2} flexDirection="column" h="full" w="full">
<Prompts />
<Tabs
defaultIndex={0}
variant="enclosed"
display="flex"
flexDir="column"
w="full"
h="full"
gap={2}
onChange={onChangeTabs}
>
<TabList gap={2} fontSize="sm" borderColor="base.800">
<Tab sx={baseStyles} _selected={selectedStyles} data-testid="generation-tab-settings-tab-button">
{t('common.settingsLabel')}
</Tab>
<Tab
sx={baseStyles}
_selected={selectedStyles}
data-testid="generation-tab-control-layers-tab-button"
>
{controlLayersTitle}
</Tab>
</TabList>
<TabPanels w="full" h="full">
<TabPanel p={0} w="full" h="full">
<Flex gap={2} flexDirection="column" h="full" w="full">
<ImageSettingsAccordion />
<GenerationSettingsAccordion />
{activeTabName !== 'generation' && <ControlSettingsAccordion />}
{activeTabName === 'canvas' && <CompositingSettingsAccordion />}
{isSDXL && <RefinerSettingsAccordion />}
<AdvancedSettingsAccordion />
</Flex>
</TabPanel>
<TabPanel p={0} w="full" h="full">
<ControlLayersPanelContent />
</TabPanel>
</TabPanels>
</Tabs>
</Flex>
</OverlayScrollbarsComponent>
{!isMenuOpen && (
<OverlayScrollbarsComponent defer style={overlayScrollbarsStyles} options={overlayScrollbarsParams.options}>
<Flex gap={2} flexDirection="column" h="full" w="full">
<Prompts />
<Tabs
defaultIndex={0}
variant="enclosed"
display="flex"
flexDir="column"
w="full"
h="full"
gap={2}
onChange={onChangeTabs}
>
<TabList gap={2} fontSize="sm" borderColor="base.800">
<Tab sx={baseStyles} _selected={selectedStyles} data-testid="generation-tab-settings-tab-button">
{t('common.settingsLabel')}
</Tab>
<Tab
sx={baseStyles}
_selected={selectedStyles}
data-testid="generation-tab-control-layers-tab-button"
>
{controlLayersTitle}
</Tab>
</TabList>
<TabPanels w="full" h="full">
<TabPanel p={0} w="full" h="full">
<Flex gap={2} flexDirection="column" h="full" w="full">
<ImageSettingsAccordion />
<GenerationSettingsAccordion />
{activeTabName !== 'generation' && <ControlSettingsAccordion />}
{activeTabName === 'canvas' && <CompositingSettingsAccordion />}
{isSDXL && <RefinerSettingsAccordion />}
<AdvancedSettingsAccordion />
</Flex>
</TabPanel>
<TabPanel p={0} w="full" h="full">
<ControlLayersPanelContent />
</TabPanel>
</TabPanels>
</Tabs>
</Flex>
</OverlayScrollbarsComponent>
)}
</Box>
</Flex>
</Flex>