feat(agent_builder): Updates to Builder GUI + Nodes (#7345)

* Updates to Builder GUI + Nodes

* fix apiUrl back to local host
pull/7347/head
Bently 2024-07-08 21:33:07 +01:00 committed by GitHub
parent f9bedb0fd9
commit 1e755f9e8d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 119 additions and 44 deletions

View File

@ -3,6 +3,9 @@ import { Handle, Position, NodeProps } from 'reactflow';
import 'reactflow/dist/style.css';
import './customnode.css';
import ModalComponent from './ModalComponent';
import { Button } from './ui/button';
import { Input } from './ui/input';
import { Textarea } from './ui/textarea';
type Schema = {
type: string;
@ -184,16 +187,16 @@ const CustomNode: FC<NodeProps<CustomNodeData>> = ({ data, id }) => {
<div className="clickable-input" onClick={() => handleInputClick(`${fullKey}.${propKey}`)}>
{propKey}: {typeof propValue === 'object' ? JSON.stringify(propValue, null, 2) : propValue}
</div>
<button onClick={() => handleInputChange(`${fullKey}.${propKey}`, undefined)} className="array-item-remove">
<Button onClick={() => handleInputChange(`${fullKey}.${propKey}`, undefined)} className="array-item-remove">
&times;
</button>
</Button>
</div>
))}
{key === 'expected_format' && (
<div className="nested-input">
{keyValuePairs.map((pair, index) => (
<div key={index} className="key-value-input">
<input
<Input
type="text"
placeholder="Key"
value={pair.key}
@ -205,7 +208,7 @@ const CustomNode: FC<NodeProps<CustomNodeData>> = ({ data, id }) => {
handleInputChange('expected_format', expectedFormat);
}}
/>
<input
<Input
type="text"
placeholder="Value"
value={pair.value}
@ -220,20 +223,20 @@ const CustomNode: FC<NodeProps<CustomNodeData>> = ({ data, id }) => {
</div>
))}
<div className="key-value-input">
<input
<Input
type="text"
placeholder="Key"
value={newKey}
onChange={(e) => setNewKey(e.target.value)}
/>
<input
<Input
type="text"
placeholder="Value"
value={newValue}
onChange={(e) => setNewValue(e.target.value)}
/>
</div>
<button onClick={handleAddProperty}>Add Property</button>
<Button onClick={handleAddProperty}>Add Property</Button>
</div>
)}
{error && <span className="error-message">{error}</span>}
@ -344,14 +347,14 @@ const CustomNode: FC<NodeProps<CustomNodeData>> = ({ data, id }) => {
onChange={(e) => handleInputChange(`${fullKey}.${index}`, e.target.value)}
className="array-item-input"
/>
<button onClick={() => handleInputChange(`${fullKey}.${index}`, '')} className="array-item-remove">
<Button onClick={() => handleInputChange(`${fullKey}.${index}`, '')} className="array-item-remove">
&times;
</button>
</Button>
</div>
))}
<button onClick={() => handleInputChange(fullKey, [...arrayValues, ''])} className="array-item-add">
<Button onClick={() => handleInputChange(fullKey, [...arrayValues, ''])} className="array-item-add">
Add Item
</button>
</Button>
{error && <span className="error-message">{error}</span>}
</div>
);
@ -398,12 +401,12 @@ const CustomNode: FC<NodeProps<CustomNodeData>> = ({ data, id }) => {
};
return (
<div className={`custom-node ${data.status === 'RUNNING' ? 'running' : data.status === 'COMPLETED' ? 'completed' : ''}`}>
<div className={`custom-node dark-theme ${data.status === 'RUNNING' ? 'running' : data.status === 'COMPLETED' ? 'completed' : ''}`}>
<div className="node-header">
<div className="node-title">{data.blockType || data.title}</div>
<button onClick={toggleProperties} className="toggle-button">
<Button onClick={toggleProperties} className="toggle-button">
&#9776;
</button>
</Button>
</div>
<div className="node-content">
<div className="input-section">
@ -442,7 +445,7 @@ const CustomNode: FC<NodeProps<CustomNodeData>> = ({ data, id }) => {
</p>
</div>
)}
<button onClick={handleSubmit}>Submit</button>
<Button onClick={handleSubmit}>Submit</Button>
<ModalComponent
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}

View File

@ -17,6 +17,8 @@ import CustomNode from './CustomNode';
import './flow.css';
import AutoGPTServerAPI, { Block, Flow } from '@/lib/autogpt_server_api';
import { ObjectSchema } from '@/lib/types';
import { Button } from './ui/button';
import { Input } from './ui/input';
type CustomNodeData = {
blockType: string;
@ -43,18 +45,18 @@ const Sidebar: React.FC<{isOpen: boolean, availableNodes: Block[], addNode: (id:
);
return (
<div className={`sidebar ${isOpen ? 'open' : ''}`}>
<div className={`sidebar dark-theme ${isOpen ? 'open' : ''}`}>
<h3>Nodes</h3>
<input
<Input
type="text"
placeholder="Search nodes..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
{filteredNodes.map((node) => (
<div key={node.id} className="sidebarNodeRowStyle">
<div key={node.id} className="sidebarNodeRowStyle dark-theme">
<span>{node.name}</span>
<button onClick={() => addNode(node.id, node.name)}>Add</button>
<Button onClick={() => addNode(node.id, node.name)}>Add</Button>
</div>
))}
</div>
@ -352,18 +354,18 @@ const updateNodesWithExecutionData = (executionData: any[]) => {
return (
<div style={{ height: '100vh', width: '100%' }}>
<button
<Button
onClick={toggleSidebar}
style={{
position: 'absolute',
left: isSidebarOpen ? '260px' : '10px',
top: '10px',
zIndex: 5,
zIndex: 10000,
transition: 'left 0.3s'
}}
>
{isSidebarOpen ? 'Hide Sidebar' : 'Show Sidebar'}
</button>
</Button>
<Sidebar isOpen={isSidebarOpen} availableNodes={availableNodes} addNode={addNode} />
<ReactFlow
nodes={nodes}
@ -374,7 +376,7 @@ const updateNodesWithExecutionData = (executionData: any[]) => {
nodeTypes={nodeTypes}
>
<div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}>
<button onClick={runAgent}>Run Agent</button>
<Button onClick={runAgent}>Run Agent</Button>
</div>
</ReactFlow>
</div>

View File

@ -1,5 +1,7 @@
import React, { FC } from 'react';
import './modal.css';
import { Button } from './ui/button';
import { Textarea } from './ui/textarea';
interface ModalProps {
isOpen: boolean;
@ -22,15 +24,16 @@ const ModalComponent: FC<ModalProps> = ({ isOpen, onClose, onSave, value }) => {
return (
<div className="modal-overlay">
<div className="modal">
<textarea
<div className="modal dark-theme">
<center><h1>Enter input text</h1></center>
<Textarea
className="modal-textarea"
value={tempValue}
onChange={(e) => setTempValue(e.target.value)}
/>
<div className="modal-actions">
<button onClick={onClose}>Cancel</button>
<button onClick={handleSave}>Save</button>
<Button onClick={onClose}>Cancel</Button>
<Button onClick={handleSave}>Save</Button>
</div>
</div>
</div>

View File

@ -4,8 +4,8 @@
border-radius: 12px;
background: #2c2c2c;
color: #e0e0e0;
width: 300px; /* Adjust width for better layout */
box-sizing: border-box; /* Ensure padding doesn't affect overall width */
width: 500px;
box-sizing: border-box;
}
.node-header {
@ -29,28 +29,38 @@
.node-content {
display: flex;
flex-direction: column; /* Use column to stack elements */
gap: 10px;
justify-content: space-between;
align-items: flex-start;
gap: 1px;
}
.input-section {
flex: 1;
}
.output-section {
flex: 1;
}
.handle-container {
display: flex;
align-items: center;
position: relative;
margin-bottom: 5px;
flex-direction: column;
flex: 1;
}
.handle-label {
color: #e0e0e0;
margin-left: 10px;
margin-right: 10px;
}
.output-section {
display: flex;
flex-direction: column;
flex: 1;
align-items: flex-end;
}
.handle-label {
margin-left: 10px;
}
.handle-container {
display: flex;
position: relative;
margin-bottom: 5px;
}
.input-container {
@ -59,11 +69,26 @@
.clickable-input {
padding: 5px;
width: 325px;
border-radius: 4px;
border: 1px solid #555;
background: #444;
color: #e0e0e0;
cursor: pointer;
word-break: break-all;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
position: relative;
}
.clickable-input span {
display: inline-block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: calc(100% - 100px);
vertical-align: middle;
}
.select-input {
@ -126,10 +151,12 @@
}
.node-properties {
margin-top: 20px;
margin-top: 5px;
margin-bottom: 5px;
background: #444;
padding: 10px;
border-radius: 10px;
width: 325px;
}
.error-message {
@ -183,3 +210,7 @@
.custom-node {
transition: all 0.3s ease-in-out;
}
.dark-theme {
@apply dark:border-gray-800 dark:bg-gray-950;
}

View File

@ -105,7 +105,11 @@ input::placeholder, textarea::placeholder {
}
.sidebar h3 {
margin: 0 0 20px;
margin: 0 0 10px;
}
.sidebar input {
margin: 0 0 10px;
}
.sidebarNodeRowStyle {
@ -148,3 +152,7 @@ input::placeholder, textarea::placeholder {
.flow-controls.open {
transform: translateX(350px);
}
.dark-theme {
@apply dark:border-gray-800 dark:bg-gray-950;
}

View File

@ -32,3 +32,7 @@
gap: 10px;
margin-top: 10px;
}
.dark-theme {
@apply dark:border-gray-800 dark:bg-gray-950;
}

View File

@ -0,0 +1,24 @@
import * as React from "react"
import { cn } from "@/lib/utils"
export interface TextareaProps
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
({ className, ...props }, ref) => {
return (
<textarea
className={cn(
"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
}
)
Textarea.displayName = "Textarea"
export { Textarea }