Fixes & Styling updates

This commit is contained in:
Kent Keirsey
2025-06-30 20:16:19 -04:00
committed by psychedelicious
parent 90201be670
commit 8b8e29d22d
5 changed files with 137 additions and 84 deletions

View File

@@ -2471,6 +2471,10 @@
"title": "Upload Image to Upscale",
"description": "Click or drag an image to upscale (JPG, PNG, WebP up to 100MB)"
},
"replaceImage": {
"title": "Replace Current Image",
"description": "Click or drag a new image to replace the current one"
},
"imageReady": {
"title": "Image Ready",
"description": "Press Invoke to begin upscaling"

View File

@@ -11,6 +11,7 @@ export const LaunchpadButton = memo(
display="flex"
position="relative"
alignItems="center"
justifyContent="left"
borderWidth={1}
borderRadius="base"
p={4}

View File

@@ -1,17 +1,14 @@
import { Box, Flex, Grid, Heading, Icon, Text } from '@invoke-ai/ui-library';
import { Box, Button, Flex, Grid, Heading, Icon, Text } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useImageUploadButton } from 'common/hooks/useImageUploadButton';
import { setUpscaleInitialImageDndTarget } from 'features/dnd/dnd';
import { DndDropTarget } from 'features/dnd/DndDropTarget';
import { DndImage } from 'features/dnd/DndImage';
import { DndImageIcon } from 'features/dnd/DndImageIcon';
import ParamSpandrelModel from 'features/parameters/components/Upscale/ParamSpandrelModel';
import { selectUpscaleInitialImage, upscaleInitialImageChanged } from 'features/parameters/store/upscaleSlice';
import { MainModelPicker } from 'features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker';
import { UpscaleScaleSlider } from 'features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleScaleSlider';
import { selectUpscaleInitialImage, upscaleInitialImageChanged, creativityChanged, structureChanged, selectCreativity, selectStructure } from 'features/parameters/store/upscaleSlice';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiArrowCounterClockwiseBold, PiImageBold, PiUploadBold } from 'react-icons/pi';
import { PiArrowCounterClockwiseBold, PiImageBold, PiUploadBold, PiShieldCheckBold, PiScalesBold, PiPaletteBold, PiSparkleBold } from 'react-icons/pi';
import type { ImageDTO } from 'services/api/types';
import { LaunchpadButton } from './LaunchpadButton';
@@ -20,6 +17,8 @@ export const UpscalingLaunchpadPanel = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const upscaleInitialImage = useAppSelector(selectUpscaleInitialImage);
const creativity = useAppSelector(selectCreativity);
const structure = useAppSelector(selectStructure);
const dndTargetData = useMemo(() => setUpscaleInitialImageDndTarget.getData(), []);
@@ -36,6 +35,27 @@ export const UpscalingLaunchpadPanel = memo(() => {
const uploadApi = useImageUploadButton({ allowMultiple: false, onUpload });
// Preset button handlers
const onConservativeClick = useCallback(() => {
dispatch(creativityChanged(-5));
dispatch(structureChanged(5));
}, [dispatch]);
const onBalancedClick = useCallback(() => {
dispatch(creativityChanged(0));
dispatch(structureChanged(0));
}, [dispatch]);
const onCreativeClick = useCallback(() => {
dispatch(creativityChanged(5));
dispatch(structureChanged(-2));
}, [dispatch]);
const onArtisticClick = useCallback(() => {
dispatch(creativityChanged(8));
dispatch(structureChanged(-5));
}, [dispatch]);
return (
<Flex flexDir="column" h="full" w="full" alignItems="center" gap={2}>
<Flex flexDir="column" w="full" gap={4} px={14} maxW={768} pt="20vh">
@@ -57,34 +77,14 @@ export const UpscalingLaunchpadPanel = memo(() => {
</>
) : (
<>
<Flex position="relative" w={16} h={16} alignItems="center" justifyContent="center">
<DndImage imageDTO={upscaleInitialImage} />
<Flex position="absolute" flexDir="column" top={1} insetInlineEnd={1} gap={1}>
<DndImageIcon
onClick={onReset}
icon={<PiArrowCounterClockwiseBold size={12} />}
tooltip={t('common.reset')}
/>
</Flex>
<Text
position="absolute"
background="base.900"
color="base.50"
fontSize="xs"
fontWeight="semibold"
bottom={0}
left={0}
opacity={0.7}
px={1}
lineHeight={1.25}
borderTopEndRadius="base"
borderBottomStartRadius="base"
pointerEvents="none"
>{`${upscaleInitialImage.width}x${upscaleInitialImage.height}`}</Text>
</Flex>
<Icon as={PiImageBold} boxSize={8} color="base.500" />
<Flex flexDir="column" alignItems="flex-start" gap={2}>
<Heading size="sm">{t('ui.launchpad.upscaling.imageReady.title')}</Heading>
<Text color="base.300">{t('ui.launchpad.upscaling.imageReady.description')}</Text>
<Heading size="sm">{t('ui.launchpad.upscaling.replaceImage.title')}</Heading>
<Text color="base.300">{t('ui.launchpad.upscaling.replaceImage.description')}</Text>
</Flex>
<Flex position="absolute" right={3} bottom={3}>
<PiUploadBold />
<input {...uploadApi.getUploadInputProps()} />
</Flex>
</>
)}
@@ -108,33 +108,54 @@ export const UpscalingLaunchpadPanel = memo(() => {
{/* Controls */}
<style>{`.launchpad-hide-label .chakra-form__label { display: none !important; }`}</style>
<Grid gridTemplateColumns="1fr 1fr" gap={10} alignItems="start" mt={upscaleInitialImage ? 8 : 12}>
{/* Left Column: All parameters stacked */}
<Flex flexDir="column" gap={6} alignItems="flex-start">
<Box w="full">
<Text fontWeight="semibold" fontSize="sm" mb={1}>
{t('ui.launchpad.upscaling.upscaleModel')}
</Text>
<Box className="launchpad-hide-label">
<ParamSpandrelModel />
</Box>
</Box>
<Box w="full">
<Text fontWeight="semibold" fontSize="sm" mb={1}>
{t('ui.launchpad.upscaling.model')}
</Text>
<Box className="launchpad-hide-label">
<MainModelPicker />
</Box>
</Box>
<Box w="full">
<Text fontWeight="semibold" fontSize="sm" mb={1}>
{t('ui.launchpad.upscaling.scale')}
</Text>
<Box className="launchpad-hide-label">
<UpscaleScaleSlider />
</Box>
</Box>
</Flex>
{/* Left Column: Creativity and Structural Defaults */}
<Box>
<Text fontWeight="semibold" fontSize="sm" mb={3}>
Creativity & Structural Defaults
</Text>
<Flex flexDir="column" gap={2}>
<Button
size="sm"
variant={creativity === -5 && structure === 5 ? "solid" : "outline"}
colorScheme="base"
justifyContent="center"
onClick={onConservativeClick}
leftIcon={<PiShieldCheckBold />}
>
Conservative
</Button>
<Button
size="sm"
variant={creativity === 0 && structure === 0 ? "solid" : "outline"}
colorScheme="base"
justifyContent="center"
onClick={onBalancedClick}
leftIcon={<PiScalesBold />}
>
Balanced
</Button>
<Button
size="sm"
variant={creativity === 5 && structure === -2 ? "solid" : "outline"}
colorScheme="base"
justifyContent="center"
onClick={onCreativeClick}
leftIcon={<PiPaletteBold />}
>
Creative
</Button>
<Button
size="sm"
variant={creativity === 8 && structure === -5 ? "solid" : "outline"}
colorScheme="base"
justifyContent="center"
onClick={onArtisticClick}
leftIcon={<PiSparkleBold />}
>
Artistic
</Button>
</Flex>
</Box>
{/* Right Column: Description/help text */}
<Box>
<Text variant="subtext" fontSize="sm" lineHeight="1.6">

View File

@@ -1,4 +1,4 @@
import { Flex, Heading, Icon, Link, Text } from '@invoke-ai/ui-library';
import { Button, Flex, Heading, Icon, Text } from '@invoke-ai/ui-library';
import { useWorkflowLibraryModal } from 'features/nodes/store/workflowLibraryModal';
import { useNewWorkflow } from 'features/workflowLibrary/components/NewWorkflowConfirmationAlertDialog';
import { useLoadWorkflowFromFile } from 'features/workflowLibrary/hooks/useLoadWorkflowFromFile';
@@ -53,14 +53,18 @@ export const WorkflowsLaunchpadPanel = memo(() => {
{t('ui.launchpad.workflows.description')}
</Text>
<Link
href="https://support.invoke.ai/support/solutions/articles/151000189610-getting-started-with-workflows-denoise-latents"
isExternal
color="invokeBlue.400"
fontSize="sm"
>
{t('ui.launchpad.workflows.learnMoreLink')}
</Link>
<Text>
<Button
as="a"
variant="link"
href="https://support.invoke.ai/support/solutions/articles/151000189610-getting-started-with-workflows-denoise-latents"
target="_blank"
rel="noopener noreferrer"
size="sm"
>
{t('ui.launchpad.workflows.learnMoreLink')}
</Button>
</Text>
{/* Action Buttons */}
<Flex flexDir="column" gap={8}>

View File

@@ -1,11 +1,13 @@
import { Button, Flex, Image, Link, Text } from '@invoke-ai/ui-library';
import { Flex, Heading, Icon, Image, Link, Text } from '@invoke-ai/ui-library';
import { useAppDispatch } from 'app/store/storeHooks';
import { LaunchpadButton } from 'features/controlLayers/components/SimpleSession/LaunchpadButton';
import { useIsWorkflowUntouched } from 'features/nodes/components/sidePanel/workflow/IsolatedWorkflowBuilderWatcher';
import { useWorkflowLibraryModal } from 'features/nodes/store/workflowLibraryModal';
import { workflowModeChanged } from 'features/nodes/store/workflowLibrarySlice';
import InvokeLogoSVG from 'public/assets/images/invoke-symbol-wht-lrg.svg';
import { useCallback } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { PiFolderOpenBold, PiPlusBold } from 'react-icons/pi';
export const EmptyState = () => {
const isWorkflowUntouched = useIsWorkflowUntouched();
@@ -18,8 +20,9 @@ export const EmptyState = () => {
borderRadius="base"
flexDir="column"
gap={5}
maxW="230px"
maxW="400px"
pt={24}
px={4}
>
<Image
src={InvokeLogoSVG}
@@ -49,15 +52,27 @@ const CleanEditorContent = () => {
return (
<>
<Flex gap={2}>
<Button size="sm" onClick={onClickNewWorkflow}>
{t('nodes.newWorkflow')}
</Button>
<Button size="sm" colorScheme="invokeBlue" onClick={workflowLibraryModal.open}>
{t('nodes.loadWorkflow')}
</Button>
<Flex flexDir="column" gap={4} w="full">
<LaunchpadButton onClick={onClickNewWorkflow} gap={4}>
<Icon as={PiPlusBold} boxSize={6} color="base.500" />
<Flex flexDir="column" alignItems="flex-start" gap={1}>
<Heading size="sm">{t('nodes.newWorkflow')}</Heading>
<Text color="base.300" fontSize="sm">
Create a new workflow from scratch
</Text>
</Flex>
</LaunchpadButton>
<LaunchpadButton onClick={workflowLibraryModal.open} gap={4}>
<Icon as={PiFolderOpenBold} boxSize={6} color="base.500" />
<Flex flexDir="column" alignItems="flex-start" gap={1}>
<Heading size="sm">{t('nodes.loadWorkflow')}</Heading>
<Text color="base.300" fontSize="sm">
Browse and load existing workflows
</Text>
</Flex>
</LaunchpadButton>
</Flex>
<Text textAlign="center" fontSize="md">
<Text textAlign="center" fontSize="sm" color="base.400" mt={4}>
<Trans i18nKey="nodes.workflowHelpText" size="sm" components={workflowHelpTextComponents} />
</Text>
</>
@@ -74,12 +89,20 @@ const DirtyEditorContent = () => {
return (
<>
<Text textAlign="center" fontSize="md">
<Text textAlign="center" fontSize="md" mb={4}>
{t('nodes.noFieldsViewMode')}
</Text>
<Button size="sm" colorScheme="invokeBlue" onClick={onClick}>
{t('nodes.edit')}
</Button>
<Flex flexDir="column" gap={4} w="full">
<LaunchpadButton onClick={onClick} gap={4}>
<Icon as={PiPlusBold} boxSize={6} color="base.500" />
<Flex flexDir="column" alignItems="flex-start" gap={1}>
<Heading size="sm">{t('nodes.edit')}</Heading>
<Text color="base.300" fontSize="sm">
Switch to edit mode to build workflows
</Text>
</Flex>
</LaunchpadButton>
</Flex>
</>
);
};
@@ -93,4 +116,4 @@ const workflowHelpTextComponents = {
target="_blank"
/>
),
};
};