feat: updated components header styling (#8085)

* Removed unused styles

* Updated node icon to follow design

* Updated node name to follow design and include Beta

* Removed Beta from node status

* Removed unused classes and parameters from GenericNode

* Changed node description padding on input

* Changed paddings and gaps

* removed unused classes

* Added accent purple foreground color to Experimental

* Fixed classes and gaps in generic node

* Fixed node name gaps

* Fixed node status classes and styling

* Removed unused classes and changed run-bg size

* Changed test to use new test id

* Changed Node Name to have beta tooltip

* Changed Build Failed icon to be a circle alert

* Changed Node Status gap and conditions to show spacings correctly

* Changed padding to not change height of other components

* Changed nodeStatus to show validation on small node

* Changed classes to show correct spacing and overflow

* Changed description size

* Fixed description text size

* Fixed input margin

* Fixed description editing not appearing when no description is available

* Fixed status not breaking words

* Updated colors

* Updated node output color

* [autofix.ci] apply automated fixes

* Changed duration style in chat

* Re-added output color

* Updated timeout on mcp server tab test

* Added more timeout to mcp server tab test

* fixed loop component test

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Lucas Oliveira 2025-05-22 15:32:16 -03:00 • committed by GitHub
commit 4f9e2705d8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 148 additions and 221 deletions

View file

@ -78,7 +78,8 @@ export default function NodeDescription({
<MemoizedMarkdown <MemoizedMarkdown
linkTarget="_blank" linkTarget="_blank"
className={cn( className={cn(
"markdown prose flex w-full flex-col text-mmd leading-5 word-break-break-word [&_pre]:whitespace-break-spaces [&_pre]:!bg-code-description-background [&_pre_code]:!bg-code-description-background", "markdown prose flex w-full flex-col leading-5 word-break-break-word [&_pre]:whitespace-break-spaces [&_pre]:!bg-code-description-background [&_pre_code]:!bg-code-description-background",
stickyNote ? "text-mmd" : "text-xs",
mdClassName, mdClassName,
)} )}
> >
@ -153,7 +154,8 @@ export default function NodeDescription({
<Textarea <Textarea
maxLength={charLimit} maxLength={charLimit}
className={cn( className={cn(
"nowheel h-full w-full p-0 text-mmd focus:border-primary focus:ring-0", "nowheel w-full text-xs focus:border-primary focus:ring-0",
stickyNote ? "p-0" : "px-2 py-0.5",
inputClassName, inputClassName,
)} )}
autoFocus autoFocus
@ -183,7 +185,7 @@ export default function NodeDescription({
data-testid="generic-node-desc" data-testid="generic-node-desc"
ref={overflowRef} ref={overflowRef}
className={cn( className={cn(
"nodoubleclick generic-node-desc-text h-full cursor-grab text-mmd text-muted-foreground word-break-break-word", "nodoubleclick generic-node-desc-text h-full cursor-grab text-muted-foreground word-break-break-word",
description === "" || !description ? "font-light italic" : "", description === "" || !description ? "font-light italic" : "",
placeholderClassName, placeholderClassName,
)} )}

View file

@ -1,7 +1,8 @@
import ForwardedIconComponent from "@/components/common/genericIconComponent";
import ShadTooltip from "@/components/common/shadTooltipComponent";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import useFlowsManagerStore from "@/stores/flowsManagerStore"; import useFlowsManagerStore from "@/stores/flowsManagerStore";
import useFlowStore from "@/stores/flowStore"; import useFlowStore from "@/stores/flowStore";
import { VertexBuildTypeAPI } from "@/types/api";
import { cn } from "@/utils/utils"; import { cn } from "@/utils/utils";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
@ -10,8 +11,6 @@ export default function NodeName({
selected, selected,
nodeId, nodeId,
showNode, showNode,
validationStatus,
isOutdated,
beta, beta,
editNameDescription, editNameDescription,
toggleEditNameDescription, toggleEditNameDescription,
@ -21,8 +20,6 @@ export default function NodeName({
selected?: boolean; selected?: boolean;
nodeId: string; nodeId: string;
showNode: boolean; showNode: boolean;
validationStatus: VertexBuildTypeAPI | null;
isOutdated: boolean;
beta: boolean; beta: boolean;
editNameDescription: boolean; editNameDescription: boolean;
toggleEditNameDescription: () => void; toggleEditNameDescription: () => void;
@ -77,7 +74,7 @@ export default function NodeName({
}; };
return editNameDescription ? ( return editNameDescription ? (
<div className="m-[1px] w-full"> <div className="w-full">
<Input <Input
onBlur={handleBlur} onBlur={handleBlur}
value={nodeName} value={nodeName}
@ -85,33 +82,36 @@ export default function NodeName({
onChange={onChange} onChange={onChange}
data-testid={`input-title-${display_name}`} data-testid={`input-title-${display_name}`}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
className="py-1" className="px-2 py-0"
/> />
</div> </div>
) : ( ) : (
<div className="group flex w-full items-center gap-1"> <div className="group my-px flex flex-1 items-center gap-2 overflow-hidden">
<div <div
data-testid={"title-" + display_name} data-testid={"title-" + display_name}
className={cn( className={cn(
"nodoubleclick w-full truncate font-medium text-primary", "nodoubleclick truncate font-medium text-primary",
showNode ? "cursor-text" : "cursor-default", showNode ? "cursor-text" : "cursor-default",
)} )}
> >
<div className="flex cursor-grab items-center gap-2"> <div className="flex cursor-grab items-center gap-2">
<span <span className={cn("cursor-grab truncate text-sm")}>
className={cn(
"max-w-44 cursor-grab truncate text-sm",
validationStatus?.data?.duration && "max-w-36",
beta && "max-w-36",
validationStatus?.data?.duration && beta && "max-w-20",
isOutdated && "max-w-40",
!showNode && "max-w-28",
)}
>
{display_name} {display_name}
</span> </span>
</div> </div>
</div> </div>
{beta && (
<div className="shrink-0">
<ShadTooltip content="Beta component">
<div className="flex h-4 w-4 items-center justify-center rounded-sm border border-accent-purple-foreground p-0.5">
<ForwardedIconComponent
name="FlaskConical"
className="text-accent-purple-foreground"
/>
</div>
</ShadTooltip>
</div>
)}
</div> </div>
); );
} }

View file

@ -381,7 +381,7 @@ function NodeOutputField({
<div <div
ref={ref} ref={ref}
className={cn( className={cn(
"relative mt-1 flex h-11 w-full flex-wrap items-center justify-between bg-muted px-5 py-2", "relative flex h-11 w-full flex-wrap items-center justify-between bg-muted px-5 py-2",
lastOutput ? "rounded-b-[0.69rem]" : "", lastOutput ? "rounded-b-[0.69rem]" : "",
isToolMode && "bg-primary", isToolMode && "bg-primary",
)} )}

View file

@ -30,9 +30,11 @@ const ValidationDetails = ({
validationStatus, validationStatus,
}) => ( }) => (
<div className="max-h-100 px-1 py-2.5"> <div className="max-h-100 px-1 py-2.5">
<div className="flex max-h-80 flex-col gap-2 overflow-auto"> <div className="flex max-h-80 flex-col gap-2">
{validationString && ( {validationString && (
<div className="text-sm text-foreground">{validationString}</div> <div className="break-words text-sm text-foreground">
{validationString}
</div>
)} )}
{lastRunTime && ( {lastRunTime && (
<TimeStamp prefix={RUN_TIMESTAMP_PREFIX} time={lastRunTime} /> <TimeStamp prefix={RUN_TIMESTAMP_PREFIX} time={lastRunTime} />

View file

@ -1,9 +1,7 @@
import ShadTooltip from "@/components/common/shadTooltipComponent"; import ShadTooltip from "@/components/common/shadTooltipComponent";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { ICON_STROKE_WIDTH } from "@/constants/constants"; import { ICON_STROKE_WIDTH } from "@/constants/constants";
import { BuildStatus, EventDeliveryType } from "@/constants/enums"; import { BuildStatus } from "@/constants/enums";
import { useGetConfig } from "@/controllers/API/queries/config/use-get-config";
import { usePostTemplateValue } from "@/controllers/API/queries/nodes/use-post-template-value"; import { usePostTemplateValue } from "@/controllers/API/queries/nodes/use-post-template-value";
import { track } from "@/customization/utils/analytics"; import { track } from "@/customization/utils/analytics";
import { customOpenNewTab } from "@/customization/utils/custom-open-new-tab"; import { customOpenNewTab } from "@/customization/utils/custom-open-new-tab";
@ -21,7 +19,6 @@ import { VertexBuildTypeAPI } from "@/types/api";
import { NodeDataType } from "@/types/flow"; import { NodeDataType } from "@/types/flow";
import { findLastNode } from "@/utils/reactflowUtils"; import { findLastNode } from "@/utils/reactflowUtils";
import { classNames, cn } from "@/utils/utils"; import { classNames, cn } from "@/utils/utils";
import { Check } from "lucide-react";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { useHotkeys } from "react-hotkeys-hook"; import { useHotkeys } from "react-hotkeys-hook";
import IconComponent from "../../../../components/common/genericIconComponent"; import IconComponent from "../../../../components/common/genericIconComponent";
@ -81,6 +78,12 @@ export default function NodeStatus({
buildStatus === BuildStatus.BUILT || buildStatus === BuildStatus.BUILT ||
(buildStatus !== BuildStatus.TO_BUILD && validationStatus?.valid); (buildStatus !== BuildStatus.TO_BUILD && validationStatus?.valid);
const conditionError = buildStatus === BuildStatus.ERROR;
const conditionInactive = buildStatus === BuildStatus.INACTIVE;
const showNodeStatus =
conditionSuccess || conditionError || conditionInactive;
const lastRunTime = useFlowStore( const lastRunTime = useFlowStore(
(state) => state.flowBuildStatus[nodeId_]?.timestamp, (state) => state.flowBuildStatus[nodeId_]?.timestamp,
); );
@ -249,7 +252,6 @@ export default function NodeStatus({
} }
}, [buildStatus, isBuilding]); }, [buildStatus, isBuilding]);
const divRef = useRef<HTMLDivElement>(null);
const [isHovered, setIsHovered] = useState(false); const [isHovered, setIsHovered] = useState(false);
const stopBuilding = useFlowStore((state) => state.stopBuilding); const stopBuilding = useFlowStore((state) => state.stopBuilding);
@ -275,8 +277,8 @@ export default function NodeStatus({
: "Play"; : "Play";
const iconClasses = cn( const iconClasses = cn(
"play-button-icon", "h-3.5 w-3.5 transition-all group-hover/node:opacity-100",
isHovered ? "text-foreground" : "text-placeholder-foreground", isHovered ? "text-foreground" : "text-muted-foreground",
BuildStatus.BUILDING === buildStatus && BuildStatus.BUILDING === buildStatus &&
(isHovered ? "text-status-red" : "animate-spin"), (isHovered ? "text-status-red" : "animate-spin"),
); );
@ -307,7 +309,7 @@ export default function NodeStatus({
isPolling: boolean, isPolling: boolean,
): string => { ): string => {
return cn( return cn(
"nodrag button-run-bg hit-area-icon group relative h-5 w-5 rounded-sm border border-accent-amber-foreground transition-colors hover:bg-accent-amber", "nodrag button-run-bg group relative h-4 w-4 p-0.5 rounded-sm border border-accent-amber-foreground transition-colors hover:bg-accent-amber",
connectionLink === "error" connectionLink === "error"
? "border-destructive text-destructive" ? "border-destructive text-destructive"
: isAuthenticated && !isPolling : isAuthenticated && !isPolling
@ -327,7 +329,7 @@ export default function NodeStatus({
isPolling: boolean, isPolling: boolean,
): string => { ): string => {
return cn( return cn(
"h-3 w-3 transition-opacity", "transition-opacity h-2.5 w-2.5",
connectionLink === "error" connectionLink === "error"
? "text-destructive" ? "text-destructive"
: isAuthenticated && !isPolling : isAuthenticated && !isPolling
@ -348,56 +350,47 @@ export default function NodeStatus({
return `button_disconnected_${display_name.toLowerCase()}`; return `button_disconnected_${display_name.toLowerCase()}`;
}; };
return showNode ? ( return (
<> <div className="flex shrink-0 items-center gap-2">
<div className="flex flex-shrink-0 items-center gap-1"> {(showNodeStatus || nodeAuth) && (
<div className="flex items-center gap-2 self-center"> <div className="flex items-center gap-2 self-center">
<ShadTooltip {showNodeStatus && (
styleClasses={cn( <ShadTooltip
"border rounded-xl", styleClasses={cn(
conditionSuccess "border rounded-xl",
? "border-accent-emerald-foreground bg-success-background" conditionSuccess
: "border-destructive bg-error-background", ? "border-accent-emerald-foreground bg-success-background"
)} : "border-destructive bg-error-background",
content={
<BuildStatusDisplay
buildStatus={buildStatus}
validationStatus={validationStatus}
validationString={validationString}
lastRunTime={lastRunTime}
/>
}
side="bottom"
>
<div className="cursor-help">
{conditionSuccess && validationStatus?.data?.duration ? (
<div className="font-jetbrains mr-1 flex gap-1 rounded-sm bg-accent-emerald px-1 text-xxs font-bold text-accent-emerald-foreground">
<Check className="h-4 w-4 items-center self-center" />
<span>
{normalizeTimeString(validationStatus?.data?.duration)}
</span>
</div>
) : (
<div className="flex items-center self-center pr-1">
{iconStatus}
</div>
)} )}
</div> content={
</ShadTooltip> <BuildStatusDisplay
buildStatus={buildStatus}
{data.node?.beta && showNode && ( validationStatus={validationStatus}
<Badge validationString={validationString}
size="sq" lastRunTime={lastRunTime}
className="pointer-events-none mr-1 flex h-[22px] w-10 justify-center rounded-[8px] bg-accent-pink text-accent-pink-foreground" />
}
side="bottom"
> >
<span className="text-xxs">Beta</span> <div className="cursor-help">
</Badge> {conditionSuccess && validationStatus?.data?.duration ? (
<div className="flex rounded-sm px-1 font-mono text-xs text-accent-emerald-foreground transition-colors hover:bg-accent-emerald">
<span>
{normalizeTimeString(validationStatus?.data?.duration)}
</span>
</div>
) : (
<div className="flex items-center self-center">
{iconStatus}
</div>
)}
</div>
</ShadTooltip>
)} )}
</div>
{nodeAuth && ( {nodeAuth && showNode && (
<ShadTooltip content={nodeAuth.auth_tooltip || "Connect"}> <ShadTooltip content={nodeAuth.auth_tooltip || "Connect"}>
<div> <div>
{showNode && (
<Button <Button
unstyled unstyled
disabled={connectionLink === "" || connectionLink === "error"} disabled={connectionLink === "" || connectionLink === "error"}
@ -430,7 +423,7 @@ export default function NodeStatus({
<IconComponent <IconComponent
name={"Unlink"} name={"Unlink"}
className={cn( className={cn(
"h-3 w-3 text-accent-amber-foreground opacity-0 transition-opacity", "h-2.5 w-2.5 text-accent-amber-foreground opacity-0 transition-opacity",
isAuthenticated && !isPolling isAuthenticated && !isPolling
? "group-hover:opacity-100" ? "group-hover:opacity-100"
: "", : "",
@ -439,34 +432,31 @@ export default function NodeStatus({
/> />
</div> </div>
</Button> </Button>
)} </div>
</div> </ShadTooltip>
</ShadTooltip> )}
)} </div>
)}
{showNode && (
<ShadTooltip content={getTooltipContent()}> <ShadTooltip content={getTooltipContent()}>
<div <div
ref={divRef}
className="button-run-bg hit-area-icon"
onMouseEnter={() => setIsHovered(true)} onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)} onMouseLeave={() => setIsHovered(false)}
onClick={handleClickRun} onClick={handleClickRun}
className="-m-0.5"
> >
{showNode && ( <Button unstyled className="nodrag button-run-bg group">
<Button unstyled className="nodrag group"> <div data-testid={`button_run_` + display_name.toLowerCase()}>
<div data-testid={`button_run_` + display_name.toLowerCase()}> <IconComponent
<IconComponent name={iconName}
name={iconName} className={iconClasses}
className={iconClasses} strokeWidth={ICON_STROKE_WIDTH}
strokeWidth={ICON_STROKE_WIDTH} />
/> </div>
</div> </Button>
</Button>
)}
</div> </div>
</ShadTooltip> </ShadTooltip>
</div> )}
</> </div>
) : (
<></>
); );
} }

View file

@ -5,18 +5,15 @@ import { useEffect, useState } from "react";
import { ICON_STROKE_WIDTH } from "@/constants/constants"; import { ICON_STROKE_WIDTH } from "@/constants/constants";
import { checkLucideIcons } from "@/CustomNodes/helpers/check-lucide-icons"; import { checkLucideIcons } from "@/CustomNodes/helpers/check-lucide-icons";
import { cn } from "@/utils/utils";
import IconComponent from "../../../../components/common/genericIconComponent"; import IconComponent from "../../../../components/common/genericIconComponent";
export function NodeIcon({ export function NodeIcon({
icon, icon,
dataType, dataType,
showNode,
isGroup, isGroup,
}: { }: {
icon?: string; icon?: string;
dataType: string; dataType: string;
showNode: boolean;
isGroup?: boolean; isGroup?: boolean;
}) { }) {
const types = useTypesStore((state) => state.types); const types = useTypesStore((state) => state.types);
@ -34,41 +31,18 @@ export function NodeIcon({
const isLucideIcon = checkLucideIcons(iconName); const isLucideIcon = checkLucideIcons(iconName);
const iconClassName = cn(
"generic-node-icon",
isLucideIcon ? "lucide-icon" : "integration-icon",
);
const renderIcon = () => { const renderIcon = () => {
if (icon && isEmoji) { if (icon && isEmoji) {
return <span className="text-lg">{icon}</span>; return <span className="text-lg">{icon}</span>;
} }
if (isLucideIcon) {
return (
<div
className={cn(
"text-foreground",
!showNode && "flex min-h-8 min-w-8 items-center justify-center",
"bg-lucide-icon",
)}
>
<IconComponent
strokeWidth={ICON_STROKE_WIDTH}
name={iconName}
className={cn(iconClassName)}
/>
</div>
);
}
return ( return (
<div className={cn(!showNode && "min-h-8 min-w-8")}> <div className="flex h-4 w-4 items-center justify-center">
<IconComponent {isLucideIcon ? (
name={iconName} <IconComponent strokeWidth={ICON_STROKE_WIDTH} name={iconName} />
className={iconClassName} ) : (
iconColor={iconColor} <IconComponent name={iconName} iconColor={iconColor} />
/> )}
</div> </div>
); );
}; };

View file

@ -30,7 +30,6 @@ import useUpdateNodeCode from "../hooks/use-update-node-code";
import NodeDescription from "./components/NodeDescription"; import NodeDescription from "./components/NodeDescription";
import NodeName from "./components/NodeName"; import NodeName from "./components/NodeName";
import NodeOutputs from "./components/NodeOutputParameter/NodeOutputs"; import NodeOutputs from "./components/NodeOutputParameter/NodeOutputs";
import NodeStatus from "./components/NodeStatus";
import NodeUpdateComponent from "./components/NodeUpdateComponent"; import NodeUpdateComponent from "./components/NodeUpdateComponent";
import RenderInputParameters from "./components/RenderInputParameters"; import RenderInputParameters from "./components/RenderInputParameters";
import { NodeIcon } from "./components/nodeIcon"; import { NodeIcon } from "./components/nodeIcon";
@ -415,37 +414,31 @@ function GenericNode({
<div <div
data-testid={`${data.id}-main-node`} data-testid={`${data.id}-main-node`}
className={cn( className={cn(
"grid text-wrap p-4 leading-5", "grid text-wrap leading-5",
showNode ? "border-b" : "relative", showNode ? "border-b" : "relative",
hasDescription && "gap-3",
)} )}
> >
<div <div
data-testid={"div-generic-node"} data-testid={"div-generic-node"}
className={ className={cn(
!showNode "flex w-full flex-1 items-center justify-between gap-2 overflow-hidden px-4 py-3",
? "" )}
: "generic-node-div-title justify-between rounded-t-lg"
}
> >
<div <div
className={"generic-node-title-arrangement"} className="flex-max-width items-center overflow-hidden"
data-testid="generic-node-title-arrangement" data-testid="generic-node-title-arrangement"
> >
<MemoizedNodeIcon <MemoizedNodeIcon
dataType={data.type} dataType={data.type}
showNode={showNode}
icon={data.node?.icon} icon={data.node?.icon}
isGroup={!!data.node?.flow} isGroup={!!data.node?.flow}
/> />
<div className="generic-node-tooltip-div truncate"> <div className="ml-3 flex flex-1 overflow-hidden">
<MemoizedNodeName <MemoizedNodeName
display_name={data.node?.display_name} display_name={data.node?.display_name}
nodeId={data.id} nodeId={data.id}
selected={selected} selected={selected}
showNode={showNode} showNode={showNode}
validationStatus={validationStatus}
isOutdated={isOutdated}
beta={data.node?.beta || false} beta={data.node?.beta || false}
editNameDescription={editNameDescription} editNameDescription={editNameDescription}
toggleEditNameDescription={toggleEditNameDescription} toggleEditNameDescription={toggleEditNameDescription}
@ -453,9 +446,9 @@ function GenericNode({
/> />
</div> </div>
</div> </div>
<div data-testid={`${showNode ? "show" : "hide"}-node-content`}> {!showNode && (
{!showNode && ( <>
<> <div data-testid={`${showNode ? "show" : "hide"}-node-content`}>
<MemoizedRenderInputParameters <MemoizedRenderInputParameters
data={data} data={data}
types={types} types={types}
@ -474,9 +467,9 @@ function GenericNode({
isToolMode={isToolMode} isToolMode={isToolMode}
showHiddenOutputs={showHiddenOutputs} showHiddenOutputs={showHiddenOutputs}
/> />
</> </div>
)} </>
</div> )}
<MemoizedNodeStatus <MemoizedNodeStatus
data={data} data={data}
frozen={data.node?.frozen} frozen={data.node?.frozen}
@ -493,8 +486,8 @@ function GenericNode({
getValidationStatus={getValidationStatus} getValidationStatus={getValidationStatus}
/> />
</div> </div>
{showNode && ( {showNode && (hasDescription || editNameDescription) && (
<div> <div className="px-4 pb-3">
<MemoizedNodeDescription <MemoizedNodeDescription
description={data.node?.description} description={data.node?.description}
mdClassName={"dark:prose-invert"} mdClassName={"dark:prose-invert"}

View file

@ -1,5 +1,4 @@
import ForwardedIconComponent from "../../components/common/genericIconComponent"; import ForwardedIconComponent from "../../components/common/genericIconComponent";
import Xmark from "../../components/ui/xmark";
import { BuildStatus } from "../../constants/enums"; import { BuildStatus } from "../../constants/enums";
const useIconStatus = (buildStatus: BuildStatus | undefined) => { const useIconStatus = (buildStatus: BuildStatus | undefined) => {
@ -8,23 +7,16 @@ const useIconStatus = (buildStatus: BuildStatus | undefined) => {
const renderIconStatus = () => { const renderIconStatus = () => {
if (buildStatus === BuildStatus.BUILDING) { if (buildStatus === BuildStatus.BUILDING) {
return ( return <></>;
// <Loading
// data-testid="loading_icon"
// className="mr-1 text-medium-indigo"
// size={20}
// />
<></>
);
} }
const iconConditions = [ const iconConditions = [
{ {
condition: conditionError, condition: conditionError,
icon: ( icon: (
<Xmark <ForwardedIconComponent
isVisible={true} name="CircleAlert"
className="h-4 w-4 fill-current stroke-2 text-destructive" className="h-4 w-4 text-destructive"
/> />
), ),
}, },

View file

@ -85,7 +85,7 @@ export function ContentBlockDisplay({
name={headerIcon} name={headerIcon}
className={cn( className={cn(
"h-4 w-4", "h-4 w-4",
state !== "partial" && "text-status-green", state !== "partial" && "text-accent-emerald-foreground",
)} )}
strokeWidth={1.5} strokeWidth={1.5}
/> />

View file

@ -46,17 +46,13 @@ export default function DurationDisplay({
return ( return (
<div <div
data-testid="duration-display" data-testid="duration-display"
className={`inline-flex items-center justify-between gap-1 rounded-[3px] px-2 text-sm ${ className={`inline-flex items-center justify-between gap-1.5 rounded-[3px] px-2 text-sm ${
duration !== undefined duration !== undefined
? "bg-accent-emerald text-accent-emerald-foreground" ? "text-accent-emerald-foreground"
: "bg-muted text-muted-foreground" : "text-muted-foreground"
}`} }`}
> >
{duration === undefined ? ( {duration === undefined && <Loading className="h-3 w-3" />}
<Loading className="h-4 w-4" />
) : (
<ForwardedIconComponent name="check" className="h-4 w-4" />
)}
<div className="w-fit"> <div className="w-fit">
<AnimatedNumber <AnimatedNumber
value={secondsValue} value={secondsValue}
@ -65,7 +61,7 @@ export default function DurationDisplay({
bounce: 0, bounce: 0,
duration: 300, duration: 300,
}} }}
className="text-xxs font-bold tabular-nums" className="font-mono text-xxs font-bold tabular-nums"
/> />
</div> </div>
</div> </div>

View file

@ -321,18 +321,6 @@
.generic-node-div { .generic-node-div {
@apply flex flex-col justify-center bg-background transition-all; @apply flex flex-col justify-center bg-background transition-all;
} }
.generic-node-div-title {
@apply flex flex-1 items-center gap-2 overflow-hidden;
}
.generic-node-title-arrangement {
@apply flex-max-width items-center truncate;
}
.generic-node-icon {
@apply h-12 w-12 rounded;
}
.generic-node-tooltip-div {
@apply ml-2 flex w-full truncate;
}
.generic-node-validation-div { .generic-node-validation-div {
@apply max-h-96 overflow-auto; @apply max-h-96 overflow-auto;
} }
@ -1183,23 +1171,7 @@
} }
.button-run-bg { .button-run-bg {
@apply flex h-7 w-7 cursor-pointer items-center justify-center rounded-sm bg-transparent p-0 hover:bg-muted hover:p-1; @apply flex h-6 w-6 cursor-pointer items-center justify-center rounded-sm bg-transparent hover:bg-muted;
}
.play-button-icon {
@apply h-4 w-4 transition-all group-hover/node:opacity-100;
}
.lucide-icon {
@apply h-5 w-5;
}
.bg-lucide-icon {
@apply flex h-8 w-8 items-center justify-center rounded-md bg-muted p-[6px];
}
.integration-icon {
@apply h-8 w-8 rounded-md;
} }
.show-node-icon { .show-node-icon {

View file

@ -37,7 +37,7 @@
--canvas: 240 5% 96%; /* hsl(240, 5%, 96%) */ --canvas: 240 5% 96%; /* hsl(240, 5%, 96%) */
--canvas-dot: 240 5% 65%; /* hsl(240, 5%, 65%) */ --canvas-dot: 240 5% 65%; /* hsl(240, 5%, 65%) */
--accent-emerald: 149 80% 90%; /* hsl(149, 80%, 90%) */ --accent-emerald: 149 80% 90%; /* hsl(149, 80%, 90%) */
--accent-emerald-foreground: 161 41% 30%; /* hsl(161, 41%, 30%) */ --accent-emerald-foreground: 161 94% 30%; /* hsl(161, 94%, 30%) */
--accent-emerald-hover: 152.4 76% 80.4%; /* hsl(152.4, 76%, 80.4%) */ --accent-emerald-hover: 152.4 76% 80.4%; /* hsl(152.4, 76%, 80.4%) */
--accent-indigo: 226 100% 94%; /* hsl(226, 100%, 94%) */ --accent-indigo: 226 100% 94%; /* hsl(226, 100%, 94%) */
--accent-indigo-foreground: 243 75% 59%; /* hsl(243, 75%, 59%) */ --accent-indigo-foreground: 243 75% 59%; /* hsl(243, 75%, 59%) */
@ -45,6 +45,9 @@
--accent-pink: 326, 78%, 95%; /* hsl(326, 78%, 95%) */ --accent-pink: 326, 78%, 95%; /* hsl(326, 78%, 95%) */
--accent-pink-foreground: 333 71% 51%; /* hsl(333, 71%, 51%) */ --accent-pink-foreground: 333 71% 51%; /* hsl(333, 71%, 51%) */
--accent-purple-foreground: 271 81% 56%;
--tooltip: 0 0% 0%; /* hsl(0, 0%, 0%) */ --tooltip: 0 0% 0%; /* hsl(0, 0%, 0%) */
--tooltip-foreground: 0 0% 100%; /* hsl(0, 0%, 100%) */ --tooltip-foreground: 0 0% 100%; /* hsl(0, 0%, 100%) */
@ -232,6 +235,7 @@
--accent-indigo-foreground: 234 89% 74%; /* hsl(234, 89%, 74%) */ --accent-indigo-foreground: 234 89% 74%; /* hsl(234, 89%, 74%) */
--accent-pink: 336, 69%, 30%; /* hsl(336, 69%, 30%) */ --accent-pink: 336, 69%, 30%; /* hsl(336, 69%, 30%) */
--accent-pink-foreground: 329 86% 70%; /* hsl(329, 86%, 70%) */ --accent-pink-foreground: 329 86% 70%; /* hsl(329, 86%, 70%) */
--accent-purple-foreground: 270, 95%, 75%;
--accent-red-foreground: 0 91% 71%; /* hsl(0, 91%, 71%) */ --accent-red-foreground: 0 91% 71%; /* hsl(0, 91%, 71%) */
--tooltip: 0 0% 100%; /* hsl(0, 0%, 100%) */ --tooltip: 0 0% 100%; /* hsl(0, 0%, 100%) */

View file

@ -170,6 +170,7 @@ const config = {
"success-foreground": "var(--success-foreground)", "success-foreground": "var(--success-foreground)",
"accent-pink": "hsl(var(--accent-pink))", "accent-pink": "hsl(var(--accent-pink))",
"accent-pink-foreground": "hsl(var(--accent-pink-foreground))", "accent-pink-foreground": "hsl(var(--accent-pink-foreground))",
"accent-purple-foreground": "hsl(var(--accent-purple-foreground))",
"accent-red-foreground": "hsl(var(--accent-red-foreground))", "accent-red-foreground": "hsl(var(--accent-red-foreground))",
filter: { filter: {
foreground: "var(--filter-foreground)", foreground: "var(--filter-foreground)",

View file

@ -31,7 +31,7 @@ test(
await page await page
.getByTestId("dataURL") .getByTestId("dataURL")
.dragTo(page.locator('//*[@id="react-flow-id"]'), { .dragTo(page.locator('//*[@id="react-flow-id"]'), {
targetPosition: { x: 100, y: 100 }, targetPosition: { x: 50, y: 100 },
}); });
// Add Loop component // Add Loop component
@ -44,7 +44,7 @@ test(
await page await page
.getByTestId("logicLoop") .getByTestId("logicLoop")
.dragTo(page.locator('//*[@id="react-flow-id"]'), { .dragTo(page.locator('//*[@id="react-flow-id"]'), {
targetPosition: { x: 300, y: 100 }, targetPosition: { x: 280, y: 100 },
}); });
// Add Update Data component // Add Update Data component
@ -70,14 +70,14 @@ test(
await page await page
.getByTestId("processingData to Message") .getByTestId("processingData to Message")
.dragTo(page.locator('//*[@id="react-flow-id"]'), { .dragTo(page.locator('//*[@id="react-flow-id"]'), {
targetPosition: { x: 700, y: 100 }, targetPosition: { x: 720, y: 100 },
}); });
//This one is for testing the wrong loop message //This one is for testing the wrong loop message
await page await page
.getByTestId("processingData to Message") .getByTestId("processingData to Message")
.dragTo(page.locator('//*[@id="react-flow-id"]'), { .dragTo(page.locator('//*[@id="react-flow-id"]'), {
targetPosition: { x: 700, y: 400 }, targetPosition: { x: 720, y: 400 },
}); });
await page await page
@ -100,7 +100,7 @@ test(
await page await page
.getByTestId("outputsChat Output") .getByTestId("outputsChat Output")
.dragTo(page.locator('//*[@id="react-flow-id"]'), { .dragTo(page.locator('//*[@id="react-flow-id"]'), {
targetPosition: { x: 900, y: 100 }, targetPosition: { x: 940, y: 100 },
}); });
await page.getByTestId("fit_view").click(); await page.getByTestId("fit_view").click();

View file

@ -64,7 +64,7 @@ test(
expect(cellsCount).toBeGreaterThan(0); expect(cellsCount).toBeGreaterThan(0);
await page.getByRole("gridcell").first().click(); await page.getByRole("gridcell").first().click();
await page.waitForTimeout(500); await page.waitForTimeout(1000);
const isChecked = await page const isChecked = await page
.locator('input[data-ref="eInput"]') .locator('input[data-ref="eInput"]')
@ -73,7 +73,7 @@ test(
if (!isChecked) { if (!isChecked) {
await page.locator('input[data-ref="eInput"]').first().click(); await page.locator('input[data-ref="eInput"]').first().click();
await page.waitForTimeout(500); await page.waitForTimeout(1000);
} }
const isCheckedAgain = await page const isCheckedAgain = await page
.locator('input[data-ref="eInput"]') .locator('input[data-ref="eInput"]')
@ -82,7 +82,7 @@ test(
if (isCheckedAgain) { if (isCheckedAgain) {
await page.locator('input[data-ref="eInput"]').first().click(); await page.locator('input[data-ref="eInput"]').first().click();
await page.waitForTimeout(500); await page.waitForTimeout(1000);
} }
const isCheckedAgainAgain = await page const isCheckedAgainAgain = await page
@ -97,13 +97,13 @@ test(
.locator('input[data-ref="eInput"]') .locator('input[data-ref="eInput"]')
.nth(rowsCount + 1) .nth(rowsCount + 1)
.click(); .click();
await page.waitForTimeout(500); await page.waitForTimeout(1000);
await page await page
.getByRole("gridcell") .getByRole("gridcell")
.nth(cellsCount - 1) .nth(cellsCount - 1)
.click(); .click();
await page.waitForTimeout(500); await page.waitForTimeout(1000);
const isLastChecked = await page const isLastChecked = await page
.locator('input[data-ref="eInput"]') .locator('input[data-ref="eInput"]')
@ -224,8 +224,9 @@ test(
} }
// Verify tools are available // Verify tools are available
await page.waitForTimeout(3000);
await page.getByTestId("dropdown_str_tool").click(); await page.getByTestId("dropdown_str_tool").click();
await page.waitForTimeout(2000); await page.waitForTimeout(3000);
const fetchOptionCount = await page.getByText("mcp_test_name").count(); const fetchOptionCount = await page.getByText("mcp_test_name").count();
expect(fetchOptionCount).toBeGreaterThan(0); expect(fetchOptionCount).toBeGreaterThan(0);

View file

@ -2,10 +2,10 @@ import { expect, Page, test } from "@playwright/test";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test"; import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
async function toggleNodeState(page: Page, action: "minimize" | "expand") { async function toggleNodeState(page: Page, action: "minimize" | "expand") {
const expectedCount = action === "minimize" ? 0 : 1; const expectedCount = action === "minimize" ? 1 : 0;
await page.getByTestId("more-options-modal").click(); await page.getByTestId("more-options-modal").click();
await page.getByTestId(`${action}-button-modal`).click(); await page.getByTestId(`${action}-button-modal`).click();
expect(await page.getByTestId("show-node-content").count()).toBe( expect(await page.getByTestId("hide-node-content").count()).toBe(
expectedCount, expectedCount,
); );
} }
@ -36,7 +36,7 @@ test(
expect(await page.getByText("Toolset", { exact: true }).count()).toBe(0); expect(await page.getByText("Toolset", { exact: true }).count()).toBe(0);
await page.getByTestId("title-Text Output").click(); await page.getByTestId("title-Text Output").click();
expect(await page.getByTestId("show-node-content").count()).toBe(1); expect(await page.getByTestId("hide-node-content").count()).toBe(0);
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
await toggleNodeState(page, "minimize"); await toggleNodeState(page, "minimize");