mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
feat(frontend): add UI for sticky notes on new builder (#11123)
Currently, the new builder doesn’t support sticky notes. We’re rendering them as normal nodes with an input, which is why I’ve added a UI for this. <img width="1512" height="982" alt="Screenshot 2025-10-08 at 4 12 58 PM" src="https://github.com/user-attachments/assets/be716e45-71c6-4cc4-81ba-97313426222f" /> To add sticky notes, go to the search menu of the block menu and search for “Note block”. Then, add them from there. ### Changes 🏗️ - Updated CustomNodeData to include uiType. - Conditional rendering in CustomNode based on uiType. - Added a custom sticky note UI component called `StickyNoteBlock.tsx`. - Adjusted FormCreator and FieldTemplate to pass and utilize uiType. - Enhanced TextInputWidget to render differently based on uiType. ### Checklist 📋 #### For code changes: - [x] I have clearly listed my changes in the PR description - [x] I have made a test plan - [x] I have tested my changes according to the test plan: - [x] Able to attach sticky notes to the builder. - [x] Able to accurately capture data while writing on sticky notes and data is persistent also
This commit is contained in:
@@ -9,6 +9,8 @@ import { preprocessInputSchema } from "../processors/input-schema-pre-processor"
|
||||
import { OutputHandler } from "./OutputHandler";
|
||||
import { useNodeStore } from "../../../stores/nodeStore";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { BlockUIType } from "../../types";
|
||||
import { StickyNoteBlock } from "./StickyNoteBlock";
|
||||
|
||||
export type CustomNodeData = {
|
||||
hardcodedValues: {
|
||||
@@ -18,6 +20,7 @@ export type CustomNodeData = {
|
||||
description: string;
|
||||
inputSchema: RJSFSchema;
|
||||
outputSchema: RJSFSchema;
|
||||
uiType: BlockUIType;
|
||||
};
|
||||
|
||||
export type CustomNode = XYNode<CustomNodeData, "custom">;
|
||||
@@ -29,6 +32,10 @@ export const CustomNode: React.FC<NodeProps<CustomNode>> = React.memo(
|
||||
);
|
||||
const setShowAdvanced = useNodeStore((state) => state.setShowAdvanced);
|
||||
|
||||
if (data.uiType === BlockUIType.NOTE) {
|
||||
return <StickyNoteBlock selected={selected} data={data} id={id} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
@@ -51,6 +58,7 @@ export const CustomNode: React.FC<NodeProps<CustomNode>> = React.memo(
|
||||
<FormCreator
|
||||
jsonSchema={preprocessInputSchema(data.inputSchema)}
|
||||
nodeId={id}
|
||||
uiType={data.uiType}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -7,9 +7,18 @@ import { fields } from "./fields";
|
||||
import { templates } from "./templates";
|
||||
import { uiSchema } from "./uiSchema";
|
||||
import { useNodeStore } from "../../../stores/nodeStore";
|
||||
import { BlockUIType } from "../../types";
|
||||
|
||||
export const FormCreator = React.memo(
|
||||
({ jsonSchema, nodeId }: { jsonSchema: RJSFSchema; nodeId: string }) => {
|
||||
({
|
||||
jsonSchema,
|
||||
nodeId,
|
||||
uiType,
|
||||
}: {
|
||||
jsonSchema: RJSFSchema;
|
||||
nodeId: string;
|
||||
uiType: BlockUIType;
|
||||
}) => {
|
||||
const updateNodeData = useNodeStore((state) => state.updateNodeData);
|
||||
const handleChange = ({ formData }: any) => {
|
||||
updateNodeData(nodeId, { hardcodedValues: formData });
|
||||
@@ -22,7 +31,7 @@ export const FormCreator = React.memo(
|
||||
fields={fields}
|
||||
templates={templates}
|
||||
widgets={widgets}
|
||||
formContext={{ nodeId: nodeId }}
|
||||
formContext={{ nodeId: nodeId, uiType: uiType }}
|
||||
onChange={handleChange}
|
||||
uiSchema={uiSchema}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import { useMemo } from "react";
|
||||
import { FormCreator } from "./FormCreator";
|
||||
import { preprocessInputSchema } from "../processors/input-schema-pre-processor";
|
||||
import { CustomNodeData } from "./CustomNode";
|
||||
import { Text } from "@/components/atoms/Text/Text";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type StickyNoteBlockType = {
|
||||
selected: boolean;
|
||||
data: CustomNodeData;
|
||||
id: string;
|
||||
};
|
||||
|
||||
export const StickyNoteBlock = ({ data, id }: StickyNoteBlockType) => {
|
||||
const { angle, color } = useMemo(() => {
|
||||
const hash = id.split("").reduce((acc, char) => {
|
||||
return char.charCodeAt(0) + ((acc << 5) - acc);
|
||||
}, 0);
|
||||
|
||||
const colors = [
|
||||
"bg-orange-200",
|
||||
"bg-red-200",
|
||||
"bg-yellow-200",
|
||||
"bg-green-200",
|
||||
"bg-blue-200",
|
||||
"bg-purple-200",
|
||||
"bg-pink-200",
|
||||
];
|
||||
|
||||
return {
|
||||
angle: (hash % 7) - 3,
|
||||
color: colors[Math.abs(hash) % colors.length],
|
||||
};
|
||||
}, [id]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"relative h-76 w-76 p-4 text-black shadow-[rgba(0,0,0,0.3)_-2px_5px_5px_0px]",
|
||||
color,
|
||||
)}
|
||||
style={{ transform: `rotate(${angle}deg)` }}
|
||||
>
|
||||
<Text variant="h3" className="tracking-tight text-slate-800">
|
||||
Notes #{id}
|
||||
</Text>
|
||||
<FormCreator
|
||||
jsonSchema={preprocessInputSchema(data.inputSchema)}
|
||||
nodeId={id}
|
||||
uiType={data.uiType}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
toDisplayName,
|
||||
} from "../fields/CredentialField/helpers";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { BlockUIType } from "@/lib/autogpt-server-api";
|
||||
|
||||
const FieldTemplate: React.FC<FieldTemplateProps> = ({
|
||||
id,
|
||||
@@ -65,6 +66,10 @@ const FieldTemplate: React.FC<FieldTemplateProps> = ({
|
||||
|
||||
const { displayType, colorClass } = getTypeDisplayInfo(schema);
|
||||
|
||||
if (formContext.uiType === BlockUIType.NOTE) {
|
||||
return <div className="w-full space-y-1">{children}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mt-4 w-[400px] space-y-1">
|
||||
{label && schema.type && (
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { WidgetProps } from "@rjsf/utils";
|
||||
import { InputType, mapJsonSchemaTypeToInputType } from "../helpers";
|
||||
import { Input } from "@/components/atoms/Input/Input";
|
||||
import { BlockUIType } from "@/lib/autogpt-server-api/types";
|
||||
|
||||
export const TextInputWidget = (props: WidgetProps) => {
|
||||
const { schema } = props;
|
||||
const { schema, formContext } = props;
|
||||
const { uiType } = formContext as { uiType: BlockUIType };
|
||||
|
||||
const mapped = mapJsonSchemaTypeToInputType(schema);
|
||||
|
||||
type InputConfig = {
|
||||
@@ -50,6 +53,25 @@ export const TextInputWidget = (props: WidgetProps) => {
|
||||
return props.onChange(config.handleChange(v));
|
||||
};
|
||||
|
||||
if (uiType === BlockUIType.NOTE) {
|
||||
return (
|
||||
<Input
|
||||
id={props.id}
|
||||
hideLabel={true}
|
||||
type={"textarea"}
|
||||
label={""}
|
||||
size="small"
|
||||
wrapperClassName="mb-0"
|
||||
value={props.value ?? ""}
|
||||
className="!h-[230px] resize-none rounded-none border-none bg-transparent p-0 placeholder:text-black/60 focus:ring-0"
|
||||
onChange={handleChange}
|
||||
placeholder={"Write your note here..."}
|
||||
required={props.required}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Input
|
||||
id={props.id}
|
||||
@@ -59,7 +81,7 @@ export const TextInputWidget = (props: WidgetProps) => {
|
||||
size="small"
|
||||
wrapperClassName="mb-0"
|
||||
value={props.value ?? ""}
|
||||
onChange={handleChange as any}
|
||||
onChange={handleChange}
|
||||
placeholder={schema.placeholder || config.placeholder}
|
||||
required={props.required}
|
||||
disabled={props.disabled}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { BlockInfo } from "@/app/api/__generated__/models/blockInfo";
|
||||
import { CustomNodeData } from "./FlowEditor/nodes/CustomNode";
|
||||
import { BlockUIType } from "./types";
|
||||
|
||||
export const convertBlockInfoIntoCustomNodeData = (block: BlockInfo) => {
|
||||
const customNodeData: CustomNodeData = {
|
||||
@@ -8,6 +9,7 @@ export const convertBlockInfoIntoCustomNodeData = (block: BlockInfo) => {
|
||||
description: block.description,
|
||||
inputSchema: block.inputSchema,
|
||||
outputSchema: block.outputSchema,
|
||||
uiType: block.uiType as BlockUIType,
|
||||
};
|
||||
return customNodeData;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// Currently I am using it, but we will transfer it to the backend, so we can have automated created types
|
||||
export enum BlockUIType {
|
||||
STANDARD = "Standard",
|
||||
INPUT = "Input",
|
||||
OUTPUT = "Output",
|
||||
NOTE = "Note",
|
||||
WEBHOOK = "Webhook",
|
||||
WEBHOOK_MANUAL = "Webhook (manual)",
|
||||
AGENT = "Agent",
|
||||
AI = "AI",
|
||||
AYRSHARE = "Ayrshare",
|
||||
}
|
||||
Reference in New Issue
Block a user