diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNode.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNode.tsx index 7bb92c1494..66e4befb08 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNode.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNode.tsx @@ -8,6 +8,7 @@ import { useInputFieldNamesConnection, useInputFieldNamesMissing, } from 'features/nodes/hooks/useInputFieldNamesByStatus'; +import { useNodeHasErrors } from 'features/nodes/hooks/useNodeIsInvalid'; import { useOutputFieldNames } from 'features/nodes/hooks/useOutputFieldNames'; import { useWithFooter } from 'features/nodes/hooks/useWithFooter'; import { memo } from 'react'; @@ -37,11 +38,12 @@ const sx: SystemStyleObject = { }; const InvocationNode = ({ nodeId, isOpen }: Props) => { + const isInvalid = useNodeHasErrors(); const withFooter = useWithFooter(); return ( <> - + {isOpen && ( <> diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeHeader.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeHeader.tsx index 42a0bf1ec0..016f96e287 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeHeader.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeHeader.tsx @@ -3,7 +3,6 @@ import { Flex } from '@invoke-ai/ui-library'; import NodeCollapseButton from 'features/nodes/components/flow/nodes/common/NodeCollapseButton'; import NodeTitle from 'features/nodes/components/flow/nodes/common/NodeTitle'; import InvocationNodeClassificationIcon from 'features/nodes/components/flow/nodes/Invocation/InvocationNodeClassificationIcon'; -import { useNodeHasErrors } from 'features/nodes/hooks/useNodeIsInvalid'; import { memo } from 'react'; import InvocationNodeCollapsedHandles from './InvocationNodeCollapsedHandles'; @@ -13,6 +12,7 @@ import InvocationNodeStatusIndicator from './InvocationNodeStatusIndicator'; type Props = { nodeId: string; isOpen: boolean; + isInvalid?: boolean; }; const sx: SystemStyleObject = { @@ -28,14 +28,12 @@ const sx: SystemStyleObject = { }, }; -const InvocationNodeHeader = ({ nodeId, isOpen }: Props) => { - const isInvalid = useNodeHasErrors(); - +const InvocationNodeHeader = ({ nodeId, isOpen, isInvalid }: Props) => { return ( - + diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldTitle.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldTitle.tsx index ebbbfd9c2e..ef3020a741 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldTitle.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldTitle.tsx @@ -30,12 +30,12 @@ const labelSx: SystemStyleObject = { _hover: { fontWeight: 'semibold !important', }, - '&[data-is-invalid="true"]': { - color: 'error.300', - }, '&[data-is-added-to-form="true"]': { color: 'blue.300', }, + '&[data-is-invalid="true"]': { + color: 'error.300', + }, '&[data-is-disabled="true"]': { opacity: 0.5, }, 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 d9f77c2176..e08b3fe8d0 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,3 +1,4 @@ +import type { SystemStyleObject } from '@invoke-ai/ui-library'; import { Flex, Input, Text } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; import { useEditable } from 'common/hooks/useEditable'; @@ -10,12 +11,20 @@ import { NO_FIT_ON_DOUBLE_CLICK_CLASS } from 'features/nodes/types/constants'; import { memo, useCallback, useMemo, useRef } from 'react'; import { useTranslation } from 'react-i18next'; +const labelSx: SystemStyleObject = { + fontWeight: 'semibold', + '&[data-is-invalid="true"]': { + color: 'error.300', + }, +}; + type Props = { nodeId: string; title?: string; + isInvalid?: boolean; }; -const NodeTitle = ({ nodeId, title }: Props) => { +const NodeTitle = ({ nodeId, title, isInvalid }: Props) => { const dispatch = useAppDispatch(); const label = useNodeUserTitleSafe(); const batchGroupId = useBatchGroupId(nodeId); @@ -53,10 +62,11 @@ const NodeTitle = ({ nodeId, title }: Props) => { {!editable.isEditing && ( {titleWithBatchGroupId} diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeWrapper.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeWrapper.tsx index 26d46b1b2f..315ca57e9e 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeWrapper.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeWrapper.tsx @@ -5,6 +5,7 @@ import { useInvocationNodeContext } from 'features/nodes/components/flow/nodes/I import { useIsWorkflowEditorLocked } from 'features/nodes/hooks/useIsWorkflowEditorLocked'; import { useMouseOverFormField, useMouseOverNode } from 'features/nodes/hooks/useMouseOverNode'; import { useNodeExecutionState } from 'features/nodes/hooks/useNodeExecutionState'; +import { useNodeHasErrors } from 'features/nodes/hooks/useNodeIsInvalid'; import { useZoomToNode } from 'features/nodes/hooks/useZoomToNode'; import { selectNodeOpacity } from 'features/nodes/store/workflowSettingsSlice'; import { DRAG_HANDLE_CLASSNAME, NO_FIT_ON_DOUBLE_CLICK_CLASS, NODE_WIDTH } from 'features/nodes/types/constants'; @@ -29,6 +30,8 @@ const NodeWrapper = (props: NodeWrapperProps) => { const mouseOverFormField = useMouseOverFormField(nodeId); const zoomToNode = useZoomToNode(nodeId); const isLocked = useIsWorkflowEditorLocked(); + const isInvalid = useNodeHasErrors(); + const hasError = isMissingTemplate || isInvalid; const executionState = useNodeExecutionState(nodeId); const isInProgress = executionState?.status === zNodeStatus.enum.IN_PROGRESS; @@ -74,7 +77,7 @@ const NodeWrapper = (props: NodeWrapperProps) => { data-is-editor-locked={isLocked} data-is-selected={selected} data-is-mouse-over-form-field={mouseOverFormField.isMouseOverFormField} - data-status={isMissingTemplate ? 'error' : needsUpdate ? 'warning' : undefined} + data-status={hasError ? 'error' : needsUpdate ? 'warning' : undefined} > diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/shared.ts b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/shared.ts index 721c816b19..95b5b9c9e8 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/shared.ts +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/shared.ts @@ -56,6 +56,9 @@ export const containerSx: SystemStyleObject = { display: 'block', shadow: '0 0 0 2px var(--border-color-selected)', }, + '&[data-is-invalid="true"]': { + color: 'error.300', + }, '&[data-is-editor-locked="true"]': { '& *': { cursor: 'not-allowed',