From 92d25d304a057f9c13e40cfd2dacae2ad6a76581 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 17 Jan 2025 16:31:08 +1100 Subject: [PATCH 01/58] feat(ui): add notes state to fields --- .../src/features/nodes/store/nodesSlice.ts | 28 +++++++++++++++---- .../web/src/features/nodes/types/field.ts | 1 + 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts b/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts index 9bf859dc01a..7f12c23e8d9 100644 --- a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts +++ b/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts @@ -95,23 +95,30 @@ type FieldValueAction = PayloadAction<{ value: T; }>; +const getField = (nodeId: string, fieldName: string, state: NodesState) => { + const nodeIndex = state.nodes.findIndex((n) => n.id === nodeId); + const node = state.nodes?.[nodeIndex]; + if (!isInvocationNode(node)) { + return; + } + return node.data?.inputs[fieldName]; +}; + const fieldValueReducer = ( state: NodesState, action: FieldValueAction, schema: z.ZodTypeAny ) => { const { nodeId, fieldName, value } = action.payload; - const nodeIndex = state.nodes.findIndex((n) => n.id === nodeId); - const node = state.nodes?.[nodeIndex]; - if (!isInvocationNode(node)) { + const field = getField(nodeId, fieldName, state); + if (!field) { return; } - const input = node.data?.inputs[fieldName]; const result = schema.safeParse(value); - if (!input || nodeIndex < 0 || !result.success) { + if (!result.success) { return; } - input.value = result.data; + field.value = result.data; }; export const nodesSlice = createSlice({ @@ -409,6 +416,14 @@ export const nodesSlice = createSlice({ fieldStringGeneratorValueChanged: (state, action: FieldValueAction) => { fieldValueReducer(state, action, zStringGeneratorFieldValue); }, + fieldNotesChanged: (state, action: PayloadAction<{ nodeId: string; fieldName: string; val?: string }>) => { + const { nodeId, fieldName, val } = action.payload; + const field = getField(nodeId, fieldName, state); + if (!field) { + return; + } + field.notes = val; + }, notesNodeValueChanged: (state, action: PayloadAction<{ nodeId: string; value: string }>) => { const { nodeId, value } = action.payload; const nodeIndex = state.nodes.findIndex((n) => n.id === nodeId); @@ -476,6 +491,7 @@ export const { fieldFloatGeneratorValueChanged, fieldIntegerGeneratorValueChanged, fieldStringGeneratorValueChanged, + fieldNotesChanged, nodeEditorReset, nodeIsIntermediateChanged, nodeIsOpenChanged, diff --git a/invokeai/frontend/web/src/features/nodes/types/field.ts b/invokeai/frontend/web/src/features/nodes/types/field.ts index 6c516ca0867..fa9759bd284 100644 --- a/invokeai/frontend/web/src/features/nodes/types/field.ts +++ b/invokeai/frontend/web/src/features/nodes/types/field.ts @@ -38,6 +38,7 @@ const zFieldUIComponent = z.enum(['none', 'textarea', 'slider']); const zFieldInputInstanceBase = z.object({ name: z.string().trim().min(1), label: z.string().nullish(), + notes: z.string().nullish(), }); const zFieldTemplateBase = z.object({ name: z.string().min(1), From b03479107d950841d34f92e0877f3ba54cba6a15 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 17 Jan 2025 16:44:00 +1100 Subject: [PATCH 02/58] feat(ui): add notes popover to field title bar --- .../fields/FieldNotesIconButton.tsx | 64 +++++++++++++++++++ .../nodes/Invocation/fields/InputField.tsx | 2 + .../src/features/nodes/hooks/useFieldNotes.ts | 22 +++++++ 3 files changed, 88 insertions(+) create mode 100644 invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/FieldNotesIconButton.tsx create mode 100644 invokeai/frontend/web/src/features/nodes/hooks/useFieldNotes.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/FieldNotesIconButton.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/FieldNotesIconButton.tsx new file mode 100644 index 00000000000..44595e27b17 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/FieldNotesIconButton.tsx @@ -0,0 +1,64 @@ +import { + FormControl, + FormLabel, + IconButton, + Popover, + PopoverContent, + PopoverTrigger, + Textarea, +} from '@invoke-ai/ui-library'; +import { useAppDispatch } from 'app/store/storeHooks'; +import { useFieldNotes } from 'features/nodes/hooks/useFieldNotes'; +import { fieldNotesChanged } from 'features/nodes/store/nodesSlice'; +import type { ChangeEvent } from 'react'; +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { PiNoteBold } from 'react-icons/pi'; + +type Props = { + nodeId: string; + fieldName: string; +}; + +export const FieldNotesIconButton = memo(({ nodeId, fieldName }: Props) => { + const dispatch = useAppDispatch(); + const { t } = useTranslation(); + const notes = useFieldNotes(nodeId, fieldName); + const onChange = useCallback( + (e: ChangeEvent) => { + dispatch(fieldNotesChanged({ nodeId, fieldName, val: e.target.value })); + }, + [dispatch, fieldName, nodeId] + ); + + return ( + + + } + pointerEvents="auto" + size="xs" + /> + + + + {t('nodes.notes')} +