feat(ui): workflows panel redesign WIP

This commit is contained in:
psychedelicious
2025-02-20 18:32:44 +10:00
parent 9ecafc8706
commit b78ac40a22
14 changed files with 42 additions and 50 deletions

View File

@@ -1707,7 +1707,9 @@
"copyShareLink": "Copy Share Link",
"copyShareLinkForWorkflow": "Copy Share Link for Workflow",
"delete": "Delete",
"openLibrary": "Open Library",
"builder": {
"resetForm": "Reset Form",
"builder": "Form Builder",
"layout": "Layout",
"row": "Row",

View File

@@ -22,7 +22,7 @@ export const NewWorkflowButton = memo(() => {
return (
<IconButton
onClick={onClickNewWorkflow}
variant="outline"
variant="ghost"
size="sm"
aria-label={t('nodes.newWorkflow')}
tooltip={t('nodes.newWorkflow')}

View File

@@ -11,7 +11,7 @@ export const ActiveWorkflowDescription = memo(() => {
}
return (
<Text color="base.300" fontStyle="italic" noOfLines={1}>
<Text color="base.300" fontStyle="italic" noOfLines={1} pb={2}>
{description}
</Text>
);

View File

@@ -37,7 +37,7 @@ const SaveWorkflowButton = () => {
icon={<PiFloppyDiskBold />}
onClick={handleClickSave}
pointerEvents="auto"
variant="outline"
variant="ghost"
size="sm"
/>
);

View File

@@ -2,7 +2,7 @@ import { IconButton, Popover, PopoverBody, PopoverContent, PopoverTrigger, Porta
import { WorkflowListMenuContent } from 'features/nodes/components/sidePanel/WorkflowListMenu/WorkflowListMenuContent';
import { useWorkflowListMenu } from 'features/nodes/store/workflowListMenu';
import { useTranslation } from 'react-i18next';
import { PiCaretDownBold } from 'react-icons/pi';
import { PiFolderOpenFill } from 'react-icons/pi';
export const WorkflowListMenuTrigger = () => {
const workflowListMenu = useWorkflowListMenu();
@@ -11,7 +11,13 @@ export const WorkflowListMenuTrigger = () => {
return (
<Popover isOpen={workflowListMenu.isOpen} onClose={workflowListMenu.close} onOpen={workflowListMenu.open}>
<PopoverTrigger>
<IconButton aria-label={t('stylePresets.viewList')} variant="ghost" icon={<PiCaretDownBold />} size="sm" />
<IconButton
aria-label={t('workflows.openLibrary')}
tooltip={t('workflows.openLibrary')}
variant="ghost"
icon={<PiFolderOpenFill />}
size="sm"
/>
</PopoverTrigger>
<Portal appendToParentPortal={false}>
<PopoverContent p={4} w={512} maxW="full" minH={512} maxH="full">

View File

@@ -4,7 +4,7 @@ import { selectWorkflowMode, workflowModeChanged } from 'features/nodes/store/wo
import type { MouseEventHandler } from 'react';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiEyeBold, PiPencilBold } from 'react-icons/pi';
import { PiEyeBold, PiPencilSimpleFill } from 'react-icons/pi';
export const WorkflowViewEditToggleButton = memo(() => {
const dispatch = useAppDispatch();
@@ -33,8 +33,8 @@ export const WorkflowViewEditToggleButton = memo(() => {
aria-label={t('nodes.editMode')}
tooltip={t('nodes.editMode')}
onClick={onClickEdit}
icon={<PiPencilBold />}
variant="outline"
icon={<PiPencilSimpleFill />}
variant="ghost"
size="sm"
/>
);
@@ -47,7 +47,7 @@ export const WorkflowViewEditToggleButton = memo(() => {
tooltip={t('nodes.viewMode')}
onClick={onClickView}
icon={<PiEyeBold />}
variant="outline"
variant="ghost"
size="sm"
/>
);

View File

@@ -1,4 +1,4 @@
import { Divider, Flex } from '@invoke-ai/ui-library';
import { Flex } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
import { EditModeLeftPanelContent } from 'features/nodes/components/sidePanel/EditModeLeftPanelContent';
import { ActiveWorkflowDescription } from 'features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowDescription';
@@ -15,7 +15,6 @@ const WorkflowsTabLeftPanel = () => {
<Flex w="full" h="full" gap={2} flexDir="column">
<ActiveWorkflowNameAndActions />
<ActiveWorkflowDescription />
<Divider />
{mode === 'view' && <ViewModeLeftPanelContent />}
{mode === 'edit' && <EditModeLeftPanelContent />}
</Flex>

View File

@@ -27,7 +27,7 @@ import type { Equals } from 'tsafe';
import { assert } from 'tsafe';
const sx: SystemStyleObject = {
gap: 2,
gap: 4,
flex: '1 1 0',
'&[data-depth="0"]': {
flex: 1,
@@ -71,7 +71,7 @@ const ContainerElementComponentViewMode = memo(({ el }: { el: ContainerElement }
<FormElementComponent key={childId} id={childId} />
))}
{children.length === 0 && (
<Flex p={4} w="full" h="full" alignItems="center" justifyContent="center">
<Flex p={8} w="full" h="full" alignItems="center" justifyContent="center">
<Text variant="subtext">{t('workflows.builder.containerPlaceholder')}</Text>
</Flex>
)}
@@ -97,7 +97,7 @@ const ContainerElementComponentEditMode = memo(({ el }: { el: ContainerElement }
<FormElementComponent key={childId} id={childId} />
))}
{children.length === 0 && (
<Flex p={4} w="full" h="full" alignItems="center" justifyContent="center">
<Flex p={8} w="full" h="full" alignItems="center" justifyContent="center">
<Text variant="subtext">{t('workflows.builder.containerPlaceholderDesc')}</Text>
</Flex>
)}

View File

@@ -9,7 +9,7 @@ import type { CenterOrEdge } from 'features/nodes/components/sidePanel/builder/c
*/
const line = {
thickness: 2,
backgroundColor: 'base.500',
backgroundColor: 'base.600',
};
type DropIndicatorProps = {

View File

@@ -21,12 +21,12 @@ const sx: SystemStyleObject = {
maxH: 8,
borderTopRadius: 'base',
alignItems: 'center',
cursor: 'grab',
color: 'base.300',
bg: 'baseAlpha.250',
'&[data-depth="0"]': { bg: 'baseAlpha.100' },
'&[data-depth="1"]': { bg: 'baseAlpha.150' },
'&[data-depth="2"]': { bg: 'baseAlpha.200' },
'&[data-is-root="false"]': { cursor: 'grab' },
};
export const FormElementEditModeHeader = memo(
@@ -42,18 +42,20 @@ export const FormElementEditModeHeader = memo(
dispatch(formElementRemoved({ id: element.id }));
}, [dispatch, element.id, isRootElement]);
const label = useMemo(() => {
if (isContainerElement(element)) {
const baseLabel = isRootElement ? 'Root Container' : 'Container';
if (element.data.layout === 'column') {
return `${baseLabel} (column layout)`;
}
return `${baseLabel} (row layout)`;
if (isRootElement) {
return 'Root Container';
}
if (isContainerElement(element) && element.data.layout === 'column') {
return `Container (column layout)`;
}
if (isContainerElement(element) && element.data.layout === 'row') {
return `Container (row layout)`;
}
return startCase(element.type);
}, [element, isRootElement]);
return (
<Flex ref={ref} sx={sx} data-depth={depth}>
<Flex ref={ref} sx={sx} data-depth={depth} data-is-root={isRootElement}>
<Text fontWeight="semibold" noOfLines={1} wordBreak="break-all">
{label}
</Text>

View File

@@ -36,7 +36,7 @@ const innerSx: SystemStyleObject = {
},
'&[data-active-drop-region="center"]': {
opacity: 1,
bg: 'base.700',
bg: 'base.850',
},
'&[data-element-type="divider"]&[data-layout="row"]': {
w: 'min-content',
@@ -49,8 +49,8 @@ const innerSx: SystemStyleObject = {
const contentWrapperSx: SystemStyleObject = {
w: 'full',
h: 'full',
p: 2,
gap: 2,
p: 4,
gap: 4,
borderWidth: 1,
borderRadius: 'base',
borderTopRadius: 'unset',

View File

@@ -54,7 +54,7 @@ const NodeFieldElementComponentViewMode = memo(({ el }: { el: NodeFieldElement }
);
return (
<Flex id={id} className={NODE_FIELD_CLASS_NAME}>
<Flex id={id} className={NODE_FIELD_CLASS_NAME} flex={1}>
<FormControl flex="1 1 0" orientation="vertical">
<Flex w="full" gap={4}>
<FormLabel>{_label}</FormLabel>

View File

@@ -1,6 +1,6 @@
import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
import { Alert, AlertDescription, AlertIcon, Button, ButtonGroup, Flex, Spacer } from '@invoke-ai/ui-library';
import { Button, ButtonGroup, Flex, Spacer } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
import { firefoxDndFix } from 'features/dnd/util';
@@ -28,19 +28,15 @@ export const WorkflowBuilder = memo(() => {
return (
<Flex justifyContent="center" w="full" h="full">
<Flex flexDir="column" w="full" maxW="768px" gap={4}>
<Alert status="warning" variant="subtle" borderRadius="base" flexShrink={0}>
<AlertIcon />
<AlertDescription fontSize="sm">{t('workflows.builder.workflowBuilderAlphaWarning')}</AlertDescription>
</Alert>
<ButtonGroup isAttached={false} justifyContent="center">
<Flex flexDir="column" w="full" maxW="768px" gap={2}>
<ButtonGroup isAttached={false} justifyContent="center" size="md">
<AddFormElementDndButton type="container" />
<AddFormElementDndButton type="divider" />
<AddFormElementDndButton type="heading" />
<AddFormElementDndButton type="text" />
<Spacer />
<Button onClick={resetForm} variant="ghost" leftIcon={<PiArrowCounterClockwiseBold />}>
{t('common.reset')}
{t('workflows.builder.resetForm')}
</Button>
</ButtonGroup>
<ScrollableContent>
@@ -106,20 +102,7 @@ const AddFormElementDndButton = ({ type }: { type: Parameters<typeof useAddFormE
const isDragging = useAddFormElementDnd(type, draggableRef);
return (
<Button
as="div"
ref={draggableRef}
pointerEvents="all"
variant="unstyled"
borderWidth={2}
borderStyle="dashed"
borderRadius="base"
px={4}
py={1}
cursor="grab"
_hover={{ bg: 'base.800' }}
isDisabled={isDragging}
>
<Button as="div" ref={draggableRef} variant="outline" cursor="grab" borderStyle="dashed" isDisabled={isDragging}>
{startCase(type)}
</Button>
);

View File

@@ -65,7 +65,7 @@ const isFormElementDndData = (data: Record<string | symbol, unknown>): data is F
const flashElement = (elementId: ElementId) => {
const element = document.querySelector(`#${getEditModeWrapperId(elementId)}`);
if (element instanceof HTMLElement) {
triggerPostMoveFlash(element, colorTokenToCssVar('base.700'));
triggerPostMoveFlash(element, colorTokenToCssVar('base.800'));
}
};