Merge branch 'dev' into python_custom_node_component

This commit is contained in:
Gabriel Luiz Freitas Almeida 2023-07-26 07:55:25 -03:00
commit 02ca9739a0
28 changed files with 528 additions and 458 deletions

View file

@ -1,14 +1,17 @@
[tool.poetry] [tool.poetry]
name = "langflow" name = "langflow"
version = "0.3.2" version = "0.3.3"
description = "A Python package with a built-in web application" description = "A Python package with a built-in web application"
authors = ["Logspace <contact@logspace.ai>"] authors = ["Logspace <contact@logspace.ai>"]
maintainers = [ maintainers = [
"Carlos Coelho <carlos@logspace.ai>",
"Cristhian Zanforlin <cristhian.lousa@gmail.com>", "Cristhian Zanforlin <cristhian.lousa@gmail.com>",
"Gabriel Almeida <gabriel@logspace.ai>", "Gabriel Almeida <gabriel@logspace.ai>",
"Gustavo Schaedler <gustavopoa@gmail.com>", "Gustavo Schaedler <gustavopoa@gmail.com>",
"Igor Carvalho <igorr.ackerman@gmail.com>",
"Lucas Eduoli <lucaseduoli@gmail.com>", "Lucas Eduoli <lucaseduoli@gmail.com>",
"Otávio Anovazzi <otavio2204@gmail.com>", "Otávio Anovazzi <otavio2204@gmail.com>",
"Rodrigo Nader <rodrigo@logspace.ai>",
] ]
repository = "https://github.com/logspace-ai/langflow" repository = "https://github.com/logspace-ai/langflow"
license = "MIT" license = "MIT"

View file

@ -8,6 +8,7 @@ FORCE_SHOW_FIELDS = [
"headers", "headers",
"max_value_length", "max_value_length",
"max_tokens", "max_tokens",
"google_cse_id",
] ]
DEFAULT_PROMPT = """ DEFAULT_PROMPT = """

View file

@ -20,6 +20,7 @@
"@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2", "@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-menubar": "^1.0.3", "@radix-ui/react-menubar": "^1.0.3",
"@radix-ui/react-popover": "^1.0.6",
"@radix-ui/react-progress": "^1.0.3", "@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-slot": "^1.0.2",
@ -29,6 +30,7 @@
"@tabler/icons-react": "^2.18.0", "@tabler/icons-react": "^2.18.0",
"@tailwindcss/forms": "^0.5.3", "@tailwindcss/forms": "^0.5.3",
"@tailwindcss/line-clamp": "^0.4.4", "@tailwindcss/line-clamp": "^0.4.4",
"@types/axios": "^0.14.0",
"accordion": "^3.0.2", "accordion": "^3.0.2",
"ace-builds": "^1.16.0", "ace-builds": "^1.16.0",
"add": "^2.0.6", "add": "^2.0.6",
@ -143,6 +145,8 @@
}, },
"node_modules/@babel/compat-data": { "node_modules/@babel/compat-data": {
"version": "7.22.9", "version": "7.22.9",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.6.tgz",
"integrity": "sha512-29tfsWTq2Ftu7MXmimyC0C5FDZv5DYxOZkh3XD3+QW4V/BYuv/LyEsjj3c0hqedEaDt6DBfDvexMKU8YevdqFg==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@ -1460,6 +1464,43 @@
} }
} }
}, },
"node_modules/@radix-ui/react-popover": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.0.6.tgz",
"integrity": "sha512-cZ4defGpkZ0qTRtlIBzJLSzL6ht7ofhhW4i1+pkemjV1IKXm0wgCRnee154qlV6r9Ttunmh2TNZhMfV2bavUyA==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.1",
"@radix-ui/react-compose-refs": "1.0.1",
"@radix-ui/react-context": "1.0.1",
"@radix-ui/react-dismissable-layer": "1.0.4",
"@radix-ui/react-focus-guards": "1.0.1",
"@radix-ui/react-focus-scope": "1.0.3",
"@radix-ui/react-id": "1.0.1",
"@radix-ui/react-popper": "1.1.2",
"@radix-ui/react-portal": "1.0.3",
"@radix-ui/react-presence": "1.0.1",
"@radix-ui/react-primitive": "1.0.3",
"@radix-ui/react-slot": "1.0.2",
"@radix-ui/react-use-controllable-state": "1.0.1",
"aria-hidden": "^1.1.1",
"react-remove-scroll": "2.5.5"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popper": { "node_modules/@radix-ui/react-popper": {
"version": "1.1.2", "version": "1.1.2",
"license": "MIT", "license": "MIT",
@ -2610,6 +2651,15 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/axios": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.14.0.tgz",
"integrity": "sha512-KqQnQbdYE54D7oa/UmYVMZKq7CO4l8DEENzOKc4aBRwxCXSlJXGz83flFx5L7AWrOQnmuN3kVsRdt+GZPPjiVQ==",
"deprecated": "This is a stub types definition for axios (https://github.com/mzabriskie/axios). axios provides its own type definitions, so you don't need @types/axios installed!",
"dependencies": {
"axios": "*"
}
},
"node_modules/@types/cacheable-request": { "node_modules/@types/cacheable-request": {
"version": "6.0.3", "version": "6.0.3",
"dev": true, "dev": true,
@ -9533,8 +9583,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/word-wrap": { "node_modules/word-wrap": {
"version": "1.2.3", "version": "1.2.5",
"license": "MIT", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }

View file

@ -15,6 +15,7 @@
"@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2", "@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-menubar": "^1.0.3", "@radix-ui/react-menubar": "^1.0.3",
"@radix-ui/react-popover": "^1.0.6",
"@radix-ui/react-progress": "^1.0.3", "@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-slot": "^1.0.2",
@ -24,6 +25,7 @@
"@tabler/icons-react": "^2.18.0", "@tabler/icons-react": "^2.18.0",
"@tailwindcss/forms": "^0.5.3", "@tailwindcss/forms": "^0.5.3",
"@tailwindcss/line-clamp": "^0.4.4", "@tailwindcss/line-clamp": "^0.4.4",
"@types/axios": "^0.14.0",
"accordion": "^3.0.2", "accordion": "^3.0.2",
"ace-builds": "^1.16.0", "ace-builds": "^1.16.0",
"add": "^2.0.6", "add": "^2.0.6",

View file

@ -1,38 +1,41 @@
import { useContext, useRef } from "react"; import { useContext, useState } from "react";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "../../components/ui/popover";
import { alertContext } from "../../contexts/alertContext"; import { alertContext } from "../../contexts/alertContext";
import { PopUpContext } from "../../contexts/popUpContext";
import { AlertDropdownType } from "../../types/alerts"; import { AlertDropdownType } from "../../types/alerts";
import { useOnClickOutside } from "../hooks/useOnClickOutside";
import SingleAlert from "./components/singleAlertComponent"; import SingleAlert from "./components/singleAlertComponent";
export default function AlertDropdown({}: AlertDropdownType) { export default function AlertDropdown({ children }: AlertDropdownType) {
const { closePopUp } = useContext(PopUpContext);
const componentRef = useRef<HTMLDivElement>(null);
// Use the custom hook
useOnClickOutside(componentRef, () => {
closePopUp();
});
const { const {
notificationList, notificationList,
clearNotificationList, clearNotificationList,
removeFromNotificationList, removeFromNotificationList,
setNotificationCenter,
} = useContext(alertContext); } = useContext(alertContext);
const [open, setOpen] = useState(false);
return ( return (
<div <Popover
ref={componentRef} open={open}
className="z-10 flex h-[500px] w-[400px] flex-col overflow-hidden rounded-md bg-muted px-2 py-3 pb-4 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none" onOpenChange={(k) => {
setOpen(k);
if (k) setNotificationCenter(false);
}}
> >
<PopoverTrigger>{children}</PopoverTrigger>
<PopoverContent className="flex h-[500px] w-[500px] flex-col">
<div className="text-md flex flex-row justify-between pl-3 font-medium text-foreground"> <div className="text-md flex flex-row justify-between pl-3 font-medium text-foreground">
Notifications Notifications
<div className="flex gap-3 pr-3 "> <div className="flex gap-3 pr-3 ">
<button <button
className="text-foreground hover:text-status-red" className="text-foreground hover:text-status-red"
onClick={() => { onClick={() => {
closePopUp(); setOpen(false);
setTimeout(clearNotificationList, 100); setTimeout(clearNotificationList, 100);
}} }}
> >
@ -40,7 +43,9 @@ export default function AlertDropdown({}: AlertDropdownType) {
</button> </button>
<button <button
className="text-foreground hover:text-status-red" className="text-foreground hover:text-status-red"
onClick={closePopUp} onClick={() => {
setOpen(false);
}}
> >
<IconComponent name="X" className="h-5 w-5" /> <IconComponent name="X" className="h-5 w-5" />
</button> </button>
@ -61,6 +66,7 @@ export default function AlertDropdown({}: AlertDropdownType) {
</div> </div>
)} )}
</div> </div>
</div> </PopoverContent>
</Popover>
); );
} }

View file

@ -1,5 +1,6 @@
import { useEffect } from "react"; import { useEffect } from "react";
import { FloatComponentType } from "../../types/components"; import { FloatComponentType } from "../../types/components";
import { Input } from "../ui/input";
export default function FloatComponent({ export default function FloatComponent({
value, value,
@ -19,8 +20,8 @@ export default function FloatComponent({
}, [disabled, onChange]); }, [disabled, onChange]);
return ( return (
<div className={"w-full " + (disabled ? "float-component-pointer" : "")}> <div className="w-full">
<input <Input
type="number" type="number"
step={step} step={step}
min={min} min={min}
@ -34,12 +35,8 @@ export default function FloatComponent({
}} }}
max={max} max={max}
value={value ?? ""} value={value ?? ""}
className={ disabled={disabled}
"nopan nodrag noundo nocopy " + className={editNode ? "input-edit-node" : ""}
(editNode
? "input-edit-node"
: "input-primary" + (disabled ? " input-disable " : ""))
}
placeholder={ placeholder={
editNode ? "Number 0 to 1" : "Type a number from zero to one" editNode ? "Number 0 to 1" : "Type a number from zero to one"
} }

View file

@ -1,13 +1,11 @@
import { useContext, useEffect, useState } from "react"; import { useContext, useEffect, useState } from "react";
import { FaDiscord, FaGithub, FaTwitter } from "react-icons/fa"; import { FaDiscord, FaGithub, FaTwitter } from "react-icons/fa";
import { Link, useLocation, useParams } from "react-router-dom"; import { Link, useLocation } from "react-router-dom";
import AlertDropdown from "../../alerts/alertDropDown"; import AlertDropdown from "../../alerts/alertDropDown";
import { USER_PROJECTS_HEADER } from "../../constants/constants"; import { USER_PROJECTS_HEADER } from "../../constants/constants";
import { alertContext } from "../../contexts/alertContext"; import { alertContext } from "../../contexts/alertContext";
import { darkContext } from "../../contexts/darkContext"; import { darkContext } from "../../contexts/darkContext";
import { PopUpContext } from "../../contexts/popUpContext";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { typesContext } from "../../contexts/typesContext";
import { getRepoStars } from "../../controllers/API"; import { getRepoStars } from "../../controllers/API";
import IconComponent from "../genericIconComponent"; import IconComponent from "../genericIconComponent";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
@ -15,14 +13,9 @@ import { Separator } from "../ui/separator";
import MenuBar from "./components/menuBar"; import MenuBar from "./components/menuBar";
export default function Header() { export default function Header() {
const { flows, addFlow, tabId } = useContext(TabsContext); const { flows, tabId } = useContext(TabsContext);
const { openPopUp } = useContext(PopUpContext);
const { templates } = useContext(typesContext);
const { id } = useParams();
const AlertWidth = 384;
const { dark, setDark } = useContext(darkContext); const { dark, setDark } = useContext(darkContext);
const { notificationCenter, setNotificationCenter, setErrorData } = const { notificationCenter } = useContext(alertContext);
useContext(alertContext);
const location = useLocation(); const location = useLocation();
const [stars, setStars] = useState(null); const [stars, setStars] = useState(null);
@ -111,33 +104,18 @@ export default function Header() {
<IconComponent name="MoonIcon" className="side-bar-button-size" /> <IconComponent name="MoonIcon" className="side-bar-button-size" />
)} )}
</button> </button>
<button <AlertDropdown>
className="extra-side-bar-save-disable relative" <div className="extra-side-bar-save-disable relative">
onClick={(event: React.MouseEvent<HTMLElement>) => { {notificationCenter && (
setNotificationCenter(false); <div className="header-notifications"></div>
const { top, left } = ( )}
event.target as Element
).getBoundingClientRect();
openPopUp(
<>
<div
className="absolute z-10"
style={{ top: top + 40, left: left - AlertWidth }}
>
<AlertDropdown />
</div>
<div className="header-notifications-box"></div>
</>
);
}}
>
{notificationCenter && <div className="header-notifications"></div>}
<IconComponent <IconComponent
name="Bell" name="Bell"
className="side-bar-button-size" className="side-bar-button-size"
aria-hidden="true" aria-hidden="true"
/> />
</button> </div>
</AlertDropdown>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,6 +1,7 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { InputComponentType } from "../../types/components"; import { InputComponentType } from "../../types/components";
import { classNames } from "../../utils/utils"; import { classNames } from "../../utils/utils";
import { Input } from "../ui/input";
export default function InputComponent({ export default function InputComponent({
value, value,
@ -19,16 +20,15 @@ export default function InputComponent({
}, [disabled, onChange]); }, [disabled, onChange]);
return ( return (
<div className={disabled ? "input-component-div" : "relative"}> <div className="relative w-full">
<input <Input
value={value} value={value}
disabled={disabled}
className={classNames( className={classNames(
disabled ? " input-disable " : "",
password && !pwdVisible && value !== "" ? " text-clip password " : "", password && !pwdVisible && value !== "" ? " text-clip password " : "",
editNode ? " input-edit-node " : " input-primary ", editNode ? " input-edit-node " : "",
password && editNode ? "pr-8" : "", password && editNode ? "pr-8" : "",
password && !editNode ? "pr-10" : "", password && !editNode ? "pr-10" : ""
"nopan nodrag noundo nocopy"
)} )}
placeholder={password && editNode ? "Key" : "Type something..."} placeholder={password && editNode ? "Key" : "Type something..."}
onChange={(e) => { onChange={(e) => {

View file

@ -2,7 +2,9 @@ import { useEffect } from "react";
import { InputListComponentType } from "../../types/components"; import { InputListComponentType } from "../../types/components";
import _ from "lodash"; import _ from "lodash";
import { classNames } from "../../utils/utils";
import IconComponent from "../genericIconComponent"; import IconComponent from "../genericIconComponent";
import { Input } from "../ui/input";
export default function InputListComponent({ export default function InputListComponent({
value, value,
@ -18,23 +20,19 @@ export default function InputListComponent({
return ( return (
<div <div
className={ className={classNames(
(disabled ? "pointer-events-none cursor-not-allowed" : "") + value.length > 1 && editNode ? "my-1" : "",
"flex flex-col gap-3" "flex flex-col gap-3"
} )}
> >
{value.map((i, idx) => { {value.map((i, idx) => {
return ( return (
<div key={idx} className="flex w-full gap-3"> <div key={idx} className="flex w-full gap-3">
<input <Input
disabled={disabled}
type="text" type="text"
value={i} value={i}
className={ className={editNode ? "input-edit-node" : ""}
"nopan nodrag noundo nocopy " +
(editNode
? "input-edit-node "
: "input-primary " + (disabled ? "input-disable" : ""))
}
placeholder="Type something..." placeholder="Type something..."
onChange={(e) => { onChange={(e) => {
let newInputList = _.cloneDeep(value); let newInputList = _.cloneDeep(value);

View file

@ -1,5 +1,6 @@
import { useEffect } from "react"; import { useEffect } from "react";
import { FloatComponentType } from "../../types/components"; import { FloatComponentType } from "../../types/components";
import { Input } from "../ui/input";
export default function IntComponent({ export default function IntComponent({
value, value,
@ -17,13 +18,8 @@ export default function IntComponent({
}, [disabled, onChange]); }, [disabled, onChange]);
return ( return (
<div <div className="w-full">
className={ <Input
"w-full " +
(disabled ? "pointer-events-none w-full cursor-not-allowed" : "")
}
>
<input
onKeyDown={(event) => { onKeyDown={(event) => {
if ( if (
event.key !== "Backspace" && event.key !== "Backspace" &&
@ -51,12 +47,8 @@ export default function IntComponent({
} }
}} }}
value={value ?? ""} value={value ?? ""}
className={ className={editNode ? "input-edit-node" : ""}
"nopan nodrag noundo nocopy " + disabled={disabled}
(editNode
? " input-edit-node "
: " input-primary " + (disabled ? " input-disable" : ""))
}
placeholder={editNode ? "Integer number" : "Type an integer number"} placeholder={editNode ? "Integer number" : "Type an integer number"}
onChange={(e) => { onChange={(e) => {
onChange(e.target.value); onChange(e.target.value);

View file

@ -3,6 +3,7 @@ import { TypeModal } from "../../constants/enums";
import GenericModal from "../../modals/genericModal"; import GenericModal from "../../modals/genericModal";
import { TextAreaComponentType } from "../../types/components"; import { TextAreaComponentType } from "../../types/components";
import IconComponent from "../genericIconComponent"; import IconComponent from "../genericIconComponent";
import { Input } from "../ui/input";
export default function TextAreaComponent({ export default function TextAreaComponent({
value, value,
@ -18,16 +19,11 @@ export default function TextAreaComponent({
}, [disabled]); }, [disabled]);
return ( return (
<div className={disabled ? "pointer-events-none w-full " : " w-full"}>
<div className="flex w-full items-center"> <div className="flex w-full items-center">
<input <Input
value={value} value={value}
className={ disabled={disabled}
(editNode className={editNode ? "input-edit-node" : ""}
? " input-edit-node "
: " input-primary " + (disabled ? " input-disable" : "")) +
" nopan nodrag noundo nocopy w-full"
}
placeholder={"Type something..."} placeholder={"Type something..."}
onChange={(e) => { onChange={(e) => {
onChange(e.target.value); onChange(e.target.value);
@ -55,6 +51,5 @@ export default function TextAreaComponent({
</GenericModal> </GenericModal>
</div> </div>
</div> </div>
</div>
); );
} }

View file

@ -9,10 +9,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
return ( return (
<input <input
type={type} type={type}
className={cn( className={cn("nopan nodrag noundo nocopy input-primary", className)}
"nopan nodrag noundo nocopy flex h-10 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref} ref={ref}
{...props} {...props}
/> />

View file

@ -0,0 +1,30 @@
"use client";
import * as PopoverPrimitive from "@radix-ui/react-popover";
import * as React from "react";
import { cn } from "../../utils/utils";
const Popover = PopoverPrimitive.Root;
const PopoverTrigger = PopoverPrimitive.Trigger;
const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
</PopoverPrimitive.Portal>
));
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
export { Popover, PopoverTrigger, PopoverContent };

View file

@ -8,10 +8,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
({ className, ...props }, ref) => { ({ className, ...props }, ref) => {
return ( return (
<textarea <textarea
className={cn( className={cn("nopan nodrag noundo nocopy textarea-primary", className)}
"flex min-h-[80px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref} ref={ref}
{...props} {...props}
/> />

View file

@ -5,7 +5,6 @@ import { SSEProvider } from "./SSEContext";
import { AlertProvider } from "./alertContext"; import { AlertProvider } from "./alertContext";
import { DarkProvider } from "./darkContext"; import { DarkProvider } from "./darkContext";
import { LocationProvider } from "./locationContext"; import { LocationProvider } from "./locationContext";
import PopUpProvider from "./popUpContext";
import { TabsProvider } from "./tabsContext"; import { TabsProvider } from "./tabsContext";
import { TypesProvider } from "./typesContext"; import { TypesProvider } from "./typesContext";
import { UndoRedoProvider } from "./undoRedoContext"; import { UndoRedoProvider } from "./undoRedoContext";
@ -22,9 +21,7 @@ export default function ContextWrapper({ children }: { children: ReactNode }) {
<AlertProvider> <AlertProvider>
<SSEProvider> <SSEProvider>
<TabsProvider> <TabsProvider>
<UndoRedoProvider> <UndoRedoProvider>{children}</UndoRedoProvider>
<PopUpProvider>{children}</PopUpProvider>
</UndoRedoProvider>
</TabsProvider> </TabsProvider>
</SSEProvider> </SSEProvider>
</AlertProvider> </AlertProvider>

View file

@ -1,38 +0,0 @@
import React, { createContext, useState } from "react";
// context to set JSX element on the DOM
export const PopUpContext = createContext({
openPopUp: (popUpElement: JSX.Element) => {},
closePopUp: () => {},
setCloseEdit: (value: string) => {},
closeEdit: "",
});
interface PopUpProviderProps {
children: React.ReactNode;
}
const PopUpProvider = ({ children }: PopUpProviderProps) => {
const [popUpElements, setPopUpElements] = useState<JSX.Element[]>([]);
const openPopUp = (element: JSX.Element) => {
setPopUpElements((prevPopUps) => [element, ...prevPopUps]);
};
const closePopUp = () => {
setPopUpElements((prevPopUps) => prevPopUps.slice(1));
};
const [closeEdit, setCloseEdit] = useState("");
return (
<PopUpContext.Provider
value={{ openPopUp, closePopUp, closeEdit, setCloseEdit }}
>
{children}
{popUpElements[0]}
</PopUpContext.Provider>
);
};
export default PopUpProvider;

View file

@ -71,18 +71,7 @@ export function TypesProvider({ children }: { children: ReactNode }) {
// Clear the interval if successful. // Clear the interval if successful.
clearInterval(intervalId); clearInterval(intervalId);
} catch (error) { } catch (error) {
retryCount++; console.error("An error has occurred while fetching types.");
// On error, double the delay for the next attempt up to a maximum.
delay = Math.min(30000, delay * 2);
// Log errors but don't do anything else - the function will try again on the next interval.
console.error(error);
// Clear the old interval and start a new one with the new delay.
if (retryCount <= maxRetryCount) {
clearInterval(intervalId);
intervalId = setInterval(getTypes, delay);
} else {
console.error("Max retry attempts reached. Stopping retries.");
}
} }
} }

View file

@ -0,0 +1,58 @@
import axios, { AxiosError, AxiosInstance } from "axios";
import { useContext, useEffect, useRef } from "react";
import { alertContext } from "../../contexts/alertContext";
// Create a new Axios instance
const api: AxiosInstance = axios.create({
baseURL: "",
});
function ApiInterceptor() {
const retryCounts = useRef([]);
const { setErrorData } = useContext(alertContext);
useEffect(() => {
const interceptor = api.interceptors.response.use(
(response) => response,
async (error: AxiosError) => {
let retryCount = 0;
while (retryCount < 4) {
await sleep(5000); // Sleep for 5 seconds
retryCount++;
try {
const response = await axios.request(error.config);
return response;
} catch (error) {
if (retryCount === 3) {
setErrorData({
title: "There was an error on web connection, please: ",
list: [
"Refresh the page",
"Use a new flow tab",
"Check if the backend is up",
"Endpoint: " + error.config.url,
],
});
return Promise.reject(error);
}
}
}
}
);
return () => {
// Clean up the interceptor when the component unmounts
api.interceptors.response.eject(interceptor);
};
}, [retryCounts]);
return null;
}
// Function to sleep for a given duration in milliseconds
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export { ApiInterceptor, api };

View file

@ -1,5 +1,6 @@
import axios, { AxiosResponse } from "axios"; import { AxiosResponse } from "axios";
import { ReactFlowJsonObject } from "reactflow"; import { ReactFlowJsonObject } from "reactflow";
import { api } from "../../controllers/API/api";
import { APIObjectType, sendAllProps } from "../../types/api/index"; import { APIObjectType, sendAllProps } from "../../types/api/index";
import { FlowStyleType, FlowType } from "../../types/flow"; import { FlowStyleType, FlowType } from "../../types/flow";
import { import {
@ -17,16 +18,14 @@ import {
* @returns {Promise<AxiosResponse<APIObjectType>>} A promise that resolves to an AxiosResponse containing all the objects. * @returns {Promise<AxiosResponse<APIObjectType>>} A promise that resolves to an AxiosResponse containing all the objects.
*/ */
export async function getAll(): Promise<AxiosResponse<APIObjectType>> { export async function getAll(): Promise<AxiosResponse<APIObjectType>> {
return await axios.get(`/api/v1/all`); return await api.get(`/api/v1/all`);
} }
const GITHUB_API_URL = "https://api.github.com"; const GITHUB_API_URL = "https://api.github.com";
export async function getRepoStars(owner, repo) { export async function getRepoStars(owner, repo) {
try { try {
const response = await axios.get( const response = await api.get(`${GITHUB_API_URL}/repos/${owner}/${repo}`);
`${GITHUB_API_URL}/repos/${owner}/${repo}`
);
return response.data.stargazers_count; return response.data.stargazers_count;
} catch (error) { } catch (error) {
console.error("Error fetching repository data:", error); console.error("Error fetching repository data:", error);
@ -41,13 +40,13 @@ export async function getRepoStars(owner, repo) {
* @returns {AxiosResponse<any>} The API response. * @returns {AxiosResponse<any>} The API response.
*/ */
export async function sendAll(data: sendAllProps) { export async function sendAll(data: sendAllProps) {
return await axios.post(`/api/v1/predict`, data); return await api.post(`/api/v1/predict`, data);
} }
export async function postValidateCode( export async function postValidateCode(
code: string code: string
): Promise<AxiosResponse<errorsTypeAPI>> { ): Promise<AxiosResponse<errorsTypeAPI>> {
return await axios.post("/api/v1/validate/code", { code }); return await api.post("/api/v1/validate/code", { code });
} }
/** /**
@ -62,7 +61,7 @@ export async function postValidatePrompt(
template: string, template: string,
frontend_node: APIClassType frontend_node: APIClassType
): Promise<AxiosResponse<PromptTypeAPI>> { ): Promise<AxiosResponse<PromptTypeAPI>> {
return await axios.post("/api/v1/validate/prompt", { return await api.post("/api/v1/validate/prompt", {
name: name, name: name,
template: template, template: template,
frontend_node: frontend_node, frontend_node: frontend_node,
@ -77,14 +76,14 @@ export async function postValidatePrompt(
export async function getExamples(): Promise<FlowType[]> { export async function getExamples(): Promise<FlowType[]> {
const url = const url =
"https://api.github.com/repos/logspace-ai/langflow_examples/contents/examples?ref=main"; "https://api.github.com/repos/logspace-ai/langflow_examples/contents/examples?ref=main";
const response = await axios.get(url); const response = await api.get(url);
const jsonFiles = response.data.filter((file: any) => { const jsonFiles = response.data.filter((file: any) => {
return file.name.endsWith(".json"); return file.name.endsWith(".json");
}); });
const contentsPromises = jsonFiles.map(async (file: any) => { const contentsPromises = jsonFiles.map(async (file: any) => {
const contentResponse = await axios.get(file.download_url); const contentResponse = await api.get(file.download_url);
return contentResponse.data; return contentResponse.data;
}); });
@ -106,11 +105,12 @@ export async function saveFlowToDatabase(newFlow: {
style?: FlowStyleType; style?: FlowStyleType;
}): Promise<FlowType> { }): Promise<FlowType> {
try { try {
const response = await axios.post("/api/v1/flows/", { const response = await api.post("/api/v1/flows/", {
name: newFlow.name, name: newFlow.name,
data: newFlow.data, data: newFlow.data,
description: newFlow.description, description: newFlow.description,
}); });
if (response.status !== 201) { if (response.status !== 201) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
} }
@ -131,7 +131,7 @@ export async function updateFlowInDatabase(
updatedFlow: FlowType updatedFlow: FlowType
): Promise<FlowType> { ): Promise<FlowType> {
try { try {
const response = await axios.patch(`/api/v1/flows/${updatedFlow.id}`, { const response = await api.patch(`/api/v1/flows/${updatedFlow.id}`, {
name: updatedFlow.name, name: updatedFlow.name,
data: updatedFlow.data, data: updatedFlow.data,
description: updatedFlow.description, description: updatedFlow.description,
@ -155,7 +155,7 @@ export async function updateFlowInDatabase(
*/ */
export async function readFlowsFromDatabase() { export async function readFlowsFromDatabase() {
try { try {
const response = await axios.get("/api/v1/flows/"); const response = await api.get("/api/v1/flows/");
if (response.status !== 200) { if (response.status !== 200) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
} }
@ -168,7 +168,7 @@ export async function readFlowsFromDatabase() {
export async function downloadFlowsFromDatabase() { export async function downloadFlowsFromDatabase() {
try { try {
const response = await axios.get("/api/v1/flows/download/"); const response = await api.get("/api/v1/flows/download/");
if (response.status !== 200) { if (response.status !== 200) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
} }
@ -181,7 +181,7 @@ export async function downloadFlowsFromDatabase() {
export async function uploadFlowsToDatabase(flows) { export async function uploadFlowsToDatabase(flows) {
try { try {
const response = await axios.post(`/api/v1/flows/upload/`, flows); const response = await api.post(`/api/v1/flows/upload/`, flows);
if (response.status !== 201) { if (response.status !== 201) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
@ -202,7 +202,7 @@ export async function uploadFlowsToDatabase(flows) {
*/ */
export async function deleteFlowFromDatabase(flowId: string) { export async function deleteFlowFromDatabase(flowId: string) {
try { try {
const response = await axios.delete(`/api/v1/flows/${flowId}`); const response = await api.delete(`/api/v1/flows/${flowId}`);
if (response.status !== 200) { if (response.status !== 200) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
} }
@ -222,7 +222,7 @@ export async function deleteFlowFromDatabase(flowId: string) {
*/ */
export async function getFlowFromDatabase(flowId: number) { export async function getFlowFromDatabase(flowId: number) {
try { try {
const response = await axios.get(`/api/v1/flows/${flowId}`); const response = await api.get(`/api/v1/flows/${flowId}`);
if (response.status !== 200) { if (response.status !== 200) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
} }
@ -241,7 +241,7 @@ export async function getFlowFromDatabase(flowId: number) {
*/ */
export async function getFlowStylesFromDatabase() { export async function getFlowStylesFromDatabase() {
try { try {
const response = await axios.get("/api/v1/flow_styles/"); const response = await api.get("/api/v1/flow_styles/");
if (response.status !== 200) { if (response.status !== 200) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
} }
@ -261,7 +261,7 @@ export async function getFlowStylesFromDatabase() {
*/ */
export async function saveFlowStyleToDatabase(flowStyle: FlowStyleType) { export async function saveFlowStyleToDatabase(flowStyle: FlowStyleType) {
try { try {
const response = await axios.post("/api/v1/flow_styles/", flowStyle, { const response = await api.post("/api/v1/flow_styles/", flowStyle, {
headers: { headers: {
accept: "application/json", accept: "application/json",
"Content-Type": "application/json", "Content-Type": "application/json",
@ -284,7 +284,7 @@ export async function saveFlowStyleToDatabase(flowStyle: FlowStyleType) {
* @returns {Promise<AxiosResponse<any>>} A promise that resolves to an AxiosResponse containing the version information. * @returns {Promise<AxiosResponse<any>>} A promise that resolves to an AxiosResponse containing the version information.
*/ */
export async function getVersion() { export async function getVersion() {
const respnose = await axios.get("/api/v1/version"); const respnose = await api.get("/api/v1/version");
return respnose.data; return respnose.data;
} }
@ -294,7 +294,7 @@ export async function getVersion() {
* @returns {Promise<AxiosResponse<any>>} A promise that resolves to an AxiosResponse containing the health status. * @returns {Promise<AxiosResponse<any>>} A promise that resolves to an AxiosResponse containing the health status.
*/ */
export async function getHealth() { export async function getHealth() {
return await axios.get("/health"); // Health is the only endpoint that doesn't require /api/v1 return await api.get("/health"); // Health is the only endpoint that doesn't require /api/v1
} }
/** /**
@ -306,7 +306,7 @@ export async function getHealth() {
export async function getBuildStatus( export async function getBuildStatus(
flowId: string flowId: string
): Promise<BuildStatusTypeAPI> { ): Promise<BuildStatusTypeAPI> {
return await axios.get(`/api/v1/build/${flowId}/status`); return await api.get(`/api/v1/build/${flowId}/status`);
} }
//docs for postbuildinit //docs for postbuildinit
@ -319,7 +319,7 @@ export async function getBuildStatus(
export async function postBuildInit( export async function postBuildInit(
flow: FlowType flow: FlowType
): Promise<AxiosResponse<InitTypeAPI>> { ): Promise<AxiosResponse<InitTypeAPI>> {
return await axios.post(`/api/v1/build/init/${flow.id}`, flow); return await api.post(`/api/v1/build/init/${flow.id}`, flow);
} }
// fetch(`/upload/${id}`, { // fetch(`/upload/${id}`, {
@ -337,7 +337,7 @@ export async function uploadFile(
): Promise<AxiosResponse<UploadFileTypeAPI>> { ): Promise<AxiosResponse<UploadFileTypeAPI>> {
const formData = new FormData(); const formData = new FormData();
formData.append("file", file); formData.append("file", file);
return await axios.post(`/api/v1/upload/${id}`, formData); return await api.post(`/api/v1/upload/${id}`, formData);
} }
export async function postCustomComponent( export async function postCustomComponent(

View file

@ -4,7 +4,10 @@ import App from "./App";
import ContextWrapper from "./contexts"; import ContextWrapper from "./contexts";
import reportWebVitals from "./reportWebVitals"; import reportWebVitals from "./reportWebVitals";
import "./index.css"; import { ApiInterceptor } from "./controllers/API/api";
import "./style/applies.css";
import "./style/classes.css";
import "./style/index.css";
const root = ReactDOM.createRoot( const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement document.getElementById("root") as HTMLElement
@ -13,6 +16,7 @@ root.render(
<ContextWrapper> <ContextWrapper>
<BrowserRouter> <BrowserRouter>
<App /> <App />
<ApiInterceptor />
</BrowserRouter> </BrowserRouter>
</ContextWrapper> </ContextWrapper>
); );

View file

@ -1,5 +1,6 @@
import { useEffect } from "react"; import { useEffect } from "react";
import IconComponent from "../../../components/genericIconComponent"; import IconComponent from "../../../components/genericIconComponent";
import { Textarea } from "../../../components/ui/textarea";
import { classNames } from "../../../utils/utils"; import { classNames } from "../../../utils/utils";
export default function ChatInput({ export default function ChatInput({
@ -25,7 +26,7 @@ export default function ChatInput({
return ( return (
<div className="relative"> <div className="relative">
<textarea <Textarea
onKeyDown={(event) => { onKeyDown={(event) => {
if (event.key === "Enter" && !lockChat && !event.shiftKey) { if (event.key === "Enter" && !lockChat && !event.shiftKey) {
sendMessage(); sendMessage();
@ -68,10 +69,10 @@ export default function ChatInput({
className={classNames( className={classNames(
"form-modal-send-button", "form-modal-send-button",
noInput noInput
? "bg-indigo-600 text-background" ? "bg-high-indigo text-background"
: chatValue === "" : chatValue === ""
? "text-primary" ? "text-primary"
: "bg-emerald-600 text-background" : "bg-chat-send text-background"
)} )}
disabled={lockChat} disabled={lockChat}
onClick={() => sendMessage()} onClick={() => sendMessage()}

View file

@ -395,7 +395,7 @@ export default function FormModal({
<span className="pr-2">Chat</span> <span className="pr-2">Chat</span>
<IconComponent <IconComponent
name="prompts" name="prompts"
className="h-6 w-6 pl-1 text-gray-800 dark:text-white" className="h-6 w-6 pl-1 text-foreground"
aria-hidden="true" aria-hidden="true"
/> />
</DialogTitle> </DialogTitle>

View file

@ -1,6 +1,7 @@
import { useContext, useState } from "react"; import { useContext, useState } from "react";
import ShadTooltip from "../../../../components/ShadTooltipComponent"; import ShadTooltip from "../../../../components/ShadTooltipComponent";
import IconComponent from "../../../../components/genericIconComponent"; import IconComponent from "../../../../components/genericIconComponent";
import { Input } from "../../../../components/ui/input";
import { Separator } from "../../../../components/ui/separator"; import { Separator } from "../../../../components/ui/separator";
import { alertContext } from "../../../../contexts/alertContext"; import { alertContext } from "../../../../contexts/alertContext";
import { TabsContext } from "../../../../contexts/tabsContext"; import { TabsContext } from "../../../../contexts/tabsContext";
@ -109,7 +110,7 @@ export default function ExtraSidebar() {
</div> </div>
<Separator /> <Separator />
<div className="side-bar-search-div-placement"> <div className="side-bar-search-div-placement">
<input <Input
type="text" type="text"
name="search" name="search"
id="search" id="search"

View file

@ -2,127 +2,6 @@
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
/* TODO: Confirm that all colors here are found in tailwind config */
@layer base {
:root {
--background: 0 0% 100%; /* hsl(0 0% 100%) */
--foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
--muted: 210 40% 98%; /* hsl(210 40% 98%) */
--muted-foreground: 215.4 16.3% 46.9%; /* hsl(215 16% 46%) */
--popover: 0 0% 100%; /* hsl(0 0% 100%) */
--popover-foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
--card: 0 0% 100%; /* hsl(0 0% 100%) */
--card-foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
--border: 214.3 21.8% 91.4%; /* hsl(214 32% 91%) */
--input: 214.3 21.8% 91.4%; /* hsl(214 32% 91%) */
--primary: 222.2 27% 11.2%; /* hsl(222 27% 18%) */
--primary-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
--secondary: 210 40% 96.1%; /* hsl(210 40% 96%) */
--secondary-foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
--accent: 210 30% 96.1%; /* hsl(210 30% 96%) */
--accent-foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
--destructive: 0 100% 50%; /* hsl(0 100% 50%) */
--destructive-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
--radius: 0.5rem;
--ring: 215 20.2% 65.1%; /* hsl(215 20% 65%) */
--round-btn-shadow: #00000063;
--error-background: #fef2f2;
--error-foreground: #991b1b;
--success-background: #f0fdf4;
--success-foreground: #14532d;
--info-background: #f0f4fd;
--info-foreground: #141653;
--high-indigo: #4338ca;
--medium-indigo: #6366f1;
--chat-bot-icon: #afe6ef;
--chat-user-icon: #aface9;
--beta-background: rgb(219 234 254);
--beta-foreground: rgb(37 99 235);
/* Colors that are shared in dark and light mode */
--blur-shared: #151923de;
--build-trigger: #dc735b;
--chat-trigger: #5c8be1;
--chat-trigger-disabled: #b4c3da;
--status-red: #ef4444;
--status-yellow: #eab308;
--status-green: #4ade80;
--status-blue: #2563eb;
--connection: #555;
}
.dark {
--background: 224 35% 7.5%; /* hsl(224 40% 10%) */
--foreground: 213 31% 80%; /* hsl(213 31% 91%) */
--muted: 223 27% 11%; /* hsl(223 27% 11%) */
--muted-foreground: 215.4 16.3% 56.9%; /* hsl(215 16% 56%) */
--popover: 224 71% 4%; /* hsl(224 71% 4%) */
--popover-foreground: 215 20.2% 65.1%; /* hsl(215 20% 65%) */
--card: 224 25% 15.5%; /* hsl(224 71% 4%) */
--card-foreground: 213 31% 80%; /* hsl(213 31% 91%) */
--border: 216 24% 17%; /* hsl(216 34% 17%) */
--input: 216 24% 17%; /* hsl(216 34% 17%) */
--primary: 210 20% 80%; /* hsl(210 20% 80%) */
--primary-foreground: 222.2 27.4% 1.2%; /* hsl(222 47% 1%) */
--secondary: 222.2 37.4% 7.2%; /* hsl(222 47% 11%) */
--secondary-foreground: 210 40% 80%; /* hsl(210 40% 80%) */
--accent: 216 24% 20%; /* hsl(216 34% 17%) */
--accent-foreground: 210 30% 98%; /* hsl(210 40% 98%) */
--destructive: 0 63% 31%; /* hsl(0 63% 31%) */
--destructive-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
--ring: 216 24% 30%; /* hsl(216 24% 30%) */
--radius: 0.5rem;
--round-btn-shadow: #00000063;
--success-background: #022c22;
--success-foreground: #ecfdf5;
--error-foreground: #fef2f2;
--error-background: #450a0a;
--info-foreground: #eff6ff;
--info-background: #172554;
--high-indigo: #4338ca;
--medium-indigo: #6366f1;
--beta-background: rgb(37 99 235);
--beta-foreground: rgb(219 234 254);
/* Colors that are shared in dark and light mode */
--blur-shared: #151923d2;
--build-trigger: #dc735b;
--chat-trigger: #5c8be1;
--chat-trigger-disabled: #2d3b54;
--status-red: #ef4444;
--status-yellow: #eab308;
--status-green: #4ade80;
--status-blue: #2563eb;
--connection: #555;
--chat-bot-icon: #235d70;
--chat-user-icon: #4f3d6e;
}
}
@layer base { @layer base {
* { * {
@apply border-border; @apply border-border;
@ -134,39 +13,6 @@
} }
} }
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
pre {
font-family: inherit;
}
/* The style below sets the cursor property of the element with the class .react-flow__pane to the default cursor.
The cursor: default; property value restores the browser's default cursor style for the targeted element. By applying this style, the element will no longer have a custom cursor appearance such as "grab" or any other custom cursor defined elsewhere in the application. Instead, it will revert to the default cursor style determined by the browser, typically an arrow-shaped cursor. */
.react-flow__pane {
cursor: default;
}
.AccordionContent {
overflow: hidden;
}
.AccordionContent[data-state="open"] {
animation: slideDown 300ms ease-out;
}
.AccordionContent[data-state="closed"] {
animation: slideUp 300ms ease-out;
}
@keyframes slideDown { @keyframes slideDown {
from { from {
height: 0; height: 0;
@ -185,6 +31,36 @@ The cursor: default; property value restores the browser's default cursor style
} }
} }
@keyframes gradient-motion-start {
0% {
stop-color: rgb(156, 138, 236);
}
50% {
stop-color: rgb(255, 130, 184);
}
80% {
stop-color: rgb(255, 165, 100);
}
100% {
stop-color: rgb(156, 138, 236);
}
}
@keyframes gradient-motion-end {
0% {
stop-color: rgb(156, 138, 236);
}
50% {
stop-color: rgb(255, 165, 100);
}
80% {
stop-color: rgb(255, 130, 184);
}
100% {
stop-color: rgb(156, 138, 236);
}
}
@layer components { @layer components {
.round-buttons-position { .round-buttons-position {
@apply fixed right-4; @apply fixed right-4;
@ -240,15 +116,17 @@ The cursor: default; property value restores the browser's default cursor style
.button-div-style { .button-div-style {
@apply flex gap-2; @apply flex gap-2;
} }
.input-primary:focus {
@apply focus:border-ring focus:placeholder-transparent focus:ring-ring;
}
.input-primary { .input-primary {
@apply form-input block w-full truncate rounded-md border-border bg-background px-3 text-left shadow-sm placeholder:text-muted-foreground sm:text-sm; @apply form-input block w-full truncate rounded-md border-border bg-background px-3 text-left shadow-sm placeholder:text-muted-foreground focus:border-ring focus:placeholder-transparent focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm;
}
/* The same as input-primary but no-truncate */
.textarea-primary {
@apply form-input block w-full rounded-md border-border bg-background px-3 text-left shadow-sm placeholder:text-muted-foreground focus:border-ring focus:placeholder-transparent focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm;
} }
.input-edit-node { .input-edit-node {
@apply input-primary border-border pt-0.5 pb-0.5 text-left w-full @apply input-primary w-full border-border pb-0.5 pt-0.5 text-left;
} }
.input-search { .input-search {
@apply input-primary mx-2 pr-7; @apply input-primary mx-2 pr-7;
@ -375,7 +253,7 @@ The cursor: default; property value restores the browser's default cursor style
} }
.generic-node-status-position { .generic-node-status-position {
@apply relative top-[1.5px] h-5 w-5; @apply relative top-[3px] h-5 w-5;
} }
.generic-node-status-animation { .generic-node-status-animation {
@ -551,7 +429,7 @@ The cursor: default; property value restores the browser's default cursor style
@apply z-10 mt-1 max-h-60 overflow-auto rounded-md bg-background py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm; @apply z-10 mt-1 max-h-60 overflow-auto rounded-md bg-background py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm;
} }
.dropdown-component-true-options { .dropdown-component-true-options {
@apply dropdown-component-options lg:w-[32%] @apply dropdown-component-options lg:w-[32%];
} }
.dropdown-component-false-options { .dropdown-component-false-options {
@apply dropdown-component-options w-full; @apply dropdown-component-options w-full;
@ -597,7 +475,7 @@ The cursor: default; property value restores the browser's default cursor style
} }
.header-arrangement { .header-arrangement {
@apply flex-max-width h-12 items-center justify-between border-b bg-muted; @apply flex-max-width h-12 items-center justify-between border-border bg-muted;
} }
.header-start-display { .header-start-display {
@apply flex w-96 items-center justify-start gap-2; @apply flex w-96 items-center justify-start gap-2;
@ -1117,54 +995,19 @@ The cursor: default; property value restores the browser's default cursor style
.ace-editor-save-btn { .ace-editor-save-btn {
@apply flex-max-width h-fit justify-end; @apply flex-max-width h-fit justify-end;
} }
.export-modal-save-api {
@apply font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70;
}
.beta-badge-wrapper { .beta-badge-wrapper {
@apply absolute right-0 top-0 h-16 w-16 overflow-hidden rounded-tr-lg; @apply absolute right-0 top-0 h-16 w-16 overflow-hidden rounded-tr-lg;
} }
.beta-badge-content { .beta-badge-content {
@apply mt-2 w-24 rotate-45 bg-beta-background text-center text-xs font-semibold text-beta-foreground @apply mt-2 w-24 rotate-45 bg-beta-background text-center text-xs font-semibold text-beta-foreground;
}
.export-modal-save-api {
@apply font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70;
} }
.chat-message-highlight { .chat-message-highlight {
@apply rounded-md bg-indigo-100 px-0.5 dark:bg-indigo-900; @apply rounded-md bg-indigo-100 px-0.5 dark:bg-indigo-900;
} }
} }
@keyframes gradient-motion-start {
0% {
stop-color: rgb(156, 138, 236);
}
50% {
stop-color: rgb(255, 130, 184);
}
80% {
stop-color: rgb(255, 165, 100);
}
100% {
stop-color: rgb(156, 138, 236);
}
}
@keyframes gradient-motion-end {
0% {
stop-color: rgb(156, 138, 236);
}
50% {
stop-color: rgb(255, 165, 100);
}
80% {
stop-color: rgb(255, 130, 184);
}
100% {
stop-color: rgb(156, 138, 236);
}
}
.gradient-end {
animation: gradient-motion-end 3s infinite forwards;
}
.gradient-start {
animation: gradient-motion-start 4s infinite forwards;
}

View file

@ -0,0 +1,38 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
pre {
font-family: inherit;
}
.react-flow__pane {
cursor: default;
}
.AccordionContent {
overflow: hidden;
}
.AccordionContent[data-state="open"] {
animation: slideDown 300ms ease-out;
}
.AccordionContent[data-state="closed"] {
animation: slideUp 300ms ease-out;
}
.gradient-end {
animation: gradient-motion-end 3s infinite forwards;
}
.gradient-start {
animation: gradient-motion-start 4s infinite forwards;
}

View file

@ -0,0 +1,126 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
/* TODO: Confirm that all colors here are found in tailwind config */
@layer base {
:root {
--background: 0 0% 100%; /* hsl(0 0% 100%) */
--foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
--muted: 210 40% 98%; /* hsl(210 40% 98%) */
--muted-foreground: 215.4 16.3% 46.9%; /* hsl(215 16% 46%) */
--popover: 0 0% 100%; /* hsl(0 0% 100%) */
--popover-foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
--card: 0 0% 100%; /* hsl(0 0% 100%) */
--card-foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
--border: 214.3 21.8% 91.4%; /* hsl(214 32% 91%) */
--input: 214.3 21.8% 91.4%; /* hsl(214 32% 91%) */
--primary: 222.2 27% 11.2%; /* hsl(222 27% 18%) */
--primary-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
--secondary: 210 40% 96.1%; /* hsl(210 40% 96%) */
--secondary-foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
--accent: 210 30% 96.1%; /* hsl(210 30% 96%) */
--accent-foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
--destructive: 0 100% 50%; /* hsl(0 100% 50%) */
--destructive-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
--radius: 0.5rem;
--ring: 215 20.2% 65.1%; /* hsl(215 20% 65%) */
--round-btn-shadow: #00000063;
--error-background: #fef2f2;
--error-foreground: #991b1b;
--success-background: #f0fdf4;
--success-foreground: #14532d;
--info-background: #f0f4fd;
--info-foreground: #141653;
--high-indigo: #4338ca;
--medium-indigo: #6366f1;
--low-indigo: #e0e7ff;
--chat-bot-icon: #afe6ef;
--chat-user-icon: #aface9;
/* Colors that are shared in dark and light mode */
--blur-shared: #151923de;
--build-trigger: #dc735b;
--chat-trigger: #5c8be1;
--chat-trigger-disabled: #b4c3da;
--status-red: #ef4444;
--status-yellow: #eab308;
--chat-send: #059669;
--status-green: #4ade80;
--status-blue:#2563eb;
--connection: #555;
}
.dark {
--background: 224 35% 7.5%; /* hsl(224 40% 10%) */
--foreground: 213 31% 80%; /* hsl(213 31% 91%) */
--muted: 223 27% 11%; /* hsl(223 27% 11%) */
--muted-foreground: 215.4 16.3% 56.9%; /* hsl(215 16% 56%) */
--popover: 224 71% 4%; /* hsl(224 71% 4%) */
--popover-foreground: 215 20.2% 65.1%; /* hsl(215 20% 65%) */
--card: 224 25% 15.5%; /* hsl(224 71% 4%) */
--card-foreground: 213 31% 80%; /* hsl(213 31% 91%) */
--border: 216 24% 17%; /* hsl(216 34% 17%) */
--input: 216 24% 17%; /* hsl(216 34% 17%) */
--primary: 210 20% 80%; /* hsl(210 20% 80%) */
--primary-foreground: 222.2 27.4% 1.2%; /* hsl(222 47% 1%) */
--secondary: 222.2 37.4% 7.2%; /* hsl(222 47% 11%) */
--secondary-foreground: 210 40% 80%; /* hsl(210 40% 80%) */
--accent: 216 24% 20%; /* hsl(216 34% 17%) */
--accent-foreground: 210 30% 98%; /* hsl(210 40% 98%) */
--destructive: 0 63% 31%; /* hsl(0 63% 31%) */
--destructive-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
--ring: 216 24% 30%; /* hsl(216 24% 30%) */
--radius: 0.5rem;
--round-btn-shadow: #00000063;
--success-background: #022c22;
--success-foreground: #ecfdf5;
--error-foreground: #fef2f2;
--error-background: #450a0a;
--info-foreground: #eff6ff;
--info-background: #172554;
--high-indigo: #4338ca;
--medium-indigo: #6366f1;
--low-indigo: #e0e7ff;
/* Colors that are shared in dark and light mode */
--blur-shared: #151923d2;
--build-trigger: #dc735b;
--chat-trigger: #5c8be1;
--chat-trigger-disabled: #2d3b54;
--status-red: #ef4444;
--status-yellow: #eab308;
--chat-send: #059669;
--status-green: #4ade80;
--status-blue: #2563eb;
--connection: #555;
--chat-bot-icon: #235d70;
--chat-user-icon: #4f3d6e;
}}

View file

@ -19,7 +19,9 @@ export type SingleAlertComponentType = {
dropItem: AlertItemType; dropItem: AlertItemType;
removeAlert: (index: string) => void; removeAlert: (index: string) => void;
}; };
export type AlertDropdownType = {}; export type AlertDropdownType = {
children: JSX.Element;
};
export type AlertItemType = { export type AlertItemType = {
type: "notice" | "error" | "success"; type: "notice" | "error" | "success";
title: string; title: string;

View file

@ -29,6 +29,8 @@ module.exports = {
}, },
extend: { extend: {
colors: { colors: {
"low-indigo": "var(--low-indigo)",
"chat-send": "var(--chat-send)",
connection: "var(--connection)", connection: "var(--connection)",
"almost-dark-gray": "var(--almost-dark-gray)", "almost-dark-gray": "var(--almost-dark-gray)",
"almost-light-blue": "var(--almost-light-blue)", "almost-light-blue": "var(--almost-light-blue)",