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: '',