diff --git a/src/frontend/src/App.css b/src/frontend/src/App.css index fc28a9c83..a7a8bad51 100644 --- a/src/frontend/src/App.css +++ b/src/frontend/src/App.css @@ -6,6 +6,13 @@ text-align: center; } +.react-flow__node { + width: auto; + height: auto; + border-radius: auto; + min-width: inherit; +} + .App-logo { height: 40vmin; pointer-events: none; diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx index 6abdfd3b4..d8f8fdc14 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx @@ -52,6 +52,7 @@ export default function ParameterComponent({ required = false, optionalHandle = null, info = "", + showNode, }: ParameterComponentType): JSX.Element { const ref = useRef(null); const refHtml = useRef(null); @@ -192,7 +193,43 @@ export default function ParameterComponent({ renderTooltips(); }, [tooltipTitle, flow]); - return ( + return !showNode ? ( + left && + (type === "str" || + type === "bool" || + type === "float" || + type === "code" || + type === "prompt" || + type === "file" || + type === "int") && + !optionalHandle ? ( + <> + ) : ( + + + isValidConnection(connection, reactFlowInstance!) + } + className={classNames( + left ? "my-12 -ml-0.5 " : " my-12 -mr-0.5 ", + "h-3 w-3 rounded-full border-2 bg-background" + )} + style={{ + borderColor: color, + top: position, + }} + > + + ) + ) : (
(null); + const [showNode, setShowNode] = useState(true); + const [handles, setHandles] = useState([]); + let numberOfInputs: boolean[] = []; + + function countHandles(): void { + numberOfInputs = Object.keys(data.node!.template) + .filter((templateField) => templateField.charAt(0) !== "_") + .map((templateCamp) => { + const { template } = data.node!; + if (template[templateCamp].input_types) return true; + if (!template[templateCamp].show) return false; + switch (template[templateCamp].type) { + case "str": + return false; + case "bool": + return false; + case "float": + return false; + case "code": + return false; + case "prompt": + return false; + case "file": + return false; + case "int": + return false; + default: + return true; + } + }); + setHandles(numberOfInputs); + } + + useEffect(() => { + countHandles(); + }, []); + // State for outline color const { sseData, isBuilding } = useSSE(); useEffect(() => { @@ -50,8 +87,15 @@ export default function GenericNode({ }); updateFlow(flow); } + countHandles(); }, [data]); + useEffect(() => { + setTimeout(() => { + updateNodeInternals(data.id); + }, 300); + }, [showNode]); + // New useEffect to watch for changes in sseData and update validation status useEffect(() => { const relevantData = sseData[data.id]; @@ -70,192 +114,325 @@ export default function GenericNode({ data={data} setData={setData} deleteNode={deleteNode} + setShowNode={setShowNode} + numberOfHandles={handles} + showNode={showNode} >
- {data.node?.beta && ( + {data.node?.beta && showNode && (
BETA
)} -
-
- -
- -
- {data.node?.display_name} -
-
-
-
-
-
- Building... - ) : !validationStatus ? ( - - Build{" "} - {" "} - flow to validate status. - - ) : ( -
- {typeof validationStatus.params === "string" - ? validationStatus.params - .split("\n") - .map((line: string, index: number) => ( -
{line}
- )) - : ""} -
- ) - } - > -
-
-
-
-
-
-
-
-
- -
- {data.node?.description !== "" && ( -
- {data.node?.description} -
- )} - - <> - {Object.keys(data.node!.template) - .filter((templateField) => templateField.charAt(0) !== "_") - .map((templateField: string, idx) => ( -
- {data.node!.template[templateField].show && - !data.node!.template[templateField].advanced ? ( - - ) : ( - <> - )} -
- ))} +
+
- {" "} -
- 0 - ? data.node.output_types.join("|") - : data.type + className={ + "generic-node-title-arrangement rounded-full" + + (!showNode && "justify-center") } - tooltipTitle={data.node?.base_classes.join("\n")} - id={[data.type, data.id, ...data.node!.base_classes].join("|")} - type={data.node?.base_classes.join("|")} - left={false} - /> - + > + + {showNode && ( +
+ +
+ {data.node?.display_name} +
+
+
+ )} +
+
+ {!showNode && ( + <> + {Object.keys(data.node!.template) + .filter((templateField) => templateField.charAt(0) !== "_") + .map( + (templateField: string, idx) => + data.node!.template[templateField].show && + !data.node!.template[templateField].advanced && ( + + ) + )} + 0 + ? data.node.output_types.join("|") + : data.type + } + tooltipTitle={data.node?.base_classes.join("\n")} + id={[data.type, data.id, ...data.node!.base_classes].join( + "|" + )} + type={data.node?.base_classes.join("|")} + left={false} + showNode={showNode} + /> + + )} +
+ + {showNode && ( +
+
+ Building... + ) : !validationStatus ? ( + + Build{" "} + {" "} + flow to validate status. + + ) : ( +
+ {typeof validationStatus.params === "string" + ? validationStatus.params + .split("\n") + .map((line: string, index: number) => ( +
{line}
+ )) + : ""} +
+ ) + } + > +
+
+
+
+
+
+
+
+ )} +
+ + {showNode && ( +
+ {data.node?.description !== "" && showNode && ( +
+ {data.node?.description} +
+ )} + + <> + {Object.keys(data.node!.template) + .filter((templateField) => templateField.charAt(0) !== "_") + .map((templateField: string, idx) => ( +
+ {data.node!.template[templateField].show && + !data.node!.template[templateField].advanced ? ( + + ) : ( + <> + )} +
+ ))} +
+ {" "} +
+ 0 + ? data.node.output_types.join("|") + : data.type + } + tooltipTitle={data.node?.base_classes.join("\n")} + id={[data.type, data.id, ...data.node!.base_classes].join("|")} + type={data.node?.base_classes.join("|")} + left={false} + showNode={showNode} + /> + +
+ )}
); diff --git a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx index c628e4003..7b782a656 100644 --- a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx @@ -1,5 +1,5 @@ import { useContext, useState } from "react"; -import { useReactFlow } from "reactflow"; +import { useReactFlow, useUpdateNodeInternals } from "reactflow"; import ShadTooltip from "../../../../components/ShadTooltipComponent"; import IconComponent from "../../../../components/genericIconComponent"; import { TabsContext } from "../../../../contexts/tabsContext"; @@ -11,6 +11,9 @@ export default function NodeToolbarComponent({ data, setData, deleteNode, + setShowNode, + numberOfHandles, + showNode, }: nodeToolbarPropsType): JSX.Element { const [nodeLength, setNodeLength] = useState( Object.keys(data.node!.template).filter( @@ -27,6 +30,16 @@ export default function NodeToolbarComponent({ data.node.template[templateField].type === "int") ).length ); + const updateNodeInternals = useUpdateNodeInternals(); + + function canMinimize() { + let countHandles: number = 0; + numberOfHandles.forEach((bool) => { + if (bool) countHandles += 1; + }); + if (countHandles > 1) return false; + return true; + } const { paste } = useContext(TabsContext); const reactFlowInstance = useReactFlow(); @@ -106,7 +119,8 @@ export default function NodeToolbarComponent({ >
+ + {canMinimize() && ( + + + + )}
diff --git a/src/frontend/src/style/applies.css b/src/frontend/src/style/applies.css index 1a2882a71..203c7da69 100644 --- a/src/frontend/src/style/applies.css +++ b/src/frontend/src/style/applies.css @@ -264,10 +264,10 @@ @apply grid w-full gap-4 p-4 md:grid-cols-2 lg:grid-cols-4; } .generic-node-div { - @apply relative flex w-96 flex-col justify-center rounded-lg bg-background; + @apply relative flex flex-col justify-center bg-background; } .generic-node-div-title { - @apply flex w-full items-center justify-between gap-8 rounded-t-lg border-b bg-muted p-4; + @apply flex w-full items-center gap-8 border-b bg-muted p-4; } .generic-node-title-arrangement { @apply flex-max-width items-center truncate; diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index f92a54fd8..be442a4d1 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -46,6 +46,7 @@ export type ParameterComponentType = { dataContext?: typesContextType; optionalHandle?: Array | null; info?: string; + showNode?: boolean; }; export type InputListComponentType = { value: string[]; @@ -198,6 +199,7 @@ export type IconComponentProps = { name: string; className?: string; iconColor?: string; + onClick?: () => void; }; export type InputProps = { @@ -422,6 +424,9 @@ export type nodeToolbarPropsType = { data: NodeDataType; deleteNode: (idx: string) => void; setData: (newState: NodeDataType) => void; + setShowNode: (boolean: any) => void; + numberOfHandles: boolean[] | []; + showNode: boolean; }; export type parsedDataType = { diff --git a/src/frontend/src/utils/styleUtils.ts b/src/frontend/src/utils/styleUtils.ts index 158ab9b36..14bafb78b 100644 --- a/src/frontend/src/utils/styleUtils.ts +++ b/src/frontend/src/utils/styleUtils.ts @@ -45,10 +45,13 @@ import { Link, Lock, LucideSend, + Maximize2, Menu, MessageCircle, MessageSquare, MessagesSquare, + Minimize2, + Minus, MoonIcon, MoreHorizontal, Paperclip, @@ -62,6 +65,7 @@ import { Settings2, Shield, Sparkles, + Square, SunIcon, TerminalSquare, Trash2, @@ -310,4 +314,8 @@ export const nodeIconsLucide: iconsType = { Unplug, BookMarked, ChevronUp, + Minus, + Square, + Minimize2, + Maximize2, };