Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion invokeai/frontend/web/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1983,7 +1983,7 @@
"loadWorkflow": "$t(common.load) Workflow",
"autoLayout": "Auto Layout",
"edit": "Edit",
"view": "View",
"view": "Load",
"download": "Download",
"copyShareLink": "Copy Share Link",
"copyShareLinkForWorkflow": "Copy Share Link for Workflow",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const DeleteWorkflow = ({ workflowId }: { workflowId: string }) => {
<Tooltip label={t('workflows.delete')} closeOnScroll>
<IconButton
size="sm"
variant="link"
variant="ghost"
alignSelf="stretch"
aria-label={t('workflows.delete')}
onClick={handleClickDelete}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const DownloadWorkflow = ({ workflowId }: { workflowId: string }) => {
<Tooltip label={t('workflows.download')} closeOnScroll>
<IconButton
size="sm"
variant="link"
variant="ghost"
alignSelf="stretch"
aria-label={t('workflows.download')}
onClick={handleClickDownload}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const EditWorkflow = ({ workflowId }: { workflowId: string }) => {
<Tooltip label={t('workflows.edit')} closeOnScroll>
<IconButton
size="sm"
variant="link"
variant="ghost"
alignSelf="stretch"
aria-label={t('workflows.edit')}
onClick={handleClickEdit}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { useLoadWorkflowWithDialog } from 'features/workflowLibrary/components/L
import type { MouseEvent } from 'react';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiEyeBold } from 'react-icons/pi';
import { PiCheckBold } from 'react-icons/pi';

export const ViewWorkflow = ({ workflowId }: { workflowId: string }) => {
export const LoadWorkflow = ({ workflowId }: { workflowId: string }) => {
const dispatch = useAppDispatch();
const loadWorkflowWithDialog = useLoadWorkflowWithDialog();
const { t } = useTranslation();
Expand All @@ -30,11 +30,11 @@ export const ViewWorkflow = ({ workflowId }: { workflowId: string }) => {
<Tooltip label={t('workflows.view')} closeOnScroll>
<IconButton
size="sm"
variant="link"
variant="ghost"
alignSelf="stretch"
aria-label={t('workflows.view')}
onClick={handleClickLoad}
icon={<PiEyeBold />}
icon={<PiCheckBold />}
/>
</Tooltip>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const LockedWorkflowIcon = memo(() => {
<IconButton
size="sm"
cursor="not-allowed"
variant="link"
variant="ghost"
alignSelf="stretch"
aria-label={t('workflows.builder.publishedWorkflowsLocked')}
icon={<PiLockBold />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const ShareWorkflowButton = memo(({ workflow }: { workflow: WorkflowRecor
<Tooltip label={t('workflows.copyShareLink')} closeOnScroll>
<IconButton
size="sm"
variant="link"
variant="ghost"
alignSelf="stretch"
aria-label={t('workflows.copyShareLink')}
onClick={handleClickShare}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const WorkflowLibraryModal = memo(() => {
maxW="calc(100% - var(--invoke-sizes-40))"
h="calc(100% - var(--invoke-sizes-40))"
maxH="calc(100% - var(--invoke-sizes-40))"
bg="base.850"
>
<ModalHeader>{t('workflows.workflowLibrary')}</ModalHeader>
<ModalCloseButton />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ButtonProps, CheckboxProps } from '@invoke-ai/ui-library';
import type { ButtonProps, CheckboxProps, SystemStyleObject } from '@invoke-ai/ui-library';
import {
Box,
Button,
Expand Down Expand Up @@ -43,13 +43,13 @@ export const WorkflowLibrarySideNav = () => {
const allowPublishWorkflows = useAppSelector(selectAllowPublishWorkflows);

return (
<Flex h="full" minH={0} overflow="hidden" flexDir="column" w={64} gap={0}>
<Flex flexDir="column" w="full" pb={2} gap={2}>
<Flex h="full" minH={0} overflow="hidden" flexDir="column" w={64}>
<Flex flexDir="column" w="full">
<WorkflowLibraryViewButton view="recent">{t('workflows.recentlyOpened')}</WorkflowLibraryViewButton>
<WorkflowLibraryViewButton view="yours">{t('workflows.yourWorkflows')}</WorkflowLibraryViewButton>
{categoryOptions.includes('project') && (
<Collapse in={view === 'yours' || view === 'shared' || view === 'private'}>
<Flex flexDir="column" gap={2} pl={4} pt={2}>
<Flex flexDir="column" pl={4}>
<WorkflowLibraryViewButton size="sm" view="private">
{t('workflows.private')}
</WorkflowLibraryViewButton>
Expand All @@ -64,7 +64,7 @@ export const WorkflowLibrarySideNav = () => {
<WorkflowLibraryViewButton view="published">{t('workflows.published')}</WorkflowLibraryViewButton>
)}
</Flex>
<Flex h="full" minH={0} overflow="hidden" flexDir="column">
<Flex w="full" h="full" minH={0} flexDir="column">
<BrowseWorkflowsButton />
<DefaultsViewCheckboxesCollapsible />
</Flex>
Expand All @@ -87,18 +87,17 @@ const BrowseWorkflowsButton = memo(() => {
if (view === 'defaults' && selectedTags.length > 0) {
return (
<ButtonGroup>
<WorkflowLibraryViewButton view="defaults" w="auto">
{t('workflows.browseWorkflows')}
</WorkflowLibraryViewButton>
<WorkflowLibraryViewButton view="defaults">{t('workflows.browseWorkflows')}</WorkflowLibraryViewButton>
<Tooltip label={t('workflows.deselectAll')}>
<IconButton
onClick={resetTags}
size="md"
aria-label={t('workflows.deselectAll')}
icon={<PiArrowCounterClockwiseBold size={12} />}
variant="ghost"
bg="base.700"
bg="base.750"
color="base.50"
flexShrink={0}
/>
</Tooltip>
</ButtonGroup>
Expand Down Expand Up @@ -193,17 +192,34 @@ const WorkflowLibraryViewButton = memo(({ view, ...rest }: ButtonProps & { view:
dispatch(workflowLibraryViewChanged(view));
}, [dispatch, view]);

const workflowLibraryButtonSx: SystemStyleObject = {
position: 'relative',
_after: {
content: '""',
position: 'absolute',
insetY: 2,
left: 1,
w: 0.5,
rounded: 'sm',
bg: selectedView === view ? 'invokeBlue.300' : 'transparent',
},
...(selectedView === view
? {
bg: 'base.750',
color: 'base.50',
}
: {}),
};

return (
<Button
variant="ghost"
justifyContent="flex-start"
size="md"
flexShrink={0}
w="full"
onClick={onClick}
sx={workflowLibraryButtonSx}
{...rest}
bg={selectedView === view ? 'base.700' : undefined}
color={selectedView === view ? 'base.50' : undefined}
/>
);
});
Expand All @@ -222,7 +238,7 @@ const TagCategory = memo(({ tagCategory }: { tagCategory: WorkflowTagCategory })
<Text fontWeight="semibold" color="base.300" flexShrink={0}>
{t(tagCategory.categoryTKey)}
</Text>
<Flex flexDir="column" gap={2} pl={4}>
<Flex flexDir="column">
{tagCategory.tags.map((tag) => (
<TagCheckbox key={tag.label} tag={tag} />
))}
Expand All @@ -247,18 +263,31 @@ const TagCheckbox = memo(({ tag, ...rest }: CheckboxProps & { tag: { label: stri
return null;
}

const tagCheckbox: SystemStyleObject = {
py: 1,
px: 2,
transition: 'background-color 0.1s',
rounded: 'base',
_hover: {
bg: 'base.750',
},
_active: {
bg: 'transparent',
},
flexShrink: 0,
};

return (
<Flex alignItems="center" gap={2}>
<Checkbox isChecked={isChecked} onChange={onChange} {...rest} flexShrink={0} />
<Text>{`${tag.label} (${count})`}</Text>
<Checkbox isChecked={isChecked} onChange={onChange} sx={tagCheckbox} {...rest}>
<Text>{`${t(tag.label)} (${count})`}</Text>
{tag.recommended && (
<Tooltip label={t('workflows.recommended')}>
<Box as="span" lineHeight={0}>
<Icon as={PiStarFill} boxSize={4} fill="invokeYellow.500" />
</Box>
</Tooltip>
)}
</Flex>
</Checkbox>
);
});
TagCheckbox.displayName = 'TagCheckbox';
Original file line number Diff line number Diff line change
Expand Up @@ -168,25 +168,6 @@ const WorkflowListContent = memo(
fetchNextPage();
}, [hasNextPage, isFetching, fetchNextPage]);

// // TODO(psyche): this causes an infinite loop, the scrollIntoView triggers the onScroll which triggers the
// // fetchNextPage which triggers the scrollIntoView again...
// useEffect(() => {
// const el = ref.current;
// if (!el) {
// return;
// }

// const observer = new MutationObserver(() => {
// el.querySelector(':scope > :last-child')?.scrollIntoView({ behavior: 'smooth' });
// });

// observer.observe(el, { childList: true });

// return () => {
// observer.disconnect();
// };
// }, []);

return (
<Flex flexDir="column" gap={4} flex={1} minH={0}>
<Grid
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,26 @@ import type { WorkflowRecordListItemWithThumbnailDTO } from 'services/api/types'
import { DeleteWorkflow } from './WorkflowLibraryListItemActions/DeleteWorkflow';
import { DownloadWorkflow } from './WorkflowLibraryListItemActions/DownloadWorkflow';
import { EditWorkflow } from './WorkflowLibraryListItemActions/EditWorkflow';
import { ViewWorkflow } from './WorkflowLibraryListItemActions/ViewWorkflow';
import { LoadWorkflow } from './WorkflowLibraryListItemActions/LoadWorkflow';

const IMAGE_THUMBNAIL_SIZE = '108px';
const IMAGE_THUMBNAIL_SIZE = '92px';
const FALLBACK_ICON_SIZE = '32px';

const WORKFLOW_ACTION_BUTTONS_CN = 'workflow-action-buttons';

const sx: SystemStyleObject = {
const workflowListItemSx: SystemStyleObject = {
position: 'relative',
cursor: 'pointer',
role: 'button',
bg: 'base.850',
rounded: 'base',
w: 'full',
alignItems: 'stretch',
gap: 2,
borderWidth: 1,
borderColor: 'base.750',
_hover: {
bg: 'base.700',
bg: 'base.800',
[`& .${WORKFLOW_ACTION_BUTTONS_CN}`]: {
display: 'flex',
},
Expand Down Expand Up @@ -52,17 +62,7 @@ export const WorkflowListItem = memo(({ workflow }: { workflow: WorkflowRecordLi
}, [dispatch, loadWorkflowWithDialog, workflow.workflow_id]);

return (
<Flex
position="relative"
role="button"
onClick={handleClickLoad}
bg="base.750"
borderRadius="base"
w="full"
alignItems="stretch"
sx={sx}
gap={2}
>
<Flex onClick={handleClickLoad} sx={workflowListItemSx}>
<Flex p={2} pr={0}>
<Image
src={workflow.thumbnail_url ?? undefined}
Expand All @@ -74,7 +74,7 @@ export const WorkflowListItem = memo(({ workflow }: { workflow: WorkflowRecordLi
width={IMAGE_THUMBNAIL_SIZE}
minHeight={IMAGE_THUMBNAIL_SIZE}
minWidth={IMAGE_THUMBNAIL_SIZE}
borderRadius="base"
borderRadius="sm"
/>
</Flex>
<Flex flexDir="column" gap={1} justifyContent="space-between" w="full">
Expand Down Expand Up @@ -121,19 +121,19 @@ export const WorkflowListItem = memo(({ workflow }: { workflow: WorkflowRecordLi
)}
</Flex>
</Flex>
<Text variant="subtext" fontSize="xs" noOfLines={3}>
<Text variant="subtext" fontSize="sm" noOfLines={3}>
{workflow.description}
</Text>
</Flex>
<Flex className={WORKFLOW_ACTION_BUTTONS_CN} alignItems="center" display="none" h={8}>
<Flex className={WORKFLOW_ACTION_BUTTONS_CN} alignItems="center" display="none" pr={1} py={1}>
{workflow.opened_at && (
<Text variant="subtext" fontSize="xs" noOfLines={1} justifySelf="flex-end" pb={0.5}>
{t('workflows.opened')} {new Date(workflow.opened_at).toLocaleString()}
</Text>
)}
<Spacer />
{workflow.category === 'default' && !workflow.is_published && (
<ViewWorkflow workflowId={workflow.workflow_id} />
<LoadWorkflow workflowId={workflow.workflow_id} />
)}
{workflow.category !== 'default' && !workflow.is_published && (
<>
Expand All @@ -157,7 +157,7 @@ const UserThumbnailFallback = memo(() => {
height={IMAGE_THUMBNAIL_SIZE}
minWidth={IMAGE_THUMBNAIL_SIZE}
bg="base.600"
borderRadius="base"
borderRadius="sm"
alignItems="center"
justifyContent="center"
opacity={0.3}
Expand All @@ -174,7 +174,7 @@ const DefaultThumbnailFallback = memo(() => {
height={IMAGE_THUMBNAIL_SIZE}
minWidth={IMAGE_THUMBNAIL_SIZE}
bg="base.600"
borderRadius="base"
borderRadius="sm"
alignItems="center"
justifyContent="center"
opacity={0.3}
Expand Down