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

View File

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

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

View File

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

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 type { MouseEvent } from 'react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import ModelImage from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelImage'; import ModelImage from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelImage';
@ -14,6 +14,7 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithI
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const [deleteStylePreset] = useDeleteStylePresetMutation(); const [deleteStylePreset] = useDeleteStylePresetMutation();
const activeStylePreset = useAppSelector((s) => s.stylePreset.activeStylePreset); const activeStylePreset = useAppSelector((s) => s.stylePreset.activeStylePreset);
const { isOpen, onOpen, onClose } = useDisclosure();
const handleClickEdit = useCallback( const handleClickEdit = useCallback(
(e: MouseEvent<HTMLButtonElement>) => { (e: MouseEvent<HTMLButtonElement>) => {
@ -29,17 +30,22 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithI
dispatch(isMenuOpenChanged(false)); dispatch(isMenuOpenChanged(false));
}, [dispatch, preset]); }, [dispatch, preset]);
const handleDeletePreset = useCallback( const handleClickDelete = useCallback(
async (e: MouseEvent<HTMLButtonElement>) => { (e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation(); e.stopPropagation();
onOpen();
},
[dispatch, preset]
);
const handleDeletePreset = useCallback(async () => {
try { try {
await deleteStylePreset(preset.id); await deleteStylePreset(preset.id);
} catch (error) {} } catch (error) {}
}, }, [preset]);
[preset]
);
return ( return (
<>
<Flex <Flex
gap="4" gap="4"
onClick={handleClickApply} onClick={handleClickApply}
@ -69,12 +75,18 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithI
</Flex> </Flex>
<Flex alignItems="center" gap="1"> <Flex alignItems="center" gap="1">
<IconButton size="sm" variant="ghost" aria-label="Edit" onClick={handleClickEdit} icon={<PiPencilBold />} /> <IconButton
size="sm"
variant="ghost"
aria-label="Edit"
onClick={handleClickEdit}
icon={<PiPencilBold />}
/>
<IconButton <IconButton
size="sm" size="sm"
variant="ghost" variant="ghost"
aria-label="Delete" aria-label="Delete"
onClick={handleDeletePreset} onClick={handleClickDelete}
icon={<PiTrashBold />} icon={<PiTrashBold />}
/> />
</Flex> </Flex>
@ -96,5 +108,16 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithI
</Flex> </Flex>
</Flex> </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 ( return (
<Flex <Flex
as="button"
onClick={handleToggle} onClick={handleToggle}
backgroundColor="base.800" backgroundColor="base.800"
justifyContent="space-between" justifyContent="space-between"
alignItems="center" alignItems="center"
padding="5px 10px" padding="5px 10px"
borderRadius="base" borderRadius="base"
gap="2"
> >
<ActiveStylePreset /> <ActiveStylePreset />
<Icon as={PiCaretDownBold} /> <Icon boxSize="15px" as={PiCaretDownBold} color="base.300" />
</Flex> </Flex>
); );
}; };

View File

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

View File

@ -1,5 +1,5 @@
import type { ChakraProps } from '@invoke-ai/ui-library'; import type { ChakraProps } from '@invoke-ai/ui-library';
import { Box, Flex, Portal,Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library'; import { Box, Flex, Portal, Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants'; import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
import { ControlLayersPanelContent } from 'features/controlLayers/components/ControlLayersPanelContent'; import { ControlLayersPanelContent } from 'features/controlLayers/components/ControlLayersPanelContent';
@ -71,20 +71,20 @@ const ParametersPanelTextToImage = () => {
<Box position="absolute" top={0} left={0} right={0} bottom={0} ref={ref}> <Box position="absolute" top={0} left={0} right={0} bottom={0} ref={ref}>
<Portal containerRef={ref}> <Portal containerRef={ref}>
{isMenuOpen && ( {isMenuOpen && (
<Box position="absolute" top={0} left={0} right={0} bottom={0}> <Box position="absolute" top={0} left={0} right={0} bottom={0} layerStyle="second">
<OverlayScrollbarsComponent <OverlayScrollbarsComponent
defer defer
style={overlayScrollbarsStyles} style={overlayScrollbarsStyles}
options={overlayScrollbarsParams.options} options={overlayScrollbarsParams.options}
// backgroundColor="rgba(0,0,0,0.5)"
> >
<Flex gap={2} flexDirection="column" h="full" w="full" layerStyle="second"> <Flex gap={2} flexDirection="column" h="full" w="full">
<StylePresetMenu /> <StylePresetMenu />
</Flex> </Flex>
</OverlayScrollbarsComponent> </OverlayScrollbarsComponent>
</Box> </Box>
)} )}
</Portal> </Portal>
{!isMenuOpen && (
<OverlayScrollbarsComponent defer style={overlayScrollbarsStyles} options={overlayScrollbarsParams.options}> <OverlayScrollbarsComponent defer style={overlayScrollbarsStyles} options={overlayScrollbarsParams.options}>
<Flex gap={2} flexDirection="column" h="full" w="full"> <Flex gap={2} flexDirection="column" h="full" w="full">
<Prompts /> <Prompts />
@ -128,6 +128,7 @@ const ParametersPanelTextToImage = () => {
</Tabs> </Tabs>
</Flex> </Flex>
</OverlayScrollbarsComponent> </OverlayScrollbarsComponent>
)}
</Box> </Box>
</Flex> </Flex>
</Flex> </Flex>