fix: workflow text-area input fix

This commit is contained in:
Soumyadas15 2025-06-23 19:19:50 +05:30
parent 519c1da09c
commit 8a9644fcef
3 changed files with 89 additions and 17 deletions

View File

@ -1,6 +1,6 @@
"use client"; "use client";
import { memo, useState } from "react"; import { memo, useState, useEffect, useCallback } from "react";
import { Handle, Position, NodeProps } from "@xyflow/react"; import { Handle, Position, NodeProps } from "@xyflow/react";
import { Play, Settings, Clock, Webhook, User, ChevronDown, ChevronUp, Brain } from "lucide-react"; import { Play, Settings, Clock, Webhook, User, ChevronDown, ChevronUp, Brain } from "lucide-react";
import { CardContent, CardHeader } from "@/components/ui/card"; import { CardContent, CardHeader } from "@/components/ui/card";
@ -35,6 +35,7 @@ const InputNode = memo(({ data, selected, id }: NodeProps) => {
const [isConfigOpen, setIsConfigOpen] = useState(false); const [isConfigOpen, setIsConfigOpen] = useState(false);
const [isWebhookDialogOpen, setIsWebhookDialogOpen] = useState(false); const [isWebhookDialogOpen, setIsWebhookDialogOpen] = useState(false);
const [isScheduleDialogOpen, setIsScheduleDialogOpen] = useState(false); const [isScheduleDialogOpen, setIsScheduleDialogOpen] = useState(false);
const [localPrompt, setLocalPrompt] = useState(nodeData.prompt || '');
const { updateNodeData, workflowId } = useWorkflow(); const { updateNodeData, workflowId } = useWorkflow();
// Use the model selection hook // Use the model selection hook
@ -50,6 +51,30 @@ const InputNode = memo(({ data, selected, id }: NodeProps) => {
// Initialize model if not set // Initialize model if not set
const currentModel = nodeData.model || selectedModel; const currentModel = nodeData.model || selectedModel;
// Update local prompt when nodeData changes
useEffect(() => {
setLocalPrompt(nodeData.prompt || '');
}, [nodeData.prompt]);
// eslint-disable-next-line react-hooks/exhaustive-deps
const debouncedUpdatePrompt = useCallback(
(() => {
let timeoutId: NodeJS.Timeout;
return (value: string) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
updateNodeData(id, { prompt: value });
}, 300);
};
})(),
[id, updateNodeData]
);
const handlePromptChange = (value: string) => {
setLocalPrompt(value);
debouncedUpdatePrompt(value);
};
const handleModelChange = (modelId: string) => { const handleModelChange = (modelId: string) => {
updateNodeData(id, { model: modelId }); updateNodeData(id, { model: modelId });
}; };
@ -182,8 +207,8 @@ const InputNode = memo(({ data, selected, id }: NodeProps) => {
<Textarea <Textarea
id={`prompt-${id}`} id={`prompt-${id}`}
placeholder="Describe what this workflow should accomplish..." placeholder="Describe what this workflow should accomplish..."
value={nodeData.prompt || ''} value={localPrompt}
onChange={(e) => updateNodeData(id, { prompt: e.target.value })} onChange={(e) => handlePromptChange(e.target.value)}
className="min-h-[80px] text-sm" className="min-h-[80px] text-sm"
/> />
</div> </div>

View File

@ -1,6 +1,6 @@
"use client"; "use client";
import { memo, useState, useEffect } from "react"; import { memo, useState, useEffect, useCallback } from "react";
import { Handle, Position, NodeProps } from "@xyflow/react"; import { Handle, Position, NodeProps } from "@xyflow/react";
import { Card, CardContent, CardHeader } from "@/components/ui/card"; import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
@ -39,6 +39,7 @@ const MCPNode = memo(({ data, selected, id }: NodeProps) => {
const nodeData = data as unknown as MCPNodeData; const nodeData = data as unknown as MCPNodeData;
const [showConfigDialog, setShowConfigDialog] = useState(false); const [showConfigDialog, setShowConfigDialog] = useState(false);
const [isConfigOpen, setIsConfigOpen] = useState(false); const [isConfigOpen, setIsConfigOpen] = useState(false);
const [localInstructions, setLocalInstructions] = useState(nodeData.instructions || '');
const { updateNodeData } = useWorkflow(); const { updateNodeData } = useWorkflow();
const { data: credentialProfiles } = useCredentialProfilesForMcp( const { data: credentialProfiles } = useCredentialProfilesForMcp(
@ -54,6 +55,28 @@ const MCPNode = memo(({ data, selected, id }: NodeProps) => {
nodeData.mcpType === "smithery" && !!nodeData.qualifiedName nodeData.mcpType === "smithery" && !!nodeData.qualifiedName
); );
useEffect(() => {
setLocalInstructions(nodeData.instructions || '');
}, [nodeData.instructions]);
const debouncedUpdateInstructions = useCallback(
(() => {
let timeoutId: NodeJS.Timeout;
return (value: string) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
updateNodeData(id, { instructions: value });
}, 300);
};
})(),
[id, updateNodeData]
);
const handleInstructionsChange = (value: string) => {
setLocalInstructions(value);
debouncedUpdateInstructions(value);
};
useEffect(() => { useEffect(() => {
if (nodeData.mcpType === "smithery" && nodeData.qualifiedName && credentialProfiles && !nodeData.isConfigured) { if (nodeData.mcpType === "smithery" && nodeData.qualifiedName && credentialProfiles && !nodeData.isConfigured) {
const hasProfiles = credentialProfiles.length > 0; const hasProfiles = credentialProfiles.length > 0;
@ -215,13 +238,13 @@ const MCPNode = memo(({ data, selected, id }: NodeProps) => {
</div> </div>
</div> </div>
)} )}
{nodeData.instructions && !isConfigOpen && ( {localInstructions && !isConfigOpen && (
<div> <div>
<Label className="text-xs font-medium text-muted-foreground">Instructions</Label> <Label className="text-xs font-medium text-muted-foreground">Instructions</Label>
<div className="border-primary/20 text-xs text-muted-foreground bg-primary/10 p-2 rounded-lg border mt-1"> <div className="border-primary/20 text-xs text-muted-foreground bg-primary/10 p-2 rounded-lg border mt-1">
{nodeData.instructions.length > 50 {localInstructions.length > 50
? `${nodeData.instructions.substring(0, 50)}...` ? `${localInstructions.substring(0, 50)}...`
: nodeData.instructions} : localInstructions}
</div> </div>
</div> </div>
)} )}
@ -246,8 +269,8 @@ const MCPNode = memo(({ data, selected, id }: NodeProps) => {
<Textarea <Textarea
id={`instructions-${id}`} id={`instructions-${id}`}
placeholder="Provide specific instructions for how this MCP server should be used in the workflow..." placeholder="Provide specific instructions for how this MCP server should be used in the workflow..."
value={nodeData.instructions || ''} value={localInstructions}
onChange={(e) => updateNodeData(id, { instructions: e.target.value })} onChange={(e) => handleInstructionsChange(e.target.value)}
className="border-primary/20 min-h-[80px] text-sm" className="border-primary/20 min-h-[80px] text-sm"
/> />
</div> </div>

View File

@ -1,6 +1,6 @@
"use client"; "use client";
import { memo, useState } from "react"; import { memo, useState, useEffect, useCallback } from "react";
import { Handle, Position, NodeProps } from "@xyflow/react"; import { Handle, Position, NodeProps } from "@xyflow/react";
import { Card, CardContent, CardHeader } from "@/components/ui/card"; import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
@ -36,8 +36,32 @@ interface ToolConnectionNodeData {
const ToolConnectionNode = memo(({ data, selected, id }: NodeProps) => { const ToolConnectionNode = memo(({ data, selected, id }: NodeProps) => {
const nodeData = data as unknown as ToolConnectionNodeData; const nodeData = data as unknown as ToolConnectionNodeData;
const [isConfigOpen, setIsConfigOpen] = useState(false); const [isConfigOpen, setIsConfigOpen] = useState(false);
const [localInstructions, setLocalInstructions] = useState(nodeData.instructions || '');
const { updateNodeData } = useWorkflow(); const { updateNodeData } = useWorkflow();
useEffect(() => {
setLocalInstructions(nodeData.instructions || '');
}, [nodeData.instructions]);
// eslint-disable-next-line react-hooks/exhaustive-deps
const debouncedUpdateInstructions = useCallback(
(() => {
let timeoutId: NodeJS.Timeout;
return (value: string) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
updateNodeData?.(id!, { instructions: value });
}, 300);
};
})(),
[id, updateNodeData]
);
const handleInstructionsChange = (value: string) => {
setLocalInstructions(value);
debouncedUpdateInstructions(value);
};
const getToolConfig = () => { const getToolConfig = () => {
const toolId = nodeData.nodeId || nodeData.toolType; const toolId = nodeData.nodeId || nodeData.toolType;
@ -145,13 +169,13 @@ const ToolConnectionNode = memo(({ data, selected, id }: NodeProps) => {
<span className="text-xs text-muted-foreground">Ready</span> <span className="text-xs text-muted-foreground">Ready</span>
</div> </div>
</div> </div>
{nodeData.instructions && !isConfigOpen && ( {localInstructions && !isConfigOpen && (
<div> <div>
<Label className="text-xs font-medium text-muted-foreground">Instructions</Label> <Label className="text-xs font-medium text-muted-foreground">Instructions</Label>
<div className="border-primary/20 text-xs text-muted-foreground bg-primary/10 p-2 rounded-lg border mt-1"> <div className="border-primary/20 text-xs text-muted-foreground bg-primary/10 p-2 rounded-lg border mt-1">
{nodeData.instructions.length > 50 {localInstructions.length > 50
? `${nodeData.instructions.substring(0, 50)}...` ? `${localInstructions.substring(0, 50)}...`
: nodeData.instructions} : localInstructions}
</div> </div>
</div> </div>
)} )}
@ -175,8 +199,8 @@ const ToolConnectionNode = memo(({ data, selected, id }: NodeProps) => {
<Textarea <Textarea
id={`instructions-${id}`} id={`instructions-${id}`}
placeholder="Provide specific instructions for how this tool should be used in the workflow..." placeholder="Provide specific instructions for how this tool should be used in the workflow..."
value={nodeData.instructions || ''} value={localInstructions}
onChange={(e) => updateNodeData?.(id!, { instructions: e.target.value })} onChange={(e) => handleInstructionsChange(e.target.value)}
className="border-primary/20 min-h-[80px] text-sm" className="border-primary/20 min-h-[80px] text-sm"
/> />
</div> </div>