| export default function PropertiesPanel({ selectedNode, onUpdateNode, onDeleteNode }) { | |
| if (!selectedNode) { | |
| return ( | |
| <div className="flex flex-col h-full"> | |
| <h2 className="font-semibold mb-4 pb-2 border-b border-border">Properties</h2> | |
| <div className="flex-1 flex items-center justify-center text-muted text-sm text-center px-4"> | |
| Select a node to configure its parameters. | |
| </div> | |
| </div> | |
| ); | |
| } | |
| const handleChange = (field, value) => { | |
| onUpdateNode(selectedNode.id, { [field]: value }); | |
| }; | |
| return ( | |
| <div className="flex flex-col h-full"> | |
| <h2 className="font-semibold mb-4 pb-2 border-b border-border">Properties</h2> | |
| <div className="space-y-4 flex-1 overflow-y-auto pr-2"> | |
| <div className="space-y-1"> | |
| <label className="text-xs text-muted uppercase tracking-wider">Node ID</label> | |
| <input | |
| type="text" | |
| value={selectedNode.id} | |
| disabled | |
| className="w-full bg-surface border border-border rounded px-2 py-1.5 text-sm text-muted cursor-not-allowed" | |
| /> | |
| </div> | |
| <div className="space-y-1"> | |
| <label className="text-xs text-muted uppercase tracking-wider">Label</label> | |
| <input | |
| type="text" | |
| value={selectedNode.label} | |
| onChange={(e) => handleChange('label', e.target.value)} | |
| className="w-full bg-surface border border-border rounded px-2 py-1.5 text-sm focus:border-primary focus:outline-none transition-colors" | |
| /> | |
| </div> | |
| {selectedNode.type === 'llm' && ( | |
| <> | |
| <div className="space-y-1"> | |
| <label className="text-xs text-muted uppercase tracking-wider">Model</label> | |
| <select | |
| value={selectedNode.model} | |
| onChange={(e) => handleChange('model', e.target.value)} | |
| className="w-full bg-surface border border-border rounded px-2 py-1.5 text-sm focus:border-primary focus:outline-none transition-colors" | |
| > | |
| <option value="gpt-4-turbo">GPT-4 Turbo</option> | |
| <option value="gpt-3.5-turbo">GPT-3.5 Turbo</option> | |
| <option value="claude-3-opus">Claude 3 Opus</option> | |
| <option value="llama-3-70b">Llama 3 70B</option> | |
| </select> | |
| </div> | |
| <div className="space-y-1"> | |
| <label className="text-xs text-muted uppercase tracking-wider">Temperature: {selectedNode.temp}</label> | |
| <input | |
| type="range" | |
| min="0" | |
| max="1" | |
| step="0.1" | |
| value={selectedNode.temp} | |
| onChange={(e) => handleChange('temp', parseFloat(e.target.value))} | |
| className="w-full accent-primary" | |
| /> | |
| <div className="flex justify-between text-[10px] text-muted"> | |
| <span>Precise</span> | |
| <span>Creative</span> | |
| </div> | |
| </div> | |
| </> | |
| )} | |
| <div className="space-y-1"> | |
| <label className="text-xs text-muted uppercase tracking-wider">System Prompt</label> | |
| <textarea | |
| value={selectedNode.prompt} | |
| onChange={(e) => handleChange('prompt', e.target.value)} | |
| placeholder="Enter system instructions..." | |
| className="w-full bg-surface border border-border rounded px-2 py-1.5 text-sm font-mono h-24 focus:border-primary focus:outline-none resize-none transition-colors" | |
| /> | |
| </div> | |
| </div> | |
| <div className="pt-4 border-t border-border mt-4 space-y-2"> | |
| <button | |
| onClick={() => onDeleteNode(selectedNode.id)} | |
| className="w-full py-2 rounded border border-red-900/50 text-red-400 hover:bg-red-900/20 text-sm transition-colors" | |
| > | |
| Delete Node | |
| </button> | |
| </div> | |
| </div> | |
| ); | |
| } |