diff --git a/.github/workflows/mkdocs-material.yml b/.github/workflows/mkdocs-material.yml
index c8e55f0b1b..be665aee54 100644
--- a/.github/workflows/mkdocs-material.yml
+++ b/.github/workflows/mkdocs-material.yml
@@ -36,7 +36,7 @@ jobs:
--verbose
- name: deploy to gh-pages
- if: ${{ github.ref == 'refs/heads/main' }}
+ if: ${{ github.ref == 'refs/heads/v2.3' }}
run: |
python -m \
mkdocs gh-deploy \
diff --git a/invokeai/frontend/web/src/features/nodes/components/InputFieldComponent.tsx b/invokeai/frontend/web/src/features/nodes/components/InputFieldComponent.tsx
index 01d6d01b48..b4502d1ff3 100644
--- a/invokeai/frontend/web/src/features/nodes/components/InputFieldComponent.tsx
+++ b/invokeai/frontend/web/src/features/nodes/components/InputFieldComponent.tsx
@@ -10,6 +10,7 @@ import ConditioningInputFieldComponent from './fields/ConditioningInputFieldComp
import ModelInputFieldComponent from './fields/ModelInputFieldComponent';
import NumberInputFieldComponent from './fields/NumberInputFieldComponent';
import StringInputFieldComponent from './fields/StringInputFieldComponent';
+import ItemInputFieldComponent from './fields/ItemInputFieldComponent';
type InputFieldComponentProps = {
nodeId: string;
@@ -115,6 +116,16 @@ const InputFieldComponent = (props: InputFieldComponentProps) => {
);
}
+ if (type === 'item' && template.type === 'item') {
+ return (
+
+ );
+ }
+
return Unknown field type: {type};
};
diff --git a/invokeai/frontend/web/src/features/nodes/components/fields/ItemInputFieldComponent.tsx b/invokeai/frontend/web/src/features/nodes/components/fields/ItemInputFieldComponent.tsx
new file mode 100644
index 0000000000..85ce887e50
--- /dev/null
+++ b/invokeai/frontend/web/src/features/nodes/components/fields/ItemInputFieldComponent.tsx
@@ -0,0 +1,17 @@
+import {
+ ItemInputFieldTemplate,
+ ItemInputFieldValue,
+} from 'features/nodes/types/types';
+import { memo } from 'react';
+import { FaAddressCard, FaList } from 'react-icons/fa';
+import { FieldComponentProps } from './types';
+
+const ItemInputFieldComponent = (
+ props: FieldComponentProps
+) => {
+ const { nodeId, field } = props;
+
+ return ;
+};
+
+export default memo(ItemInputFieldComponent);
diff --git a/invokeai/frontend/web/src/features/nodes/types/constants.ts b/invokeai/frontend/web/src/features/nodes/types/constants.ts
index 73bd7bb0a1..e3b8d0563d 100644
--- a/invokeai/frontend/web/src/features/nodes/types/constants.ts
+++ b/invokeai/frontend/web/src/features/nodes/types/constants.ts
@@ -14,6 +14,7 @@ export const FIELD_TYPE_MAP: Record = {
ConditioningField: 'conditioning',
model: 'model',
array: 'array',
+ item: 'item',
};
const COLOR_TOKEN_VALUE = 500;
@@ -82,4 +83,10 @@ export const FIELDS: Record = {
title: 'Array',
description: 'TODO: Array type description.',
},
+ item: {
+ color: 'gray',
+ colorCssVar: getColorTokenCssVariable('gray'),
+ title: 'Collection Item',
+ description: 'TODO: Collection Item type description.',
+ },
};
diff --git a/invokeai/frontend/web/src/features/nodes/types/types.ts b/invokeai/frontend/web/src/features/nodes/types/types.ts
index 568c5fa831..f637fc965f 100644
--- a/invokeai/frontend/web/src/features/nodes/types/types.ts
+++ b/invokeai/frontend/web/src/features/nodes/types/types.ts
@@ -58,7 +58,8 @@ export type FieldType =
| 'latents'
| 'conditioning'
| 'model'
- | 'array';
+ | 'array'
+ | 'item';
/**
* An input field is persisted across reloads as part of the user's local state.
@@ -78,7 +79,8 @@ export type InputFieldValue =
| ConditioningInputFieldValue
| EnumInputFieldValue
| ModelInputFieldValue
- | ArrayInputFieldValue;
+ | ArrayInputFieldValue
+ | ItemInputFieldValue;
/**
* An input field template is generated on each page load from the OpenAPI schema.
@@ -96,7 +98,8 @@ export type InputFieldTemplate =
| ConditioningInputFieldTemplate
| EnumInputFieldTemplate
| ModelInputFieldTemplate
- | ArrayInputFieldTemplate;
+ | ArrayInputFieldTemplate
+ | ItemInputFieldTemplate;
/**
* An output field is persisted across as part of the user's local state.
@@ -185,6 +188,11 @@ export type ArrayInputFieldValue = FieldValueBase & {
value?: (string | number)[];
};
+export type ItemInputFieldValue = FieldValueBase & {
+ type: 'item';
+ value?: undefined;
+};
+
export type InputFieldTemplateBase = {
name: string;
title: string;
@@ -255,10 +263,15 @@ export type ModelInputFieldTemplate = InputFieldTemplateBase & {
};
export type ArrayInputFieldTemplate = InputFieldTemplateBase & {
- default: (string | number)[];
+ default: [];
type: 'array';
};
+export type ItemInputFieldTemplate = InputFieldTemplateBase & {
+ default: undefined;
+ type: 'item';
+};
+
/**
* JANKY CUSTOMISATION OF OpenAPI SCHEMA TYPES
*/
diff --git a/invokeai/frontend/web/src/features/nodes/util/fieldTemplateBuilders.ts b/invokeai/frontend/web/src/features/nodes/util/fieldTemplateBuilders.ts
index 9ce942f797..1912cb483e 100644
--- a/invokeai/frontend/web/src/features/nodes/util/fieldTemplateBuilders.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/fieldTemplateBuilders.ts
@@ -16,6 +16,8 @@ import {
OutputFieldTemplate,
TypeHints,
FieldType,
+ ArrayInputFieldTemplate,
+ ItemInputFieldTemplate,
} from '../types/types';
export type BaseFieldProperties = 'name' | 'title' | 'description';
@@ -230,6 +232,36 @@ const buildEnumInputFieldTemplate = ({
return template;
};
+const buildArrayInputFieldTemplate = ({
+ schemaObject,
+ baseField,
+}: BuildInputFieldArg): ArrayInputFieldTemplate => {
+ const template: ArrayInputFieldTemplate = {
+ ...baseField,
+ type: 'array',
+ inputRequirement: 'always',
+ inputKind: 'direct',
+ default: [],
+ };
+
+ return template;
+};
+
+const buildItemInputFieldTemplate = ({
+ schemaObject,
+ baseField,
+}: BuildInputFieldArg): ItemInputFieldTemplate => {
+ const template: ItemInputFieldTemplate = {
+ ...baseField,
+ type: 'item',
+ inputRequirement: 'always',
+ inputKind: 'direct',
+ default: undefined,
+ };
+
+ return template;
+};
+
export const getFieldType = (
schemaObject: OpenAPIV3.SchemaObject,
name: string,
@@ -303,6 +335,12 @@ export const buildInputFieldTemplate = (
if (['boolean'].includes(fieldType)) {
return buildBooleanInputFieldTemplate({ schemaObject, baseField });
}
+ if (['array'].includes(fieldType)) {
+ return buildArrayInputFieldTemplate({ schemaObject, baseField });
+ }
+ if (['item'].includes(fieldType)) {
+ return buildItemInputFieldTemplate({ schemaObject, baseField });
+ }
return;
};
diff --git a/invokeai/frontend/web/src/features/nodes/util/linearGraphBuilder/buildIterateNode.ts b/invokeai/frontend/web/src/features/nodes/util/linearGraphBuilder/buildIterateNode.ts
index 6764038da4..4f1b8d8930 100644
--- a/invokeai/frontend/web/src/features/nodes/util/linearGraphBuilder/buildIterateNode.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/linearGraphBuilder/buildIterateNode.ts
@@ -7,7 +7,7 @@ export const buildIterateNode = (): IterateInvocation => {
return {
id: nodeId,
type: 'iterate',
- collection: [],
- index: 0,
+ // collection: [],
+ // index: 0,
};
};
diff --git a/invokeai/frontend/web/src/features/nodes/util/parseSchema.ts b/invokeai/frontend/web/src/features/nodes/util/parseSchema.ts
index 7cb8132b65..85b187dca4 100644
--- a/invokeai/frontend/web/src/features/nodes/util/parseSchema.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/parseSchema.ts
@@ -13,7 +13,7 @@ import {
buildOutputFieldTemplates,
} from './fieldTemplateBuilders';
-const invocationDenylist = ['Graph', 'Collect', 'LoadImage'];
+const invocationDenylist = ['Graph', 'LoadImage'];
export const parseSchema = (openAPI: OpenAPIV3.Document) => {
// filter out non-invocation schemas, plus some tricky invocations for now
@@ -32,49 +32,62 @@ export const parseSchema = (openAPI: OpenAPIV3.Document) => {
if (isInvocationSchemaObject(schema)) {
const type = schema.properties.type.default;
- const title =
- schema.ui?.title ??
- schema.title
- .replace('Invocation', '')
- .split(/(?=[A-Z])/) // split PascalCase into array
- .join(' ');
+ const title = schema.ui?.title ?? schema.title.replace('Invocation', '');
const typeHints = schema.ui?.type_hints;
- const inputs = reduce(
- schema.properties,
- (inputsAccumulator, property, propertyName) => {
- if (
- // `type` and `id` are not valid inputs/outputs
- !['type', 'id'].includes(propertyName) &&
- isSchemaObject(property)
- ) {
- let field: InputFieldTemplate | undefined;
- if (propertyName === 'collection') {
- field = {
- default: property.default ?? [],
- name: 'collection',
- title: property.title ?? '',
- description: property.description ?? '',
- type: 'array',
- inputRequirement: 'always',
- inputKind: 'connection',
- };
- } else {
- field = buildInputFieldTemplate(
- property,
- propertyName,
- typeHints
- );
+ const inputs: Record = {};
+
+ if (type === 'collect') {
+ const itemProperty = schema.properties[
+ 'item'
+ ] as InvocationSchemaObject;
+ // Handle the special Collect node
+ inputs.item = {
+ type: 'item',
+ name: 'item',
+ description: itemProperty.description ?? '',
+ title: 'Collection Item',
+ inputKind: 'connection',
+ inputRequirement: 'always',
+ default: undefined,
+ };
+ } else if (type === 'iterate') {
+ const itemProperty = schema.properties[
+ 'collection'
+ ] as InvocationSchemaObject;
+
+ inputs.collection = {
+ type: 'array',
+ name: 'collection',
+ title: itemProperty.title ?? '',
+ default: [],
+ description: itemProperty.description ?? '',
+ inputRequirement: 'always',
+ inputKind: 'connection',
+ };
+ } else {
+ // All other nodes
+ reduce(
+ schema.properties,
+ (inputsAccumulator, property, propertyName) => {
+ if (
+ // `type` and `id` are not valid inputs/outputs
+ !['type', 'id'].includes(propertyName) &&
+ isSchemaObject(property)
+ ) {
+ const field: InputFieldTemplate | undefined =
+ buildInputFieldTemplate(property, propertyName, typeHints);
+
+ if (field) {
+ inputsAccumulator[propertyName] = field;
+ }
}
- if (field) {
- inputsAccumulator[propertyName] = field;
- }
- }
- return inputsAccumulator;
- },
- {} as Record
- );
+ return inputsAccumulator;
+ },
+ inputs
+ );
+ }
const rawOutput = (schema as InvocationSchemaObject).output;
diff --git a/invokeai/frontend/web/src/features/system/store/systemSlice.ts b/invokeai/frontend/web/src/features/system/store/systemSlice.ts
index a74e6dca7e..41cb33f88c 100644
--- a/invokeai/frontend/web/src/features/system/store/systemSlice.ts
+++ b/invokeai/frontend/web/src/features/system/store/systemSlice.ts
@@ -107,7 +107,7 @@ const initialSystemState: SystemState = {
subscribedNodeIds: [],
wereModelsReceived: false,
wasSchemaParsed: false,
- consoleLogLevel: 'error',
+ consoleLogLevel: 'debug',
shouldLogToConsole: true,
statusTranslationKey: 'common.statusDisconnected',
canceledSession: '',