Skip to content

Commit f123888

Browse files
feat(ui): tidy workflows tab launchapd
1 parent aeab7d0 commit f123888

File tree

3 files changed

+55
-73
lines changed

3 files changed

+55
-73
lines changed

invokeai/frontend/web/public/locales/en.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2464,8 +2464,8 @@
24642464
"description": "Start a new workflow from scratch"
24652465
},
24662466
"loadFromFile": {
2467-
"title": "Load workflow from existing image or file",
2468-
"description": "Drag or upload a workflow to start with an existing setup"
2467+
"title": "Load workflow from file",
2468+
"description": "Upload a workflow to start with an existing setup"
24692469
}
24702470
},
24712471
"upscaling": {

invokeai/frontend/web/src/features/controlLayers/components/SimpleSession/WorkflowsLaunchpadPanel.tsx

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { Button, Flex, Heading, Icon, Text } from '@invoke-ai/ui-library';
22
import { useWorkflowLibraryModal } from 'features/nodes/store/workflowLibraryModal';
3+
import { useLoadWorkflowWithDialog } from 'features/workflowLibrary/components/LoadWorkflowConfirmationAlertDialog';
34
import { useNewWorkflow } from 'features/workflowLibrary/components/NewWorkflowConfirmationAlertDialog';
4-
import { useLoadWorkflowFromFile } from 'features/workflowLibrary/hooks/useLoadWorkflowFromFile';
55
import { memo, useCallback } from 'react';
6+
import { useDropzone } from 'react-dropzone';
67
import { useTranslation } from 'react-i18next';
78
import { PiFilePlusBold, PiFolderOpenBold, PiUploadBold } from 'react-icons/pi';
89

@@ -12,14 +13,6 @@ export const WorkflowsLaunchpadPanel = memo(() => {
1213
const { t } = useTranslation();
1314
const workflowLibraryModal = useWorkflowLibraryModal();
1415
const newWorkflow = useNewWorkflow();
15-
const loadWorkflowFromFile = useLoadWorkflowFromFile();
16-
17-
const onUploadWorkflow = useCallback(
18-
(file: File) => {
19-
loadWorkflowFromFile(file);
20-
},
21-
[loadWorkflowFromFile]
22-
);
2316

2417
const handleBrowseTemplates = useCallback(() => {
2518
workflowLibraryModal.open();
@@ -29,24 +22,32 @@ export const WorkflowsLaunchpadPanel = memo(() => {
2922
newWorkflow.createWithDialog();
3023
}, [newWorkflow]);
3124

32-
const handleLoadFromFile = useCallback(() => {
33-
// Create file input for workflow files
34-
const input = document.createElement('input');
35-
input.type = 'file';
36-
input.accept = '.json';
37-
input.onchange = (e) => {
38-
const file = (e.target as HTMLInputElement).files?.[0];
39-
if (file) {
40-
onUploadWorkflow(file);
25+
const loadWorkflowWithDialog = useLoadWorkflowWithDialog();
26+
27+
const onDropAccepted = useCallback(
28+
([file]: File[]) => {
29+
if (!file) {
30+
return;
4131
}
42-
};
43-
input.click();
44-
}, [onUploadWorkflow]);
32+
loadWorkflowWithDialog({
33+
type: 'file',
34+
data: file,
35+
});
36+
},
37+
[loadWorkflowWithDialog]
38+
);
39+
40+
const uploadApi = useDropzone({
41+
accept: { 'application/json': ['.json'] },
42+
onDropAccepted,
43+
noDrag: true,
44+
multiple: false,
45+
});
4546

4647
return (
4748
<Flex flexDir="column" h="full" w="full" alignItems="center" gap={2}>
4849
<Flex flexDir="column" w="full" gap={4} px={14} maxW={768} pt="20vh">
49-
<Heading mb={4}>{t('ui.launchpad.workflowsTitle')}</Heading>
50+
<Heading>{t('ui.launchpad.workflowsTitle')}</Heading>
5051

5152
{/* Description */}
5253
<Text variant="subtext" fontSize="md" lineHeight="1.6">
@@ -87,12 +88,16 @@ export const WorkflowsLaunchpadPanel = memo(() => {
8788
</LaunchpadButton>
8889

8990
{/* Load workflow from existing image or file */}
90-
<LaunchpadButton onClick={handleLoadFromFile} position="relative" gap={8}>
91+
<LaunchpadButton {...uploadApi.getRootProps()} position="relative" gap={8}>
9192
<Icon as={PiUploadBold} boxSize={8} color="base.500" />
9293
<Flex flexDir="column" alignItems="flex-start" gap={2}>
9394
<Heading size="sm">{t('ui.launchpad.workflows.loadFromFile.title')}</Heading>
9495
<Text color="base.300">{t('ui.launchpad.workflows.loadFromFile.description')}</Text>
9596
</Flex>
97+
<Flex position="absolute" right={3} bottom={3}>
98+
<PiUploadBold />
99+
<input {...uploadApi.getInputProps()} />
100+
</Flex>
96101
</LaunchpadButton>
97102
</Flex>
98103
</Flex>

invokeai/frontend/web/src/features/nodes/components/sidePanel/viewMode/EmptyState.tsx

Lines changed: 25 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,21 @@
1-
import { Flex, Heading, Icon, Image, Link, Text } from '@invoke-ai/ui-library';
1+
import { Flex, Heading, Icon, Link, Text } from '@invoke-ai/ui-library';
22
import { useAppDispatch } from 'app/store/storeHooks';
33
import { LaunchpadButton } from 'features/controlLayers/components/SimpleSession/LaunchpadButton';
44
import { useIsWorkflowUntouched } from 'features/nodes/components/sidePanel/workflow/IsolatedWorkflowBuilderWatcher';
55
import { useWorkflowLibraryModal } from 'features/nodes/store/workflowLibraryModal';
66
import { workflowModeChanged } from 'features/nodes/store/workflowLibrarySlice';
7-
import InvokeLogoSVG from 'public/assets/images/invoke-symbol-wht-lrg.svg';
87
import { useCallback } from 'react';
98
import { Trans, useTranslation } from 'react-i18next';
109
import { PiFolderOpenBold, PiPlusBold } from 'react-icons/pi';
1110

1211
export const EmptyState = () => {
1312
const isWorkflowUntouched = useIsWorkflowUntouched();
1413

15-
return (
16-
<Flex w="full" h="full" userSelect="none" justifyContent="center">
17-
<Flex
18-
alignItems="center"
19-
justifyContent="center"
20-
borderRadius="base"
21-
flexDir="column"
22-
gap={5}
23-
maxW="400px"
24-
pt={24}
25-
px={4}
26-
>
27-
<Image
28-
src={InvokeLogoSVG}
29-
alt="invoke-ai-logo"
30-
opacity={0.2}
31-
mixBlendMode="overlay"
32-
w={16}
33-
h={16}
34-
minW={16}
35-
minH={16}
36-
userSelect="none"
37-
/>
38-
{isWorkflowUntouched ? <CleanEditorContent /> : <DirtyEditorContent />}
39-
</Flex>
40-
</Flex>
41-
);
14+
if (isWorkflowUntouched) {
15+
return <CleanEditorContent />;
16+
}
17+
18+
return <DirtyEditorContent />;
4219
};
4320

4421
const CleanEditorContent = () => {
@@ -51,31 +28,31 @@ const CleanEditorContent = () => {
5128
}, [dispatch]);
5229

5330
return (
54-
<>
55-
<Flex flexDir="column" gap={4} w="full">
56-
<LaunchpadButton onClick={onClickNewWorkflow} gap={4}>
31+
<Flex flexDir="column" h="full" w="full" alignItems="center">
32+
<Flex flexDir="column" gap={8} w="full" pt="20vh" px={8} maxW={768}>
33+
<LaunchpadButton onClick={onClickNewWorkflow} gap={8}>
5734
<Icon as={PiPlusBold} boxSize={6} color="base.500" />
58-
<Flex flexDir="column" alignItems="flex-start" gap={1}>
35+
<Flex flexDir="column" alignItems="flex-start" gap={2}>
5936
<Heading size="sm">{t('nodes.newWorkflow')}</Heading>
6037
<Text color="base.300" fontSize="sm">
6138
Create a new workflow from scratch
6239
</Text>
6340
</Flex>
6441
</LaunchpadButton>
65-
<LaunchpadButton onClick={workflowLibraryModal.open} gap={4}>
42+
<LaunchpadButton onClick={workflowLibraryModal.open} gap={8}>
6643
<Icon as={PiFolderOpenBold} boxSize={6} color="base.500" />
67-
<Flex flexDir="column" alignItems="flex-start" gap={1}>
44+
<Flex flexDir="column" alignItems="flex-start" gap={2}>
6845
<Heading size="sm">{t('nodes.loadWorkflow')}</Heading>
6946
<Text color="base.300" fontSize="sm">
7047
Browse and load existing workflows
7148
</Text>
7249
</Flex>
7350
</LaunchpadButton>
51+
<Text textAlign="center" fontSize="sm" color="base.400">
52+
<Trans i18nKey="nodes.workflowHelpText" size="sm" components={workflowHelpTextComponents} />
53+
</Text>
7454
</Flex>
75-
<Text textAlign="center" fontSize="sm" color="base.400" mt={4}>
76-
<Trans i18nKey="nodes.workflowHelpText" size="sm" components={workflowHelpTextComponents} />
77-
</Text>
78-
</>
55+
</Flex>
7956
);
8057
};
8158

@@ -88,29 +65,29 @@ const DirtyEditorContent = () => {
8865
}, [dispatch]);
8966

9067
return (
91-
<>
92-
<Text textAlign="center" fontSize="md" mb={4}>
93-
{t('nodes.noFieldsViewMode')}
94-
</Text>
95-
<Flex flexDir="column" gap={4} w="full">
96-
<LaunchpadButton onClick={onClick} gap={4}>
68+
<Flex flexDir="column" h="full" w="full" alignItems="center">
69+
<Flex flexDir="column" gap={8} w="full" pt="20vh" px={8} maxW={768}>
70+
<Text textAlign="center" fontSize="sm" color="base.300">
71+
{t('nodes.noFieldsViewMode')}
72+
</Text>
73+
<LaunchpadButton onClick={onClick} gap={8}>
9774
<Icon as={PiPlusBold} boxSize={6} color="base.500" />
98-
<Flex flexDir="column" alignItems="flex-start" gap={1}>
75+
<Flex flexDir="column" alignItems="flex-start" gap={2}>
9976
<Heading size="sm">{t('nodes.edit')}</Heading>
10077
<Text color="base.300" fontSize="sm">
10178
Switch to edit mode to build workflows
10279
</Text>
10380
</Flex>
10481
</LaunchpadButton>
10582
</Flex>
106-
</>
83+
</Flex>
10784
);
10885
};
10986

11087
const workflowHelpTextComponents = {
11188
LinkComponent: (
11289
<Link
113-
fontSize="md"
90+
fontSize="sm"
11491
fontWeight="semibold"
11592
href="https://support.invoke.ai/support/solutions/articles/151000159663-example-workflows"
11693
target="_blank"

0 commit comments

Comments
 (0)