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";
import { memo, useState } from "react";
import { memo, useState, useEffect, useCallback } from "react";
import { Handle, Position, NodeProps } from "@xyflow/react";
import { Play, Settings, Clock, Webhook, User, ChevronDown, ChevronUp, Brain } from "lucide-react";
import { CardContent, CardHeader } from "@/components/ui/card";
@ -35,6 +35,7 @@ const InputNode = memo(({ data, selected, id }: NodeProps) => {
const [isConfigOpen, setIsConfigOpen] = useState(false);
const [isWebhookDialogOpen, setIsWebhookDialogOpen] = useState(false);
const [isScheduleDialogOpen, setIsScheduleDialogOpen] = useState(false);
const [localPrompt, setLocalPrompt] = useState(nodeData.prompt || '');
const { updateNodeData, workflowId } = useWorkflow();
// Use the model selection hook
@ -50,6 +51,30 @@ const InputNode = memo(({ data, selected, id }: NodeProps) => {
// Initialize model if not set
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) => {
updateNodeData(id, { model: modelId });
};
@ -182,8 +207,8 @@ const InputNode = memo(({ data, selected, id }: NodeProps) => {
<Textarea
id={`prompt-${id}`}
placeholder="Describe what this workflow should accomplish..."
value={nodeData.prompt || ''}
onChange={(e) => updateNodeData(id, { prompt: e.target.value })}
value={localPrompt}
onChange={(e) => handlePromptChange(e.target.value)}
className="min-h-[80px] text-sm"
/>
</div>

View File

@ -1,6 +1,6 @@
"use client";
import { memo, useState, useEffect } from "react";
import { memo, useState, useEffect, useCallback } from "react";
import { Handle, Position, NodeProps } from "@xyflow/react";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
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 [showConfigDialog, setShowConfigDialog] = useState(false);
const [isConfigOpen, setIsConfigOpen] = useState(false);
const [localInstructions, setLocalInstructions] = useState(nodeData.instructions || '');
const { updateNodeData } = useWorkflow();
const { data: credentialProfiles } = useCredentialProfilesForMcp(
@ -54,6 +55,28 @@ const MCPNode = memo(({ data, selected, id }: NodeProps) => {
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(() => {
if (nodeData.mcpType === "smithery" && nodeData.qualifiedName && credentialProfiles && !nodeData.isConfigured) {
const hasProfiles = credentialProfiles.length > 0;
@ -215,13 +238,13 @@ const MCPNode = memo(({ data, selected, id }: NodeProps) => {
</div>
</div>
)}
{nodeData.instructions && !isConfigOpen && (
{localInstructions && !isConfigOpen && (
<div>
<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">
{nodeData.instructions.length > 50
? `${nodeData.instructions.substring(0, 50)}...`
: nodeData.instructions}
{localInstructions.length > 50
? `${localInstructions.substring(0, 50)}...`
: localInstructions}
</div>
</div>
)}
@ -246,8 +269,8 @@ const MCPNode = memo(({ data, selected, id }: NodeProps) => {
<Textarea
id={`instructions-${id}`}
placeholder="Provide specific instructions for how this MCP server should be used in the workflow..."
value={nodeData.instructions || ''}
onChange={(e) => updateNodeData(id, { instructions: e.target.value })}
value={localInstructions}
onChange={(e) => handleInstructionsChange(e.target.value)}
className="border-primary/20 min-h-[80px] text-sm"
/>
</div>

View File

@ -1,6 +1,6 @@
"use client";
import { memo, useState } from "react";
import { memo, useState, useEffect, useCallback } from "react";
import { Handle, Position, NodeProps } from "@xyflow/react";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
@ -36,8 +36,32 @@ interface ToolConnectionNodeData {
const ToolConnectionNode = memo(({ data, selected, id }: NodeProps) => {
const nodeData = data as unknown as ToolConnectionNodeData;
const [isConfigOpen, setIsConfigOpen] = useState(false);
const [localInstructions, setLocalInstructions] = useState(nodeData.instructions || '');
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 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>
</div>
</div>
{nodeData.instructions && !isConfigOpen && (
{localInstructions && !isConfigOpen && (
<div>
<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">
{nodeData.instructions.length > 50
? `${nodeData.instructions.substring(0, 50)}...`
: nodeData.instructions}
{localInstructions.length > 50
? `${localInstructions.substring(0, 50)}...`
: localInstructions}
</div>
</div>
)}
@ -175,8 +199,8 @@ const ToolConnectionNode = memo(({ data, selected, id }: NodeProps) => {
<Textarea
id={`instructions-${id}`}
placeholder="Provide specific instructions for how this tool should be used in the workflow..."
value={nodeData.instructions || ''}
onChange={(e) => updateNodeData?.(id!, { instructions: e.target.value })}
value={localInstructions}
onChange={(e) => handleInstructionsChange(e.target.value)}
className="border-primary/20 min-h-[80px] text-sm"
/>
</div>