diff --git a/invokeai/frontend/web/src/common/components/IAINumberInput.tsx b/invokeai/frontend/web/src/common/components/IAINumberInput.tsx index 8f675cc148..de3b44564a 100644 --- a/invokeai/frontend/web/src/common/components/IAINumberInput.tsx +++ b/invokeai/frontend/web/src/common/components/IAINumberInput.tsx @@ -28,7 +28,7 @@ import { useState, } from 'react'; -const numberStringRegex = /^-?(0\.)?\.?$/; +export const numberStringRegex = /^-?(0\.)?\.?$/; interface Props extends Omit { label?: string; diff --git a/invokeai/frontend/web/src/features/nodes/components/IAINode/IAINodeHeader.tsx b/invokeai/frontend/web/src/features/nodes/components/IAINode/IAINodeHeader.tsx index 73705769b6..226aaed7be 100644 --- a/invokeai/frontend/web/src/features/nodes/components/IAINode/IAINodeHeader.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/IAINode/IAINodeHeader.tsx @@ -1,4 +1,5 @@ import { Flex, Heading, Icon, Tooltip } from '@chakra-ui/react'; +import { DRAG_HANDLE_CLASSNAME } from 'features/nodes/hooks/useBuildInvocation'; import { memo } from 'react'; import { FaInfoCircle } from 'react-icons/fa'; @@ -12,6 +13,7 @@ const IAINodeHeader = (props: IAINodeHeaderProps) => { const { nodeId, title, description } = props; return ( { }); return ( - + {IAINodeInputsToRender} ); diff --git a/invokeai/frontend/web/src/features/nodes/components/InvocationComponent.tsx b/invokeai/frontend/web/src/features/nodes/components/InvocationComponent.tsx index 12817679e2..608f98d6d2 100644 --- a/invokeai/frontend/web/src/features/nodes/components/InvocationComponent.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/InvocationComponent.tsx @@ -23,7 +23,14 @@ export const InvocationComponent = memo((props: NodeProps) => { if (!template) { return ( - + ) => { description={template.description} /> { 'dark-lg', ]); + const shift = useAppSelector((state) => state.hotkeys.shift); + return ( ) => { /> ) => { const { nodeId, field } = props; - const dispatch = useAppDispatch(); + const [valueAsString, setValueAsString] = useState( + String(field.value) + ); - const handleValueChanged = (_: string, value: number) => { - dispatch(fieldValueChanged({ nodeId, fieldName: field.name, value })); + const handleValueChanged = (v: string) => { + setValueAsString(v); + // This allows negatives and decimals e.g. '-123', `.5`, `-0.2`, etc. + if (!v.match(numberStringRegex)) { + // Cast the value to number. Floor it if it should be an integer. + dispatch( + fieldValueChanged({ + nodeId, + fieldName: field.name, + value: + props.template.type === 'integer' + ? Math.floor(Number(v)) + : Number(v), + }) + ); + } }; + useEffect(() => { + if ( + !valueAsString.match(numberStringRegex) && + field.value !== Number(valueAsString) + ) { + setValueAsString(String(field.value)); + } + }, [field.value, valueAsString]); + return ( diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useBuildInvocation.ts b/invokeai/frontend/web/src/features/nodes/hooks/useBuildInvocation.ts index 5ed631e9fa..50a19d6b45 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useBuildInvocation.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useBuildInvocation.ts @@ -18,6 +18,12 @@ const templatesSelector = createSelector( (nodes) => nodes.invocationTemplates ); +export const DRAG_HANDLE_CLASSNAME = 'node-drag-handle'; + +export const SHARED_NODE_PROPERTIES: Partial = { + dragHandle: `.${DRAG_HANDLE_CLASSNAME}`, +}; + export const useBuildInvocation = () => { const invocationTemplates = useAppSelector(templatesSelector); @@ -32,6 +38,7 @@ export const useBuildInvocation = () => { }); const node: Node = { + ...SHARED_NODE_PROPERTIES, id: 'progress_image', type: 'progress_image', position: { x: x, y: y }, @@ -91,6 +98,7 @@ export const useBuildInvocation = () => { }); const invocation: Node = { + ...SHARED_NODE_PROPERTIES, id: nodeId, type: 'invocation', position: { x: x, y: y },