Skip to content

Commit 1a27eb1

Browse files
committed
ui(web): refine workflow editor components
1 parent 15e7656 commit 1a27eb1

3 files changed

Lines changed: 84 additions & 8 deletions

File tree

apps/web/src/components/workflow/input-edit-popover.tsx

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export function InputEditPopover({
5050
readonly,
5151
anchorElement,
5252
}: InputEditPopoverProps) {
53-
const { updateNodeData } = useWorkflow();
53+
const { updateNodeData, edges, deleteEdge } = useWorkflow();
5454
const [inputValue, setInputValue] = useState("");
5555
const { secrets, isSecretsLoading } = useSecrets();
5656
const { uploadBinaryData, createObjectUrl } = useObjectService();
@@ -113,7 +113,15 @@ export function InputEditPopover({
113113
// Debounce the actual node data update
114114
debounceTimeoutRef.current = window.setTimeout(() => {
115115
const typedValue = convertValueByType(value, input.type);
116-
updateNodeInput(nodeId, input.id, typedValue, nodeInputs, updateNodeData);
116+
updateNodeInput(
117+
nodeId,
118+
input.id,
119+
typedValue,
120+
nodeInputs,
121+
updateNodeData,
122+
edges,
123+
deleteEdge
124+
);
117125
debounceTimeoutRef.current = null;
118126
}, 300);
119127
};
@@ -129,7 +137,15 @@ export function InputEditPopover({
129137
if (!input || readonly || !updateNodeData) return;
130138

131139
setInputValue(secretName);
132-
updateNodeInput(nodeId, input.id, secretName, nodeInputs, updateNodeData);
140+
updateNodeInput(
141+
nodeId,
142+
input.id,
143+
secretName,
144+
nodeInputs,
145+
updateNodeData,
146+
edges,
147+
deleteEdge
148+
);
133149
};
134150

135151
const handleImageUpload = async (
@@ -149,7 +165,15 @@ export function InputEditPopover({
149165

150166
const arrayBuffer = await file.arrayBuffer();
151167
const reference = await uploadBinaryData(arrayBuffer, file.type);
152-
updateNodeInput(nodeId, input.id, reference, nodeInputs, updateNodeData);
168+
updateNodeInput(
169+
nodeId,
170+
input.id,
171+
reference,
172+
nodeInputs,
173+
updateNodeData,
174+
edges,
175+
deleteEdge
176+
);
153177
setInputValue(file.name);
154178
setIsUploading(false);
155179
} catch (err) {
@@ -181,7 +205,15 @@ export function InputEditPopover({
181205

182206
const arrayBuffer = await file.arrayBuffer();
183207
const reference = await uploadBinaryData(arrayBuffer, mimeType);
184-
updateNodeInput(nodeId, input.id, reference, nodeInputs, updateNodeData);
208+
updateNodeInput(
209+
nodeId,
210+
input.id,
211+
reference,
212+
nodeInputs,
213+
updateNodeData,
214+
edges,
215+
deleteEdge
216+
);
185217
setInputValue(file.name);
186218
setIsUploading(false);
187219
} catch (err) {
@@ -209,7 +241,15 @@ export function InputEditPopover({
209241

210242
const arrayBuffer = await file.arrayBuffer();
211243
const reference = await uploadBinaryData(arrayBuffer, file.type);
212-
updateNodeInput(nodeId, input.id, reference, nodeInputs, updateNodeData);
244+
updateNodeInput(
245+
nodeId,
246+
input.id,
247+
reference,
248+
nodeInputs,
249+
updateNodeData,
250+
edges,
251+
deleteEdge
252+
);
213253
setInputValue(file.name);
214254
setIsUploading(false);
215255
} catch (err) {
@@ -242,7 +282,15 @@ export function InputEditPopover({
242282

243283
const arrayBuffer = await file.arrayBuffer();
244284
const reference = await uploadBinaryData(arrayBuffer, mimeType);
245-
updateNodeInput(nodeId, input.id, reference, nodeInputs, updateNodeData);
285+
updateNodeInput(
286+
nodeId,
287+
input.id,
288+
reference,
289+
nodeInputs,
290+
updateNodeData,
291+
edges,
292+
deleteEdge
293+
);
246294
setInputValue(file.name);
247295
setIsUploading(false);
248296
} catch (err) {
@@ -336,6 +384,7 @@ export function InputEditPopover({
336384
placeholder="Enter number"
337385
disabled={readonly}
338386
className="h-8 text-sm"
387+
autoFocus
339388
/>
340389
) : input.type === "string" ? (
341390
<Textarea
@@ -350,6 +399,7 @@ export function InputEditPopover({
350399
placeholder="Enter text"
351400
className="min-h-[80px] resize-none text-sm"
352401
disabled={readonly}
402+
autoFocus
353403
/>
354404
) : input.type === "json" ? (
355405
<Textarea
@@ -364,6 +414,7 @@ export function InputEditPopover({
364414
placeholder="Enter JSON"
365415
className="min-h-[80px] resize-none text-sm"
366416
disabled={readonly}
417+
autoFocus
367418
/>
368419
) : input.type === "secret" ? (
369420
<Select

apps/web/src/components/workflow/workflow-builder.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ export function WorkflowBuilder({
124124
isValidConnection,
125125
updateNodeData,
126126
updateEdgeData,
127+
deleteEdge,
127128
deleteSelected,
128129
duplicateSelected,
129130
isEditNodeNameDialogOpen,
@@ -411,6 +412,8 @@ export function WorkflowBuilder({
411412
<WorkflowProvider
412413
updateNodeData={readonly ? undefined : updateNodeData}
413414
updateEdgeData={readonly ? undefined : updateEdgeData}
415+
deleteEdge={readonly ? undefined : deleteEdge}
416+
edges={edges}
414417
readonly={readonly}
415418
expandedOutputs={currentExpandedOutputs}
416419
>

apps/web/src/components/workflow/workflow-context.tsx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { Edge as ReactFlowEdge } from "@xyflow/react";
12
import { createContext, ReactNode, useContext } from "react";
23

34
import {
@@ -8,10 +9,13 @@ import {
89

910
type UpdateNodeFn = (nodeId: string, data: Partial<WorkflowNodeType>) => void;
1011
type UpdateEdgeFn = (edgeId: string, data: Partial<WorkflowEdgeType>) => void;
12+
type DeleteEdgeFn = (edgeId: string) => void;
1113

1214
export interface WorkflowContextProps {
1315
updateNodeData?: UpdateNodeFn;
1416
updateEdgeData?: UpdateEdgeFn;
17+
deleteEdge?: DeleteEdgeFn;
18+
edges?: ReactFlowEdge<WorkflowEdgeType>[];
1519
readonly?: boolean;
1620
expandedOutputs?: boolean;
1721
}
@@ -20,6 +24,8 @@ export interface WorkflowContextProps {
2024
const WorkflowContext = createContext<WorkflowContextProps>({
2125
updateNodeData: () => {},
2226
updateEdgeData: () => {},
27+
deleteEdge: () => {},
28+
edges: [],
2329
readonly: false,
2430
});
2531

@@ -30,6 +36,8 @@ export interface WorkflowProviderProps {
3036
readonly children: ReactNode;
3137
readonly updateNodeData?: UpdateNodeFn;
3238
readonly updateEdgeData?: UpdateEdgeFn;
39+
readonly deleteEdge?: DeleteEdgeFn;
40+
readonly edges?: ReactFlowEdge<WorkflowEdgeType>[];
3341
readonly readonly?: boolean;
3442
readonly expandedOutputs?: boolean;
3543
}
@@ -38,12 +46,16 @@ export function WorkflowProvider({
3846
children,
3947
updateNodeData = () => {},
4048
updateEdgeData = () => {},
49+
deleteEdge = () => {},
50+
edges = [],
4151
readonly = false,
4252
expandedOutputs = false,
4353
}: WorkflowProviderProps) {
4454
const workflowContextValue = {
4555
updateNodeData,
4656
updateEdgeData,
57+
deleteEdge,
58+
edges,
4759
readonly,
4860
expandedOutputs,
4961
};
@@ -77,12 +89,22 @@ export const updateNodeInput = (
7789
inputId: string,
7890
value: unknown,
7991
inputs: readonly WorkflowParameter[],
80-
updateNodeData?: UpdateNodeFn
92+
updateNodeData?: UpdateNodeFn,
93+
edges?: ReactFlowEdge<WorkflowEdgeType>[],
94+
deleteEdge?: DeleteEdgeFn
8195
): readonly WorkflowParameter[] => {
8296
const updatedInputs = inputs.map((input) =>
8397
input.id === inputId ? { ...input, value } : input
8498
);
8599

100+
// Delete any edges connected to this input when manually setting a value
101+
if (edges && deleteEdge) {
102+
const connectedEdges = edges.filter(
103+
(edge) => edge.target === nodeId && edge.targetHandle === inputId
104+
);
105+
connectedEdges.forEach((edge) => deleteEdge(edge.id));
106+
}
107+
86108
updateNodeData?.(nodeId, { inputs: updatedInputs });
87109
return updatedInputs;
88110
};

0 commit comments

Comments
 (0)