diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index 74443fbb7f4..fac5150cb70 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -854,6 +854,7 @@ "defaultVAE": "Default VAE" }, "nodes": { + "noBatchGroup": "no group", "addNode": "Add Node", "addNodeToolTip": "Add Node (Shift+A, Space)", "addLinearView": "Add to Linear View", diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/BatchGroupId.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/BatchGroupId.tsx new file mode 100644 index 00000000000..3f4231230ff --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/BatchGroupId.tsx @@ -0,0 +1,24 @@ +import type { TextProps } from '@invoke-ai/ui-library'; +import { Text } from '@invoke-ai/ui-library'; +import { useBatchGroupColorToken } from 'features/nodes/hooks/useBatchGroupColorToken'; +import { memo } from 'react'; + +type Props = TextProps & { + batchGroupId?: string; +}; + +export const BatchGroupId = memo(({ batchGroupId, ...rest }: Props) => { + const batchGroupColorToken = useBatchGroupColorToken(batchGroupId); + + if (!batchGroupColorToken || !batchGroupId) { + return null; + } + + return ( + + {batchGroupId} + + ); +}); + +BatchGroupId.displayName = 'BatchGroupId'; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeTitle.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeTitle.tsx index 58e9bd99159..71261155067 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeTitle.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeTitle.tsx @@ -1,12 +1,14 @@ -import type { SystemStyleObject } from '@invoke-ai/ui-library'; -import { Box, Editable, EditableInput, EditablePreview, Flex, useEditableControls } from '@invoke-ai/ui-library'; +import type { SystemStyleObject, TextProps } from '@invoke-ai/ui-library'; +import { Box, Editable, EditableInput, Flex, Text, useEditableControls } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; +import { useBatchGroupColorToken } from 'features/nodes/hooks/useBatchGroupColorToken'; +import { useBatchGroupId } from 'features/nodes/hooks/useBatchGroupId'; import { useNodeLabel } from 'features/nodes/hooks/useNodeLabel'; import { useNodeTemplateTitle } from 'features/nodes/hooks/useNodeTemplateTitle'; import { nodeLabelChanged } from 'features/nodes/store/nodesSlice'; import { DRAG_HANDLE_CLASSNAME } from 'features/nodes/types/constants'; import type { MouseEvent } from 'react'; -import { memo, useCallback, useEffect, useState } from 'react'; +import { memo, useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; type Props = { @@ -17,6 +19,8 @@ type Props = { const NodeTitle = ({ nodeId, title }: Props) => { const dispatch = useAppDispatch(); const label = useNodeLabel(nodeId); + const batchGroupId = useBatchGroupId(nodeId); + const batchGroupColorToken = useBatchGroupColorToken(batchGroupId); const templateTitle = useNodeTemplateTitle(nodeId); const { t } = useTranslation(); @@ -29,6 +33,16 @@ const NodeTitle = ({ nodeId, title }: Props) => { [dispatch, nodeId, title, templateTitle, label, t] ); + const localTitleWithBatchGroupId = useMemo(() => { + if (!batchGroupId) { + return localTitle; + } + if (batchGroupId === 'None') { + return `${localTitle} (${t('nodes.noBatchGroup')})`; + } + return `${localTitle} (${batchGroupId})`; + }, [batchGroupId, localTitle, t]); + const handleChange = useCallback((newTitle: string) => { setLocalTitle(newTitle); }, []); @@ -50,7 +64,16 @@ const NodeTitle = ({ nodeId, title }: Props) => { w="full" h="full" > - + + {localTitleWithBatchGroupId} + @@ -60,6 +83,16 @@ const NodeTitle = ({ nodeId, title }: Props) => { export default memo(NodeTitle); +const Preview = (props: TextProps) => { + const { isEditing } = useEditableControls(); + + if (isEditing) { + return null; + } + + return ; +}; + function EditableControls() { const { isEditing, getEditButtonProps } = useEditableControls(); const handleDoubleClick = useCallback( diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useBatchGroupColorToken.ts b/invokeai/frontend/web/src/features/nodes/hooks/useBatchGroupColorToken.ts new file mode 100644 index 00000000000..0426258e0e0 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/hooks/useBatchGroupColorToken.ts @@ -0,0 +1,22 @@ +import { useMemo } from 'react'; + +export const useBatchGroupColorToken = (batchGroupId?: string) => { + const batchGroupColorToken = useMemo(() => { + switch (batchGroupId) { + case 'Group 1': + return 'invokeGreen.300'; + case 'Group 2': + return 'invokeBlue.300'; + case 'Group 3': + return 'invokePurple.200'; + case 'Group 4': + return 'invokeRed.300'; + case 'Group 5': + return 'invokeYellow.300'; + default: + return undefined; + } + }, [batchGroupId]); + + return batchGroupColorToken; +}; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useBatchGroupId.ts b/invokeai/frontend/web/src/features/nodes/hooks/useBatchGroupId.ts new file mode 100644 index 00000000000..9ae03fa39ad --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/hooks/useBatchGroupId.ts @@ -0,0 +1,19 @@ +import { useNode } from 'features/nodes/hooks/useNode'; +import { isBatchNode, isInvocationNode } from 'features/nodes/types/invocation'; +import { useMemo } from 'react'; + +export const useBatchGroupId = (nodeId: string) => { + const node = useNode(nodeId); + + const batchGroupId = useMemo(() => { + if (!isInvocationNode(node)) { + return; + } + if (!isBatchNode(node)) { + return; + } + return node.data.inputs['batch_group_id']?.value as string; + }, [node]); + + return batchGroupId; +};