diff --git a/src/frontend/src/CustomNodes/GenericNode/components/NodeDescription/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/NodeDescription/index.tsx
new file mode 100644
index 000000000..81d334b19
--- /dev/null
+++ b/src/frontend/src/CustomNodes/GenericNode/components/NodeDescription/index.tsx
@@ -0,0 +1,100 @@
+import { Textarea } from "@/components/ui/textarea";
+import useFlowsManagerStore from "@/stores/flowsManagerStore";
+import useFlowStore from "@/stores/flowStore";
+import { handleKeyDown } from "@/utils/reactflowUtils";
+import { cn } from "@/utils/utils";
+import { useEffect, useState } from "react";
+import Markdown from "react-markdown";
+
+export default function NodeDescription({
+ description,
+ selected,
+ nodeId,
+}: {
+ description?: string;
+ selected: boolean;
+ nodeId: string;
+}) {
+ const [inputDescription, setInputDescription] = useState(false);
+ const [nodeDescription, setNodeDescription] = useState(description);
+ const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
+ const setNode = useFlowStore((state) => state.setNode);
+
+ useEffect(() => {
+ if (!selected) {
+ setInputDescription(false);
+ }
+ }, [selected]);
+
+ useEffect(() => {
+ setNodeDescription(description);
+ }, [description]);
+
+ return (
+
+ {inputDescription ? (
+
+ );
+}
diff --git a/src/frontend/src/CustomNodes/GenericNode/components/NodeName/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/NodeName/index.tsx
new file mode 100644
index 000000000..c33ec1e46
--- /dev/null
+++ b/src/frontend/src/CustomNodes/GenericNode/components/NodeName/index.tsx
@@ -0,0 +1,78 @@
+import InputComponent from "@/components/inputComponent";
+import ShadTooltip from "@/components/shadTooltipComponent";
+import useFlowsManagerStore from "@/stores/flowsManagerStore";
+import useFlowStore from "@/stores/flowStore";
+import { useEffect, useState } from "react";
+
+export default function NodeName({
+ display_name,
+ selected,
+ nodeId,
+}: {
+ display_name?: string;
+ selected: boolean;
+ nodeId: string;
+}) {
+ const [inputName, setInputName] = useState(false);
+ const [nodeName, setNodeName] = useState(display_name);
+ const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
+ const setNode = useFlowStore((state) => state.setNode);
+
+ useEffect(() => {
+ if (!selected) {
+ setInputName(false);
+ }
+ }, [selected]);
+
+ useEffect(() => {
+ setNodeName(display_name);
+ }, [display_name]);
+
+ return inputName ? (
+
+ {
+ setInputName(false);
+ if (nodeName?.trim() !== "") {
+ setNodeName(nodeName);
+ setNode(nodeId, (old) => ({
+ ...old,
+ data: {
+ ...old.data,
+ node: {
+ ...old.data.node,
+ display_name: nodeName,
+ },
+ },
+ }));
+ } else {
+ setNodeName(display_name);
+ }
+ }}
+ value={nodeName}
+ autoFocus
+ onChange={setNodeName}
+ password={false}
+ blurOnEnter={true}
+ id={`input-title-${display_name}`}
+ />
+
+ ) : (
+
+
+ {
+ setInputName(true);
+ takeSnapshot();
+ event.stopPropagation();
+ event.preventDefault();
+ }}
+ data-testid={"title-" + display_name}
+ className="nodoubleclick generic-node-tooltip-div cursor-text text-primary"
+ >
+ {display_name}
+
+
+
+ );
+}
diff --git a/src/frontend/src/CustomNodes/GenericNode/components/NodeStatus/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/NodeStatus/index.tsx
new file mode 100644
index 000000000..5951b7935
--- /dev/null
+++ b/src/frontend/src/CustomNodes/GenericNode/components/NodeStatus/index.tsx
@@ -0,0 +1,190 @@
+import { getSpecificClassFromBuildStatus } from "@/CustomNodes/helpers/get-class-from-build-status";
+import useIconStatus from "@/CustomNodes/hooks/use-icons-status";
+import useUpdateValidationStatus from "@/CustomNodes/hooks/use-update-validation-status";
+import useValidationStatusString from "@/CustomNodes/hooks/use-validation-status-string";
+import ShadTooltip from "@/components/shadTooltipComponent";
+import { Button } from "@/components/ui/button";
+import {
+ RUN_TIMESTAMP_PREFIX,
+ STATUS_BUILD,
+ STATUS_BUILDING,
+ STATUS_INACTIVE,
+} from "@/constants/constants";
+import { BuildStatus } from "@/constants/enums";
+import { useDarkStore } from "@/stores/darkStore";
+import useFlowStore from "@/stores/flowStore";
+import { useShortcutsStore } from "@/stores/shortcuts";
+import { VertexBuildTypeAPI } from "@/types/api";
+import { classNames } from "@/utils/utils";
+import { useEffect, useState } from "react";
+import { useHotkeys } from "react-hotkeys-hook";
+import IconComponent from "../../../../components/genericIconComponent";
+
+export default function NodeStatus({
+ nodeId,
+ display_name,
+ selected,
+ setBorderColor,
+ frozen,
+ showNode,
+}: {
+ nodeId: string;
+ display_name: string;
+ selected: boolean;
+ setBorderColor: (color: string) => void;
+ frozen?: boolean;
+ showNode: boolean;
+}) {
+ const [validationString, setValidationString] = useState("");
+ const [validationStatus, setValidationStatus] =
+ useState(null);
+ const buildStatus = useFlowStore(
+ (state) => state.flowBuildStatus[nodeId]?.status,
+ );
+ const lastRunTime = useFlowStore(
+ (state) => state.flowBuildStatus[nodeId]?.timestamp,
+ );
+ const iconStatus = useIconStatus(buildStatus, validationStatus);
+ const buildFlow = useFlowStore((state) => state.buildFlow);
+ const isBuilding = useFlowStore((state) => state.isBuilding);
+ const setNode = useFlowStore((state) => state.setNode);
+ const version = useDarkStore((state) => state.version);
+ const isDark = useDarkStore((state) => state.dark);
+
+ function handlePlayWShortcut() {
+ if (buildStatus === BuildStatus.BUILDING || isBuilding || !selected) return;
+ setValidationStatus(null);
+ buildFlow({ stopNodeId: nodeId });
+ }
+
+ const play = useShortcutsStore((state) => state.play);
+ const flowPool = useFlowStore((state) => state.flowPool);
+ useHotkeys(play, handlePlayWShortcut, { preventDefault: true });
+ useValidationStatusString(validationStatus, setValidationString);
+ useUpdateValidationStatus(nodeId, flowPool, setValidationStatus);
+
+ const getBaseBorderClass = (selected) => {
+ let className = selected
+ ? "border border-ring hover:shadow-node"
+ : "border hover:shadow-node";
+ let frozenClass = selected ? "border-ring-frozen" : "border-frozen";
+ return frozen ? frozenClass : className;
+ };
+
+ const getNodeSizeClass = (showNode) =>
+ showNode ? "w-96 rounded-lg" : "w-26 h-26 rounded-full";
+
+ const getNodeBorderClassName = (
+ selected: boolean,
+ showNode: boolean,
+ buildStatus: BuildStatus | undefined,
+ validationStatus: VertexBuildTypeAPI | null,
+ ) => {
+ const specificClassFromBuildStatus = getSpecificClassFromBuildStatus(
+ buildStatus,
+ validationStatus,
+ isDark,
+ );
+
+ const baseBorderClass = getBaseBorderClass(selected);
+ const nodeSizeClass = getNodeSizeClass(showNode);
+ const names = classNames(
+ baseBorderClass,
+ nodeSizeClass,
+ "generic-node-div group/node",
+ specificClassFromBuildStatus,
+ );
+ return names;
+ };
+
+ useEffect(() => {
+ console.log(selected);
+ setBorderColor(
+ getNodeBorderClassName(selected, showNode, buildStatus, validationStatus),
+ );
+ }, [selected, showNode, buildStatus, validationStatus, frozen]);
+
+ useEffect(() => {
+ if (buildStatus === BuildStatus.BUILT && !isBuilding) {
+ setNode(
+ nodeId,
+ (old) => {
+ return {
+ ...old,
+ data: {
+ ...old.data,
+ node: {
+ ...old.data.node,
+ lf_version: version,
+ },
+ },
+ };
+ },
+ false,
+ );
+ }
+ }, [buildStatus, isBuilding]);
+
+ return (
+ <>
+
+
{STATUS_BUILDING}
+ ) : buildStatus === BuildStatus.INACTIVE ? (
+ {STATUS_INACTIVE}
+ ) : !validationStatus ? (
+ {STATUS_BUILD}
+ ) : (
+
+
+ {validationString && (
+
+ {validationString}
+
+ )}
+ {lastRunTime && (
+
+
{RUN_TIMESTAMP_PREFIX}
+
{lastRunTime}
+
+ )}
+
+
+
Duration:
+
+ {validationStatus?.data.duration}
+
+
+
+ )
+ }
+ side="bottom"
+ >
+ {iconStatus}
+
+ {showNode && (
+
+ )}
+
+ >
+ );
+}
diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx
index 09dd6f6a4..e6dde996e 100644
--- a/src/frontend/src/CustomNodes/GenericNode/index.tsx
+++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx
@@ -1,54 +1,41 @@
import emojiRegex from "emoji-regex";
import { useEffect, useMemo, useState } from "react";
import { useHotkeys } from "react-hotkeys-hook";
-import Markdown from "react-markdown";
import { NodeToolbar, useUpdateNodeInternals } from "reactflow";
import IconComponent, {
ForwardedIconComponent,
} from "../../components/genericIconComponent";
-import InputComponent from "../../components/inputComponent";
import ShadTooltip from "../../components/shadTooltipComponent";
import { Button } from "../../components/ui/button";
-import { Textarea } from "../../components/ui/textarea";
-import {
- RUN_TIMESTAMP_PREFIX,
- STATUS_BUILD,
- STATUS_BUILDING,
- STATUS_INACTIVE,
- TOOLTIP_OUTDATED_NODE,
-} from "../../constants/constants";
-import { BuildStatus } from "../../constants/enums";
+import { TOOLTIP_OUTDATED_NODE } from "../../constants/constants";
import { postCustomComponent } from "../../controllers/API";
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
import useAlertStore from "../../stores/alertStore";
-import { useDarkStore } from "../../stores/darkStore";
import useFlowStore from "../../stores/flowStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
import { useShortcutsStore } from "../../stores/shortcuts";
import { useTypesStore } from "../../stores/typesStore";
-import { OutputFieldType, VertexBuildTypeAPI } from "../../types/api";
+import { OutputFieldType } from "../../types/api";
import { NodeDataType } from "../../types/flow";
-import { handleKeyDown, scapedJSONStringfy } from "../../utils/reactflowUtils";
+import { scapedJSONStringfy } from "../../utils/reactflowUtils";
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
import { classNames, cn } from "../../utils/utils";
import { countHandlesFn } from "../helpers/count-handles";
-import { getSpecificClassFromBuildStatus } from "../helpers/get-class-from-build-status";
import { getNodeInputColors } from "../helpers/get-node-input-colors";
import { getNodeOutputColors } from "../helpers/get-node-output-colors";
import useCheckCodeValidity from "../hooks/use-check-code-validity";
import useIconNodeRender from "../hooks/use-icon-render";
-import useIconStatus from "../hooks/use-icons-status";
import useUpdateNodeCode from "../hooks/use-update-node-code";
-import useUpdateValidationStatus from "../hooks/use-update-validation-status";
-import useValidationStatusString from "../hooks/use-validation-status-string";
import getFieldTitle from "../utils/get-field-title";
import sortFields from "../utils/sort-fields";
+import NodeDescription from "./components/NodeDescription";
import NodeInputField from "./components/NodeInputField";
+import NodeName from "./components/NodeName";
import NodeOutputField from "./components/NodeOutputfield";
+import NodeStatus from "./components/NodeStatus";
export default function GenericNode({
data,
-
selected,
}: {
data: NodeDataType;
@@ -56,42 +43,18 @@ export default function GenericNode({
xPos?: number;
yPos?: number;
}): JSX.Element {
- const preventDefault = true;
const types = useTypesStore((state) => state.types);
const templates = useTypesStore((state) => state.templates);
const deleteNode = useFlowStore((state) => state.deleteNode);
- const flowPool = useFlowStore((state) => state.flowPool);
- const buildFlow = useFlowStore((state) => state.buildFlow);
const setNode = useFlowStore((state) => state.setNode);
const updateNodeInternals = useUpdateNodeInternals();
const setErrorData = useAlertStore((state) => state.setErrorData);
- const isDark = useDarkStore((state) => state.dark);
- const version = useDarkStore((state) => state.version);
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
-
- const [inputName, setInputName] = useState(false);
- const [nodeName, setNodeName] = useState(data.node!.display_name);
- const [inputDescription, setInputDescription] = useState(false);
- const [nodeDescription, setNodeDescription] = useState(
- data.node?.description!,
- );
const [isOutdated, setIsOutdated] = useState(false);
const [isUserEdited, setIsUserEdited] = useState(false);
- const buildStatus = useFlowStore(
- (state) => state.flowBuildStatus[data.id]?.status,
- );
- const lastRunTime = useFlowStore(
- (state) => state.flowBuildStatus[data.id]?.timestamp,
- );
- const [validationStatus, setValidationStatus] =
- useState(null);
const [handles, setHandles] = useState(0);
- const [validationString, setValidationString] = useState("");
-
- const iconStatus = useIconStatus(buildStatus, validationStatus);
+ const [borderColor, setBorderColor] = useState("");
const [showNode, setShowNode] = useState(data.showNode ?? true);
- // State for outline color
- const isBuilding = useFlowStore((state) => state.isBuilding);
const updateNodeCode = useUpdateNodeCode(
data?.id,
@@ -118,47 +81,6 @@ export default function GenericNode({
);
};
- const renderIconStatus = () => {
- return {iconStatus}
;
- };
-
- const getNodeBorderClassName = (
- selected: boolean,
- showNode: boolean,
- buildStatus: BuildStatus | undefined,
- validationStatus: VertexBuildTypeAPI | null,
- ) => {
- const specificClassFromBuildStatus = getSpecificClassFromBuildStatus(
- buildStatus,
- validationStatus,
- isDark,
- );
-
- const baseBorderClass = getBaseBorderClass(selected);
- const nodeSizeClass = getNodeSizeClass(showNode);
- const names = classNames(
- baseBorderClass,
- nodeSizeClass,
- "generic-node-div group/node",
- specificClassFromBuildStatus,
- );
- return names;
- };
-
- // const [openWDoubleCLick, setOpenWDoubleCLick] = useState(false);
-
- const getBaseBorderClass = (selected) => {
- let className = selected
- ? "border border-ring hover:shadow-node"
- : "border hover:shadow-node";
- let frozenClass = selected ? "border-ring-frozen" : "border-frozen";
- return data.node?.frozen ? frozenClass : className;
- };
-
- const getNodeSizeClass = (showNode) =>
- showNode ? "w-96 rounded-lg" : "w-26 h-26 rounded-full";
-
- const nameEditable = true;
const isEmoji = emojiRegex().test(data?.node?.icon!);
if (!data.node!.template) {
@@ -174,8 +96,6 @@ export default function GenericNode({
}
useCheckCodeValidity(data, templates, setIsOutdated, setIsUserEdited, types);
- useUpdateValidationStatus(data?.id, flowPool, setValidationStatus);
- useValidationStatusString(validationStatus, setValidationString);
const iconNodeRender = useIconNodeRender(
data,
@@ -197,46 +117,10 @@ export default function GenericNode({
countHandles();
}, [data, data.node]);
- useEffect(() => {
- if (!selected) {
- setInputName(false);
- setInputDescription(false);
- }
- }, [selected]);
-
- useEffect(() => {
- setNodeDescription(data.node!.description);
- }, [data.node!.description]);
-
- useEffect(() => {
- setNodeName(data.node!.display_name);
- }, [data.node!.display_name]);
-
useEffect(() => {
setShowNode(data.showNode ?? true);
}, [data.showNode]);
- useEffect(() => {
- if (buildStatus === BuildStatus.BUILT && !isBuilding) {
- setNode(
- data.id,
- (old) => {
- return {
- ...old,
- data: {
- ...old.data,
- node: {
- ...old.data.node,
- lf_version: version,
- },
- },
- };
- },
- false,
- );
- }
- }, [buildStatus, isBuilding]);
-
const [loadingUpdate, setLoadingUpdate] = useState(false);
const [showHiddenOutputs, setShowHiddenOutputs] = useState(false);
@@ -286,17 +170,8 @@ export default function GenericNode({
const hiddenOutputs =
data.node!.outputs?.filter((output) => output.hidden) ?? [];
- function handlePlayWShortcut() {
- if (buildStatus === BuildStatus.BUILDING || isBuilding || !selected) return;
- setValidationStatus(null);
- buildFlow({ stopNodeId: data.id });
- }
-
const update = useShortcutsStore((state) => state.update);
- const play = useShortcutsStore((state) => state.play);
-
- useHotkeys(update, handleUpdateCodeWShortcut, { preventDefault });
- useHotkeys(play, handlePlayWShortcut, { preventDefault });
+ useHotkeys(update, handleUpdateCodeWShortcut, { preventDefault: true });
const shortcuts = useShortcutsStore((state) => state.shortcuts);
@@ -376,24 +251,55 @@ export default function GenericNode({
isUserEdited,
selected,
shortcuts,
- // openWDoubleCLick,
- // setOpenWDoubleCLick,
]);
+
+ const renderInputParameter = Object.keys(data.node!.template)
+ .filter((templateField) => templateField.charAt(0) !== "_")
+ .sort((a, b) => sortFields(a, b, data.node?.field_order ?? []))
+ .map(
+ (templateField: string, idx) =>
+ data.node!.template[templateField]?.show &&
+ !data.node!.template[templateField]?.advanced && (
+
+ ),
+ );
+
return (
<>
{memoizedNodeToolbarComponent}
- {
- // if (!isWrappedWithClass(event, "nodoubleclick"))
- // setOpenWDoubleCLick(true);
- // }}
- className={getNodeBorderClassName(
- selected,
- showNode,
- buildStatus,
- validationStatus,
- )}
- >
+
{data.node?.beta && showNode && (
BETA
@@ -419,68 +325,25 @@ export default function GenericNode({
{iconNodeRender()}
{showNode && (
- {nameEditable && inputName ? (
-
- {
- setInputName(false);
- if (nodeName.trim() !== "") {
- setNodeName(nodeName);
- setNode(data.id, (old) => ({
- ...old,
- data: {
- ...old.data,
- node: {
- ...old.data.node,
- display_name: nodeName,
- },
- },
- }));
- } else {
- setNodeName(data.node!.display_name);
- }
- }}
- value={nodeName}
- onChange={setNodeName}
- password={false}
- blurOnEnter={true}
- id={`input-title-${data.node?.display_name}`}
- />
-
- ) : (
-
-
- {
- if (nameEditable) {
- setInputName(true);
- }
- takeSnapshot();
- event.stopPropagation();
- event.preventDefault();
- }}
- data-testid={"title-" + data.node?.display_name}
- className="nodoubleclick generic-node-tooltip-div cursor-text text-primary"
- >
- {data.node?.display_name}
-
-
- {isOutdated && !isUserEdited && (
-
-
-
- )}
-
+
+ {isOutdated && !isUserEdited && (
+
+
+
)}
)}
@@ -488,271 +351,34 @@ export default function GenericNode({
{!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 && (
-
- ),
- )}
+ {renderInputParameter}
{shownOutputs &&
shownOutputs.length > 0 &&
renderOutputParameter(shownOutputs[0], 0)}
>
)}
- {showNode && (
- <>
-
-
{STATUS_BUILDING}
- ) : buildStatus === BuildStatus.INACTIVE ? (
- {STATUS_INACTIVE}
- ) : !validationStatus ? (
- {STATUS_BUILD}
- ) : (
-
-
- {validationString && (
-
- {validationString}
-
- )}
- {lastRunTime && (
-
-
{RUN_TIMESTAMP_PREFIX}
-
- {lastRunTime}
-
-
- )}
-
-
-
Duration:
-
- {validationStatus?.data.duration}
-
-
-
- )
- }
- side="bottom"
- >
- {renderIconStatus()}
-
-
-
- >
- )}
+
{showNode && (
-
+
{/* increase height!! */}
-
- {showNode && nameEditable && inputDescription ? (
-
+
<>
- {Object.keys(data.node!.template)
- .filter((templateField) => templateField.charAt(0) !== "_")
- .sort((a, b) => sortFields(a, b, data.node?.field_order ?? []))
- .map((templateField: string, idx) => (
-
- {data.node!.template[templateField]?.show &&
- !data.node!.template[templateField]?.advanced ? (
-
- ) : (
- <>>
- )}
-
- ))}
+ {renderInputParameter}
| React.KeyboardEvent,
- inputValue: string | string[] | null,
+ inputValue: string | string[] | null | undefined,
block: string,
) {
//condition to fix bug control+backspace on Windows/Linux