Compare commits

...

62 Commits

Author SHA1 Message Date
Zamil Majdy
ac6ec23528 Merge branch 'master' of github.com:Significant-Gravitas/AutoGPT into swiftyos/secrt-900-update-block-ui 2024-10-03 04:04:30 +02:00
Zamil Majdy
54aa89d5fe Fix input reset 2024-10-01 13:11:35 +02:00
Zamil Majdy
4bc9a98577 Add input-text fix 2024-10-01 11:29:41 +02:00
Zamil Majdy
b3ed7f7759 Make border darker 2024-10-01 02:40:40 +02:00
Zamil Majdy
82c6be75c1 Makr border darker 2024-10-01 02:37:05 +02:00
Zamil Majdy
8c45e3482c Cleanup executions 2024-10-01 02:13:26 +02:00
Zamil Majdy
0e027b7fff Cleanup executions 2024-10-01 02:13:00 +02:00
Zamil Majdy
c40c62df9f Add border on context menu 2024-10-01 01:24:28 +02:00
Zamil Majdy
b8deb74362 Another patch patch patch 2024-10-01 01:10:17 +02:00
Zamil Majdy
1c50022ef8 Remove another excessive shadows 2024-10-01 00:54:00 +02:00
Zamil Majdy
d296833f59 Remove excessive shadows 2024-10-01 00:49:18 +02:00
Zamil Majdy
f4c1c44207 Add X-scrolling 2024-10-01 00:38:48 +02:00
Zamil Majdy
587b124710 Fix conflicts 2024-10-01 00:24:01 +02:00
Zamil Majdy
dfd30e1d24 Fix paddings 2024-09-30 23:39:47 +02:00
Zamil Majdy
c43ad0a70b Merge branch 'master' of github.com:Significant-Gravitas/AutoGPT into swiftyos/secrt-900-update-block-ui 2024-09-30 22:30:26 +02:00
Zamil Majdy
03c55291d5 fix(platform): Fix unexpected closing block list on tutorial 2024-09-30 22:21:37 +02:00
Zamil Majdy
fe75257bf6 Update coloring 2024-09-30 18:46:23 +02:00
Zamil Majdy
2f670c903d Make inputref reliable 2024-09-30 18:45:12 +02:00
Zamil Majdy
cc8b24adf8 Cleanup context logic 2024-09-30 11:30:09 +02:00
Zamil Majdy
5a06b6e2a2 Merge branch 'master' of github.com:Significant-Gravitas/AutoGPT into swiftyos/secrt-900-update-block-ui 2024-09-30 10:51:34 +02:00
Zamil Majdy
4e3df63dcd fix lint errors 2024-09-30 10:41:31 +02:00
Zamil Majdy
bb24883651 Merge branch 'master' of github.com:Significant-Gravitas/AutoGPT into zamilmajdy/fix-input-on-textarea 2024-09-30 09:58:10 +02:00
Zamil Majdy
1eb9bdb40f Add missing color 2024-09-28 14:37:51 -05:00
Zamil Majdy
83500be782 Address comments 2024-09-27 20:12:02 -05:00
Zamil Majdy
ed91e552d1 Merge branch 'master' of github.com:Significant-Gravitas/AutoGPT into swiftyos/secrt-900-update-block-ui 2024-09-27 17:41:06 -05:00
Zamil Majdy
c3e17f7b30 fix(platform): Fix text area input not updating input field 2024-09-27 17:21:02 -05:00
SwiftyOS
70fcd55084 updated formatting of tutoral and tally buttons 2024-09-27 16:41:27 +02:00
SwiftyOS
7a238f1f4d formatting 2024-09-27 16:26:28 +02:00
SwiftyOS
0c12c69e3b changed cursor on hover in block control panel 2024-09-27 16:26:15 +02:00
SwiftyOS
33f29bbd30 Change Add Item Button Color 2024-09-27 16:22:17 +02:00
SwiftyOS
519de16f0f tidy up 2024-09-27 16:11:07 +02:00
SwiftyOS
d364406954 Added a NodeOutputs component for rendering the outputs 2024-09-27 16:10:37 +02:00
SwiftyOS
ca6b01347e getting error colors just right 2024-09-27 15:41:44 +02:00
SwiftyOS
2155199e6f Changed status badge color 2024-09-27 15:30:19 +02:00
SwiftyOS
dfd324da1f Merge remote-tracking branch 'origin/master' into swiftyos/secrt-900-update-block-ui 2024-09-27 15:21:46 +02:00
SwiftyOS
9b8f7547de Added context menu 2024-09-27 15:19:48 +02:00
SwiftyOS
eadcd7a348 fixed white spot 2024-09-27 15:03:56 +02:00
SwiftyOS
7d5e7e1f04 tweaking css to hide white spot 2024-09-27 14:58:43 +02:00
SwiftyOS
fe7a689e9e formatting 2024-09-27 10:39:50 +02:00
SwiftyOS
0dfbf48024 fix history type issue 2024-09-27 10:39:24 +02:00
SwiftyOS
9e524db279 Merge remote-tracking branch 'origin/zamilmajdy/ui-update' into swiftyos/secrt-900-update-block-ui 2024-09-27 10:03:22 +02:00
Swifty
3f77c407b9 Merge branch 'master' into zamilmajdy/ui-update 2024-09-27 08:27:48 +02:00
Swifty
7b9bffdbab Merge branch 'master' into swiftyos/secrt-900-update-block-ui 2024-09-27 08:22:16 +02:00
SwiftyOS
0fa7e72e0c Change block header so block costs line up correctly 2024-09-27 08:21:47 +02:00
Zamil Majdy
2a20106634 add highlight on navbar button 2024-09-26 13:15:12 -05:00
Zamil Majdy
841bed2c65 block list add border and remove padding 2024-09-26 12:40:35 -05:00
Zamil Majdy
8fd6b88748 Merge branch 'master' into zamilmajdy/ui-update 2024-09-26 12:34:05 -05:00
Zamil Majdy
86c05258fe simplify UI 2024-09-26 12:32:20 -05:00
Zamil Majdy
90d8f4ab7e feat(platform): Sync on new UI design 2024-09-26 12:07:02 -05:00
SwiftyOS
09afc5a670 updated the add property button 2024-09-26 16:56:52 +02:00
SwiftyOS
62e62effc6 modify layout of input items 2024-09-26 16:07:27 +02:00
SwiftyOS
320ead4862 update key name formatting 2024-09-26 15:47:29 +02:00
SwiftyOS
8f9d4adfee update font 2024-09-26 15:41:47 +02:00
SwiftyOS
53e16dca7c formatting 2024-09-26 12:29:02 +02:00
SwiftyOS
b72326e1d3 moved outputs down and disabled toggle 2024-09-26 12:28:41 +02:00
SwiftyOS
778403cd6f updated handle rendering 2024-09-26 12:28:26 +02:00
SwiftyOS
1a2ab80d0b update edge start / end positions 2024-09-26 12:26:50 +02:00
SwiftyOS
433951a668 formatting 2024-09-26 11:57:50 +02:00
SwiftyOS
034f854c0c Modifying layout of nodes 2024-09-26 11:57:22 +02:00
SwiftyOS
ef82903405 Rounding input boxes 2024-09-26 11:57:10 +02:00
SwiftyOS
a165202ae4 modifying how handle text is rendered 2024-09-26 11:57:03 +02:00
SwiftyOS
3aa68bbd9a Updated onOpenChange code style 2024-09-26 10:16:38 +02:00
19 changed files with 1068 additions and 481 deletions

View File

@@ -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",

View File

@@ -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>

View File

@@ -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) ?? {

View File

@@ -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>
);
} }

View File

@@ -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),
}); });

View File

@@ -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>

View 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>
);
}

View File

@@ -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} />

View File

@@ -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,
}} }}
> >

View File

@@ -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>

View File

@@ -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>
))} ))}

View File

@@ -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">

View File

@@ -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 && (

View File

@@ -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)}`

View File

@@ -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,
)} )}

View File

@@ -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>
); );

View File

@@ -747,6 +747,9 @@ export default function useAgentGraph(
api, api,
nodes, nodes,
edges, edges,
pathname,
router,
searchParams,
savedAgent, savedAgent,
agentName, agentName,
agentDescription, agentDescription,

View File

@@ -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