mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-02-12 15:55:03 -05:00
Compare commits
62 Commits
fix/claude
...
swiftyos/s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac6ec23528 | ||
|
|
54aa89d5fe | ||
|
|
4bc9a98577 | ||
|
|
b3ed7f7759 | ||
|
|
82c6be75c1 | ||
|
|
8c45e3482c | ||
|
|
0e027b7fff | ||
|
|
c40c62df9f | ||
|
|
b8deb74362 | ||
|
|
1c50022ef8 | ||
|
|
d296833f59 | ||
|
|
f4c1c44207 | ||
|
|
587b124710 | ||
|
|
dfd30e1d24 | ||
|
|
c43ad0a70b | ||
|
|
03c55291d5 | ||
|
|
fe75257bf6 | ||
|
|
2f670c903d | ||
|
|
cc8b24adf8 | ||
|
|
5a06b6e2a2 | ||
|
|
4e3df63dcd | ||
|
|
bb24883651 | ||
|
|
1eb9bdb40f | ||
|
|
83500be782 | ||
|
|
ed91e552d1 | ||
|
|
c3e17f7b30 | ||
|
|
70fcd55084 | ||
|
|
7a238f1f4d | ||
|
|
0c12c69e3b | ||
|
|
33f29bbd30 | ||
|
|
519de16f0f | ||
|
|
d364406954 | ||
|
|
ca6b01347e | ||
|
|
2155199e6f | ||
|
|
dfd324da1f | ||
|
|
9b8f7547de | ||
|
|
eadcd7a348 | ||
|
|
7d5e7e1f04 | ||
|
|
fe7a689e9e | ||
|
|
0dfbf48024 | ||
|
|
9e524db279 | ||
|
|
3f77c407b9 | ||
|
|
7b9bffdbab | ||
|
|
0fa7e72e0c | ||
|
|
2a20106634 | ||
|
|
841bed2c65 | ||
|
|
8fd6b88748 | ||
|
|
86c05258fe | ||
|
|
90d8f4ab7e | ||
|
|
09afc5a670 | ||
|
|
62e62effc6 | ||
|
|
320ead4862 | ||
|
|
8f9d4adfee | ||
|
|
53e16dca7c | ||
|
|
b72326e1d3 | ||
|
|
778403cd6f | ||
|
|
1a2ab80d0b | ||
|
|
433951a668 | ||
|
|
034f854c0c | ||
|
|
ef82903405 | ||
|
|
a165202ae4 | ||
|
|
3aa68bbd9a |
@@ -23,6 +23,7 @@
|
|||||||
"@radix-ui/react-avatar": "^1.1.0",
|
"@radix-ui/react-avatar": "^1.1.0",
|
||||||
"@radix-ui/react-checkbox": "^1.1.1",
|
"@radix-ui/react-checkbox": "^1.1.1",
|
||||||
"@radix-ui/react-collapsible": "^1.1.0",
|
"@radix-ui/react-collapsible": "^1.1.0",
|
||||||
|
"@radix-ui/react-context-menu": "^2.2.1",
|
||||||
"@radix-ui/react-dialog": "^1.1.1",
|
"@radix-ui/react-dialog": "^1.1.1",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ const AgentCard: React.FC<{ agent: Agent; featured?: boolean }> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`flex cursor-pointer flex-col justify-between rounded-lg border p-6 transition-colors duration-200 hover:bg-gray-50 ${featured ? "border-indigo-500 shadow-md" : "border-gray-200"}`}
|
className={`flex cursor-pointer flex-col justify-between rounded-lg border p-6 transition-colors duration-200 hover:bg-gray-50 ${featured ? "border-indigo-500 shadow-md" : "border-gray-300"}`}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -48,9 +48,9 @@ export function CustomEdge({
|
|||||||
}>({ beads: [], created: 0, destroyed: 0 });
|
}>({ beads: [], created: 0, destroyed: 0 });
|
||||||
const { svgPath, length, getPointForT, getTForDistance } = useBezierPath(
|
const { svgPath, length, getPointForT, getTForDistance } = useBezierPath(
|
||||||
sourceX - 5,
|
sourceX - 5,
|
||||||
sourceY,
|
sourceY - 5,
|
||||||
targetX + 3,
|
targetX - 9,
|
||||||
targetY,
|
targetY - 5,
|
||||||
);
|
);
|
||||||
const { deleteElements } = useReactFlow<Node, CustomEdge>();
|
const { deleteElements } = useReactFlow<Node, CustomEdge>();
|
||||||
const { visualizeBeads } = useContext(FlowContext) ?? {
|
const { visualizeBeads } = useContext(FlowContext) ?? {
|
||||||
|
|||||||
@@ -21,19 +21,20 @@ import {
|
|||||||
import { beautifyString, cn, setNestedProperty } from "@/lib/utils";
|
import { beautifyString, cn, setNestedProperty } from "@/lib/utils";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { Copy, Trash2 } from "lucide-react";
|
|
||||||
import { history } from "./history";
|
import { history } from "./history";
|
||||||
import NodeHandle from "./NodeHandle";
|
import NodeHandle from "./NodeHandle";
|
||||||
import {
|
import {
|
||||||
NodeGenericInputField,
|
NodeGenericInputField,
|
||||||
NodeTextBoxInput,
|
NodeTextBoxInput,
|
||||||
} from "./node-input-components";
|
} from "./node-input-components";
|
||||||
import SchemaTooltip from "./SchemaTooltip";
|
|
||||||
import { getPrimaryCategoryColor } from "@/lib/utils";
|
import { getPrimaryCategoryColor } from "@/lib/utils";
|
||||||
import { FlowContext } from "./Flow";
|
import { FlowContext } from "./Flow";
|
||||||
import { Badge } from "./ui/badge";
|
import { Badge } from "./ui/badge";
|
||||||
import DataTable from "./DataTable";
|
import NodeOutputs from "./NodeOutputs";
|
||||||
import { IconCoin } from "./ui/icons";
|
import { IconCoin } from "./ui/icons";
|
||||||
|
import * as Separator from "@radix-ui/react-separator";
|
||||||
|
import * as ContextMenu from "@radix-ui/react-context-menu";
|
||||||
|
import { DotsVerticalIcon, TrashIcon, CopyIcon } from "@radix-ui/react-icons";
|
||||||
|
|
||||||
type ParsedKey = { key: string; index?: number };
|
type ParsedKey = { key: string; index?: number };
|
||||||
|
|
||||||
@@ -72,14 +73,19 @@ export type CustomNodeData = {
|
|||||||
|
|
||||||
export type CustomNode = Node<CustomNodeData, "custom">;
|
export type CustomNode = Node<CustomNodeData, "custom">;
|
||||||
|
|
||||||
export function CustomNode({ data, id, width, height }: NodeProps<CustomNode>) {
|
export function CustomNode({
|
||||||
|
data,
|
||||||
|
id,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
selected,
|
||||||
|
}: NodeProps<CustomNode>) {
|
||||||
const [isOutputOpen, setIsOutputOpen] = useState(data.isOutputOpen || false);
|
const [isOutputOpen, setIsOutputOpen] = useState(data.isOutputOpen || false);
|
||||||
const [isAdvancedOpen, setIsAdvancedOpen] = useState(false);
|
const [isAdvancedOpen, setIsAdvancedOpen] = useState(false);
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const [activeKey, setActiveKey] = useState<string | null>(null);
|
const [activeKey, setActiveKey] = useState<string | null>(null);
|
||||||
const [inputModalValue, setInputModalValue] = useState<string>("");
|
const [inputModalValue, setInputModalValue] = useState<string>("");
|
||||||
const [isOutputModalOpen, setIsOutputModalOpen] = useState(false);
|
const [isOutputModalOpen, setIsOutputModalOpen] = useState(false);
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
|
||||||
const { updateNodeData, deleteElements, addNodes, getNode } = useReactFlow<
|
const { updateNodeData, deleteElements, addNodes, getNode } = useReactFlow<
|
||||||
CustomNode,
|
CustomNode,
|
||||||
Edge
|
Edge
|
||||||
@@ -166,10 +172,10 @@ export function CustomNode({ data, id, width, height }: NodeProps<CustomNode>) {
|
|||||||
return (
|
return (
|
||||||
(isRequired || isAdvancedOpen || !isAdvanced) && (
|
(isRequired || isAdvancedOpen || !isAdvanced) && (
|
||||||
<div key={propKey}>
|
<div key={propKey}>
|
||||||
<span className="text-m green -mb-1 text-gray-900">
|
<span className="text-m green mb-0 text-gray-900">
|
||||||
{propSchema.title || beautifyString(propKey)}
|
{propSchema.title || beautifyString(propKey)}
|
||||||
</span>
|
</span>
|
||||||
<div key={propKey} onMouseOver={() => {}}>
|
<div key={propKey}>
|
||||||
{!isConnected && (
|
{!isConnected && (
|
||||||
<NodeGenericInputField
|
<NodeGenericInputField
|
||||||
className="mb-2 mt-1"
|
className="mb-2 mt-1"
|
||||||
@@ -215,9 +221,9 @@ export function CustomNode({ data, id, width, height }: NodeProps<CustomNode>) {
|
|||||||
const isAdvanced = propSchema.advanced;
|
const isAdvanced = propSchema.advanced;
|
||||||
return (
|
return (
|
||||||
(isRequired || isAdvancedOpen || !isAdvanced) && (
|
(isRequired || isAdvancedOpen || !isAdvanced) && (
|
||||||
<div key={propKey} onMouseOver={() => {}}>
|
<div key={propKey}>
|
||||||
{propKey !== "value" ? (
|
{propKey !== "value" ? (
|
||||||
<span className="text-m green -mb-1 text-gray-900">
|
<span className="text-m green mb-0 text-gray-900">
|
||||||
{propSchema.title || beautifyString(propKey)}
|
{propSchema.title || beautifyString(propKey)}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
@@ -254,9 +260,9 @@ export function CustomNode({ data, id, width, height }: NodeProps<CustomNode>) {
|
|||||||
const isAdvanced = propSchema.advanced;
|
const isAdvanced = propSchema.advanced;
|
||||||
return (
|
return (
|
||||||
(isRequired || isAdvancedOpen || isConnected || !isAdvanced) && (
|
(isRequired || isAdvancedOpen || isConnected || !isAdvanced) && (
|
||||||
<div key={propKey} onMouseOver={() => {}}>
|
<div key={propKey}>
|
||||||
{"credentials_provider" in propSchema ? (
|
{"credentials_provider" in propSchema ? (
|
||||||
<span className="text-m green -mb-1 text-gray-900">
|
<span className="text-m green mb-0 text-gray-900">
|
||||||
Credentials
|
Credentials
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
@@ -392,7 +398,7 @@ export function CustomNode({ data, id, width, height }: NodeProps<CustomNode>) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleInputClick = (key: string) => {
|
const handleInputClick = (key: string) => {
|
||||||
console.log(`Opening modal for key: ${key}`);
|
console.debug(`Opening modal for key: ${key}`);
|
||||||
setActiveKey(key);
|
setActiveKey(key);
|
||||||
const value = getValue(key);
|
const value = getValue(key);
|
||||||
setInputModalValue(
|
setInputModalValue(
|
||||||
@@ -418,16 +424,8 @@ export function CustomNode({ data, id, width, height }: NodeProps<CustomNode>) {
|
|||||||
setIsOutputModalOpen(true);
|
setIsOutputModalOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleHovered = () => {
|
|
||||||
setIsHovered(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMouseLeave = () => {
|
|
||||||
setIsHovered(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteNode = useCallback(() => {
|
const deleteNode = useCallback(() => {
|
||||||
console.log("Deleting node:", id);
|
console.debug("Deleting node:", id);
|
||||||
|
|
||||||
// Remove the node
|
// Remove the node
|
||||||
deleteElements({ nodes: [{ id }] });
|
deleteElements({ nodes: [{ id }] });
|
||||||
@@ -464,7 +462,7 @@ export function CustomNode({ data, id, width, height }: NodeProps<CustomNode>) {
|
|||||||
|
|
||||||
history.push({
|
history.push({
|
||||||
type: "ADD_NODE",
|
type: "ADD_NODE",
|
||||||
payload: { node: newNode },
|
payload: { node: { ...newNode, ...newNode.data } as CustomNodeData },
|
||||||
undo: () => deleteElements({ nodes: [{ id: newId }] }),
|
undo: () => deleteElements({ nodes: [{ id: newId }] }),
|
||||||
redo: () => addNodes(newNode),
|
redo: () => addNodes(newNode),
|
||||||
});
|
});
|
||||||
@@ -507,20 +505,53 @@ export function CustomNode({ data, id, width, height }: NodeProps<CustomNode>) {
|
|||||||
"custom-node",
|
"custom-node",
|
||||||
"dark-theme",
|
"dark-theme",
|
||||||
"rounded-xl",
|
"rounded-xl",
|
||||||
"border",
|
|
||||||
"bg-white/[.9]",
|
"bg-white/[.9]",
|
||||||
"shadow-md",
|
"border border-gray-300",
|
||||||
|
data.uiType === BlockUIType.NOTE ? "w-[300px]" : "w-[500px]",
|
||||||
|
data.uiType === BlockUIType.NOTE ? "bg-yellow-100" : "bg-white",
|
||||||
|
selected ? "shadow-2xl" : "",
|
||||||
]
|
]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(" ");
|
.join(" ");
|
||||||
|
|
||||||
const errorClass =
|
const errorClass =
|
||||||
hasConfigErrors || hasOutputError ? "border-red-500 border-2" : "";
|
hasConfigErrors || hasOutputError ? "border-red-200 border-2" : "";
|
||||||
|
|
||||||
const statusClass =
|
const statusClass = (() => {
|
||||||
hasConfigErrors || hasOutputError
|
if (hasConfigErrors || hasOutputError) return "border-red-200 border-4";
|
||||||
? "failed"
|
switch (data.status?.toLowerCase()) {
|
||||||
: (data.status?.toLowerCase() ?? "");
|
case "completed":
|
||||||
|
return "border-green-200 border-4";
|
||||||
|
case "running":
|
||||||
|
return "border-yellow-200 border-4";
|
||||||
|
case "failed":
|
||||||
|
return "border-red-200 border-4";
|
||||||
|
case "incomplete":
|
||||||
|
return "border-purple-200 border-4";
|
||||||
|
case "queued":
|
||||||
|
return "border-cyan-200 border-4";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
const statusBackgroundClass = (() => {
|
||||||
|
if (hasConfigErrors || hasOutputError) return "bg-red-200";
|
||||||
|
switch (data.status?.toLowerCase()) {
|
||||||
|
case "completed":
|
||||||
|
return "bg-green-200";
|
||||||
|
case "running":
|
||||||
|
return "bg-yellow-200";
|
||||||
|
case "failed":
|
||||||
|
return "bg-red-200";
|
||||||
|
case "incomplete":
|
||||||
|
return "bg-purple-200";
|
||||||
|
case "queued":
|
||||||
|
return "bg-cyan-200";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
const hasAdvancedFields =
|
const hasAdvancedFields =
|
||||||
data.inputSchema &&
|
data.inputSchema &&
|
||||||
@@ -541,115 +572,205 @@ export function CustomNode({ data, id, width, height }: NodeProps<CustomNode>) {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
const LineSeparator = () => (
|
||||||
<div
|
<div className="bg-white pt-6">
|
||||||
className={`${data.uiType === BlockUIType.NOTE ? "w-[300px]" : "w-[500px]"} ${blockClasses} ${errorClass} ${statusClass} ${data.uiType === BlockUIType.NOTE ? "bg-yellow-100" : "bg-white"}`}
|
<Separator.Root className="h-[1px] w-full bg-gray-300"></Separator.Root>
|
||||||
onMouseEnter={handleHovered}
|
</div>
|
||||||
onMouseLeave={handleMouseLeave}
|
);
|
||||||
data-id={`custom-node-${id}`}
|
|
||||||
>
|
const ContextMenuContent = () => (
|
||||||
<div
|
<ContextMenu.Content className="z-10 rounded-xl border bg-white p-1 shadow-md">
|
||||||
className={`mb-2 p-3 ${data.uiType === BlockUIType.NOTE ? "bg-yellow-100" : getPrimaryCategoryColor(data.categories)} rounded-t-xl`}
|
<ContextMenu.Item
|
||||||
|
onSelect={copyNode}
|
||||||
|
className="flex cursor-pointer items-center rounded-md px-3 py-2 hover:bg-gray-100"
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between">
|
<CopyIcon className="mr-2 h-5 w-5" />
|
||||||
<div className="font-roboto p-3 text-lg font-semibold">
|
<span>Copy</span>
|
||||||
{beautifyString(
|
</ContextMenu.Item>
|
||||||
data.blockType?.replace(/Block$/, "") || data.title,
|
<ContextMenu.Separator className="my-1 h-px bg-gray-300" />
|
||||||
)}
|
<ContextMenu.Item
|
||||||
|
onSelect={deleteNode}
|
||||||
|
className="flex cursor-pointer items-center rounded-md px-3 py-2 text-red-500 hover:bg-gray-100"
|
||||||
|
>
|
||||||
|
<TrashIcon className="mr-2 h-5 w-5 text-red-500" />
|
||||||
|
<span>Delete</span>
|
||||||
|
</ContextMenu.Item>
|
||||||
|
</ContextMenu.Content>
|
||||||
|
);
|
||||||
|
|
||||||
|
const onContextButtonTrigger = (e: React.MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const rect = e.currentTarget.getBoundingClientRect();
|
||||||
|
const event = new MouseEvent("contextmenu", {
|
||||||
|
bubbles: true,
|
||||||
|
clientX: rect.left + rect.width / 2,
|
||||||
|
clientY: rect.top + rect.height / 2,
|
||||||
|
});
|
||||||
|
e.currentTarget.dispatchEvent(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
const stripeColor = getPrimaryCategoryColor(data.categories);
|
||||||
|
|
||||||
|
const NodeContent = () => (
|
||||||
|
<div
|
||||||
|
className={`${blockClasses} ${errorClass} ${statusClass}`}
|
||||||
|
data-id={`custom-node-${id}`}
|
||||||
|
z-index={1}
|
||||||
|
>
|
||||||
|
{/* Header */}
|
||||||
|
<div
|
||||||
|
className={`flex h-24 border-b border-gray-300 ${data.uiType === BlockUIType.NOTE ? "bg-yellow-100" : "bg-white"} items-center rounded-t-xl`}
|
||||||
|
>
|
||||||
|
{/* Color Stripe */}
|
||||||
|
<div className={`-ml-px h-full w-3 rounded-tl-xl ${stripeColor}`}></div>
|
||||||
|
|
||||||
|
<div className="flex w-full flex-col">
|
||||||
|
<div className="flex flex-row items-center justify-between">
|
||||||
|
<div className="font-roboto px-3 text-lg font-semibold">
|
||||||
|
{beautifyString(
|
||||||
|
data.blockType?.replace(/Block$/, "") || data.title,
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<SchemaTooltip description={data.description} />
|
{blockCost && (
|
||||||
</div>
|
<div className="px-3 text-base font-light">
|
||||||
<div className="flex gap-[5px]">
|
<span className="ml-auto flex items-center">
|
||||||
{isHovered && (
|
<IconCoin />{" "}
|
||||||
<>
|
<span className="m-1 font-medium">{blockCost.cost_amount}</span>{" "}
|
||||||
<Button
|
credits/{blockCost.cost_type}
|
||||||
variant="outline"
|
</span>
|
||||||
size="icon"
|
</div>
|
||||||
onClick={copyNode}
|
|
||||||
title="Copy node"
|
|
||||||
>
|
|
||||||
<Copy size={18} />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="icon"
|
|
||||||
onClick={deleteNode}
|
|
||||||
title="Delete node"
|
|
||||||
>
|
|
||||||
<Trash2 size={18} />
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className={`mr-5 ${getPrimaryCategoryColor(data.categories)} rounded-xl opacity-50`}
|
||||||
|
>
|
||||||
|
{data.categories[0].category}
|
||||||
|
</Badge>
|
||||||
|
<button
|
||||||
|
aria-label="Options"
|
||||||
|
className="mr-2 cursor-pointer rounded-full border-none bg-transparent p-1 hover:bg-gray-100"
|
||||||
|
onClick={onContextButtonTrigger}
|
||||||
|
>
|
||||||
|
<DotsVerticalIcon className="h-5 w-5" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<ContextMenuContent />
|
||||||
</div>
|
</div>
|
||||||
{blockCost && (
|
{/* Body */}
|
||||||
<div className="p-3 font-semibold">
|
<div className="ml-5 mt-6 rounded-b-xl">
|
||||||
<span className="ml-auto flex items-center">
|
{/* Input Handles */}
|
||||||
<IconCoin /> {blockCost.cost_amount} credits/{blockCost.cost_type}
|
{data.uiType !== BlockUIType.NOTE ? (
|
||||||
</span>
|
<div className="flex items-start justify-between">
|
||||||
</div>
|
<div>
|
||||||
)}
|
{data.inputSchema &&
|
||||||
{data.uiType !== BlockUIType.NOTE ? (
|
generateInputHandles(data.inputSchema, data.uiType)}
|
||||||
<div className="flex items-start justify-between p-3">
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
<div>
|
<div>
|
||||||
{data.inputSchema &&
|
{data.inputSchema &&
|
||||||
generateInputHandles(data.inputSchema, data.uiType)}
|
generateInputHandles(data.inputSchema, data.uiType)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-none">
|
)}
|
||||||
{data.outputSchema &&
|
|
||||||
generateOutputHandles(data.outputSchema, data.uiType)}
|
{/* Advanced Settings */}
|
||||||
</div>
|
{data.uiType !== BlockUIType.NOTE && hasAdvancedFields && (
|
||||||
</div>
|
<>
|
||||||
) : (
|
<LineSeparator />
|
||||||
<div>
|
<div className="flex items-center justify-between pt-6">
|
||||||
{data.inputSchema &&
|
Advanced
|
||||||
generateInputHandles(data.inputSchema, data.uiType)}
|
<Switch
|
||||||
</div>
|
onCheckedChange={toggleAdvancedSettings}
|
||||||
)}
|
checked={isAdvancedOpen}
|
||||||
{isOutputOpen && data.uiType !== BlockUIType.NOTE && (
|
className="mr-5"
|
||||||
<div
|
|
||||||
data-id="latest-output"
|
|
||||||
className="nodrag m-3 break-words rounded-md border-[1.5px] p-2"
|
|
||||||
>
|
|
||||||
{(data.executionResults?.length ?? 0) > 0 ? (
|
|
||||||
<>
|
|
||||||
<DataTable
|
|
||||||
title="Latest Output"
|
|
||||||
truncateLongData
|
|
||||||
data={data.executionResults!.at(-1)?.data || {}}
|
|
||||||
/>
|
/>
|
||||||
<div className="flex justify-end">
|
</div>
|
||||||
<Button variant="ghost" onClick={handleOutputClick}>
|
</>
|
||||||
View More
|
)}
|
||||||
</Button>
|
{/* Output Handles */}
|
||||||
|
{data.uiType !== BlockUIType.NOTE && (
|
||||||
|
<>
|
||||||
|
<LineSeparator />
|
||||||
|
<div className="flex items-start justify-end rounded-b-xl pb-2 pr-2 pt-6">
|
||||||
|
<div className="flex-none">
|
||||||
|
{data.outputSchema &&
|
||||||
|
generateOutputHandles(data.outputSchema, data.uiType)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</div>
|
||||||
) : (
|
</>
|
||||||
<span>No outputs yet</span>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
{/* End Body */}
|
||||||
)}
|
{/* Footer */}
|
||||||
{data.uiType !== BlockUIType.NOTE && (
|
<div className="flex rounded-b-xl">
|
||||||
<div className="mt-2.5 flex items-center pb-4 pl-4">
|
{/* Display Outputs */}
|
||||||
<Switch checked={isOutputOpen} onCheckedChange={toggleOutput} />
|
{isOutputOpen && data.uiType !== BlockUIType.NOTE && (
|
||||||
<span className="m-1 mr-4">Output</span>
|
<div
|
||||||
{hasAdvancedFields && (
|
data-id="latest-output"
|
||||||
<>
|
className={cn(
|
||||||
<Switch onCheckedChange={toggleAdvancedSettings} />
|
"nodrag w-full overflow-hidden break-words",
|
||||||
<span className="m-1">Advanced</span>
|
statusBackgroundClass,
|
||||||
</>
|
)}
|
||||||
)}
|
>
|
||||||
{data.status && (
|
{(data.executionResults?.length ?? 0) > 0 ? (
|
||||||
<Badge
|
<div className="mt-0 rounded-b-xl bg-gray-50">
|
||||||
variant="outline"
|
<LineSeparator />
|
||||||
data-id={`badge-${id}-${data.status}`}
|
<NodeOutputs
|
||||||
className={cn(data.status.toLowerCase(), "ml-auto mr-5")}
|
title="Latest Output"
|
||||||
|
truncateLongData
|
||||||
|
data={data.executionResults!.at(-1)?.data || {}}
|
||||||
|
/>
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={handleOutputClick}
|
||||||
|
className="border border-gray-300"
|
||||||
|
>
|
||||||
|
View More
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="mt-0 min-h-4 rounded-b-xl bg-white"></div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex min-h-12 items-center justify-end",
|
||||||
|
statusBackgroundClass,
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{data.status}
|
<Badge
|
||||||
</Badge>
|
variant="default"
|
||||||
)}
|
className={cn(
|
||||||
</div>
|
"mr-4 flex min-w-[114px] items-center justify-center rounded-3xl text-center text-xs font-semibold",
|
||||||
)}
|
hasConfigErrors || hasOutputError
|
||||||
|
? "border-red-600 bg-red-600 text-white"
|
||||||
|
: {
|
||||||
|
"border-green-600 bg-green-600 text-white":
|
||||||
|
data.status === "COMPLETED",
|
||||||
|
"border-yellow-600 bg-yellow-600 text-white":
|
||||||
|
data.status === "RUNNING",
|
||||||
|
"border-red-600 bg-red-600 text-white":
|
||||||
|
data.status === "FAILED",
|
||||||
|
"border-blue-600 bg-blue-600 text-white":
|
||||||
|
data.status === "QUEUED",
|
||||||
|
"border-gray-600 bg-gray-600 font-black":
|
||||||
|
data.status === "INCOMPLETE",
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{hasConfigErrors || hasOutputError
|
||||||
|
? "Error"
|
||||||
|
: data.status
|
||||||
|
? beautifyString(data.status)
|
||||||
|
: "Not Run"}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<InputModalComponent
|
<InputModalComponent
|
||||||
title={activeKey ? `Enter ${beautifyString(activeKey)}` : undefined}
|
title={activeKey ? `Enter ${beautifyString(activeKey)}` : undefined}
|
||||||
isOpen={isModalOpen}
|
isOpen={isModalOpen}
|
||||||
@@ -665,4 +786,12 @@ export function CustomNode({ data, id, width, height }: NodeProps<CustomNode>) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ContextMenu.Root>
|
||||||
|
<ContextMenu.Trigger>
|
||||||
|
<NodeContent />
|
||||||
|
</ContextMenu.Trigger>
|
||||||
|
</ContextMenu.Root>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -421,7 +421,7 @@ const FlowEditor: React.FC<{
|
|||||||
|
|
||||||
history.push({
|
history.push({
|
||||||
type: "ADD_NODE",
|
type: "ADD_NODE",
|
||||||
payload: { node: newNode.data },
|
payload: { node: { ...newNode, ...newNode.data } },
|
||||||
undo: () => deleteElements({ nodes: [{ id: newNode.id }] }),
|
undo: () => deleteElements({ nodes: [{ id: newNode.id }] }),
|
||||||
redo: () => addNodes(newNode),
|
redo: () => addNodes(newNode),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -31,20 +31,27 @@ const NodeHandle: FC<HandleProps> = ({
|
|||||||
const typeClass = `text-sm ${getTypeTextColor(schema.type || "any")} ${side === "left" ? "text-left" : "text-right"}`;
|
const typeClass = `text-sm ${getTypeTextColor(schema.type || "any")} ${side === "left" ? "text-left" : "text-right"}`;
|
||||||
|
|
||||||
const label = (
|
const label = (
|
||||||
<div className="flex flex-grow flex-col">
|
<div className="flex flex-grow flex-row">
|
||||||
<span className="text-m green -mb-1 text-gray-900">
|
<span className="text-m green flex items-end pr-2 text-gray-900">
|
||||||
{schema.title || beautifyString(keyName)}
|
{schema.title || beautifyString(keyName.toLowerCase())}
|
||||||
{isRequired ? "*" : ""}
|
{isRequired ? "*" : ""}
|
||||||
</span>
|
</span>
|
||||||
<span className={typeClass}>{typeName[schema.type] || "any"}</span>
|
<span className={`${typeClass} flex items-end`}>
|
||||||
|
({typeName[schema.type as keyof typeof typeName] || "any"})
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const dot = (
|
const Dot = ({ className = "" }) => {
|
||||||
<div
|
const color = isConnected
|
||||||
className={`m-1 h-4 w-4 border-2 bg-white ${isConnected ? getTypeBgColor(schema.type || "any") : "border-gray-300"} rounded-full transition-colors duration-100 group-hover:bg-gray-300`}
|
? getTypeBgColor(schema.type || "any")
|
||||||
/>
|
: "border-gray-300";
|
||||||
);
|
return (
|
||||||
|
<div
|
||||||
|
className={`${className} ${color} m-1 h-4 w-4 rounded-full border-2 bg-white transition-colors duration-100 group-hover:bg-gray-300`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
if (side === "left") {
|
if (side === "left") {
|
||||||
return (
|
return (
|
||||||
@@ -53,10 +60,10 @@ const NodeHandle: FC<HandleProps> = ({
|
|||||||
type="target"
|
type="target"
|
||||||
position={Position.Left}
|
position={Position.Left}
|
||||||
id={keyName}
|
id={keyName}
|
||||||
className="background-color: white; border: 2px solid black; width: 15px; height: 15px; border-radius: 50%; bottom: -7px; left: 20%; group -ml-[26px]"
|
className="-ml-[26px]"
|
||||||
>
|
>
|
||||||
<div className="pointer-events-none flex items-center">
|
<div className="pointer-events-none flex items-center">
|
||||||
{dot}
|
<Dot className={`-ml-2 mr-2`} />
|
||||||
{label}
|
{label}
|
||||||
</div>
|
</div>
|
||||||
</Handle>
|
</Handle>
|
||||||
@@ -74,7 +81,7 @@ const NodeHandle: FC<HandleProps> = ({
|
|||||||
>
|
>
|
||||||
<div className="pointer-events-none flex items-center">
|
<div className="pointer-events-none flex items-center">
|
||||||
{label}
|
{label}
|
||||||
{dot}
|
<Dot />
|
||||||
</div>
|
</div>
|
||||||
</Handle>
|
</Handle>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
45
autogpt_platform/frontend/src/components/NodeOutputs.tsx
Normal file
45
autogpt_platform/frontend/src/components/NodeOutputs.tsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { ContentRenderer } from "./ui/render";
|
||||||
|
import { beautifyString } from "@/lib/utils";
|
||||||
|
import * as Separator from "@radix-ui/react-separator";
|
||||||
|
|
||||||
|
type NodeOutputsProps = {
|
||||||
|
title?: string;
|
||||||
|
truncateLongData?: boolean;
|
||||||
|
data: { [key: string]: Array<any> };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function NodeOutputs({
|
||||||
|
title,
|
||||||
|
truncateLongData,
|
||||||
|
data,
|
||||||
|
}: NodeOutputsProps) {
|
||||||
|
return (
|
||||||
|
<div className="m-4 space-y-4">
|
||||||
|
{title && <strong className="mt-2flex">{title}</strong>}
|
||||||
|
{Object.entries(data).map(([pin, dataArray]) => (
|
||||||
|
<div key={pin} className="">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<strong className="mr-2">Pin:</strong>
|
||||||
|
<span>{beautifyString(pin)}</span>
|
||||||
|
</div>
|
||||||
|
<Separator.Root className="my-4 h-[1px] bg-gray-300" />
|
||||||
|
<div className="mt-2">
|
||||||
|
<strong className="mr-2">Data:</strong>
|
||||||
|
<div className="mt-1">
|
||||||
|
{dataArray.map((item, index) => (
|
||||||
|
<React.Fragment key={index}>
|
||||||
|
<ContentRenderer
|
||||||
|
value={item}
|
||||||
|
truncateLongData={truncateLongData}
|
||||||
|
/>
|
||||||
|
{index < dataArray.length - 1 && ", "}
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -26,7 +26,7 @@ const OutputModalComponent: FC<OutputModalProps> = ({
|
|||||||
<div className="nodrag nowheel fixed inset-0 flex items-center justify-center bg-white bg-opacity-60">
|
<div className="nodrag nowheel fixed inset-0 flex items-center justify-center bg-white bg-opacity-60">
|
||||||
<div className="w-[500px] max-w-[90%] rounded-lg border-[1.5px] bg-white p-5">
|
<div className="w-[500px] max-w-[90%] rounded-lg border-[1.5px] bg-white p-5">
|
||||||
<strong>Output Data History</strong>
|
<strong>Output Data History</strong>
|
||||||
<div className="my-2 max-h-[384px] flex-grow overflow-y-auto rounded-md border-[1.5px] p-2">
|
<div className="my-2 max-h-[384px] flex-grow overflow-y-auto rounded-md p-2">
|
||||||
{executionResults.map((data, i) => (
|
{executionResults.map((data, i) => (
|
||||||
<>
|
<>
|
||||||
<DataTable key={i} title={data.execId} data={data.data} />
|
<DataTable key={i} title={data.execId} data={data.data} />
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ const PrimaryActionBar: React.FC<PrimaryActionBarProps> = ({
|
|||||||
onClick={runButtonOnClick}
|
onClick={runButtonOnClick}
|
||||||
size="primary"
|
size="primary"
|
||||||
style={{
|
style={{
|
||||||
background: isRunning ? "#FFB3BA" : "#7544DF",
|
background: isRunning ? "#DF4444" : "#7544DF",
|
||||||
opacity: isDisabled ? 0.5 : 1,
|
opacity: isDisabled ? 0.5 : 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Button } from "./ui/button";
|
import { Button } from "./ui/button";
|
||||||
import { IconMegaphone } from "@/components/ui/icons";
|
import { QuestionMarkCircledIcon } from "@radix-ui/react-icons";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
const TallyPopupSimple = () => {
|
const TallyPopupSimple = () => {
|
||||||
@@ -48,17 +48,22 @@ const TallyPopupSimple = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed bottom-6 right-6 z-50 hidden items-center gap-4 p-3 transition-all duration-300 ease-in-out md:flex">
|
<div className="fixed bottom-1 right-6 z-50 hidden items-center gap-4 p-3 transition-all duration-300 ease-in-out md:flex">
|
||||||
<Button variant="default" onClick={resetTutorial} className="mb-0">
|
<Button
|
||||||
|
variant="default"
|
||||||
|
onClick={resetTutorial}
|
||||||
|
className="font-inter mb-0 h-14 w-28 rounded-2xl bg-[rgba(65,65,64,1)] text-left text-lg font-medium leading-6"
|
||||||
|
>
|
||||||
Tutorial
|
Tutorial
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
className="h-14 w-14 rounded-2xl bg-[rgba(65,65,64,1)]"
|
||||||
variant="default"
|
variant="default"
|
||||||
data-tally-open="3yx2L0"
|
data-tally-open="3yx2L0"
|
||||||
data-tally-emoji-text="👋"
|
data-tally-emoji-text="👋"
|
||||||
data-tally-emoji-animation="wave"
|
data-tally-emoji-animation="wave"
|
||||||
>
|
>
|
||||||
<IconMegaphone size="lg" />
|
<QuestionMarkCircledIcon className="h-6 w-6" />
|
||||||
<span className="sr-only">Reach Out</span>
|
<span className="sr-only">Reach Out</span>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import React, { useState } from "react";
|
|||||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { ToyBrick } from "lucide-react";
|
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
import { beautifyString } from "@/lib/utils";
|
import { beautifyString } from "@/lib/utils";
|
||||||
@@ -12,11 +11,9 @@ import {
|
|||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover";
|
} from "@/components/ui/popover";
|
||||||
import { Block } from "@/lib/autogpt-server-api";
|
import { Block } from "@/lib/autogpt-server-api";
|
||||||
import { PlusIcon } from "@radix-ui/react-icons";
|
import { MagnifyingGlassIcon, PlusIcon } from "@radix-ui/react-icons";
|
||||||
import { IconToyBrick } from "@/components/ui/icons";
|
import { IconToyBrick } from "@/components/ui/icons";
|
||||||
import SchemaTooltip from "@/components/SchemaTooltip";
|
|
||||||
import { getPrimaryCategoryColor } from "@/lib/utils";
|
import { getPrimaryCategoryColor } from "@/lib/utils";
|
||||||
import { Badge } from "@/components/ui/badge";
|
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipContent,
|
TooltipContent,
|
||||||
@@ -55,9 +52,10 @@ export const BlocksControl: React.FC<BlocksControlProps> = ({
|
|||||||
|
|
||||||
// Extract unique categories from blocks
|
// Extract unique categories from blocks
|
||||||
const categories = Array.from(
|
const categories = Array.from(
|
||||||
new Set(
|
new Set([
|
||||||
blocks.flatMap((block) => block.categories.map((cat) => cat.category)),
|
null,
|
||||||
),
|
...blocks.flatMap((block) => block.categories.map((cat) => cat.category)),
|
||||||
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@@ -97,80 +95,93 @@ export const BlocksControl: React.FC<BlocksControlProps> = ({
|
|||||||
side="right"
|
side="right"
|
||||||
sideOffset={22}
|
sideOffset={22}
|
||||||
align="start"
|
align="start"
|
||||||
className="w-[30rem] p-0"
|
className="absolute -top-3 w-[17rem] rounded-xl p-0 md:w-[30rem]"
|
||||||
data-id="blocks-control-popover-content"
|
data-id="blocks-control-popover-content"
|
||||||
>
|
>
|
||||||
<Card className="border-none shadow-md">
|
<Card className="border-none p-3 pb-0 shadow-md">
|
||||||
<CardHeader className="flex flex-col gap-x-8 gap-y-2 p-3 px-2">
|
<CardHeader className="flex flex-col gap-x-8 gap-y-1 p-3 px-2">
|
||||||
<div className="items-center justify-between">
|
<div className="items-center justify-between">
|
||||||
<Label
|
<Label
|
||||||
htmlFor="search-blocks"
|
htmlFor="search-blocks"
|
||||||
className="whitespace-nowrap border-b-2 border-violet-500 text-base font-semibold text-black 2xl:text-xl"
|
className="whitespace-nowrap text-base font-bold text-black 2xl:text-xl"
|
||||||
data-id="blocks-control-label"
|
data-id="blocks-control-label"
|
||||||
>
|
>
|
||||||
Blocks
|
Blocks
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
<Input
|
<div className="relative flex items-center">
|
||||||
id="search-blocks"
|
<MagnifyingGlassIcon className="absolute m-2 h-5 w-5 text-gray-500" />
|
||||||
type="text"
|
<Input
|
||||||
placeholder="Search blocks..."
|
id="search-blocks"
|
||||||
value={searchQuery}
|
type="text"
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
placeholder="Search blocks"
|
||||||
data-id="blocks-control-search-input"
|
value={searchQuery}
|
||||||
/>
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
|
className="rounded-lg px-8 py-5"
|
||||||
|
data-id="blocks-control-search-input"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="mt-2 flex flex-wrap gap-2">
|
<div className="mt-2 flex flex-wrap gap-2">
|
||||||
{categories.map((category) => (
|
{categories.map((category) => {
|
||||||
<Badge
|
const color = getPrimaryCategoryColor([
|
||||||
key={category}
|
{ category: category || "All", description: "" },
|
||||||
variant={
|
]);
|
||||||
selectedCategory === category ? "default" : "outline"
|
const colorClass =
|
||||||
}
|
selectedCategory === category ? `${color}` : "";
|
||||||
className={`cursor-pointer ${getPrimaryCategoryColor([{ category, description: "" }])}`}
|
return (
|
||||||
onClick={() =>
|
<div
|
||||||
setSelectedCategory(
|
key={category}
|
||||||
selectedCategory === category ? null : category,
|
className={`cursor-pointer rounded-xl border px-2 py-2 text-xs font-medium ${colorClass}`}
|
||||||
)
|
onClick={() =>
|
||||||
}
|
setSelectedCategory(
|
||||||
>
|
selectedCategory === category ? null : category,
|
||||||
{beautifyString(category)}
|
)
|
||||||
</Badge>
|
}
|
||||||
))}
|
>
|
||||||
|
{beautifyString(
|
||||||
|
category && category.length > 3
|
||||||
|
? category.toLowerCase()
|
||||||
|
: category || "All",
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="border-t px-1 py-0">
|
<CardContent className="overflow-scroll border-t p-0">
|
||||||
<ScrollArea
|
<ScrollArea
|
||||||
className="h-[60vh]"
|
className="h-[60vh] w-fit"
|
||||||
data-id="blocks-control-scroll-area"
|
data-id="blocks-control-scroll-area"
|
||||||
>
|
>
|
||||||
{filteredBlocks.map((block) => (
|
{filteredBlocks.map((block) => (
|
||||||
<Card
|
<Card
|
||||||
key={block.id}
|
key={block.id}
|
||||||
className="m-2 my-4 flex h-20 border"
|
className="m-2 my-4 flex h-20 cursor-pointer border shadow-none hover:shadow-lg"
|
||||||
data-id={`block-card-${block.id}`}
|
data-id={`block-card-${block.id}`}
|
||||||
onClick={() => addBlock(block.id, block.name)}
|
onClick={() => addBlock(block.id, block.name)}
|
||||||
>
|
>
|
||||||
{/* This div needs to be 10px wide and the same height as the card and be the primary color showing up on top of the card with matching rounded corners */}
|
|
||||||
<div
|
<div
|
||||||
className={`z-20 flex min-w-4 flex-shrink-0 rounded-l-xl border ${getPrimaryCategoryColor(block.categories)}`}
|
className={`-ml-px h-full w-3 rounded-l-xl ${getPrimaryCategoryColor(block.categories)}`}
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<div className="mx-3 flex flex-1 items-center justify-between">
|
<div className="mx-3 flex flex-1 items-center justify-between">
|
||||||
<div className="mr-2 min-w-0 flex-1">
|
<div className="mr-2 min-w-0">
|
||||||
<span
|
<span
|
||||||
className="block truncate text-base font-semibold"
|
className="block truncate pb-1 text-sm font-semibold"
|
||||||
data-id={`block-name-${block.id}`}
|
data-id={`block-name-${block.id}`}
|
||||||
>
|
>
|
||||||
{beautifyString(block.name)}
|
{beautifyString(block.name).replace(/ Block$/, "")}
|
||||||
</span>
|
</span>
|
||||||
<span className="block break-words text-sm font-normal text-gray-500">
|
<span className="block break-words text-xs font-normal text-gray-500">
|
||||||
{block.description}
|
{block.description}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="flex flex-shrink-0 items-center gap-1"
|
className="flex flex-shrink-0 items-center gap-1"
|
||||||
data-id={`block-tooltip-${block.id}`}
|
data-id={`block-tooltip-${block.id}`}
|
||||||
></div>
|
>
|
||||||
|
<PlusIcon className="h-6 w-6 rounded-lg bg-gray-200 stroke-black stroke-[0.5px] p-1" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ function AgentDetailContent({ agent }: { agent: AgentDetailResponse }) {
|
|||||||
{agent.description}
|
{agent.description}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="border-t border-gray-200 px-4 py-5 sm:p-0">
|
<div className="border-t border-gray-300 px-4 py-5 sm:p-0">
|
||||||
<dl className="sm:divide-y sm:divide-gray-200">
|
<dl className="sm:divide-y sm:divide-gray-200">
|
||||||
<div className="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:py-5">
|
<div className="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:py-5">
|
||||||
<dt className="flex items-center text-sm font-medium text-gray-500">
|
<dt className="flex items-center text-sm font-medium text-gray-500">
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect, useCallback } from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { IconRefresh, IconCoin } from "@/components/ui/icons";
|
import { IconRefresh } from "@/components/ui/icons";
|
||||||
import AutoGPTServerAPI from "@/lib/autogpt-server-api";
|
import AutoGPTServerAPI from "@/lib/autogpt-server-api";
|
||||||
|
|
||||||
|
const api = new AutoGPTServerAPI();
|
||||||
|
|
||||||
export default function CreditButton() {
|
export default function CreditButton() {
|
||||||
const [credit, setCredit] = useState<number | null>(null);
|
const [credit, setCredit] = useState<number | null>(null);
|
||||||
const api = new AutoGPTServerAPI();
|
|
||||||
|
|
||||||
const fetchCredit = async () => {
|
const fetchCredit = useCallback(async () => {
|
||||||
const response = await api.getUserCredit();
|
const response = await api.getUserCredit();
|
||||||
setCredit(response.credits);
|
setCredit(response.credits);
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchCredit();
|
fetchCredit();
|
||||||
}, [api]);
|
}, [fetchCredit]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
credit !== null && (
|
credit !== null && (
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
BlockIOStringSubSchema,
|
BlockIOStringSubSchema,
|
||||||
BlockIONumberSubSchema,
|
BlockIONumberSubSchema,
|
||||||
BlockIOBooleanSubSchema,
|
BlockIOBooleanSubSchema,
|
||||||
BlockIOCredentialsSubSchema,
|
|
||||||
} from "@/lib/autogpt-server-api/types";
|
} from "@/lib/autogpt-server-api/types";
|
||||||
import React, { FC, useCallback, useEffect, useState } from "react";
|
import React, { FC, useCallback, useEffect, useState } from "react";
|
||||||
import { Button } from "./ui/button";
|
import { Button } from "./ui/button";
|
||||||
@@ -313,6 +312,18 @@ const NodeCredentialsInput: FC<{
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const InputRef = (value: any): React.RefObject<HTMLInputElement> => {
|
||||||
|
const inputRef = React.useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (inputRef.current && value && inputRef.current.value !== value) {
|
||||||
|
inputRef.current.value = value;
|
||||||
|
}
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
return inputRef;
|
||||||
|
};
|
||||||
|
|
||||||
const NodeKeyValueInput: FC<{
|
const NodeKeyValueInput: FC<{
|
||||||
selfKey: string;
|
selfKey: string;
|
||||||
schema: BlockIOKVSubSchema;
|
schema: BlockIOKVSubSchema;
|
||||||
@@ -384,14 +395,15 @@ const NodeKeyValueInput: FC<{
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn(className, "flex flex-col")}>
|
<div
|
||||||
{displayName && <strong>{displayName}</strong>}
|
className={cn(className, keyValuePairs.length > 0 ? "flex flex-col" : "")}
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
{keyValuePairs.map(({ key, value }, index) => (
|
{keyValuePairs.map(({ key, value }, index) => (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
{key && (
|
{key && (
|
||||||
<NodeHandle
|
<NodeHandle
|
||||||
keyName={getEntryKey(key)}
|
keyName={key}
|
||||||
schema={{ type: "string" }}
|
schema={{ type: "string" }}
|
||||||
isConnected={isConnected(key)}
|
isConnected={isConnected(key)}
|
||||||
isRequired={false}
|
isRequired={false}
|
||||||
@@ -416,7 +428,7 @@ const NodeKeyValueInput: FC<{
|
|||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Value"
|
placeholder="Value"
|
||||||
defaultValue={value ?? ""}
|
ref={InputRef(value ?? "")}
|
||||||
onBlur={(e) =>
|
onBlur={(e) =>
|
||||||
updateKeyValuePairs(
|
updateKeyValuePairs(
|
||||||
keyValuePairs.toSpliced(index, 1, {
|
keyValuePairs.toSpliced(index, 1, {
|
||||||
@@ -445,7 +457,7 @@ const NodeKeyValueInput: FC<{
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<Button
|
<Button
|
||||||
className="w-full"
|
className="rounded-xl bg-gray-200 font-normal text-black hover:text-white"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
updateKeyValuePairs(keyValuePairs.concat({ key: "", value: "" }))
|
updateKeyValuePairs(keyValuePairs.concat({ key: "", value: "" }))
|
||||||
}
|
}
|
||||||
@@ -543,6 +555,7 @@ const NodeArrayInput: FC<{
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<Button
|
<Button
|
||||||
|
className="w-[183p] rounded-xl bg-gray-200 font-normal text-black hover:text-white"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
handleInputChange(selfKey, [...entries, isItemObject ? {} : ""])
|
handleInputChange(selfKey, [...entries, isItemObject ? {} : ""])
|
||||||
}
|
}
|
||||||
@@ -600,13 +613,15 @@ const NodeStringInput: FC<{
|
|||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
id={selfKey}
|
id={selfKey}
|
||||||
defaultValue={schema.secret && value ? "********" : value}
|
ref={InputRef(
|
||||||
|
schema.secret && value ? "*".repeat(value.length) : value,
|
||||||
|
)}
|
||||||
readOnly={schema.secret}
|
readOnly={schema.secret}
|
||||||
placeholder={
|
placeholder={
|
||||||
schema?.placeholder || `Enter ${beautifyString(displayName)}`
|
schema?.placeholder || `Enter ${beautifyString(displayName)}`
|
||||||
}
|
}
|
||||||
onBlur={(e) => handleInputChange(selfKey, e.target.value)}
|
onBlur={(e) => handleInputChange(selfKey, e.target.value)}
|
||||||
className="pr-8 read-only:cursor-pointer read-only:text-gray-500"
|
className="rounded-xl pr-8 read-only:cursor-pointer read-only:text-gray-500"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@@ -658,8 +673,7 @@ export const NodeTextBoxInput: FC<{
|
|||||||
schema?.placeholder || `Enter ${beautifyString(displayName)}`
|
schema?.placeholder || `Enter ${beautifyString(displayName)}`
|
||||||
}
|
}
|
||||||
onChange={(e) => handleInputChange(selfKey, e.target.value)}
|
onChange={(e) => handleInputChange(selfKey, e.target.value)}
|
||||||
onBlur={(e) => handleInputChange(selfKey, e.target.value)}
|
className="h-full w-full resize-none overflow-hidden rounded-xl border-none bg-transparent text-lg text-black outline-none"
|
||||||
className="h-full w-full resize-none overflow-hidden border-none bg-transparent text-lg text-black outline-none"
|
|
||||||
style={{
|
style={{
|
||||||
fontSize: "min(1em, 16px)",
|
fontSize: "min(1em, 16px)",
|
||||||
lineHeight: "1.2",
|
lineHeight: "1.2",
|
||||||
@@ -696,7 +710,7 @@ const NodeNumberInput: FC<{
|
|||||||
<Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
id={selfKey}
|
id={selfKey}
|
||||||
defaultValue={value}
|
ref={InputRef(value)}
|
||||||
onBlur={(e) => handleInputChange(selfKey, parseFloat(e.target.value))}
|
onBlur={(e) => handleInputChange(selfKey, parseFloat(e.target.value))}
|
||||||
placeholder={
|
placeholder={
|
||||||
schema.placeholder || `Enter ${beautifyString(displayName)}`
|
schema.placeholder || `Enter ${beautifyString(displayName)}`
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|||||||
<input
|
<input
|
||||||
type={type}
|
type={type}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-9 w-full rounded-md border border-gray-200 bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-gray-500 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-gray-400 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-800 dark:placeholder:text-gray-400 dark:focus-visible:ring-gray-300",
|
"flex h-9 w-full rounded-md border border-gray-300 bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-gray-500 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-gray-400 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-800 dark:placeholder:text-gray-400 dark:focus-visible:ring-gray-300",
|
||||||
type == "file" ? "pb-0.5 pt-1.5" : "", // fix alignment
|
type == "file" ? "pb-0.5 pt-1.5" : "", // fix alignment
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -45,13 +45,15 @@ const VideoRenderer: React.FC<{ videoUrl: string }> = ({ videoUrl }) => {
|
|||||||
|
|
||||||
const ImageRenderer: React.FC<{ imageUrl: string }> = ({ imageUrl }) => (
|
const ImageRenderer: React.FC<{ imageUrl: string }> = ({ imageUrl }) => (
|
||||||
<div className="w-full p-2">
|
<div className="w-full p-2">
|
||||||
<img
|
<picture>
|
||||||
src={imageUrl}
|
<img
|
||||||
alt="Image"
|
src={imageUrl}
|
||||||
className="h-auto max-w-full"
|
alt="Image"
|
||||||
width="100%"
|
className="h-auto max-w-full"
|
||||||
height="auto"
|
width="100%"
|
||||||
/>
|
height="auto"
|
||||||
|
/>
|
||||||
|
</picture>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -747,6 +747,9 @@ export default function useAgentGraph(
|
|||||||
api,
|
api,
|
||||||
nodes,
|
nodes,
|
||||||
edges,
|
edges,
|
||||||
|
pathname,
|
||||||
|
router,
|
||||||
|
searchParams,
|
||||||
savedAgent,
|
savedAgent,
|
||||||
agentName,
|
agentName,
|
||||||
agentDescription,
|
agentDescription,
|
||||||
|
|||||||
@@ -26,13 +26,10 @@ export function deepEquals(x: any, y: any): boolean {
|
|||||||
ty = typeof y;
|
ty = typeof y;
|
||||||
|
|
||||||
const res =
|
const res =
|
||||||
x &&
|
x && y && tx === ty && tx === "object"
|
||||||
y &&
|
|
||||||
tx === ty &&
|
|
||||||
(tx === "object"
|
|
||||||
? ok(x).length === ok(y).length &&
|
? ok(x).length === ok(y).length &&
|
||||||
ok(x).every((key) => deepEquals(x[key], y[key]))
|
ok(x).every((key) => deepEquals(x[key], y[key]))
|
||||||
: x === y);
|
: x === y;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,21 +177,22 @@ export function removeEmptyStringsAndNulls(obj: any): any {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const categoryColorMap: Record<string, string> = {
|
export const categoryColorMap: Record<string, string> = {
|
||||||
AI: "bg-orange-300/[.7]",
|
AI: "bg-orange-300",
|
||||||
SOCIAL: "bg-yellow-300/[.7]",
|
SOCIAL: "bg-yellow-300",
|
||||||
TEXT: "bg-green-300/[.7]",
|
TEXT: "bg-green-300",
|
||||||
SEARCH: "bg-blue-300/[.7]",
|
SEARCH: "bg-blue-300",
|
||||||
BASIC: "bg-purple-300/[.7]",
|
BASIC: "bg-purple-300",
|
||||||
INPUT: "bg-cyan-300/[.7]",
|
INPUT: "bg-cyan-300",
|
||||||
OUTPUT: "bg-red-300/[.7]",
|
OUTPUT: "bg-red-300",
|
||||||
LOGIC: "bg-teal-300/[.7]",
|
LOGIC: "bg-teal-300",
|
||||||
|
DEVELOPER_TOOLS: "bg-fuchsia-300",
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getPrimaryCategoryColor(categories: Category[]): string {
|
export function getPrimaryCategoryColor(categories: Category[]): string {
|
||||||
if (categories.length === 0) {
|
if (categories.length === 0) {
|
||||||
return "bg-gray-300/[.7]";
|
return "bg-gray-300";
|
||||||
}
|
}
|
||||||
return categoryColorMap[categories[0].category] || "bg-gray-300/[.7]";
|
return categoryColorMap[categories[0].category] || "bg-gray-300";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filterBlocksByType<T>(
|
export function filterBlocksByType<T>(
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user