refactor: handle rendering and filtering functionality (#3512)

This commit is contained in:
Lucas Oliveira 2024-09-13 17:27:30 -03:00 • committed by GitHub
commit 2f1f1808b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 761 additions and 322 deletions

View file

@ -145,6 +145,10 @@ body {
transition-duration: 150ms;
}
.react-flow__edge.running .react-flow__edge-path {
stroke: var(--status-blue) !important;
}
.ag-react-container {
width: 100%;
height: 100%;

View file

@ -0,0 +1,33 @@
import useFlowStore from "@/stores/flowStore";
import { BaseEdge, EdgeProps, getBezierPath, Position } from "reactflow";
export function DefaultEdge({
sourceHandleId,
source,
sourceX,
sourceY,
target,
targetHandleId,
targetX,
targetY,
...props
}: EdgeProps) {
const getNode = useFlowStore((state) => state.getNode);
const sourceNode = getNode(source);
const targetNode = getNode(target);
const sourceXNew = (sourceNode?.position.x ?? 0) + (sourceNode?.width ?? 0);
const targetXNew = targetNode?.position.x ?? 0;
const [edgePath] = getBezierPath({
sourceX: sourceXNew,
sourceY,
sourcePosition: Position.Right,
targetPosition: Position.Left,
targetX: targetXNew,
targetY,
});
return <BaseEdge path={edgePath} {...props} />;
}

View file

@ -1,30 +1,65 @@
import { TOOLTIP_EMPTY } from "../../../../constants/constants";
import useFlowStore from "../../../../stores/flowStore";
import { useTypesStore } from "../../../../stores/typesStore";
import { NodeType } from "../../../../types/flow";
import { groupByFamily } from "../../../../utils/utils";
import TooltipRenderComponent from "../tooltipRenderComponent";
import { convertTestName } from "@/components/storeCardComponent/utils/convert-test-name";
export default function HandleTooltips({
left,
export default function HandleTooltipComponent({
isInput,
tooltipTitle,
colors,
isConnecting,
isCompatible,
isSameNode,
}: {
left: boolean;
nodes: NodeType[];
isInput: boolean;
colors: string[];
tooltipTitle: string;
isConnecting: boolean;
isCompatible: boolean;
isSameNode: boolean;
}) {
const myData = useTypesStore((state) => state.data);
const nodes = useFlowStore((state) => state.nodes);
let groupedObj: any = groupByFamily(myData, tooltipTitle!, left, nodes!);
if (groupedObj && groupedObj.length > 0) {
//@ts-ignore
return groupedObj.map((item, index) => {
return <TooltipRenderComponent index={index} item={item} left={left} />;
});
} else {
//@ts-ignore
return <span data-testid={`empty-tooltip-filter`}>{TOOLTIP_EMPTY}</span>;
}
const tooltips = tooltipTitle.split("\n");
const plural = tooltips.length > 1 ? "s" : "";
return (
<div className="py-1.5 font-medium text-muted-foreground">
{isSameNode ? (
"Can't connect to the same node"
) : (
<div className="flex items-start gap-1.5">
{isConnecting ? (
isCompatible ? (
<span>
<span className="font-semibold text-foreground">Connect</span>{" "}
to
</span>
) : (
<span>Incompatible with</span>
)
) : (
<span className="text-foreground">
{isInput ? `Input${plural}` : `Output${plural}`}:{" "}
</span>
)}
{tooltips.map((word, index) => (
<div
className="rounded-sm px-1.5 text-background"
style={{ backgroundColor: colors[index] }}
data-testid={`${isInput ? "input" : "output"}-tooltip-${convertTestName(word)}`}
>
{word}
</div>
))}
{isConnecting && <span>{isInput ? `input` : `output`}</span>}
</div>
)}
{!isConnecting && (
<div className="mt-2 flex flex-col gap-0.5 text-xs">
<div>
<b>Drag</b> to connect compatible {!isInput ? "inputs" : "outputs"}
</div>
<div>
<b>Select</b> to filter compatible {!isInput ? "inputs" : "outputs"}{" "}
and components
</div>
</div>
)}
</div>
);
}

View file

@ -81,6 +81,7 @@ export default function NodeInputField({
setFilterEdge={setFilterEdge}
showNode={showNode}
testIdComplement={`${data?.type?.toLowerCase()}-${showNode ? "shownode" : "noshownode"}`}
nodeId={data.id}
/>
);

View file

@ -110,6 +110,7 @@ export default function NodeOutputField({
id={id}
title={title}
edges={edges}
nodeId={data.id}
myData={myData}
colors={colors}
setFilterEdge={setFilterEdge}

View file

@ -1,3 +1,6 @@
import { useDarkStore } from "@/stores/darkStore";
import useFlowStore from "@/stores/flowStore";
import { useMemo, useState } from "react";
import { Handle, Position } from "reactflow";
import ShadTooltip from "../../../../components/shadTooltipComponent";
import {
@ -5,7 +8,7 @@ import {
scapedJSONStringfy,
} from "../../../../utils/reactflowUtils";
import { classNames, cn, groupByFamily } from "../../../../utils/utils";
import HandleTooltips from "../HandleTooltipComponent";
import HandleTooltipComponent from "../HandleTooltipComponent";
export default function HandleRenderComponent({
left,
@ -20,6 +23,7 @@ export default function HandleRenderComponent({
setFilterEdge,
showNode,
testIdComplement,
nodeId,
}: {
left: boolean;
nodes: any;
@ -33,17 +37,168 @@ export default function HandleRenderComponent({
setFilterEdge: any;
showNode: any;
testIdComplement?: string;
nodeId: string;
}) {
const setHandleDragging = useFlowStore((state) => state.setHandleDragging);
const setFilterType = useFlowStore((state) => state.setFilterType);
const handleDragging = useFlowStore((state) => state.handleDragging);
const filterType = useFlowStore((state) => state.filterType);
const dark = useDarkStore((state) => state.dark);
const onConnect = useFlowStore((state) => state.onConnect);
const handleMouseUp = () => {
setHandleDragging(undefined);
document.removeEventListener("mouseup", handleMouseUp);
};
const myId = useMemo(
() => scapedJSONStringfy(proxy ? { ...id, proxy } : id),
[id, proxy],
);
const getConnection = useMemo(
() =>
(semiConnection: {
source: string | undefined;
sourceHandle: string | undefined;
target: string | undefined;
targetHandle: string | undefined;
}) => ({
source: semiConnection.source ?? nodeId,
sourceHandle: semiConnection.sourceHandle ?? myId,
target: semiConnection.target ?? nodeId,
targetHandle: semiConnection.targetHandle ?? myId,
}),
[nodeId, myId],
);
const sameDraggingNode = useMemo(
() => (!left ? handleDragging?.target : handleDragging?.source) === nodeId,
[left, handleDragging, nodeId],
);
const ownDraggingHandle = useMemo(
() =>
handleDragging &&
(left ? handleDragging?.target : handleDragging?.source) &&
(left ? handleDragging.targetHandle : handleDragging.sourceHandle) ===
myId,
[handleDragging, left, myId],
);
const sameFilterNode = useMemo(
() => (!left ? filterType?.target : filterType?.source) === nodeId,
[left, filterType, nodeId],
);
const ownFilterHandle = useMemo(
() =>
filterType &&
(left ? filterType?.target : filterType?.source) === nodeId &&
(left ? filterType.targetHandle : filterType.sourceHandle) === myId,
[filterType, left, myId],
);
const sameNode = useMemo(
() => sameDraggingNode || sameFilterNode,
[sameDraggingNode, sameFilterNode],
);
const ownHandle = useMemo(
() => ownDraggingHandle || ownFilterHandle,
[ownDraggingHandle, ownFilterHandle],
);
const draggingOpenHandle = useMemo(
() =>
handleDragging &&
(left ? handleDragging.source : handleDragging.target) &&
!ownDraggingHandle
? isValidConnection(getConnection(handleDragging), nodes, edges)
: false,
[handleDragging, left, ownDraggingHandle, getConnection, nodes, edges],
);
const filterOpenHandle = useMemo(
() =>
filterType &&
(left ? filterType.source : filterType.target) &&
!ownFilterHandle
? isValidConnection(getConnection(filterType), nodes, edges)
: false,
[filterType, left, ownFilterHandle, getConnection, nodes, edges],
);
const openHandle = useMemo(
() => filterOpenHandle || draggingOpenHandle,
[filterOpenHandle, draggingOpenHandle],
);
const filterPresent = useMemo(
() => handleDragging || filterType,
[handleDragging, filterType],
);
const currentFilter = useMemo(
() =>
left
? {
targetHandle: myId,
target: nodeId,
source: undefined,
sourceHandle: undefined,
type: tooltipTitle,
color: colors[0],
}
: {
sourceHandle: myId,
source: nodeId,
target: undefined,
targetHandle: undefined,
type: tooltipTitle,
color: colors[0],
},
[left, myId, nodeId, tooltipTitle, colors],
);
const handleColor = useMemo(
() =>
filterPresent && !(openHandle || ownHandle)
? dark
? "conic-gradient(#374151 0deg 360deg)"
: "conic-gradient(#cbd5e1 0deg 360deg)"
: "conic-gradient(" +
colors
.concat(colors[0])
.map(
(color, index) =>
color +
" " +
((360 / colors.length) * index - 360 / (colors.length * 4)) +
"deg " +
((360 / colors.length) * index + 360 / (colors.length * 4)) +
"deg",
)
.join(" ,") +
")",
[filterPresent, openHandle, ownHandle, dark, colors],
);
const [openTooltip, setOpenTooltip] = useState(false);
return (
<div>
<ShadTooltip
open={openTooltip}
setOpen={setOpenTooltip}
styleClasses={"tooltip-fixed-width custom-scroll nowheel"}
delayDuration={1000}
content={
<HandleTooltips
left={left}
nodes={nodes}
tooltipTitle={tooltipTitle!}
<HandleTooltipComponent
isInput={left}
colors={colors}
tooltipTitle={tooltipTitle}
isConnecting={!!filterPresent && !ownHandle}
isCompatible={openHandle}
isSameNode={sameNode && !ownHandle}
/>
}
side={left ? "left" : "right"}
@ -54,47 +209,73 @@ export default function HandleRenderComponent({
}`}
type={left ? "target" : "source"}
position={left ? Position.Left : Position.Right}
key={scapedJSONStringfy(proxy ? { ...id, proxy } : id)}
id={scapedJSONStringfy(proxy ? { ...id, proxy } : id)}
key={myId}
id={myId}
isValidConnection={(connection) =>
isValidConnection(connection, nodes, edges)
}
className={classNames(
left ? "-ml-0.5" : "-mr-0.5",
"z-20 h-3 w-3 rounded-full border-none bg-background",
`group/handle z-20 h-6 w-6 rounded-full border-none bg-transparent transition-all`,
)}
style={{
background:
"conic-gradient(" +
colors
.concat(colors[0])
.map(
(color, index) =>
color +
" " +
((360 / colors.length) * index -
360 / (colors.length * 4)) +
"deg " +
((360 / colors.length) * index +
360 / (colors.length * 4)) +
"deg",
)
.join(" ,") +
")",
WebkitMaskImage: "radial-gradient(transparent 40%, black 44%)",
maskImage: "radial-gradient(transparent 40%, black 44%)",
}}
onClick={() => {
setFilterEdge(groupByFamily(myData, tooltipTitle!, left, nodes!));
setFilterType(currentFilter);
if (filterOpenHandle && filterType) {
onConnect(getConnection(filterType));
setFilterType(undefined);
setFilterEdge([]);
}
}}
/>
onMouseUp={() => {
setOpenTooltip(false);
}}
onContextMenu={(event) => {
event.preventDefault();
}}
onMouseDown={(event) => {
if (event.button === 0) {
setHandleDragging(currentFilter);
document.addEventListener("mouseup", handleMouseUp);
}
}}
>
<div
className={cn(
"pointer-events-none absolute left-1/2 top-[50%] z-30 flex h-0 w-0 -translate-x-1/2 translate-y-[-50%] items-center justify-center rounded-full bg-background transition-all group-hover/handle:bg-transparent",
filterPresent
? openHandle || ownHandle
? cn(
"h-4 w-4",
ownHandle ? "bg-transparent" : "bg-background",
)
: ""
: "group-hover/node:h-4 group-hover/node:w-4",
)}
></div>
<div
className="pointer-events-none absolute left-1/2 top-[50%] z-10 flex h-3 w-3 -translate-x-1/2 translate-y-[-50%] items-center justify-center rounded-full opacity-50 transition-all"
style={{
background: handleColor,
}}
/>
<div
data-testid={`gradient-handle-${testIdComplement}-${title.toLowerCase()}-${
!showNode ? (left ? "target" : "source") : left ? "left" : "right"
}`}
className={classNames(
`pointer-events-none absolute left-1/2 top-[50%] z-10 flex -translate-x-1/2 translate-y-[-50%] items-center justify-center rounded-full transition-all`,
filterPresent
? openHandle || ownHandle
? cn("h-5 w-5")
: cn("h-1.5 w-1.5")
: cn("h-1.5 w-1.5 group-hover/node:h-5 group-hover/node:w-5"),
)}
style={{
background: handleColor,
}}
/>
</Handle>
</ShadTooltip>
<div
className={cn(
"absolute top-[50%] z-10 h-3 w-3 translate-y-[-50%] rounded-full bg-background",
left ? "-left-[4px] -ml-0.5" : "-right-[4px] -mr-0.5",
)}
/>
</div>
);
}

View file

@ -1,91 +0,0 @@
import React from "react";
import {
INPUT_HANDLER_HOVER,
OUTPUT_HANDLER_HOVER,
} from "../../../../constants/constants";
import {
nodeColors,
nodeIconsLucide,
nodeNames,
} from "../../../../utils/styleUtils";
import { classNames } from "../../../../utils/utils";
const TooltipRenderComponent = ({ item, index, left }) => {
const Icon = nodeIconsLucide[item.family] ?? nodeIconsLucide["unknown"];
return (
<div
key={index}
data-testid={`available-${left ? "input" : "output"}-${item.family}`}
>
{index === 0 && (
<span>{left ? INPUT_HANDLER_HOVER : OUTPUT_HANDLER_HOVER}</span>
)}
<span
key={index}
className={classNames(
index > 0 ? "mt-2 flex items-center" : "mt-3 flex items-center",
)}
>
<div
className="h-5 w-5"
style={{
color: nodeColors[item.family],
}}
>
<Icon
className="h-5 w-5"
strokeWidth={1.5}
style={{
color: nodeColors[item.family] ?? nodeColors.unknown,
}}
/>
</div>
<span
className="ps-2 text-xs text-foreground"
data-testid={`tooltip-${nodeNames[item.family] ?? "Other"}`}
>
{nodeNames[item.family] ?? "Other"}{" "}
{item?.display_name && item?.display_name?.length > 0 ? (
<span
className="text-xs"
data-testid={`tooltip-${item?.display_name}`}
>
{" "}
{item.display_name === "" ? "" : " - "}
{item.display_name.split(", ").length > 2
? item.display_name.split(", ").map((el, index) => (
<React.Fragment key={el + name}>
<span>
{index === item.display_name.split(", ").length - 1
? el
: (el += `, `)}
</span>
</React.Fragment>
))
: item.display_name}
</span>
) : (
<span className="text-xs" data-testid={`tooltip-${item?.type}`}>
{" "}
{item.type === "" ? "" : " - "}
{item.type.split(", ").length > 2
? item.type.split(", ").map((el, index) => (
<React.Fragment key={el + name}>
<span>
{index === item.type.split(", ").length - 1
? el
: (el += `, `)}
</span>
</React.Fragment>
))
: item.type}
</span>
)}
</span>
</span>
</div>
);
};
export default TooltipRenderComponent;

View file

@ -9,12 +9,19 @@ export default function ShadTooltip({
children,
styleClasses,
delayDuration = 500,
open,
setOpen,
}: ShadToolTipType): JSX.Element {
return content ? (
<Tooltip defaultOpen={!children} delayDuration={delayDuration}>
<Tooltip
defaultOpen={!children}
open={open}
onOpenChange={setOpen}
delayDuration={delayDuration}
>
<TooltipTrigger asChild={asChild}>{children}</TooltipTrigger>
<TooltipContent
className={cn(styleClasses, "max-w-96")}
className={cn("max-w-96", styleClasses)}
side={side}
avoidCollisions={false}
sticky="always"

View file

@ -1,3 +1,4 @@
import { DefaultEdge } from "@/CustomEdges";
import NoteNode from "@/CustomNodes/NoteNode";
import IconComponent from "@/components/genericIconComponent";
import LoadingComponent from "@/components/loadingComponent";
@ -8,7 +9,6 @@ import useAutoSaveFlow from "@/hooks/flows/use-autosave-flow";
import useUploadFlow from "@/hooks/flows/use-upload-flow";
import { getNodeRenderType, isSupportedNodeTypes } from "@/utils/utils";
import { ENABLE_MVPS } from "@/customization/feature-flags";
import _, { cloneDeep } from "lodash";
import {
KeyboardEvent,
@ -457,6 +457,8 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
onSelectionDragStart={onSelectionDragStart}
onSelectionEnd={onSelectionEnd}
onSelectionStart={onSelectionStart}
connectionRadius={25}
edgeTypes={{ default: DefaultEdge }}
connectionLineComponent={ConnectionLineComponent}
onDragOver={onDragOver}
onNodeDragStop={onNodeDragStop}

View file

@ -18,7 +18,7 @@ export default function ParentDisclosureComponent({
data-testid={testId}
>
<div className="flex items-baseline gap-1 align-baseline">
<span className="parent-disclosure-title">{title}</span>
<span className="text-sm font-medium">{title}</span>
{beta && (
<div className="h-fit rounded-full bg-beta-background px-2 py-1 text-xs/3 font-semibold text-beta-foreground-soft">
BETA

View file

@ -17,6 +17,7 @@ import { nodeIconsLucide } from "../../../../utils/styleUtils";
import ParentDisclosureComponent from "../ParentDisclosureComponent";
import { SidebarCategoryComponent } from "./SidebarCategoryComponent";
import { SidebarFilterComponent } from "./sidebarFilterComponent";
import { sortKeys } from "./utils";
export default function ExtraSidebar(): JSX.Element {
@ -25,6 +26,7 @@ export default function ExtraSidebar(): JSX.Element {
const getFilterEdge = useFlowStore((state) => state.getFilterEdge);
const setFilterEdge = useFlowStore((state) => state.setFilterEdge);
const hasStore = useStoreStore((state) => state.hasStore);
const filterType = useFlowStore((state) => state.filterType);
const setErrorData = useAlertStore((state) => state.setErrorData);
const [dataFilter, setFilterData] = useState(data);
@ -222,8 +224,18 @@ export default function ExtraSidebar(): JSX.Element {
<div className="side-bar-components-div-arrangement">
<div className="parent-disclosure-arrangement">
<div className="flex items-center gap-4 align-middle">
<span className="parent-disclosure-title">Components</span>
<div className="flex w-full flex-col items-start justify-between gap-2.5">
<span className="text-sm font-medium">Components</span>
{filterType && (
<SidebarFilterComponent
isInput={!!filterType.source}
type={filterType.type}
resetFilters={() => {
setFilterEdge([]);
setFilterData(data);
}}
/>
)}
</div>
</div>
<Separator />

View file

@ -0,0 +1,40 @@
import ForwardedIconComponent from "@/components/genericIconComponent";
import ShadTooltip from "@/components/shadTooltipComponent";
import { Button } from "@/components/ui/button";
export function SidebarFilterComponent({
isInput,
type,
resetFilters,
}: {
isInput: boolean;
type: string;
resetFilters: () => void;
}) {
return (
<div className="mb-0.5 flex w-full items-center justify-between rounded border bg-muted p-1 px-2 text-xs font-medium text-primary">
<div className="flex flex-1 items-center gap-1.5">
<ForwardedIconComponent
name="ListFilter"
className="h-4 w-4 shrink-0 stroke-2"
/>
<div className="flex-1 overflow-hidden truncate">
{isInput ? "Input" : "Output"}: {type}
</div>
</div>
<ShadTooltip
side="right"
styleClasses="max-w-full"
content="Remove filter"
>
<Button unstyled className="shrink-0" onClick={resetFilters}>
<ForwardedIconComponent
name="X"
className="h-4 w-4 stroke-2"
aria-hidden="true"
/>
</Button>
</ShadTooltip>
</div>
);
}

View file

@ -431,6 +431,9 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
});
},
setFilterEdge: (newState) => {
if (newState.length === 0) {
set({ filterType: undefined });
}
set({ getFilterEdge: newState });
},
getFilterEdge: [],
@ -469,8 +472,6 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
targetHandle: scapeJSONParse(connection.targetHandle!),
sourceHandle: scapeJSONParse(connection.sourceHandle!),
},
// style: { stroke: "#555" },
// className: "stroke-foreground stroke-connection",
},
oldEdges,
);
@ -528,6 +529,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
get().updateBuildStatus(ids, BuildStatus.ERROR);
throw new Error("Invalid components");
}
get().updateEdgesRunningByNodes(nodes, true);
}
function handleBuildUpdate(
vertexBuildData: VertexBuildTypeAPI,
@ -631,6 +633,10 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
});
}
}
get().updateEdgesRunningByNodes(
get().nodes.map((n) => n.id),
false,
);
get().setIsBuilding(false);
get().setLockChat(false);
},
@ -653,6 +659,10 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
title:
"There are outdated components in the flow. The error could be related to them.",
});
get().updateEdgesRunningByNodes(
get().nodes.map((n) => n.id),
false,
);
setErrorData({ list, title });
get().setIsBuilding(false);
get().setLockChat(false);
@ -662,7 +672,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
// reference is the id of the vertex or the id of the parent in a group node
.map((element) => element.reference)
.filter(Boolean) as string[];
useFlowStore.getState().updateBuildStatus(idList, BuildStatus.BUILDING);
get().updateBuildStatus(idList, BuildStatus.BUILDING);
},
onValidateNodes: validateSubgraph,
nodes: get().nodes || undefined,
@ -680,6 +690,17 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
viewport: get().reactFlowInstance?.getViewport()!,
};
},
updateEdgesRunningByNodes: (ids: string[], running: boolean) => {
const edges = get().edges;
const newEdges = edges.map((edge) => {
if (ids.includes(edge.source) && ids.includes(edge.target)) {
edge.animated = running;
edge.className = running ? "running" : "";
}
return edge;
});
set({ edges: newEdges });
},
updateVerticesBuild: (
vertices: {
verticesIds: string[];
@ -762,6 +783,15 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
setBuildController: (controller) => {
set({ buildController: controller });
},
handleDragging: undefined,
setHandleDragging: (handleDragging) => {
set({ handleDragging });
},
filterType: undefined,
setFilterType: (filterType) => {
set({ filterType });
},
}));
export default useFlowStore;

View file

@ -231,7 +231,7 @@
@apply fill-chat-trigger-disabled stroke-chat-trigger-disabled stroke-1;
}
.parent-disclosure-arrangement {
@apply flex w-full select-none items-center justify-between bg-background px-3 py-1;
@apply flex w-full select-none items-center justify-between bg-background px-5 py-3;
}
.components-disclosure-arrangement {
@apply -mt-px flex w-full select-none items-center justify-between border-y border-y-input bg-muted px-3 py-2;
@ -240,9 +240,6 @@
/* different color than the non child */
@apply -mt-px flex w-full select-none items-center justify-between border-y border-y-input bg-muted px-3 py-2;
}
.parent-disclosure-title {
@apply p-2 px-2 text-sm font-medium;
}
.components-disclosure-title {
@apply flex items-center text-sm text-primary;
}

View file

@ -169,6 +169,26 @@ textarea[class^="ag-"]:focus {
cursor: grabbing !important;
}
.react-flow__handle-right {
right: 0 !important;
transform: translate(50%, -50%) !important;
}
.react-flow__handle-left {
left: 0 !important;
transform: translate(-50%, -50%) !important;
}
.react-flow__handle-right {
right: 0 !important;
transform: translate(50%, -50%) !important;
}
.react-flow__handle-left {
left: 0 !important;
transform: translate(-50%, -50%) !important;
}
.react-flow__node-noteNode:not(.selected) {
z-index: -1 !important;
}

View file

@ -57,6 +57,8 @@
--hover: #f2f4f5;
--disabled-run: #6366f1;
--filter-foreground: #4f46e5;
--filter-background: #eef2ff;
/* Colors that are shared in dark and light mode */
--blur-shared: #151923de;
--build-trigger: #dc735b;
@ -114,6 +116,9 @@
--destructive: 0 60% 25%; /* hsl(0 60% 25%) */
--destructive-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
--filter-foreground: #eef2ff;
--filter-background: #4e46e599;
--ring: 216 24% 30%; /* hsl(216 24% 30%) */
--radius: 0.5rem;

View file

@ -339,6 +339,8 @@ export type ShadTooltipProps = {
style?: string;
};
export type ShadToolTipType = {
open?: boolean;
setOpen?: (open: boolean) => void;
content?: ReactNode | null;
side?: "top" | "right" | "bottom" | "left";
asChild?: boolean;

View file

@ -180,6 +180,52 @@ export type FlowStoreType = {
edges?: Edge[];
viewport?: Viewport;
}) => void;
handleDragging:
| {
source: string | undefined;
sourceHandle: string | undefined;
target: string | undefined;
targetHandle: string | undefined;
type: string;
color: string;
}
| undefined;
setHandleDragging: (
data:
| {
source: string | undefined;
sourceHandle: string | undefined;
target: string | undefined;
targetHandle: string | undefined;
type: string;
color: string;
}
| undefined,
) => void;
filterType:
| {
source: string | undefined;
sourceHandle: string | undefined;
target: string | undefined;
targetHandle: string | undefined;
type: string;
color: string;
}
| undefined;
setFilterType: (
data:
| {
source: string | undefined;
sourceHandle: string | undefined;
target: string | undefined;
targetHandle: string | undefined;
type: string;
color: string;
}
| undefined,
) => void;
updateEdgesRunningByNodes: (ids: string[], running: boolean) => void;
stopBuilding: () => void;
buildController: AbortController;
setBuildController: (controller: AbortController) => void;

View file

@ -215,6 +215,9 @@ export function isValidConnection(
nodes: Node[],
edges: Edge[],
) {
if (source === target) {
return false;
}
const targetHandleObject: targetHandleType = scapeJSONParse(targetHandle!);
const sourceHandleObject: sourceHandleType = scapeJSONParse(sourceHandle!);
if (

View file

@ -8,6 +8,7 @@ import {
AlertTriangle,
ArrowBigUp,
ArrowLeft,
ArrowRight,
ArrowUpToLine,
Bell,
Binary,
@ -63,6 +64,7 @@ import {
FileText,
FileType2,
FileUp,
Filter,
FlaskConical,
FolderIcon,
FolderPlus,
@ -87,6 +89,7 @@ import {
Layers,
Link,
Link2,
ListFilter,
Loader2,
Lock,
LogIn,
@ -401,10 +404,12 @@ export const nodeIconsLucide: iconsType = {
GoogleSearchRun: GoogleIcon,
Google: GoogleIcon,
GoogleGenerativeAI: GoogleGenerativeAIIcon,
ArrowRight,
Groq: GroqIcon,
HCD: HCDIcon,
HNLoader: HackerNewsIcon,
Unstructured: UnstructuredIcon,
Filter: Filter,
HuggingFaceHub: HuggingFaceIcon,
HuggingFace: HuggingFaceIcon,
HuggingFaceEmbeddings: HuggingFaceIcon,
@ -414,6 +419,7 @@ export const nodeIconsLucide: iconsType = {
Meta: MetaIcon,
CheckCheck,
Midjorney: MidjourneyIcon,
ListFilter,
MongoDBAtlasVectorSearch: MongoDBIcon,
MongoDB: MongoDBIcon,
MongoDBChatMessageHistory: MongoDBIcon,

Some files were not shown because too many files have changed in this diff Show more