Skip to content

Commit 1d0b315

Browse files
Mary HippMary Hipp
authored andcommitted
cleanup types, lint
1 parent f441ada commit 1d0b315

18 files changed

+251
-135
lines changed
Lines changed: 103 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,122 @@
1-
import type { AppStartListening } from 'app/store/store';
1+
import type { ActionCreatorWithPayload } from '@reduxjs/toolkit';
2+
import type { AppStartListening, RootState } from 'app/store/store';
23
import * as canvasWorkflowNodesActions from 'features/controlLayers/store/canvasWorkflowNodesSlice';
34
import * as nodesActions from 'features/nodes/store/nodesSlice';
5+
import type { AnyNode } from 'features/nodes/types/invocation';
46

57
/**
68
* Listens for field value changes on nodes and redirects them to the canvas workflow nodes slice
7-
* if the node belongs to a canvas workflow (exists in canvasWorkflowNodes but not in nodes).
9+
* if the node belongs to a canvas workflow.
810
*/
911
export const addCanvasWorkflowFieldChangedListener = (startListening: AppStartListening) => {
10-
// List of all field mutation actions from nodesSlice
11-
const fieldMutationActions = [
12-
nodesActions.fieldStringValueChanged,
13-
nodesActions.fieldIntegerValueChanged,
14-
nodesActions.fieldFloatValueChanged,
15-
nodesActions.fieldBooleanValueChanged,
16-
nodesActions.fieldModelIdentifierValueChanged,
17-
nodesActions.fieldEnumModelValueChanged,
18-
nodesActions.fieldSchedulerValueChanged,
19-
nodesActions.fieldBoardValueChanged,
20-
nodesActions.fieldImageValueChanged,
21-
nodesActions.fieldColorValueChanged,
22-
nodesActions.fieldImageCollectionValueChanged,
23-
nodesActions.fieldStringCollectionValueChanged,
24-
nodesActions.fieldIntegerCollectionValueChanged,
25-
nodesActions.fieldFloatCollectionValueChanged,
26-
nodesActions.fieldFloatGeneratorValueChanged,
27-
nodesActions.fieldIntegerGeneratorValueChanged,
28-
nodesActions.fieldStringGeneratorValueChanged,
29-
nodesActions.fieldImageGeneratorValueChanged,
30-
nodesActions.fieldValueReset,
12+
// List of all field mutation actions from nodesSlice with their canvas workflow counterparts
13+
const fieldMutationActionPairs: Array<{
14+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
15+
nodesAction: ActionCreatorWithPayload<any>;
16+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
17+
canvasAction: ActionCreatorWithPayload<any>;
18+
}> = [
19+
{
20+
nodesAction: nodesActions.fieldStringValueChanged,
21+
canvasAction: canvasWorkflowNodesActions.fieldStringValueChanged,
22+
},
23+
{
24+
nodesAction: nodesActions.fieldIntegerValueChanged,
25+
canvasAction: canvasWorkflowNodesActions.fieldIntegerValueChanged,
26+
},
27+
{
28+
nodesAction: nodesActions.fieldFloatValueChanged,
29+
canvasAction: canvasWorkflowNodesActions.fieldFloatValueChanged,
30+
},
31+
{
32+
nodesAction: nodesActions.fieldBooleanValueChanged,
33+
canvasAction: canvasWorkflowNodesActions.fieldBooleanValueChanged,
34+
},
35+
{
36+
nodesAction: nodesActions.fieldModelIdentifierValueChanged,
37+
canvasAction: canvasWorkflowNodesActions.fieldModelIdentifierValueChanged,
38+
},
39+
{
40+
nodesAction: nodesActions.fieldEnumModelValueChanged,
41+
canvasAction: canvasWorkflowNodesActions.fieldEnumModelValueChanged,
42+
},
43+
{
44+
nodesAction: nodesActions.fieldSchedulerValueChanged,
45+
canvasAction: canvasWorkflowNodesActions.fieldSchedulerValueChanged,
46+
},
47+
{
48+
nodesAction: nodesActions.fieldBoardValueChanged,
49+
canvasAction: canvasWorkflowNodesActions.fieldBoardValueChanged,
50+
},
51+
{
52+
nodesAction: nodesActions.fieldImageValueChanged,
53+
canvasAction: canvasWorkflowNodesActions.fieldImageValueChanged,
54+
},
55+
{
56+
nodesAction: nodesActions.fieldColorValueChanged,
57+
canvasAction: canvasWorkflowNodesActions.fieldColorValueChanged,
58+
},
59+
{
60+
nodesAction: nodesActions.fieldImageCollectionValueChanged,
61+
canvasAction: canvasWorkflowNodesActions.fieldImageCollectionValueChanged,
62+
},
63+
{
64+
nodesAction: nodesActions.fieldStringCollectionValueChanged,
65+
canvasAction: canvasWorkflowNodesActions.fieldStringCollectionValueChanged,
66+
},
67+
{
68+
nodesAction: nodesActions.fieldIntegerCollectionValueChanged,
69+
canvasAction: canvasWorkflowNodesActions.fieldIntegerCollectionValueChanged,
70+
},
71+
{
72+
nodesAction: nodesActions.fieldFloatCollectionValueChanged,
73+
canvasAction: canvasWorkflowNodesActions.fieldFloatCollectionValueChanged,
74+
},
75+
{
76+
nodesAction: nodesActions.fieldFloatGeneratorValueChanged,
77+
canvasAction: canvasWorkflowNodesActions.fieldFloatGeneratorValueChanged,
78+
},
79+
{
80+
nodesAction: nodesActions.fieldIntegerGeneratorValueChanged,
81+
canvasAction: canvasWorkflowNodesActions.fieldIntegerGeneratorValueChanged,
82+
},
83+
{
84+
nodesAction: nodesActions.fieldStringGeneratorValueChanged,
85+
canvasAction: canvasWorkflowNodesActions.fieldStringGeneratorValueChanged,
86+
},
87+
{
88+
nodesAction: nodesActions.fieldImageGeneratorValueChanged,
89+
canvasAction: canvasWorkflowNodesActions.fieldImageGeneratorValueChanged,
90+
},
91+
{ nodesAction: nodesActions.fieldValueReset, canvasAction: canvasWorkflowNodesActions.fieldValueReset },
3192
];
3293

33-
for (const actionCreator of fieldMutationActions) {
94+
for (const { nodesAction, canvasAction } of fieldMutationActionPairs) {
3495
startListening({
35-
actionCreator,
36-
effect: (action: any, { dispatch, getState }: any) => {
37-
const state = getState();
96+
actionCreator: nodesAction,
97+
effect: (action, { dispatch, getState }) => {
98+
const state = getState() as RootState;
3899
const { nodeId } = action.payload;
39100

40101
// Check if this node exists in canvas workflow nodes
41-
const canvasWorkflowNode = state.canvasWorkflowNodes.nodes.find((n: any) => n.id === nodeId);
42-
const regularNode = state.nodes.present.nodes.find((n: any) => n.id === nodeId);
102+
const canvasWorkflowNode = state.canvasWorkflowNodes.nodes.find((n: AnyNode) => n.id === nodeId);
103+
const regularNode = state.nodes.present.nodes.find((n: AnyNode) => n.id === nodeId);
43104

44-
// If the node exists in canvas workflow but NOT in regular nodes, redirect the action
45-
if (canvasWorkflowNode && !regularNode) {
46-
// Get the corresponding action from canvasWorkflowNodesSlice
47-
const actionName = actionCreator.type.split('/').pop() as keyof typeof canvasWorkflowNodesActions;
48-
const canvasWorkflowAction = canvasWorkflowNodesActions[actionName];
105+
console.log('[canvasWorkflowFieldChanged] Field changed:', {
106+
nodeId,
107+
hasCanvasNode: !!canvasWorkflowNode,
108+
hasRegularNode: !!regularNode,
109+
action: action.type,
110+
payload: action.payload,
111+
});
49112

50-
if (canvasWorkflowAction && typeof canvasWorkflowAction === 'function') {
51-
dispatch(canvasWorkflowAction(action.payload as any));
52-
}
113+
// If the node exists in canvas workflow, redirect the action
114+
// This ensures canvas workflow fields always update the canvas workflow nodes slice
115+
if (canvasWorkflowNode) {
116+
console.log('[canvasWorkflowFieldChanged] Redirecting to canvas workflow nodes:', canvasAction.type);
117+
dispatch(canvasAction(action.payload));
53118
}
54119
},
55120
});
56121
}
57-
};
122+
};

invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasWorkflowRehydrated.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
import type { AppStartListening } from 'app/store/store';
2-
import { deepClone } from 'common/util/deepClone';
32
import { selectCanvasWorkflow } from 'features/controlLayers/store/canvasWorkflowSlice';
4-
import { getFormFieldInitialValues } from 'features/nodes/store/nodesSlice';
5-
import { SHARED_NODE_PROPERTIES } from 'features/nodes/types/constants';
6-
import type { AnyNode } from 'features/nodes/types/invocation';
73
import { REMEMBER_REHYDRATED } from 'redux-remember';
84

95
/**
@@ -15,7 +11,7 @@ import { REMEMBER_REHYDRATED } from 'redux-remember';
1511
export const addCanvasWorkflowRehydratedListener = (startListening: AppStartListening) => {
1612
startListening({
1713
type: REMEMBER_REHYDRATED,
18-
effect: async (_action, { dispatch, getState }) => {
14+
effect: (_action, { dispatch, getState }) => {
1915
const state = getState();
2016
const { workflow, inputNodeId } = state.canvasWorkflow;
2117

@@ -36,4 +32,4 @@ export const addCanvasWorkflowRehydratedListener = (startListening: AppStartList
3632
}
3733
},
3834
});
39-
};
35+
};

invokeai/frontend/web/src/app/store/store.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ import { changeBoardModalSliceConfig } from 'features/changeBoardModal/store/sli
2424
import { canvasSettingsSliceConfig } from 'features/controlLayers/store/canvasSettingsSlice';
2525
import { canvasSliceConfig } from 'features/controlLayers/store/canvasSlice';
2626
import { canvasSessionSliceConfig } from 'features/controlLayers/store/canvasStagingAreaSlice';
27-
import { canvasWorkflowSliceConfig } from 'features/controlLayers/store/canvasWorkflowSlice';
2827
import { canvasWorkflowNodesSliceConfig } from 'features/controlLayers/store/canvasWorkflowNodesSlice';
28+
import { canvasWorkflowSliceConfig } from 'features/controlLayers/store/canvasWorkflowSlice';
2929
import { lorasSliceConfig } from 'features/controlLayers/store/lorasSlice';
3030
import { paramsSliceConfig } from 'features/controlLayers/store/paramsSlice';
3131
import { refImagesSliceConfig } from 'features/controlLayers/store/refImagesSlice';
@@ -57,9 +57,9 @@ import { actionSanitizer } from './middleware/devtools/actionSanitizer';
5757
import { actionsDenylist } from './middleware/devtools/actionsDenylist';
5858
import { stateSanitizer } from './middleware/devtools/stateSanitizer';
5959
import { addArchivedOrDeletedBoardListener } from './middleware/listenerMiddleware/listeners/addArchivedOrDeletedBoardListener';
60-
import { addImageUploadedFulfilledListener } from './middleware/listenerMiddleware/listeners/imageUploaded';
6160
import { addCanvasWorkflowFieldChangedListener } from './middleware/listenerMiddleware/listeners/canvasWorkflowFieldChanged';
6261
import { addCanvasWorkflowRehydratedListener } from './middleware/listenerMiddleware/listeners/canvasWorkflowRehydrated';
62+
import { addImageUploadedFulfilledListener } from './middleware/listenerMiddleware/listeners/imageUploaded';
6363

6464
export const listenerMiddleware = createListenerMiddleware();
6565

invokeai/frontend/web/src/features/controlLayers/components/CanvasWorkflowContainerElement.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ import {
88
useContainerContext,
99
useDepthContext,
1010
} from 'features/nodes/components/sidePanel/builder/contexts';
11-
import { isContainerElement } from 'features/nodes/types/workflow';
12-
import { CONTAINER_CLASS_NAME } from 'features/nodes/types/workflow';
11+
import { CONTAINER_CLASS_NAME, isContainerElement } from 'features/nodes/types/workflow';
1312
import { memo } from 'react';
1413

1514
import { CanvasWorkflowFormElementComponent } from './CanvasWorkflowFormElementComponent';
@@ -74,4 +73,4 @@ export const CanvasWorkflowContainerElement = memo(({ id }: { id: string }) => {
7473
</DepthContextProvider>
7574
);
7675
});
77-
CanvasWorkflowContainerElement.displayName = 'CanvasWorkflowContainerElement';
76+
CanvasWorkflowContainerElement.displayName = 'CanvasWorkflowContainerElement';

invokeai/frontend/web/src/features/controlLayers/components/CanvasWorkflowElementContext.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,4 @@ CanvasWorkflowElementProvider.displayName = 'CanvasWorkflowElementProvider';
3535
*/
3636
export const useCanvasWorkflowElement = (): ((id: string) => FormElement | undefined) | null => {
3737
return useContext(CanvasWorkflowElementContext)?.getElement ?? null;
38-
};
38+
};

invokeai/frontend/web/src/features/controlLayers/components/CanvasWorkflowFieldsPanel.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,13 @@ export const CanvasWorkflowFieldsPanel = memo(() => {
1717

1818
// Check if form is empty
1919
const rootElement = nodesState.form.elements[nodesState.form.rootElementId];
20-
if (!rootElement || !('data' in rootElement) || !rootElement.data || !('children' in rootElement.data) || rootElement.data.children.length === 0) {
20+
if (
21+
!rootElement ||
22+
!('data' in rootElement) ||
23+
!rootElement.data ||
24+
!('children' in rootElement.data) ||
25+
rootElement.data.children.length === 0
26+
) {
2127
return (
2228
<Flex w="full" p={4} justifyContent="center">
2329
<Text variant="subtext">No fields exposed in this workflow</Text>
@@ -33,4 +39,4 @@ export const CanvasWorkflowFieldsPanel = memo(() => {
3339
</CanvasWorkflowModeProvider>
3440
);
3541
});
36-
CanvasWorkflowFieldsPanel.displayName = 'CanvasWorkflowFieldsPanel';
42+
CanvasWorkflowFieldsPanel.displayName = 'CanvasWorkflowFieldsPanel';

invokeai/frontend/web/src/features/controlLayers/components/CanvasWorkflowFormElementComponent.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,4 @@ export const CanvasWorkflowFormElementComponent = memo(({ id }: { id: string })
5656

5757
assert<Equals<typeof el, never>>(false, `Unhandled type for element with id ${id}`);
5858
});
59-
CanvasWorkflowFormElementComponent.displayName = 'CanvasWorkflowFormElementComponent';
59+
CanvasWorkflowFormElementComponent.displayName = 'CanvasWorkflowFormElementComponent';

invokeai/frontend/web/src/features/controlLayers/components/CanvasWorkflowInvocationContext.tsx

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ import { useStore } from '@nanostores/react';
22
import type { Selector } from '@reduxjs/toolkit';
33
import { createSelector } from '@reduxjs/toolkit';
44
import type { RootState } from 'app/store/store';
5-
import { $templates } from 'features/nodes/store/nodesSlice';
6-
import { selectEdges, selectNodeFieldElements, selectNodes } from 'features/nodes/store/selectors';
75
import { InvocationNodeContext } from 'features/nodes/components/flow/nodes/Invocation/context';
8-
import type { InvocationNode, InvocationTemplate } from 'features/nodes/types/invocation';
6+
import { $templates } from 'features/nodes/store/nodesSlice';
7+
import type { InvocationNode } from 'features/nodes/types/invocation';
98
import { getNeedsUpdate } from 'features/nodes/util/node/nodeUpdate';
109
import type { PropsWithChildren } from 'react';
1110
import { memo, useMemo } from 'react';
@@ -32,19 +31,22 @@ const selectCanvasWorkflowNodes = (state: RootState) => state.canvasWorkflowNode
3231
const selectCanvasWorkflowEdges = (state: RootState) => state.canvasWorkflowNodes.edges;
3332
const selectCanvasWorkflowNodeFieldElements = (state: RootState) => {
3433
const form = state.canvasWorkflowNodes.form;
35-
return Object.values(form.elements).filter((el) => el.type === 'node-field');
34+
return Object.values(form.elements).filter(
35+
(el): el is Extract<typeof el, { type: 'node-field' }> => el.type === 'node-field'
36+
);
3637
};
3738

3839
export const CanvasWorkflowInvocationNodeContextProvider = memo(
3940
({ nodeId, children }: PropsWithChildren<{ nodeId: string }>) => {
4041
const templates = useStore($templates);
4142

4243
const value = useMemo(() => {
43-
const cache: Map<string, Selector<RootState, any>> = new Map();
44+
const cache: Map<string, Selector<RootState, unknown>> = new Map();
4445

4546
const selectNodeSafe = getSelectorFromCache(cache, 'selectNodeSafe', () =>
4647
createSelector(selectCanvasWorkflowNodes, (nodes) => {
47-
return (nodes.find(({ id, type }) => type === 'invocation' && id === nodeId) ?? null) as InvocationNode | null;
48+
return (nodes.find(({ id, type }) => type === 'invocation' && id === nodeId) ??
49+
null) as InvocationNode | null;
4850
})
4951
);
5052
const selectNodeDataSafe = getSelectorFromCache(cache, 'selectNodeDataSafe', () =>
@@ -88,7 +90,9 @@ export const CanvasWorkflowInvocationNodeContextProvider = memo(
8890

8991
const selectNodeOrThrow = getSelectorFromCache(cache, 'selectNodeOrThrow', () =>
9092
createSelector(selectCanvasWorkflowNodes, (nodes) => {
91-
const node = nodes.find(({ id, type }) => type === 'invocation' && id === nodeId) as InvocationNode | undefined;
93+
const node = nodes.find(({ id, type }) => type === 'invocation' && id === nodeId) as
94+
| InvocationNode
95+
| undefined;
9296
if (node === undefined) {
9397
throw new Error(`Cannot find node with id ${nodeId}`);
9498
}
@@ -124,7 +128,10 @@ export const CanvasWorkflowInvocationNodeContextProvider = memo(
124128
createSelector(selectNodeInputsOrThrow, (inputs) => {
125129
const field = inputs[fieldName];
126130
if (field === undefined) {
127-
console.error(`[CanvasWorkflowContext] Cannot find input field with name ${fieldName} in node ${nodeId}. Available fields:`, Object.keys(inputs));
131+
console.error(
132+
`[CanvasWorkflowContext] Cannot find input field with name ${fieldName} in node ${nodeId}. Available fields:`,
133+
Object.keys(inputs)
134+
);
128135
throw new Error(`Cannot find input field with name ${fieldName} in node ${nodeId}`);
129136
}
130137
return field;
@@ -164,7 +171,7 @@ export const CanvasWorkflowInvocationNodeContextProvider = memo(
164171
getSelectorFromCache(cache, `buildSelectIsInputFieldAddedToForm-${fieldName}`, () =>
165172
createSelector(selectCanvasWorkflowNodeFieldElements, (nodeFieldElements) => {
166173
return nodeFieldElements.some(
167-
(el: any) => el.data.fieldIdentifier.nodeId === nodeId && el.data.fieldIdentifier.fieldName === fieldName
174+
(el) => el.data.fieldIdentifier.nodeId === nodeId && el.data.fieldIdentifier.fieldName === fieldName
168175
);
169176
})
170177
);
@@ -210,4 +217,4 @@ export const CanvasWorkflowInvocationNodeContextProvider = memo(
210217
return <InvocationNodeContext.Provider value={value}>{children}</InvocationNodeContext.Provider>;
211218
}
212219
);
213-
CanvasWorkflowInvocationNodeContextProvider.displayName = 'CanvasWorkflowInvocationNodeContextProvider';
220+
CanvasWorkflowInvocationNodeContextProvider.displayName = 'CanvasWorkflowInvocationNodeContextProvider';

invokeai/frontend/web/src/features/controlLayers/components/CanvasWorkflowModeContext.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ import { memo } from 'react';
1111
export const CanvasWorkflowModeProvider = memo(({ children }: PropsWithChildren) => {
1212
return <CanvasWorkflowModeContext.Provider value="view">{children}</CanvasWorkflowModeContext.Provider>;
1313
});
14-
CanvasWorkflowModeProvider.displayName = 'CanvasWorkflowModeProvider';
14+
CanvasWorkflowModeProvider.displayName = 'CanvasWorkflowModeProvider';

invokeai/frontend/web/src/features/controlLayers/components/CanvasWorkflowRootContainer.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,8 @@ import type { SystemStyleObject } from '@invoke-ai/ui-library';
22
import { Box } from '@invoke-ai/ui-library';
33
import { useAppSelector } from 'app/store/storeHooks';
44
import { selectCanvasWorkflowNodesSlice } from 'features/controlLayers/store/canvasWorkflowNodesSlice';
5-
import {
6-
ContainerContextProvider,
7-
DepthContextProvider,
8-
} from 'features/nodes/components/sidePanel/builder/contexts';
9-
import { isContainerElement } from 'features/nodes/types/workflow';
10-
import { ROOT_CONTAINER_CLASS_NAME } from 'features/nodes/types/workflow';
5+
import { ContainerContextProvider, DepthContextProvider } from 'features/nodes/components/sidePanel/builder/contexts';
6+
import { isContainerElement, ROOT_CONTAINER_CLASS_NAME } from 'features/nodes/types/workflow';
117
import { memo } from 'react';
128

139
import { CanvasWorkflowFormElementComponent } from './CanvasWorkflowFormElementComponent';
@@ -59,4 +55,4 @@ export const CanvasWorkflowRootContainer = memo(() => {
5955
</DepthContextProvider>
6056
);
6157
});
62-
CanvasWorkflowRootContainer.displayName = 'CanvasWorkflowRootContainer';
58+
CanvasWorkflowRootContainer.displayName = 'CanvasWorkflowRootContainer';

0 commit comments

Comments
 (0)