Revert "feat(ui): rough out number generators for number collection fields"

This reverts commit 41cc6f1f96bca2a51727f21bd727ca48eab669bc.
This commit is contained in:
psychedelicious
2025-01-13 19:30:43 +11:00
parent 4e8c6d931d
commit 2c64b60d32
6 changed files with 103 additions and 286 deletions

View File

@@ -9,7 +9,6 @@ import {
isIntegerFieldCollectionInputInstance,
isStringFieldCollectionInputInstance,
} from 'features/nodes/types/field';
import { getNumberFieldCollectionValue } from 'features/nodes/types/fieldValidators';
import type { InvocationNodeEdge } from 'features/nodes/types/invocation';
import { isInvocationNode } from 'features/nodes/types/invocation';
import { buildNodesGraph } from 'features/nodes/util/graph/buildNodesGraph';
@@ -107,7 +106,7 @@ export const addEnqueueRequestedNodes = (startAppListening: AppStartListening) =
// Find outgoing edges from the batch node, we will remove these from the graph and create batch data collection items from them instead
const edgesFromStringBatch = nodes.edges.filter((e) => e.source === node.id && e.sourceHandle === 'value');
addBatchDataCollectionItem(edgesFromStringBatch, getNumberFieldCollectionValue(integers.value));
addBatchDataCollectionItem(edgesFromStringBatch, integers.value);
}
// Grab float batch nodes for special handling
@@ -126,7 +125,7 @@ export const addEnqueueRequestedNodes = (startAppListening: AppStartListening) =
// Find outgoing edges from the batch node, we will remove these from the graph and create batch data collection items from them instead
const edgesFromStringBatch = nodes.edges.filter((e) => e.source === node.id && e.sourceHandle === 'value');
addBatchDataCollectionItem(edgesFromStringBatch, getNumberFieldCollectionValue(floats.value));
addBatchDataCollectionItem(edgesFromStringBatch, floats.value);
}
const batchConfig: BatchConfig = {

View File

@@ -1,34 +1,21 @@
import type { SystemStyleObject } from '@invoke-ai/ui-library';
import {
Box,
CompositeNumberInput,
Flex,
FormControl,
FormLabel,
Grid,
GridItem,
IconButton,
Text,
} from '@invoke-ai/ui-library';
import { Box, CompositeNumberInput, Flex, Grid, GridItem, IconButton } from '@invoke-ai/ui-library';
import { NUMPY_RAND_MAX } from 'app/constants';
import { useAppStore } from 'app/store/nanostores/store';
import { useAppDispatch } from 'app/store/storeHooks';
import { getOverlayScrollbarsParams, overlayScrollbarsStyles } from 'common/components/OverlayScrollbars/constants';
import { useFieldIsInvalid } from 'features/nodes/hooks/useFieldIsInvalid';
import { fieldNumberCollectionValueChanged } from 'features/nodes/store/nodesSlice';
import type {
FloatFieldCollectionInputInstance,
FloatFieldCollectionInputTemplate,
FloatStartStepCountGenerator,
IntegerFieldCollectionInputInstance,
IntegerFieldCollectionInputTemplate,
IntegerStartStepCountGenerator,
} from 'features/nodes/types/field';
import { isNil } from 'lodash-es';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiLightbulbFill, PiPencilSimpleFill, PiPlusBold, PiXBold } from 'react-icons/pi';
import { PiPlusBold, PiXBold } from 'react-icons/pi';
import type { FieldComponentProps } from './types';
@@ -50,40 +37,68 @@ export const NumberFieldCollectionInputComponent = memo(
) => {
const { nodeId, field, fieldTemplate } = props;
const store = useAppStore();
const isInvalid = useFieldIsInvalid(nodeId, field.name);
const isIntegerField = useMemo(() => fieldTemplate.type.name === 'IntegerField', [fieldTemplate.type]);
const entryMode = useMemo(() => {
if (!field.value) {
return 'manual';
}
if (Array.isArray(field.value)) {
return 'manual';
}
return 'step';
}, [field.value]);
const toggleEntryMode = useCallback(() => {
if (!field.value || Array.isArray(field.value)) {
const newValue: IntegerStartStepCountGenerator | FloatStartStepCountGenerator = isIntegerField
? { type: 'integer-start-step-count-generator', start: 0, step: 1, count: 1 }
: { type: 'float-start-step-count-generator', start: 0, step: 1, count: 1 };
const onRemoveNumber = useCallback(
(index: number) => {
const newValue = field.value ? [...field.value] : [];
newValue.splice(index, 1);
store.dispatch(fieldNumberCollectionValueChanged({ nodeId, fieldName: field.name, value: newValue }));
} else {
store.dispatch(
fieldNumberCollectionValueChanged({
nodeId,
fieldName: field.name,
value: [0],
})
);
}
}, [field.name, field.value, isIntegerField, nodeId, store]);
},
[field.name, field.value, nodeId, store]
);
const onChangeNumber = useCallback(
(index: number, value: number) => {
const newValue = field.value ? [...field.value] : [];
newValue[index] = value;
store.dispatch(fieldNumberCollectionValueChanged({ nodeId, fieldName: field.name, value: newValue }));
},
[field.name, field.value, nodeId, store]
);
const onAddNumber = useCallback(() => {
const newValue = field.value && Array.isArray(field.value) ? [...field.value, 0] : [0];
const newValue = field.value ? [...field.value, 0] : [0];
store.dispatch(fieldNumberCollectionValueChanged({ nodeId, fieldName: field.name, value: newValue }));
}, [field.value, field.name, store, nodeId]);
}, [field.name, field.value, nodeId, store]);
const min = useMemo(() => {
let min = -NUMPY_RAND_MAX;
if (!isNil(fieldTemplate.minimum)) {
min = fieldTemplate.minimum;
}
if (!isNil(fieldTemplate.exclusiveMinimum)) {
min = fieldTemplate.exclusiveMinimum + 0.01;
}
return min;
}, [fieldTemplate.exclusiveMinimum, fieldTemplate.minimum]);
const max = useMemo(() => {
let max = NUMPY_RAND_MAX;
if (!isNil(fieldTemplate.maximum)) {
max = fieldTemplate.maximum;
}
if (!isNil(fieldTemplate.exclusiveMaximum)) {
max = fieldTemplate.exclusiveMaximum - 0.01;
}
return max;
}, [fieldTemplate.exclusiveMaximum, fieldTemplate.maximum]);
const step = useMemo(() => {
if (isNil(fieldTemplate.multipleOf)) {
return isIntegerField ? 1 : 0.1;
}
return fieldTemplate.multipleOf;
}, [fieldTemplate.multipleOf, isIntegerField]);
const fineStep = useMemo(() => {
if (isNil(fieldTemplate.multipleOf)) {
return isIntegerField ? 1 : 0.01;
}
return fieldTemplate.multipleOf;
}, [fieldTemplate.multipleOf, isIntegerField]);
return (
<Flex
@@ -94,48 +109,53 @@ export const NumberFieldCollectionInputComponent = memo(
maxH={64}
alignItems="stretch"
justifyContent="center"
flexDir="column"
overflow="hidden"
gap={1}
p={1}
borderWidth={1}
borderRadius="base"
sx={sx}
data-error={isInvalid}
>
<Flex gap={2} w="full" alignItems="center">
{!field.value ||
(Array.isArray(field.value) && (
<>
<Text flexGrow={1}>Manual</Text>
{(!field.value || field.value.length === 0) && (
<Box w="full" sx={sx} data-error={isInvalid} borderRadius="base">
<IconButton
w="full"
onClick={onAddNumber}
aria-label="Add Item"
icon={<PiPlusBold />}
variant="ghost"
size="sm"
/>
</Box>
)}
{field.value && field.value.length > 0 && (
<Box w="full" h="auto" p={1} sx={sx} data-error={isInvalid} borderRadius="base">
<OverlayScrollbarsComponent
className="nowheel"
defer
style={overlayScrollbarsStyles}
options={overlayscrollbarsOptions}
>
<Grid w="full" h="full" templateColumns="repeat(1, 1fr)" gap={1}>
<IconButton
w="full"
onClick={onAddNumber}
aria-label="Add Item"
icon={<PiPlusBold />}
variant="ghost"
size="sm"
/>
</>
))}
{field.value && !Array.isArray(field.value) && (
<>
<Text flexGrow={1}>Generator</Text>
</>
)}
<IconButton
onClick={toggleEntryMode}
aria-label="Toggle Entry Mode"
icon={entryMode === 'manual' ? <PiLightbulbFill /> : <PiPencilSimpleFill />}
variant="ghost"
size="sm"
/>
</Flex>
{field.value && !Array.isArray(field.value) && (
<GeneratorEntry nodeId={nodeId} fieldName={field.name} value={field.value} fieldTemplate={fieldTemplate} />
)}
{field.value && Array.isArray(field.value) && field.value.length > 0 && (
<ManualEntry nodeId={nodeId} fieldName={field.name} value={field.value} fieldTemplate={fieldTemplate} />
{field.value.map((value, index) => (
<GridItem key={index} position="relative" className="nodrag">
<NumberListItemContent
value={value}
index={index}
min={min}
max={max}
step={step}
fineStep={fineStep}
isIntegerField={isIntegerField}
onRemoveNumber={onRemoveNumber}
onChangeNumber={onChangeNumber}
/>
</GridItem>
))}
</Grid>
</OverlayScrollbarsComponent>
</Box>
)}
</Flex>
);
@@ -144,163 +164,6 @@ export const NumberFieldCollectionInputComponent = memo(
NumberFieldCollectionInputComponent.displayName = 'NumberFieldCollectionInputComponent';
const GeneratorEntry = ({
nodeId,
fieldName,
value,
fieldTemplate,
}: {
nodeId: string;
fieldName: string;
value: IntegerStartStepCountGenerator | FloatStartStepCountGenerator;
fieldTemplate: IntegerFieldCollectionInputTemplate | FloatFieldCollectionInputTemplate;
}) => {
const dispatch = useAppDispatch();
const isIntegerField = useMemo(() => fieldTemplate.type.name === 'IntegerField', [fieldTemplate.type]);
const onChangeStart = useCallback(
(v: number) => {
const newValue = { ...value, start: v };
dispatch(fieldNumberCollectionValueChanged({ nodeId, fieldName, value: newValue }));
},
[dispatch, fieldName, nodeId, value]
);
const onChangeCount = useCallback(
(v: number) => {
const newValue = { ...value, count: v };
dispatch(fieldNumberCollectionValueChanged({ nodeId, fieldName, value: newValue }));
},
[dispatch, fieldName, nodeId, value]
);
const onChangeStep = useCallback(
(v: number) => {
const newValue = { ...value, step: v };
dispatch(fieldNumberCollectionValueChanged({ nodeId, fieldName, value: newValue }));
},
[dispatch, fieldName, nodeId, value]
);
return (
<Flex gap={2}>
<FormControl>
<FormLabel m={0}>Start</FormLabel>
<CompositeNumberInput value={value.start} onChange={onChangeStart} min={-Infinity} max={Infinity} />
</FormControl>
<FormControl>
<FormLabel m={0}>Count</FormLabel>
<CompositeNumberInput value={value.count} onChange={onChangeCount} min={1} max={Infinity} />
</FormControl>
<FormControl>
<FormLabel m={0}>Step</FormLabel>
<CompositeNumberInput
value={value.step}
onChange={onChangeStep}
min={-Infinity}
max={Infinity}
step={isIntegerField ? 1 : 0.1}
/>
</FormControl>
</Flex>
);
};
const ManualEntry = ({
nodeId,
fieldName,
value,
fieldTemplate,
}: {
nodeId: string;
fieldName: string;
value: number[];
fieldTemplate: IntegerFieldCollectionInputTemplate | FloatFieldCollectionInputTemplate;
}) => {
const dispatch = useAppDispatch();
const isIntegerField = useMemo(() => fieldTemplate.type.name === 'IntegerField', [fieldTemplate.type]);
const onRemoveNumber = useCallback(
(index: number) => {
const newValue = [...value];
newValue.splice(index, 1);
dispatch(fieldNumberCollectionValueChanged({ nodeId, fieldName, value: newValue }));
},
[value, dispatch, nodeId, fieldName]
);
const onChangeNumber = useCallback(
(index: number, num: number) => {
const newValue = [...value];
newValue[index] = num;
dispatch(fieldNumberCollectionValueChanged({ nodeId, fieldName, value: newValue }));
},
[value, dispatch, nodeId, fieldName]
);
const min = useMemo(() => {
let min = -NUMPY_RAND_MAX;
if (!isNil(fieldTemplate.minimum)) {
min = fieldTemplate.minimum;
}
if (!isNil(fieldTemplate.exclusiveMinimum)) {
min = fieldTemplate.exclusiveMinimum + 0.01;
}
return min;
}, [fieldTemplate.exclusiveMinimum, fieldTemplate.minimum]);
const max = useMemo(() => {
let max = NUMPY_RAND_MAX;
if (!isNil(fieldTemplate.maximum)) {
max = fieldTemplate.maximum;
}
if (!isNil(fieldTemplate.exclusiveMaximum)) {
max = fieldTemplate.exclusiveMaximum - 0.01;
}
return max;
}, [fieldTemplate.exclusiveMaximum, fieldTemplate.maximum]);
const step = useMemo(() => {
if (isNil(fieldTemplate.multipleOf)) {
return isIntegerField ? 1 : 0.1;
}
return fieldTemplate.multipleOf;
}, [fieldTemplate.multipleOf, isIntegerField]);
const fineStep = useMemo(() => {
if (isNil(fieldTemplate.multipleOf)) {
return isIntegerField ? 1 : 0.01;
}
return fieldTemplate.multipleOf;
}, [fieldTemplate.multipleOf, isIntegerField]);
return (
<Box w="full" h="full">
<OverlayScrollbarsComponent
className="nowheel"
defer
style={overlayScrollbarsStyles}
options={overlayscrollbarsOptions}
>
<Grid w="full" h="full" templateColumns="repeat(1fr)" gap={1}>
{value.map((value, index) => (
<GridItem key={index} position="relative" className="nodrag">
<NumberListItemContent
value={value}
index={index}
min={min}
max={max}
step={step}
fineStep={fineStep}
isIntegerField={isIntegerField}
onRemoveNumber={onRemoveNumber}
onChangeNumber={onChangeNumber}
/>
</GridItem>
))}
</Grid>
</OverlayScrollbarsComponent>
</Box>
);
};
type NumberListItemContentProps = {
value: number;
index: number;

View File

@@ -15,7 +15,6 @@ import type {
ControlNetModelFieldValue,
EnumFieldValue,
FieldValue,
FloatFieldCollectionValue,
FloatFieldValue,
FluxVAEModelFieldValue,
ImageFieldCollectionValue,
@@ -323,10 +322,7 @@ export const nodesSlice = createSlice({
fieldNumberValueChanged: (state, action: FieldValueAction<IntegerFieldValue | FloatFieldValue>) => {
fieldValueReducer(state, action, zIntegerFieldValue.or(zFloatFieldValue));
},
fieldNumberCollectionValueChanged: (
state,
action: FieldValueAction<IntegerFieldCollectionValue | FloatFieldCollectionValue>
) => {
fieldNumberCollectionValueChanged: (state, action: FieldValueAction<IntegerFieldCollectionValue>) => {
fieldValueReducer(state, action, zIntegerFieldCollectionValue.or(zFloatFieldCollectionValue));
},
fieldBooleanValueChanged: (state, action: FieldValueAction<BooleanFieldValue>) => {

View File

@@ -279,17 +279,7 @@ export const isIntegerFieldInputTemplate = buildTypeGuard(zIntegerFieldInputTemp
// #endregion
// #region IntegerField Collection
const zIntegerStartStepCountGenerator = z.object({
type: z.literal('integer-start-step-count-generator'),
start: z.number().int(),
step: z.number().int(),
count: z.number().int().gte(1),
});
export type IntegerStartStepCountGenerator = z.infer<typeof zIntegerStartStepCountGenerator>;
export const isIntegerStartStepCountGenerator = buildTypeGuard(zIntegerStartStepCountGenerator);
export const zIntegerFieldCollectionValue = z
.union([z.array(zIntegerFieldValue), zIntegerStartStepCountGenerator])
.optional();
export const zIntegerFieldCollectionValue = z.array(zIntegerFieldValue).optional();
const zIntegerFieldCollectionInputInstance = zFieldInputInstanceBase.extend({
value: zIntegerFieldCollectionValue,
});
@@ -327,6 +317,7 @@ export const isIntegerFieldCollectionInputTemplate = buildTypeGuard(zIntegerFiel
// #endregion
// #region FloatField
export const zFloatFieldValue = z.number();
const zFloatFieldInputInstance = zFieldInputInstanceBase.extend({
value: zFloatFieldValue,
@@ -352,17 +343,7 @@ export const isFloatFieldInputTemplate = buildTypeGuard(zFloatFieldInputTemplate
// #endregion
// #region FloatField Collection
const zFloatStartStepCountGenerator = z.object({
type: z.literal('float-start-step-count-generator'),
start: z.number(),
step: z.number(),
count: z.number().gte(1),
});
export type FloatStartStepCountGenerator = z.infer<typeof zFloatStartStepCountGenerator>;
export const isFloatStartStepCountGenerator = buildTypeGuard(zFloatStartStepCountGenerator);
export const zFloatFieldCollectionValue = z
.union([z.array(zFloatFieldValue), zFloatStartStepCountGenerator])
.optional();
export const zFloatFieldCollectionValue = z.array(zFloatFieldValue).optional();
const zFloatFieldCollectionInputInstance = zFieldInputInstanceBase.extend({
value: zFloatFieldCollectionValue,
});

View File

@@ -8,7 +8,6 @@ import type {
StringFieldCollectionInputTemplate,
StringFieldCollectionValue,
} from 'features/nodes/types/field';
import { numberStartStepCountGenerator } from 'features/nodes/types/generators';
import { t } from 'i18next';
export const validateImageFieldCollectionValue = (
@@ -68,24 +67,12 @@ export const validateStringFieldCollectionValue = (
return reasons;
};
export const getNumberFieldCollectionValue = (
fieldValue: NonNullable<IntegerFieldCollectionValue> | NonNullable<FloatFieldCollectionValue>
): number[] => {
if (Array.isArray(fieldValue)) {
return fieldValue;
}
return numberStartStepCountGenerator(fieldValue);
};
export const validateNumberFieldCollectionValue = (
fieldValue: NonNullable<IntegerFieldCollectionValue> | NonNullable<FloatFieldCollectionValue>,
value: NonNullable<IntegerFieldCollectionValue> | NonNullable<FloatFieldCollectionValue>,
template: IntegerFieldCollectionInputTemplate | FloatFieldCollectionInputTemplate
): string[] => {
const reasons: string[] = [];
const { minItems, maxItems, minimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf } = template;
const value = getNumberFieldCollectionValue(fieldValue);
const count = value.length;
// Image collections may have min or max items to validate

View File

@@ -1,9 +0,0 @@
import type { FloatStartStepCountGenerator, IntegerStartStepCountGenerator } from 'features/nodes/types/field';
export const numberStartStepCountGenerator = ({
start,
step,
count,
}: FloatStartStepCountGenerator | IntegerStartStepCountGenerator): number[] => {
return Array.from({ length: count }, (_, i) => start + i * step);
};