/
alexefan136
/
flowstack
Обзор
Документация
Войти
/
alexefan136
/
flowstack
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
ui/src/components/flows/NodeConfigPanel.tsx
181 строка
6 KB
Alexander Efanov
upd fix
29 июл 2026, 23:26
29 июл 2026, 23:26
d8ac8ee
Код
Авторство
О чём код?
// src/components/flows/NodeConfigPanel.tsx import { Trash2, X } from "lucide-react"; import type { FlowNode } from "../../lib/flow-types"; import { cn } from "../../lib/utils"; interface NodeConfigPanelProps { node: FlowNode | null; onUpdate: (id: string, data: Record<string, unknown>) => void; onDelete: (id: string) => void; onClose: () => void; } const inputClass = cn( "w-full px-3 py-2 rounded-lg text-sm", "bg-black/30 border border-white/10 text-white placeholder:text-white/30", "focus:outline-none focus:border-violet-500/50 focus:ring-2 focus:ring-violet-500/20", ); const labelClass = "block text-xs text-white/60 mb-1.5 font-medium"; export function NodeConfigPanel({ node, onUpdate, onDelete, onClose, }: NodeConfigPanelProps) { if (!node) return null; const data = node.data; const set = (patch: Record<string, unknown>) => onUpdate(node.id, patch); return ( <div className="w-72 shrink-0 border-l border-white/10 bg-[#111113] flex flex-col"> {/* Header */} <div className="flex items-center justify-between px-4 py-3 border-b border-white/10"> <span className="text-sm font-semibold text-white">Настройка узла</span> <button onClick={onClose} className="p-1 rounded text-white/40 hover:text-white hover:bg-white/10 transition" aria-label="Закрыть панель" > <X className="w-4 h-4" /> </button> </div> <div className="flex-1 overflow-y-auto p-4 space-y-4"> {/* Label (для всех) */} <div> <label className={labelClass}>Название</label> <input value={data.label} onChange={(e) => set({ label: e.target.value })} className={inputClass} /> </div> {/* Agent */} {data.type === "agent" && ( <> <div> <label className={labelClass}>Тип агента</label> <select value={(data.agentType as string) ?? "research"} onChange={(e) => set({ agentType: e.target.value })} className={cn(inputClass, "cursor-pointer")} > <option value="research">Research</option> <option value="writer">Writer</option> <option value="critic">Critic</option> <option value="analyst">Analyst</option> <option value="custom">Custom</option> </select> </div> <div> <label className={labelClass}>System Prompt</label> <textarea value={(data.systemPrompt as string) ?? ""} onChange={(e) => set({ systemPrompt: e.target.value })} rows={6} className={cn(inputClass, "resize-y font-mono text-xs")} /> </div> <div> <label className={labelClass}>Модель</label> <input value={(data.model as string) ?? ""} onChange={(e) => set({ model: e.target.value })} placeholder="deepseek-ai/DeepSeek-V4-Pro" className={inputClass} /> </div> </> )} {/* LLM */} {data.type === "llm" && ( <> <div> <label className={labelClass}>Модель</label> <select value={(data.model as string) ?? "gpt-4o-mini"} onChange={(e) => set({ model: e.target.value })} className={cn(inputClass, "cursor-pointer")} > <option value="gpt-4o">GPT-4o</option> <option value="gpt-4o-mini">GPT-4o Mini</option> <option value="claude-3.5-sonnet">Claude 3.5 Sonnet</option> <option value="gemini-1.5-pro">Gemini 1.5 Pro</option> </select> </div> <div> <label className={labelClass}>Prompt</label> <textarea value={(data.prompt as string) ?? ""} onChange={(e) => set({ prompt: e.target.value })} rows={5} placeholder="Используйте {{input}} для подстановки" className={cn(inputClass, "resize-y font-mono text-xs")} /> </div> <div> <label className={labelClass}> Temperature: {String(data.temperature ?? 0.7)} </label> <input type="range" min={0} max={2} step={0.1} value={(data.temperature as number) ?? 0.7} onChange={(e) => set({ temperature: Number(e.target.value) })} className="w-full accent-violet-500" /> </div> </> )} {/* Tool */} {data.type === "tool" && ( <div> <label className={labelClass}>Тип инструмента</label> <select value={(data.toolType as string) ?? "web_search"} onChange={(e) => set({ toolType: e.target.value })} className={cn(inputClass, "cursor-pointer")} > <option value="web_search">Web Search</option> <option value="code_executor">Code Executor</option> <option value="rag_search">RAG Search</option> <option value="http_request">HTTP Request</option> <option value="custom">Custom</option> </select> </div> )} {/* Condition */} {data.type === "condition" && ( <div> <label className={labelClass}>Условие</label> <input value={(data.expression as string) ?? ""} onChange={(e) => set({ expression: e.target.value })} placeholder="input.score > 0.8" className={cn(inputClass, "font-mono text-xs")} /> </div> )} </div> {/* Delete */} <div className="p-4 border-t border-white/10"> <button onClick={() => onDelete(node.id)} className="w-full inline-flex items-center justify-center gap-2 px-3 py-2 rounded-lg text-sm text-red-400 bg-red-500/10 hover:bg-red-500/20 transition" > <Trash2 className="w-4 h-4" /> Удалить узел </button> </div> </div> ); }