mirror of https://github.com/kortix-ai/suna.git
fix: workflow text-area input fix
This commit is contained in:
parent
519c1da09c
commit
8a9644fcef
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue