mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): rework simple session initial state
This commit is contained in:
@@ -1,12 +1,10 @@
|
||||
/* eslint-disable i18next/no-literal-string */
|
||||
|
||||
import { Button, Divider, Flex, Grid, Heading, Text } from '@invoke-ai/ui-library';
|
||||
import { Alert, Button, Divider, Flex, Grid, Heading, Text } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { InitialStateAddAStyleReference } from 'features/controlLayers/components/SimpleSession/InitialStateAddAStyleReference';
|
||||
import { InitialStateEditImageCard } from 'features/controlLayers/components/SimpleSession/InitialStateEditImageCard';
|
||||
import { InitialStateGenerateFromText } from 'features/controlLayers/components/SimpleSession/InitialStateGenerateFromText';
|
||||
import { InitialStateUseALayoutImageCard } from 'features/controlLayers/components/SimpleSession/InitialStateUseALayoutImageCard';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { InitialStateMainModelPicker } from 'features/controlLayers/components/SimpleSession/InitialStateMainModelPicker';
|
||||
import { setActiveTab } from 'features/ui/store/uiSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
|
||||
@@ -14,14 +12,6 @@ export const InitialState = memo(() => {
|
||||
const dispatch = useAppDispatch();
|
||||
const newCanvasSession = useCallback(() => {
|
||||
dispatch(setActiveTab('canvas'));
|
||||
toast({
|
||||
title: 'Switched to Canvas',
|
||||
description: 'You are in advanced mode yadda yadda.',
|
||||
status: 'info',
|
||||
position: 'top',
|
||||
// isClosable: false,
|
||||
duration: 5000,
|
||||
});
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
@@ -31,24 +21,30 @@ export const InitialState = memo(() => {
|
||||
</Flex>
|
||||
<Divider />
|
||||
<Flex flexDir="column" h="full" justifyContent="center" mx={16}>
|
||||
<Heading mb={4}>Choose a starting method.</Heading>
|
||||
<Text fontSize="md" fontStyle="italic" mb={6}>
|
||||
Drag an image onto a card or click the upload icon.
|
||||
</Text>
|
||||
|
||||
<Grid gridTemplateColumns="1fr 1fr" gridTemplateRows="1fr 1fr" gap={4}>
|
||||
<Heading mb={4}>Get started with Invoke.</Heading>
|
||||
<Flex flexDir="column" gap={4}>
|
||||
<Grid gridTemplateColumns="1fr 1fr" gap={4}>
|
||||
<InitialStateMainModelPicker />
|
||||
<Flex flexDir="column" gap={2}>
|
||||
<Text>
|
||||
Want to learn what prompts work best for each model?{' '}
|
||||
<Button as="a" variant="link" href="#" size="sm">
|
||||
Check our our Model Guide.
|
||||
</Button>
|
||||
</Text>
|
||||
</Flex>
|
||||
</Grid>
|
||||
<InitialStateGenerateFromText />
|
||||
<InitialStateAddAStyleReference />
|
||||
<InitialStateUseALayoutImageCard />
|
||||
<InitialStateEditImageCard />
|
||||
</Grid>
|
||||
|
||||
<Text fontSize="md" color="base.300" alignSelf="center" mt={6}>
|
||||
or{' '}
|
||||
<Button variant="link" onClick={newCanvasSession}>
|
||||
start from a blank canvas.
|
||||
</Button>
|
||||
</Text>
|
||||
<Alert status="info" borderRadius="base" flexDir="column" gap={2} overflow="unset">
|
||||
<Text fontSize="md" fontWeight="semibold">
|
||||
Looking to get more control, edit, and iterate on your images?
|
||||
</Text>
|
||||
<Button variant="link" onClick={newCanvasSession}>
|
||||
Navigate to Canvas for more capabilities.
|
||||
</Button>
|
||||
</Alert>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
@@ -4,34 +4,43 @@ import { Flex, Heading, Icon, Text } from '@invoke-ai/ui-library';
|
||||
import { useAppStore } from 'app/store/nanostores/store';
|
||||
import { useImageUploadButton } from 'common/hooks/useImageUploadButton';
|
||||
import { InitialStateButtonGridItem } from 'features/controlLayers/components/SimpleSession/InitialStateButtonGridItem';
|
||||
import { newCanvasFromImageDndTarget } from 'features/dnd/dnd';
|
||||
import { getDefaultRefImageConfig } from 'features/controlLayers/hooks/addLayerHooks';
|
||||
import { refImageAdded } from 'features/controlLayers/store/refImagesSlice';
|
||||
import { imageDTOToImageWithDims } from 'features/controlLayers/store/util';
|
||||
import { addGlobalReferenceImageDndTarget, newCanvasFromImageDndTarget } from 'features/dnd/dnd';
|
||||
import { DndDropTarget } from 'features/dnd/DndDropTarget';
|
||||
import { newCanvasFromImage } from 'features/imageActions/actions';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { PiUploadBold, PiUserCircleGearBold } from 'react-icons/pi';
|
||||
import type { ImageDTO } from 'services/api/types';
|
||||
|
||||
const NEW_CANVAS_OPTIONS = { type: 'reference_image' } as const;
|
||||
|
||||
const dndTargetData = newCanvasFromImageDndTarget.getData(NEW_CANVAS_OPTIONS);
|
||||
const dndTargetData = addGlobalReferenceImageDndTarget.getData();
|
||||
|
||||
export const InitialStateAddAStyleReference = memo(() => {
|
||||
const { getState, dispatch } = useAppStore();
|
||||
const { dispatch, getState } = useAppStore();
|
||||
|
||||
const onUpload = useCallback(
|
||||
(imageDTO: ImageDTO) => {
|
||||
newCanvasFromImage({ imageDTO, getState, dispatch, ...NEW_CANVAS_OPTIONS });
|
||||
},
|
||||
const uploadOptions = useMemo(
|
||||
() =>
|
||||
({
|
||||
onUpload: (imageDTO: ImageDTO) => {
|
||||
const config = getDefaultRefImageConfig(getState);
|
||||
config.image = imageDTOToImageWithDims(imageDTO);
|
||||
dispatch(refImageAdded({ overrides: { config } }));
|
||||
},
|
||||
allowMultiple: false,
|
||||
}) as const,
|
||||
[dispatch, getState]
|
||||
);
|
||||
const uploadApi = useImageUploadButton({ allowMultiple: false, onUpload });
|
||||
|
||||
const uploadApi = useImageUploadButton(uploadOptions);
|
||||
|
||||
return (
|
||||
<InitialStateButtonGridItem {...uploadApi.getUploadButtonProps()}>
|
||||
<InitialStateButtonGridItem {...uploadApi.getUploadButtonProps()} position="relative" gap={8}>
|
||||
<Icon as={PiUserCircleGearBold} boxSize={8} color="base.500" />
|
||||
<Heading size="sm">Add a Style Reference</Heading>
|
||||
<Text color="base.300">Add an image to transfer its look.</Text>
|
||||
<Flex w="full" justifyContent="flex-end" p={2}>
|
||||
<Flex flexDir="column" alignItems="flex-start" gap={2}>
|
||||
<Heading size="sm">Add a Style Reference</Heading>
|
||||
<Text color="base.300">Add an image to transfer its look.</Text>
|
||||
</Flex>
|
||||
<Flex position="absolute" right={3} bottom={3}>
|
||||
<PiUploadBold />
|
||||
<input {...uploadApi.getUploadInputProps()} />
|
||||
</Flex>
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
import type { GridItemProps } from '@invoke-ai/ui-library';
|
||||
import { Button, forwardRef, GridItem } from '@invoke-ai/ui-library';
|
||||
import type { ButtonProps } from '@invoke-ai/ui-library';
|
||||
import { Button, forwardRef } from '@invoke-ai/ui-library';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const InitialStateButtonGridItem = memo(
|
||||
forwardRef(({ children, ...rest }: GridItemProps, ref) => {
|
||||
forwardRef(({ children, ...rest }: ButtonProps, ref) => {
|
||||
return (
|
||||
<GridItem
|
||||
<Button
|
||||
ref={ref}
|
||||
as={Button}
|
||||
variant="outline"
|
||||
display="flex"
|
||||
position="relative"
|
||||
flexDir="column"
|
||||
alignItems="center"
|
||||
borderWidth={1}
|
||||
borderRadius="base"
|
||||
p={2}
|
||||
p={4}
|
||||
pt={6}
|
||||
gap={2}
|
||||
w="full"
|
||||
@@ -23,7 +21,7 @@ export const InitialStateButtonGridItem = memo(
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</GridItem>
|
||||
</Button>
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
@@ -15,11 +15,13 @@ const focusOnPrompt = () => {
|
||||
|
||||
export const InitialStateGenerateFromText = memo(() => {
|
||||
return (
|
||||
<InitialStateButtonGridItem onClick={focusOnPrompt}>
|
||||
<InitialStateButtonGridItem onClick={focusOnPrompt} position="relative" gap={8}>
|
||||
<Icon as={PiTextAaBold} boxSize={8} color="base.500" />
|
||||
<Heading size="sm">Generate from Text</Heading>
|
||||
<Text color="base.300">Enter a prompt and Invoke.</Text>
|
||||
<Flex w="full" justifyContent="flex-end" p={2}>
|
||||
<Flex flexDir="column" alignItems="flex-start" gap={2}>
|
||||
<Heading size="sm">Generate from Text</Heading>
|
||||
<Text color="base.300">Enter a prompt and Invoke.</Text>
|
||||
</Flex>
|
||||
<Flex position="absolute" right={3} bottom={3}>
|
||||
<PiCursorTextBold />
|
||||
</Flex>
|
||||
</InitialStateButtonGridItem>
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/* eslint-disable i18next/no-literal-string */
|
||||
import { Flex, FormControl, FormLabel, Icon } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
|
||||
import { ModelPicker } from 'features/parameters/components/ModelPicker';
|
||||
import { modelSelected } from 'features/parameters/store/actions';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { MdMoneyOff } from 'react-icons/md';
|
||||
import { useMainModels } from 'services/api/hooks/modelsByType';
|
||||
import { useSelectedModelConfig } from 'services/api/hooks/useSelectedModelConfig';
|
||||
import { type AnyModelConfig, isCheckpointMainModelConfig } from 'services/api/types';
|
||||
|
||||
export const InitialStateMainModelPicker = memo(() => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [modelConfigs] = useMainModels();
|
||||
const selectedModelConfig = useSelectedModelConfig();
|
||||
const onChange = useCallback(
|
||||
(modelConfig: AnyModelConfig) => {
|
||||
dispatch(modelSelected(modelConfig));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const isFluxDevSelected = useMemo(
|
||||
() =>
|
||||
selectedModelConfig &&
|
||||
isCheckpointMainModelConfig(selectedModelConfig) &&
|
||||
selectedModelConfig.config_path === 'flux-dev',
|
||||
[selectedModelConfig]
|
||||
);
|
||||
|
||||
return (
|
||||
<FormControl orientation="vertical" alignItems="unset">
|
||||
<InformationalPopover feature="paramModel">
|
||||
<FormLabel>Select your Model</FormLabel>
|
||||
</InformationalPopover>
|
||||
{isFluxDevSelected && (
|
||||
<InformationalPopover feature="fluxDevLicense" hideDisable={true}>
|
||||
<Flex justifyContent="flex-start">
|
||||
<Icon as={MdMoneyOff} />
|
||||
</Flex>
|
||||
</InformationalPopover>
|
||||
)}
|
||||
<ModelPicker modelConfigs={modelConfigs} selectedModelConfig={selectedModelConfig} onChange={onChange} grouped />
|
||||
</FormControl>
|
||||
);
|
||||
});
|
||||
InitialStateMainModelPicker.displayName = 'InitialStateMainModelPicker';
|
||||
Reference in New Issue
Block a user