Merge branch 'python_custom_node_component' of github.com:logspace-ai/langflow into python_custom_node_component
This commit is contained in:
commit
1ea05b3584
92 changed files with 4582 additions and 1520 deletions
55
src/frontend/package-lock.json
generated
55
src/frontend/package-lock.json
generated
|
|
@ -20,6 +20,7 @@
|
|||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-label": "^2.0.2",
|
||||
"@radix-ui/react-menubar": "^1.0.3",
|
||||
"@radix-ui/react-popover": "^1.0.6",
|
||||
"@radix-ui/react-progress": "^1.0.3",
|
||||
"@radix-ui/react-separator": "^1.0.3",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
|
|
@ -29,6 +30,7 @@
|
|||
"@tabler/icons-react": "^2.18.0",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@tailwindcss/line-clamp": "^0.4.4",
|
||||
"@types/axios": "^0.14.0",
|
||||
"accordion": "^3.0.2",
|
||||
"ace-builds": "^1.16.0",
|
||||
"add": "^2.0.6",
|
||||
|
|
@ -143,6 +145,8 @@
|
|||
},
|
||||
"node_modules/@babel/compat-data": {
|
||||
"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",
|
||||
"engines": {
|
||||
"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": {
|
||||
"version": "1.1.2",
|
||||
"license": "MIT",
|
||||
|
|
@ -2610,6 +2651,15 @@
|
|||
"dev": true,
|
||||
"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": {
|
||||
"version": "6.0.3",
|
||||
"dev": true,
|
||||
|
|
@ -9533,8 +9583,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/word-wrap": {
|
||||
"version": "1.2.3",
|
||||
"license": "MIT",
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
|
||||
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-label": "^2.0.2",
|
||||
"@radix-ui/react-menubar": "^1.0.3",
|
||||
"@radix-ui/react-popover": "^1.0.6",
|
||||
"@radix-ui/react-progress": "^1.0.3",
|
||||
"@radix-ui/react-separator": "^1.0.3",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
|
|
@ -24,6 +25,7 @@
|
|||
"@tabler/icons-react": "^2.18.0",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@tailwindcss/line-clamp": "^0.4.4",
|
||||
"@types/axios": "^0.14.0",
|
||||
"accordion": "^3.0.2",
|
||||
"ace-builds": "^1.16.0",
|
||||
"add": "^2.0.6",
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ export default function GenericNode({
|
|||
</span>
|
||||
) : (
|
||||
<div className="max-h-96 overflow-auto">
|
||||
{validationStatus.params
|
||||
{typeof validationStatus.params === "string"
|
||||
? validationStatus.params
|
||||
.split("\n")
|
||||
.map((line, index) => <div key={index}>{line}</div>)
|
||||
|
|
|
|||
|
|
@ -1,66 +1,72 @@
|
|||
import { useContext, useRef } from "react";
|
||||
import { useContext, useState } from "react";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "../../components/ui/popover";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
import { AlertDropdownType } from "../../types/alerts";
|
||||
import { useOnClickOutside } from "../hooks/useOnClickOutside";
|
||||
import SingleAlert from "./components/singleAlertComponent";
|
||||
|
||||
export default function AlertDropdown({}: AlertDropdownType) {
|
||||
const { closePopUp } = useContext(PopUpContext);
|
||||
const componentRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Use the custom hook
|
||||
useOnClickOutside(componentRef, () => {
|
||||
closePopUp();
|
||||
});
|
||||
|
||||
export default function AlertDropdown({ children }: AlertDropdownType) {
|
||||
const {
|
||||
notificationList,
|
||||
clearNotificationList,
|
||||
removeFromNotificationList,
|
||||
setNotificationCenter,
|
||||
} = useContext(alertContext);
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={componentRef}
|
||||
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"
|
||||
<Popover
|
||||
open={open}
|
||||
onOpenChange={(k) => {
|
||||
setOpen(k);
|
||||
if (k) setNotificationCenter(false);
|
||||
}}
|
||||
>
|
||||
<div className="text-md flex flex-row justify-between pl-3 font-medium text-foreground">
|
||||
Notifications
|
||||
<div className="flex gap-3 pr-3 ">
|
||||
<button
|
||||
className="text-foreground hover:text-status-red"
|
||||
onClick={() => {
|
||||
closePopUp();
|
||||
setTimeout(clearNotificationList, 100);
|
||||
}}
|
||||
>
|
||||
<IconComponent name="Trash2" className="h-[1.1rem] w-[1.1rem]" />
|
||||
</button>
|
||||
<button
|
||||
className="text-foreground hover:text-status-red"
|
||||
onClick={closePopUp}
|
||||
>
|
||||
<IconComponent name="X" className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-high-foreground mt-3 flex h-full w-full flex-col overflow-y-scroll scrollbar-hide">
|
||||
{notificationList.length !== 0 ? (
|
||||
notificationList.map((alertItem, index) => (
|
||||
<SingleAlert
|
||||
key={alertItem.id}
|
||||
dropItem={alertItem}
|
||||
removeAlert={removeFromNotificationList}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<div className="flex h-full w-full items-center justify-center pb-16 text-ring">
|
||||
No new notifications
|
||||
<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">
|
||||
Notifications
|
||||
<div className="flex gap-3 pr-3 ">
|
||||
<button
|
||||
className="text-foreground hover:text-status-red"
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
setTimeout(clearNotificationList, 100);
|
||||
}}
|
||||
>
|
||||
<IconComponent name="Trash2" className="h-[1.1rem] w-[1.1rem]" />
|
||||
</button>
|
||||
<button
|
||||
className="text-foreground hover:text-status-red"
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<IconComponent name="X" className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-high-foreground mt-3 flex h-full w-full flex-col overflow-y-scroll scrollbar-hide">
|
||||
{notificationList.length !== 0 ? (
|
||||
notificationList.map((alertItem, index) => (
|
||||
<SingleAlert
|
||||
key={alertItem.id}
|
||||
dropItem={alertItem}
|
||||
removeAlert={removeFromNotificationList}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<div className="flex h-full w-full items-center justify-center pb-16 text-ring">
|
||||
No new notifications
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,9 +12,8 @@ import { NodeType } from "../../types/flow";
|
|||
|
||||
export default function Chat({ flow }: ChatType) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [isBuilt, setIsBuilt] = useState(false);
|
||||
const [canOpen, setCanOpen] = useState(false);
|
||||
const { tabsState } = useContext(TabsContext);
|
||||
const { tabsState, isBuilt, setIsBuilt } = useContext(TabsContext);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export default function CodeAreaComponent({
|
|||
editNode
|
||||
? "input-edit-node input-dialog"
|
||||
: (disabled ? " input-disable input-ring " : "") +
|
||||
" input-primary text-muted-foreground "
|
||||
" primary-input text-muted-foreground "
|
||||
}
|
||||
>
|
||||
{myValue !== "" ? myValue : "Type something..."}
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ export default function CodeTabsComponent({
|
|||
</TabsTrigger>
|
||||
))}
|
||||
</TabsList>
|
||||
{Number(activeTab) < 3 && (
|
||||
{Number(activeTab) < 4 && (
|
||||
<div className="float-right mx-1 flex gap-2">
|
||||
<button
|
||||
className="flex items-center gap-1.5 rounded bg-none p-1 text-xs text-gray-500 dark:text-gray-300"
|
||||
|
|
@ -174,15 +174,23 @@ export default function CodeTabsComponent({
|
|||
className="api-modal-tabs-content"
|
||||
key={index} // Remember to add a unique key prop
|
||||
>
|
||||
{index < 3 ? (
|
||||
<SyntaxHighlighter
|
||||
className="mt-0 h-full w-full overflow-auto custom-scroll"
|
||||
language={tab.mode}
|
||||
style={oneDark}
|
||||
>
|
||||
{tab.code}
|
||||
</SyntaxHighlighter>
|
||||
) : index === 3 ? (
|
||||
{index < 4 ? (
|
||||
<>
|
||||
{tab.description && (
|
||||
<div
|
||||
className="mb-2 w-full text-left text-sm"
|
||||
dangerouslySetInnerHTML={{ __html: tab.description }}
|
||||
></div>
|
||||
)}
|
||||
<SyntaxHighlighter
|
||||
className="mt-0 h-full w-full overflow-auto custom-scroll"
|
||||
language={tab.mode}
|
||||
style={oneDark}
|
||||
>
|
||||
{tab.code}
|
||||
</SyntaxHighlighter>
|
||||
</>
|
||||
) : index === 4 ? (
|
||||
<>
|
||||
<div className="api-modal-according-display">
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useEffect } from "react";
|
||||
import { FloatComponentType } from "../../types/components";
|
||||
import { Input } from "../ui/input";
|
||||
|
||||
export default function FloatComponent({
|
||||
value,
|
||||
|
|
@ -19,8 +20,8 @@ export default function FloatComponent({
|
|||
}, [disabled, onChange]);
|
||||
|
||||
return (
|
||||
<div className={"w-full " + (disabled ? "float-component-pointer" : "")}>
|
||||
<input
|
||||
<div className="w-full">
|
||||
<Input
|
||||
type="number"
|
||||
step={step}
|
||||
min={min}
|
||||
|
|
@ -34,12 +35,8 @@ export default function FloatComponent({
|
|||
}}
|
||||
max={max}
|
||||
value={value ?? ""}
|
||||
className={
|
||||
"nopan nodrag noundo nocopy " +
|
||||
(editNode
|
||||
? "input-edit-node"
|
||||
: "input-primary" + (disabled ? " input-disable " : ""))
|
||||
}
|
||||
disabled={disabled}
|
||||
className={editNode ? "input-edit-node" : ""}
|
||||
placeholder={
|
||||
editNode ? "Number 0 to 1" : "Type a number from zero to one"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
import { useContext, useEffect, useState } from "react";
|
||||
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 { USER_PROJECTS_HEADER } from "../../constants/constants";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { darkContext } from "../../contexts/darkContext";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import { typesContext } from "../../contexts/typesContext";
|
||||
import { getRepoStars } from "../../controllers/API";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
import { Button } from "../ui/button";
|
||||
|
|
@ -15,14 +13,9 @@ import { Separator } from "../ui/separator";
|
|||
import MenuBar from "./components/menuBar";
|
||||
|
||||
export default function Header() {
|
||||
const { flows, addFlow, tabId } = useContext(TabsContext);
|
||||
const { openPopUp } = useContext(PopUpContext);
|
||||
const { templates } = useContext(typesContext);
|
||||
const { id } = useParams();
|
||||
const AlertWidth = 384;
|
||||
const { flows, tabId } = useContext(TabsContext);
|
||||
const { dark, setDark } = useContext(darkContext);
|
||||
const { notificationCenter, setNotificationCenter, setErrorData } =
|
||||
useContext(alertContext);
|
||||
const { notificationCenter } = useContext(alertContext);
|
||||
const location = useLocation();
|
||||
|
||||
const [stars, setStars] = useState(null);
|
||||
|
|
@ -111,33 +104,18 @@ export default function Header() {
|
|||
<IconComponent name="MoonIcon" className="side-bar-button-size" />
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
className="extra-side-bar-save-disable relative"
|
||||
onClick={(event: React.MouseEvent<HTMLElement>) => {
|
||||
setNotificationCenter(false);
|
||||
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
|
||||
name="Bell"
|
||||
className="side-bar-button-size"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
<AlertDropdown>
|
||||
<div className="extra-side-bar-save-disable relative">
|
||||
{notificationCenter && (
|
||||
<div className="header-notifications"></div>
|
||||
)}
|
||||
<IconComponent
|
||||
name="Bell"
|
||||
className="side-bar-button-size"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
</AlertDropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { InputComponentType } from "../../types/components";
|
||||
import { classNames } from "../../utils/utils";
|
||||
import { Input } from "../ui/input";
|
||||
|
||||
export default function InputComponent({
|
||||
value,
|
||||
|
|
@ -19,16 +20,15 @@ export default function InputComponent({
|
|||
}, [disabled, onChange]);
|
||||
|
||||
return (
|
||||
<div className={disabled ? "input-component-div" : "relative"}>
|
||||
<input
|
||||
<div className="relative w-full">
|
||||
<Input
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
className={classNames(
|
||||
disabled ? " input-disable " : "",
|
||||
password && !pwdVisible && value !== "" ? " text-clip password " : "",
|
||||
editNode ? " input-edit-node " : " input-primary ",
|
||||
editNode ? " input-edit-node " : "",
|
||||
password && editNode ? "pr-8" : "",
|
||||
password && !editNode ? "pr-10" : "",
|
||||
"nopan nodrag noundo nocopy"
|
||||
password && !editNode ? "pr-10" : ""
|
||||
)}
|
||||
placeholder={password && editNode ? "Key" : "Type something..."}
|
||||
onChange={(e) => {
|
||||
|
|
|
|||
|
|
@ -102,8 +102,8 @@ export default function InputFileComponent({
|
|||
editNode
|
||||
? "input-edit-node input-dialog text-muted-foreground"
|
||||
: disabled
|
||||
? "input-disable input-dialog input-primary"
|
||||
: "input-dialog input-primary text-muted-foreground"
|
||||
? "input-disable input-dialog primary-input"
|
||||
: "input-dialog primary-input text-muted-foreground"
|
||||
}
|
||||
>
|
||||
{myValue !== "" ? myValue : "No file"}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ import { useEffect } from "react";
|
|||
import { InputListComponentType } from "../../types/components";
|
||||
|
||||
import _ from "lodash";
|
||||
import { classNames } from "../../utils/utils";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
import { Input } from "../ui/input";
|
||||
|
||||
export default function InputListComponent({
|
||||
value,
|
||||
|
|
@ -18,23 +20,19 @@ export default function InputListComponent({
|
|||
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
(disabled ? "pointer-events-none cursor-not-allowed" : "") +
|
||||
className={classNames(
|
||||
value.length > 1 && editNode ? "my-1" : "",
|
||||
"flex flex-col gap-3"
|
||||
}
|
||||
)}
|
||||
>
|
||||
{value.map((i, idx) => {
|
||||
return (
|
||||
<div key={idx} className="flex w-full gap-3">
|
||||
<input
|
||||
<Input
|
||||
disabled={disabled}
|
||||
type="text"
|
||||
value={i}
|
||||
className={
|
||||
"nopan nodrag noundo nocopy " +
|
||||
(editNode
|
||||
? "input-edit-node "
|
||||
: "input-primary " + (disabled ? "input-disable" : ""))
|
||||
}
|
||||
className={editNode ? "input-edit-node" : ""}
|
||||
placeholder="Type something..."
|
||||
onChange={(e) => {
|
||||
let newInputList = _.cloneDeep(value);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useEffect } from "react";
|
||||
import { FloatComponentType } from "../../types/components";
|
||||
import { Input } from "../ui/input";
|
||||
|
||||
export default function IntComponent({
|
||||
value,
|
||||
|
|
@ -17,13 +18,8 @@ export default function IntComponent({
|
|||
}, [disabled, onChange]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
"w-full " +
|
||||
(disabled ? "pointer-events-none w-full cursor-not-allowed" : "")
|
||||
}
|
||||
>
|
||||
<input
|
||||
<div className="w-full">
|
||||
<Input
|
||||
onKeyDown={(event) => {
|
||||
if (
|
||||
event.key !== "Backspace" &&
|
||||
|
|
@ -51,12 +47,8 @@ export default function IntComponent({
|
|||
}
|
||||
}}
|
||||
value={value ?? ""}
|
||||
className={
|
||||
"nopan nodrag noundo nocopy " +
|
||||
(editNode
|
||||
? " input-edit-node "
|
||||
: " input-primary " + (disabled ? " input-disable" : ""))
|
||||
}
|
||||
className={editNode ? "input-edit-node" : ""}
|
||||
disabled={disabled}
|
||||
placeholder={editNode ? "Integer number" : "Type an integer number"}
|
||||
onChange={(e) => {
|
||||
onChange(e.target.value);
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export default function PromptAreaComponent({
|
|||
editNode
|
||||
? "input-edit-node input-dialog"
|
||||
: (disabled ? " input-disable text-ring " : "") +
|
||||
" input-primary text-muted-foreground "
|
||||
" primary-input text-muted-foreground "
|
||||
}
|
||||
>
|
||||
{value !== "" ? value : "Type your prompt here..."}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { TypeModal } from "../../constants/enums";
|
|||
import GenericModal from "../../modals/genericModal";
|
||||
import { TextAreaComponentType } from "../../types/components";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
import { Input } from "../ui/input";
|
||||
|
||||
export default function TextAreaComponent({
|
||||
value,
|
||||
|
|
@ -18,42 +19,36 @@ export default function TextAreaComponent({
|
|||
}, [disabled]);
|
||||
|
||||
return (
|
||||
<div className={disabled ? "pointer-events-none w-full " : " w-full"}>
|
||||
<div className="flex w-full items-center">
|
||||
<input
|
||||
<div className="flex w-full items-center">
|
||||
<Input
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
className={editNode ? "input-edit-node" : ""}
|
||||
placeholder={"Type something..."}
|
||||
onChange={(e) => {
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<div>
|
||||
<GenericModal
|
||||
type={TypeModal.TEXT}
|
||||
buttonText="Finishing Editing"
|
||||
modalTitle="Edit Text"
|
||||
value={value}
|
||||
className={
|
||||
(editNode
|
||||
? " input-edit-node "
|
||||
: " input-primary " + (disabled ? " input-disable" : "")) +
|
||||
" nopan nodrag noundo nocopy w-full"
|
||||
}
|
||||
placeholder={"Type something..."}
|
||||
onChange={(e) => {
|
||||
onChange(e.target.value);
|
||||
setValue={(t: string) => {
|
||||
onChange(t);
|
||||
}}
|
||||
/>
|
||||
<div>
|
||||
<GenericModal
|
||||
type={TypeModal.TEXT}
|
||||
buttonText="Finishing Editing"
|
||||
modalTitle="Edit Text"
|
||||
value={value}
|
||||
setValue={(t: string) => {
|
||||
onChange(t);
|
||||
}}
|
||||
>
|
||||
{!editNode && (
|
||||
<IconComponent
|
||||
name="ExternalLink"
|
||||
className={
|
||||
"icons-parameters-comp" +
|
||||
(disabled ? " text-ring" : " hover:text-accent-foreground")
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</GenericModal>
|
||||
</div>
|
||||
>
|
||||
{!editNode && (
|
||||
<IconComponent
|
||||
name="ExternalLink"
|
||||
className={
|
||||
"icons-parameters-comp" +
|
||||
(disabled ? " text-ring" : " hover:text-accent-foreground")
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</GenericModal>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -9,10 +9,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|||
return (
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
"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
|
||||
)}
|
||||
className={cn("nopan nodrag noundo nocopy primary-input", className)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
|
|
|
|||
30
src/frontend/src/components/ui/popover.tsx
Normal file
30
src/frontend/src/components/ui/popover.tsx
Normal 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 };
|
||||
|
|
@ -8,10 +8,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|||
({ className, ...props }, ref) => {
|
||||
return (
|
||||
<textarea
|
||||
className={cn(
|
||||
"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
|
||||
)}
|
||||
className={cn("nopan nodrag noundo nocopy textarea-primary", className)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -491,3 +491,14 @@ export const NOUNS: string[] = [
|
|||
*
|
||||
*/
|
||||
export const USER_PROJECTS_HEADER = "My Collection";
|
||||
|
||||
/**
|
||||
* URLs excluded from error retries.
|
||||
* @constant
|
||||
*
|
||||
*/
|
||||
export const URL_EXCLUDED_FROM_ERROR_RETRIES = [
|
||||
"/api/v1/validate/code",
|
||||
"/api/v1/custom_component",
|
||||
"/api/v1/validate/prompt",
|
||||
];
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import { SSEProvider } from "./SSEContext";
|
|||
import { AlertProvider } from "./alertContext";
|
||||
import { DarkProvider } from "./darkContext";
|
||||
import { LocationProvider } from "./locationContext";
|
||||
import PopUpProvider from "./popUpContext";
|
||||
import { TabsProvider } from "./tabsContext";
|
||||
import { TypesProvider } from "./typesContext";
|
||||
import { UndoRedoProvider } from "./undoRedoContext";
|
||||
|
|
@ -22,9 +21,7 @@ export default function ContextWrapper({ children }: { children: ReactNode }) {
|
|||
<AlertProvider>
|
||||
<SSEProvider>
|
||||
<TabsProvider>
|
||||
<UndoRedoProvider>
|
||||
<PopUpProvider>{children}</PopUpProvider>
|
||||
</UndoRedoProvider>
|
||||
<UndoRedoProvider>{children}</UndoRedoProvider>
|
||||
</TabsProvider>
|
||||
</SSEProvider>
|
||||
</AlertProvider>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -40,6 +40,8 @@ const TabsContextInitialValue: TabsContextType = {
|
|||
downloadFlows: () => {},
|
||||
uploadFlows: () => {},
|
||||
uploadFlow: () => {},
|
||||
isBuilt: false,
|
||||
setIsBuilt: (state: boolean) => {},
|
||||
hardReset: () => {},
|
||||
saveFlow: async (flow: FlowType) => {},
|
||||
lastCopiedSelection: null,
|
||||
|
|
@ -583,10 +585,14 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
}
|
||||
}
|
||||
|
||||
const [isBuilt, setIsBuilt] = useState(false);
|
||||
|
||||
return (
|
||||
<TabsContext.Provider
|
||||
value={{
|
||||
saveFlow,
|
||||
isBuilt,
|
||||
setIsBuilt,
|
||||
lastCopiedSelection,
|
||||
setLastCopiedSelection,
|
||||
hardReset,
|
||||
|
|
|
|||
|
|
@ -71,18 +71,7 @@ export function TypesProvider({ children }: { children: ReactNode }) {
|
|||
// Clear the interval if successful.
|
||||
clearInterval(intervalId);
|
||||
} catch (error) {
|
||||
retryCount++;
|
||||
// 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.");
|
||||
}
|
||||
console.error("An error has occurred while fetching types.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
62
src/frontend/src/controllers/API/api.tsx
Normal file
62
src/frontend/src/controllers/API/api.tsx
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import axios, { AxiosError, AxiosInstance } from "axios";
|
||||
import { useContext, useEffect, useRef } from "react";
|
||||
import { URL_EXCLUDED_FROM_ERROR_RETRIES } from "../../constants/constants";
|
||||
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) => {
|
||||
if (URL_EXCLUDED_FROM_ERROR_RETRIES.includes(error.config?.url)) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
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 };
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import axios, { AxiosResponse } from "axios";
|
||||
import { AxiosResponse } from "axios";
|
||||
import { ReactFlowJsonObject } from "reactflow";
|
||||
import { api } from "../../controllers/API/api";
|
||||
import { APIObjectType, sendAllProps } from "../../types/api/index";
|
||||
import { FlowStyleType, FlowType } from "../../types/flow";
|
||||
import {
|
||||
|
|
@ -17,16 +18,14 @@ import {
|
|||
* @returns {Promise<AxiosResponse<APIObjectType>>} A promise that resolves to an AxiosResponse containing all the objects.
|
||||
*/
|
||||
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";
|
||||
|
||||
export async function getRepoStars(owner, repo) {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${GITHUB_API_URL}/repos/${owner}/${repo}`
|
||||
);
|
||||
const response = await api.get(`${GITHUB_API_URL}/repos/${owner}/${repo}`);
|
||||
return response.data.stargazers_count;
|
||||
} catch (error) {
|
||||
console.error("Error fetching repository data:", error);
|
||||
|
|
@ -41,13 +40,13 @@ export async function getRepoStars(owner, repo) {
|
|||
* @returns {AxiosResponse<any>} The API response.
|
||||
*/
|
||||
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(
|
||||
code: string
|
||||
): 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,
|
||||
frontend_node: APIClassType
|
||||
): Promise<AxiosResponse<PromptTypeAPI>> {
|
||||
return await axios.post("/api/v1/validate/prompt", {
|
||||
return await api.post("/api/v1/validate/prompt", {
|
||||
name: name,
|
||||
template: template,
|
||||
frontend_node: frontend_node,
|
||||
|
|
@ -77,14 +76,14 @@ export async function postValidatePrompt(
|
|||
export async function getExamples(): Promise<FlowType[]> {
|
||||
const url =
|
||||
"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) => {
|
||||
return file.name.endsWith(".json");
|
||||
});
|
||||
|
||||
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;
|
||||
});
|
||||
|
||||
|
|
@ -106,11 +105,12 @@ export async function saveFlowToDatabase(newFlow: {
|
|||
style?: FlowStyleType;
|
||||
}): Promise<FlowType> {
|
||||
try {
|
||||
const response = await axios.post("/api/v1/flows/", {
|
||||
const response = await api.post("/api/v1/flows/", {
|
||||
name: newFlow.name,
|
||||
data: newFlow.data,
|
||||
description: newFlow.description,
|
||||
});
|
||||
|
||||
if (response.status !== 201) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
|
@ -131,7 +131,7 @@ export async function updateFlowInDatabase(
|
|||
updatedFlow: FlowType
|
||||
): Promise<FlowType> {
|
||||
try {
|
||||
const response = await axios.patch(`/api/v1/flows/${updatedFlow.id}`, {
|
||||
const response = await api.patch(`/api/v1/flows/${updatedFlow.id}`, {
|
||||
name: updatedFlow.name,
|
||||
data: updatedFlow.data,
|
||||
description: updatedFlow.description,
|
||||
|
|
@ -155,7 +155,7 @@ export async function updateFlowInDatabase(
|
|||
*/
|
||||
export async function readFlowsFromDatabase() {
|
||||
try {
|
||||
const response = await axios.get("/api/v1/flows/");
|
||||
const response = await api.get("/api/v1/flows/");
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
|
@ -168,7 +168,7 @@ export async function readFlowsFromDatabase() {
|
|||
|
||||
export async function downloadFlowsFromDatabase() {
|
||||
try {
|
||||
const response = await axios.get("/api/v1/flows/download/");
|
||||
const response = await api.get("/api/v1/flows/download/");
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
|
@ -181,7 +181,7 @@ export async function downloadFlowsFromDatabase() {
|
|||
|
||||
export async function uploadFlowsToDatabase(flows) {
|
||||
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) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
|
|
@ -202,7 +202,7 @@ export async function uploadFlowsToDatabase(flows) {
|
|||
*/
|
||||
export async function deleteFlowFromDatabase(flowId: string) {
|
||||
try {
|
||||
const response = await axios.delete(`/api/v1/flows/${flowId}`);
|
||||
const response = await api.delete(`/api/v1/flows/${flowId}`);
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
|
@ -222,7 +222,7 @@ export async function deleteFlowFromDatabase(flowId: string) {
|
|||
*/
|
||||
export async function getFlowFromDatabase(flowId: number) {
|
||||
try {
|
||||
const response = await axios.get(`/api/v1/flows/${flowId}`);
|
||||
const response = await api.get(`/api/v1/flows/${flowId}`);
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
|
@ -241,7 +241,7 @@ export async function getFlowFromDatabase(flowId: number) {
|
|||
*/
|
||||
export async function getFlowStylesFromDatabase() {
|
||||
try {
|
||||
const response = await axios.get("/api/v1/flow_styles/");
|
||||
const response = await api.get("/api/v1/flow_styles/");
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
|
@ -261,7 +261,7 @@ export async function getFlowStylesFromDatabase() {
|
|||
*/
|
||||
export async function saveFlowStyleToDatabase(flowStyle: FlowStyleType) {
|
||||
try {
|
||||
const response = await axios.post("/api/v1/flow_styles/", flowStyle, {
|
||||
const response = await api.post("/api/v1/flow_styles/", flowStyle, {
|
||||
headers: {
|
||||
accept: "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.
|
||||
*/
|
||||
export async function getVersion() {
|
||||
const respnose = await axios.get("/api/v1/version");
|
||||
const respnose = await api.get("/api/v1/version");
|
||||
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.
|
||||
*/
|
||||
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(
|
||||
flowId: string
|
||||
): Promise<BuildStatusTypeAPI> {
|
||||
return await axios.get(`/api/v1/build/${flowId}/status`);
|
||||
return await api.get(`/api/v1/build/${flowId}/status`);
|
||||
}
|
||||
|
||||
//docs for postbuildinit
|
||||
|
|
@ -319,7 +319,7 @@ export async function getBuildStatus(
|
|||
export async function postBuildInit(
|
||||
flow: FlowType
|
||||
): 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}`, {
|
||||
|
|
@ -337,12 +337,12 @@ export async function uploadFile(
|
|||
): Promise<AxiosResponse<UploadFileTypeAPI>> {
|
||||
const formData = new FormData();
|
||||
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(
|
||||
code: string,
|
||||
apiClass: APIClassType
|
||||
): Promise<AxiosResponse<APIClassType>> {
|
||||
return await axios.post(`/api/v1/custom_component`, { code });
|
||||
return await api.post(`/api/v1/custom_component`, { code });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,13 @@ import App from "./App";
|
|||
import ContextWrapper from "./contexts";
|
||||
import reportWebVitals from "./reportWebVitals";
|
||||
|
||||
import "./index.css";
|
||||
import { ApiInterceptor } from "./controllers/API/api";
|
||||
// @ts-ignore
|
||||
import "./style/index.css";
|
||||
// @ts-ignore
|
||||
import "./style/applies.css";
|
||||
// @ts-ignore
|
||||
import "./style/classes.css";
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById("root") as HTMLElement
|
||||
|
|
@ -13,6 +19,7 @@ root.render(
|
|||
<ContextWrapper>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
<ApiInterceptor />
|
||||
</BrowserRouter>
|
||||
</ContextWrapper>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import {
|
|||
getCurlCode,
|
||||
getPythonApiCode,
|
||||
getPythonCode,
|
||||
getWidgetCode,
|
||||
} from "../../utils/utils";
|
||||
import BaseModal from "../baseModal";
|
||||
|
||||
|
|
@ -29,9 +30,11 @@ const ApiModal = forwardRef(
|
|||
{
|
||||
flow,
|
||||
children,
|
||||
disable,
|
||||
}: {
|
||||
flow: FlowType;
|
||||
children: ReactNode;
|
||||
disable: boolean;
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
|
|
@ -43,6 +46,7 @@ const ApiModal = forwardRef(
|
|||
const pythonApiCode = getPythonApiCode(flow, tweak.current, tabsState);
|
||||
const curl_code = getCurlCode(flow, tweak.current, tabsState);
|
||||
const pythonCode = getPythonCode(flow, tweak.current, tabsState);
|
||||
const widgetCode = getWidgetCode(flow, tabsState);
|
||||
const tweaksCode = buildTweaks(flow);
|
||||
const [tabs, setTabs] = useState([
|
||||
{
|
||||
|
|
@ -67,6 +71,15 @@ const ApiModal = forwardRef(
|
|||
language: "py",
|
||||
code: pythonCode,
|
||||
},
|
||||
{
|
||||
name: "Chat Widget HTML",
|
||||
description:
|
||||
"Insert this code anywhere in your <body> tag. To use with react and other libs, check our <a class='link-color' href='https://langflow.org/guidelines/widget'>documentation</a>.",
|
||||
mode: "html",
|
||||
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
|
||||
language: "py",
|
||||
code: widgetCode,
|
||||
},
|
||||
]);
|
||||
|
||||
function startState() {
|
||||
|
|
@ -111,6 +124,15 @@ const ApiModal = forwardRef(
|
|||
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
|
||||
code: pythonCode,
|
||||
},
|
||||
{
|
||||
name: "Chat Widget HTML",
|
||||
description:
|
||||
"Insert this code anywhere in your <body> tag. To use with react and other libs, check our <a class='link-color' href='https://langflow.org/guidelines/widget'>documentation</a>.",
|
||||
mode: "html",
|
||||
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
|
||||
language: "py",
|
||||
code: widgetCode,
|
||||
},
|
||||
{
|
||||
name: "Tweaks",
|
||||
mode: "python",
|
||||
|
|
@ -143,6 +165,15 @@ const ApiModal = forwardRef(
|
|||
language: "py",
|
||||
code: pythonCode,
|
||||
},
|
||||
{
|
||||
name: "Chat Widget HTML",
|
||||
description:
|
||||
"Insert this code anywhere in your <body> tag. To use with react and other libs, check our <a class='link-color' href='https://langflow.org/guidelines/widget'>documentation</a>.",
|
||||
mode: "html",
|
||||
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
|
||||
language: "py",
|
||||
code: widgetCode,
|
||||
},
|
||||
]);
|
||||
}
|
||||
}, [flow["data"]["nodes"], open]);
|
||||
|
|
@ -210,13 +241,15 @@ const ApiModal = forwardRef(
|
|||
tweak.current.push(newTweak);
|
||||
}
|
||||
|
||||
const pythonApiCode = getPythonApiCode(flow, tweak.current);
|
||||
const curl_code = getCurlCode(flow, tweak.current);
|
||||
const pythonCode = getPythonCode(flow, tweak.current);
|
||||
const pythonApiCode = getPythonApiCode(flow, tweak.current, tabsState);
|
||||
const curl_code = getCurlCode(flow, tweak.current, tabsState);
|
||||
const pythonCode = getPythonCode(flow, tweak.current, tabsState);
|
||||
const widgetCode = getWidgetCode(flow, tabsState);
|
||||
|
||||
tabs[0].code = curl_code;
|
||||
tabs[1].code = pythonApiCode;
|
||||
tabs[2].code = pythonCode;
|
||||
tabs[3].code = widgetCode;
|
||||
|
||||
setTweak(tweak.current);
|
||||
}
|
||||
|
|
@ -253,7 +286,7 @@ const ApiModal = forwardRef(
|
|||
}
|
||||
|
||||
return (
|
||||
<BaseModal open={open} setOpen={setOpen}>
|
||||
<BaseModal open={open} setOpen={setOpen} disable={disable}>
|
||||
<BaseModal.Trigger>{children}</BaseModal.Trigger>
|
||||
<BaseModal.Header description={EXPORT_CODE_DIALOG}>
|
||||
<span className="pr-2">Code</span>
|
||||
|
|
|
|||
|
|
@ -46,11 +46,13 @@ interface BaseModalProps {
|
|||
];
|
||||
open?: boolean;
|
||||
setOpen?: (open: boolean) => void;
|
||||
disable?: boolean;
|
||||
size?: "smaller" | "small" | "medium" | "large" | "large-h-full";
|
||||
}
|
||||
function BaseModal({
|
||||
open,
|
||||
setOpen,
|
||||
disable = false,
|
||||
children,
|
||||
size = "large",
|
||||
}: BaseModalProps) {
|
||||
|
|
@ -99,7 +101,10 @@ function BaseModal({
|
|||
//UPDATE COLORS AND STYLE CLASSSES
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger className="w-full" hidden={triggerChild ? false : true}>
|
||||
<DialogTrigger
|
||||
className={"w-full " + (disable ? "button-disable" : "")}
|
||||
hidden={triggerChild ? false : true}
|
||||
>
|
||||
{triggerChild}
|
||||
</DialogTrigger>
|
||||
<DialogContent className={minWidth}>
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ export default function CodeAreaModal({
|
|||
if (data) {
|
||||
setNodeClass(data);
|
||||
setValue(code);
|
||||
setError({ detail: { error: undefined, traceback: undefined } });
|
||||
setOpen(false);
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useEffect } from "react";
|
||||
import IconComponent from "../../../components/genericIconComponent";
|
||||
import { Textarea } from "../../../components/ui/textarea";
|
||||
import { classNames } from "../../../utils/utils";
|
||||
|
||||
export default function ChatInput({
|
||||
|
|
@ -25,7 +26,7 @@ export default function ChatInput({
|
|||
|
||||
return (
|
||||
<div className="relative">
|
||||
<textarea
|
||||
<Textarea
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Enter" && !lockChat && !event.shiftKey) {
|
||||
sendMessage();
|
||||
|
|
@ -68,10 +69,10 @@ export default function ChatInput({
|
|||
className={classNames(
|
||||
"form-modal-send-button",
|
||||
noInput
|
||||
? "bg-indigo-600 text-background"
|
||||
? "bg-high-indigo text-background"
|
||||
: chatValue === ""
|
||||
? "text-primary"
|
||||
: "bg-emerald-600 text-background"
|
||||
: "bg-chat-send text-background"
|
||||
)}
|
||||
disabled={lockChat}
|
||||
onClick={() => sendMessage()}
|
||||
|
|
|
|||
|
|
@ -395,7 +395,7 @@ export default function FormModal({
|
|||
<span className="pr-2">Chat</span>
|
||||
<IconComponent
|
||||
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"
|
||||
/>
|
||||
</DialogTitle>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useContext, useState } from "react";
|
||||
import ShadTooltip from "../../../../components/ShadTooltipComponent";
|
||||
import IconComponent from "../../../../components/genericIconComponent";
|
||||
import { Input } from "../../../../components/ui/input";
|
||||
import { Separator } from "../../../../components/ui/separator";
|
||||
import { alertContext } from "../../../../contexts/alertContext";
|
||||
import { TabsContext } from "../../../../contexts/tabsContext";
|
||||
|
|
@ -18,7 +19,7 @@ import DisclosureComponent from "../DisclosureComponent";
|
|||
|
||||
export default function ExtraSidebar() {
|
||||
const { data } = useContext(typesContext);
|
||||
const { flows, tabId, uploadFlow, tabsState, saveFlow } =
|
||||
const { flows, tabId, uploadFlow, tabsState, saveFlow, isBuilt } =
|
||||
useContext(TabsContext);
|
||||
const { setSuccessData, setErrorData } = useContext(alertContext);
|
||||
const [dataFilter, setFilterData] = useState(data);
|
||||
|
|
@ -60,56 +61,72 @@ export default function ExtraSidebar() {
|
|||
return (
|
||||
<div className="side-bar-arrangement">
|
||||
<div className="side-bar-buttons-arrangement">
|
||||
<ShadTooltip content="Import" side="top">
|
||||
<button
|
||||
className="extra-side-bar-buttons"
|
||||
onClick={() => {
|
||||
uploadFlow();
|
||||
}}
|
||||
>
|
||||
<IconComponent name="FileUp" className="side-bar-button-size " />
|
||||
</button>
|
||||
</ShadTooltip>
|
||||
|
||||
<ShadTooltip content="Export" side="top">
|
||||
<div className="side-bar-button">
|
||||
<ShadTooltip content="Import" side="top">
|
||||
<button
|
||||
className="extra-side-bar-buttons"
|
||||
onClick={() => {
|
||||
uploadFlow();
|
||||
}}
|
||||
>
|
||||
<IconComponent name="FileUp" className="side-bar-button-size " />
|
||||
</button>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
<div className="side-bar-button">
|
||||
<ExportModal>
|
||||
<div className={classNames("extra-side-bar-buttons")}>
|
||||
<IconComponent name="FileDown" className="side-bar-button-size" />
|
||||
</div>
|
||||
</ExportModal>
|
||||
</ShadTooltip>
|
||||
<ShadTooltip content="Code" side="top">
|
||||
{flow && flow.data && (
|
||||
<ApiModal flow={flow}>
|
||||
<ShadTooltip content="Export" side="top">
|
||||
<div className={classNames("extra-side-bar-buttons")}>
|
||||
<IconComponent name="Code2" className="side-bar-button-size" />
|
||||
<IconComponent
|
||||
name="FileDown"
|
||||
className="side-bar-button-size"
|
||||
/>
|
||||
</div>
|
||||
</ApiModal>
|
||||
)}
|
||||
</ShadTooltip>
|
||||
</ExportModal>
|
||||
</div>
|
||||
<ShadTooltip content={"Code"} side="top">
|
||||
<div className="side-bar-button">
|
||||
{flow && flow.data && (
|
||||
<ApiModal flow={flow} disable={!isBuilt}>
|
||||
<div className={classNames("extra-side-bar-buttons")}>
|
||||
<IconComponent
|
||||
name="Code2"
|
||||
className={
|
||||
"side-bar-button-size" +
|
||||
(isBuilt ? " " : " extra-side-bar-save-disable")
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</ApiModal>
|
||||
)}
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
|
||||
<ShadTooltip content="Save" side="top">
|
||||
<button
|
||||
className="extra-side-bar-buttons"
|
||||
onClick={(event) => {
|
||||
saveFlow(flow);
|
||||
setSuccessData({ title: "Changes saved successfully" });
|
||||
}}
|
||||
disabled={!isPending}
|
||||
>
|
||||
<IconComponent
|
||||
name="Save"
|
||||
<div className="side-bar-button">
|
||||
<ShadTooltip content="Save" side="top">
|
||||
<button
|
||||
className={
|
||||
"side-bar-button-size" +
|
||||
(isPending ? " " : " extra-side-bar-save-disable")
|
||||
"extra-side-bar-buttons " + (isPending ? "" : "button-disable")
|
||||
}
|
||||
/>
|
||||
</button>
|
||||
</ShadTooltip>
|
||||
onClick={(event) => {
|
||||
saveFlow(flow);
|
||||
setSuccessData({ title: "Changes saved successfully" });
|
||||
}}
|
||||
>
|
||||
<IconComponent
|
||||
name="Save"
|
||||
className={
|
||||
"side-bar-button-size" +
|
||||
(isPending ? " " : " extra-side-bar-save-disable")
|
||||
}
|
||||
/>
|
||||
</button>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
</div>
|
||||
<Separator />
|
||||
<div className="side-bar-search-div-placement">
|
||||
<input
|
||||
<Input
|
||||
type="text"
|
||||
name="search"
|
||||
id="search"
|
||||
|
|
|
|||
|
|
@ -2,127 +2,6 @@
|
|||
@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;
|
||||
|
||||
--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 {
|
||||
* {
|
||||
@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 {
|
||||
from {
|
||||
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 {
|
||||
.round-buttons-position {
|
||||
@apply fixed right-4;
|
||||
|
|
@ -231,6 +107,12 @@ The cursor: default; property value restores the browser's default cursor style
|
|||
.side-bar-buttons-arrangement {
|
||||
@apply mb-2 mt-2 flex w-full items-center justify-between gap-2 px-2;
|
||||
}
|
||||
.side-bar-button {
|
||||
@apply flex w-full;
|
||||
}
|
||||
.button-disable {
|
||||
@apply pointer-events-none;
|
||||
}
|
||||
.extra-side-bar-buttons {
|
||||
@apply relative inline-flex w-full items-center justify-center rounded-md bg-background px-2 py-2 text-foreground shadow-sm ring-1 ring-inset ring-input transition-all duration-500 ease-in-out;
|
||||
}
|
||||
|
|
@ -240,18 +122,20 @@ The cursor: default; property value restores the browser's default cursor style
|
|||
.button-div-style {
|
||||
@apply flex gap-2;
|
||||
}
|
||||
.input-primary:focus {
|
||||
@apply focus:border-ring focus:placeholder-transparent focus:ring-ring;
|
||||
}
|
||||
.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;
|
||||
.primary-input {
|
||||
@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;
|
||||
}
|
||||
|
||||
.input-edit-node{
|
||||
@apply input-primary border-border pt-0.5 pb-0.5 text-left w-full
|
||||
/* The same as primary-input 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 {
|
||||
@apply primary-input w-full pb-0.5 pt-0.5 text-left;
|
||||
}
|
||||
.input-search {
|
||||
@apply input-primary mx-2 pr-7;
|
||||
@apply primary-input mx-2 pr-7;
|
||||
}
|
||||
.input-disable {
|
||||
@apply border-transparent bg-border placeholder:text-ring;
|
||||
|
|
@ -375,7 +259,7 @@ The cursor: default; property value restores the browser's default cursor style
|
|||
}
|
||||
|
||||
.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 {
|
||||
|
|
@ -536,7 +420,7 @@ The cursor: default; property value restores the browser's default cursor style
|
|||
@apply input-edit-node relative pr-8;
|
||||
}
|
||||
.dropdown-component-false-outline {
|
||||
@apply input-primary py-2 pl-3 pr-10 text-left;
|
||||
@apply primary-input py-2 pl-3 pr-10 text-left;
|
||||
}
|
||||
.dropdown-component-display {
|
||||
@apply block w-full truncate bg-background;
|
||||
|
|
@ -551,7 +435,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;
|
||||
}
|
||||
.dropdown-component-true-options {
|
||||
@apply dropdown-component-options lg:w-[32%]
|
||||
@apply dropdown-component-options lg:w-[32%];
|
||||
}
|
||||
.dropdown-component-false-options {
|
||||
@apply dropdown-component-options w-full;
|
||||
|
|
@ -597,7 +481,7 @@ The cursor: default; property value restores the browser's default cursor style
|
|||
}
|
||||
|
||||
.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 {
|
||||
@apply flex w-96 items-center justify-start gap-2;
|
||||
|
|
@ -902,6 +786,9 @@ The cursor: default; property value restores the browser's default cursor style
|
|||
.node-modal-button-box {
|
||||
@apply flex-max-width flex-row-reverse bg-input px-4 pb-3;
|
||||
}
|
||||
.link-color {
|
||||
@apply font-semibold text-foreground;
|
||||
}
|
||||
.node-modal-button {
|
||||
@apply inline-flex w-full justify-center rounded-md border border-transparent bg-status-red px-4 py-2 text-base font-medium text-background shadow-sm hover:bg-ring sm:ml-3 sm:w-auto sm:text-sm;
|
||||
}
|
||||
|
|
@ -1117,54 +1004,19 @@ The cursor: default; property value restores the browser's default cursor style
|
|||
.ace-editor-save-btn {
|
||||
@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 {
|
||||
@apply absolute right-0 top-0 h-16 w-16 overflow-hidden rounded-tr-lg;
|
||||
}
|
||||
.beta-badge-content {
|
||||
@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;
|
||||
@apply mt-2 w-24 rotate-45 bg-beta-background text-center text-xs font-semibold text-beta-foreground;
|
||||
}
|
||||
|
||||
.chat-message-highlight {
|
||||
@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;
|
||||
}
|
||||
38
src/frontend/src/style/classes.css
Normal file
38
src/frontend/src/style/classes.css
Normal 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;
|
||||
}
|
||||
128
src/frontend/src/style/index.css
Normal file
128
src/frontend/src/style/index.css
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
@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;
|
||||
|
||||
--beta-background: rgb(219 234 254);
|
||||
--beta-foreground: rgb(37 99 235);
|
||||
|
||||
--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;
|
||||
|
||||
--beta-background: rgb(37 99 235);
|
||||
--beta-foreground: rgb(219 234 254);
|
||||
|
||||
--chat-bot-icon: #235d70;
|
||||
--chat-user-icon: #4f3d6e;
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,9 @@ export type SingleAlertComponentType = {
|
|||
dropItem: AlertItemType;
|
||||
removeAlert: (index: string) => void;
|
||||
};
|
||||
export type AlertDropdownType = {};
|
||||
export type AlertDropdownType = {
|
||||
children: JSX.Element;
|
||||
};
|
||||
export type AlertItemType = {
|
||||
type: "notice" | "error" | "success";
|
||||
title: string;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ export type TabsContextType = {
|
|||
) => void;
|
||||
downloadFlows: () => void;
|
||||
uploadFlows: () => void;
|
||||
isBuilt: boolean;
|
||||
setIsBuilt: (state: boolean) => void;
|
||||
uploadFlow: (newFlow?: boolean, file?: File) => void;
|
||||
hardReset: () => void;
|
||||
getNodeId: (nodeType: string) => string;
|
||||
|
|
|
|||
|
|
@ -467,7 +467,7 @@ export function getCurlCode(
|
|||
|
||||
/**
|
||||
* Function to get the python code for the API
|
||||
* @param {string} flowName - The name of the flow
|
||||
* @param {string} flow - The current flow
|
||||
* @returns {string} - The python code
|
||||
*/
|
||||
export function getPythonCode(
|
||||
|
|
@ -489,3 +489,33 @@ flow = load_flow_from_json("${flowName}.json", tweaks=TWEAKS)
|
|||
inputs = ${inputs}
|
||||
flow(inputs)`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to get the widget code for the API
|
||||
* @param {string} flow - The current flow.
|
||||
* @returns {string} - The widget code
|
||||
*/
|
||||
export function getWidgetCode(flow: FlowType, tabsState?: TabsState): string {
|
||||
const flowId = flow.id;
|
||||
const flowName = flow.name;
|
||||
const inputs = buildInputs(tabsState, flow.id);
|
||||
|
||||
return `<script src="https://cdn.jsdelivr.net/gh/logspace-ai/langflow-embedded-chat@main/dist/build/static/js/bundle.min.js"></script>
|
||||
|
||||
<!-- chat_inputs: Stringified JSON with all the input keys and its values. The value of the key that is defined
|
||||
as chat_input_field will be overwritten by the chat message.
|
||||
chat_input_field: Input key that you want the chat to send the user message with. -->
|
||||
<langflow-chat
|
||||
window_title="${flowName}"
|
||||
flow_id="${flowId}"
|
||||
${
|
||||
tabsState[flow.id] && tabsState[flow.id].formKeysData
|
||||
? `chat_inputs='${inputs}'
|
||||
chat_input_field="${
|
||||
Object.keys(tabsState[flow.id].formKeysData.input_keys)[0]
|
||||
}"
|
||||
`
|
||||
: ""
|
||||
}host_url="http://localhost:7860"
|
||||
></langflow-chat>`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ module.exports = {
|
|||
},
|
||||
extend: {
|
||||
colors: {
|
||||
"low-indigo": "var(--low-indigo)",
|
||||
"chat-send": "var(--chat-send)",
|
||||
connection: "var(--connection)",
|
||||
"almost-dark-gray": "var(--almost-dark-gray)",
|
||||
"almost-light-blue": "var(--almost-light-blue)",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue