Merge remote-tracking branch 'origin/dev' into db

This commit is contained in:
Gabriel Luiz Freitas Almeida 2023-06-09 14:51:18 -03:00
commit 79d258d7fa
39 changed files with 4583 additions and 4769 deletions

0
.githooks/pre-commit Executable file → Normal file
View file

11
.vscode/launch.json vendored
View file

@ -1,7 +1,7 @@
{ {
"configurations": [ "configurations": [
{ {
"name": "Python: FastAPI", "name": "Debug Backend",
"type": "python", "type": "python",
"request": "launch", "request": "launch",
"module": "uvicorn", "module": "uvicorn",
@ -14,7 +14,7 @@
"debug" "debug"
], ],
"jinja": true, "jinja": true,
"justMyCode": false "justMyCode": true
}, },
{ {
"name": "Python: Remote Attach", "name": "Python: Remote Attach",
@ -30,6 +30,13 @@
"remoteRoot": "." "remoteRoot": "."
} }
] ]
},
{
"name": "Debug Frontend",
"type": "chrome",
"request": "launch",
"url": "http://localhost:3000/*",
"webRoot": "${workspaceRoot}/src/frontend"
} }
] ]
} }

2301
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,7 +0,0 @@
{
"dependencies": {
"@radix-ui/react-avatar": "^1.0.3",
"@radix-ui/react-dropdown-menu": "^2.0.5",
"vite-plugin-svgr": "^3.2.0"
}
}

File diff suppressed because it is too large Load diff

View file

@ -13,16 +13,23 @@
"@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",
"@radix-ui/react-tabs": "^1.0.4", "@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tooltip": "^1.0.6", "@radix-ui/react-tooltip": "^1.0.6",
"@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",
"ace-builds": "^1.16.0", "ace-builds": "^1.16.0",
"add": "^2.0.6",
"ansi-to-html": "^0.7.2", "ansi-to-html": "^0.7.2",
"axios": "^1.3.2", "axios": "^1.3.2",
"base64-js": "^1.5.1", "base64-js": "^1.5.1",
"class-variance-authority": "^0.6.0", "class-variance-authority": "^0.6.0",
"clsx": "^1.2.1", "clsx": "^1.2.1",
"esbuild": "^0.17.18",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lucide-react": "^0.233.0", "lucide-react": "^0.233.0",
"react": "^18.2.0", "react": "^18.2.0",
@ -41,6 +48,9 @@
"rehype-mathjax": "^4.0.2", "rehype-mathjax": "^4.0.2",
"remark-gfm": "^3.0.1", "remark-gfm": "^3.0.1",
"remark-math": "^5.1.1", "remark-math": "^5.1.1",
"shadcn-ui": "^0.1.3",
"switch": "^0.0.0",
"table": "^6.8.1",
"tailwind-merge": "^1.13.0", "tailwind-merge": "^1.13.0",
"tailwindcss-animate": "^1.0.5", "tailwindcss-animate": "^1.0.5",
"uuid": "^9.0.0", "uuid": "^9.0.0",
@ -74,6 +84,8 @@
}, },
"proxy": "http://127.0.0.1:7860", "proxy": "http://127.0.0.1:7860",
"devDependencies": { "devDependencies": {
"@swc/cli": "^0.1.62",
"@swc/core": "^1.3.62",
"@tailwindcss/typography": "^0.5.9", "@tailwindcss/typography": "^0.5.9",
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",

View file

@ -123,7 +123,11 @@ export default function App() {
> >
<MainPage /> <MainPage />
</ErrorBoundary> </ErrorBoundary>
<div className="flex z-40 flex-col-reverse fixed bottom-5 left-5"> <div></div>
<div
className="flex flex-col-reverse fixed bottom-5 left-5"
style={{ zIndex: 999 }}
>
{alertsList.map((alert) => ( {alertsList.map((alert) => (
<div key={alert.id}> <div key={alert.id}>
{alert.type === "error" ? ( {alert.type === "error" ? (

View file

@ -45,7 +45,7 @@ export default function ParameterComponent({
setPosition(ref.current.offsetTop + ref.current.clientHeight / 2); setPosition(ref.current.offsetTop + ref.current.clientHeight / 2);
updateNodeInternals(data.id); updateNodeInternals(data.id);
} }
}, [data.id, ref, updateNodeInternals]); }, [data.id, ref, ref.current, ref.current?.offsetTop, updateNodeInternals]);
useEffect(() => { useEffect(() => {
updateNodeInternals(data.id); updateNodeInternals(data.id);
@ -65,7 +65,7 @@ export default function ParameterComponent({
refHtml.current = groupedObj.map((item, i) => ( refHtml.current = groupedObj.map((item, i) => (
<span <span
key={item} key={i}
className={classNames( className={classNames(
i > 0 ? "items-center flex mt-3" : "items-center flex" i > 0 ? "items-center flex mt-3" : "items-center flex"
)} )}
@ -86,7 +86,7 @@ export default function ParameterComponent({
{item.type.split(", ").length > 2 {item.type.split(", ").length > 2
? item.type.split(", ").map((el, i) => ( ? item.type.split(", ").map((el, i) => (
<> <>
<span key={el}> <span key={i}>
{i == item.type.split(", ").length - 1 {i == item.type.split(", ").length - 1
? el ? el
: (el += `, `)} : (el += `, `)}

View file

@ -29,6 +29,9 @@ import { useCallback } from "react";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { debounce } from "../../utils"; import { debounce } from "../../utils";
import Tooltip from "../../components/TooltipComponent"; import Tooltip from "../../components/TooltipComponent";
import { NodeToolbar } from "reactflow";
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
import ShadTooltip from "../../components/ShadTooltipComponent"; import ShadTooltip from "../../components/ShadTooltipComponent";
export default function GenericNode({ export default function GenericNode({
data, data,
@ -97,9 +100,17 @@ export default function GenericNode({
deleteNode(data.id); deleteNode(data.id);
return; return;
} }
console.log(data);
return ( return (
<>
<NodeToolbar>
<NodeToolbarComponent
data={data}
openPopUp={openPopUp}
deleteNode={deleteNode}
></NodeToolbarComponent>
</NodeToolbar>
<div <div
className={classNames( className={classNames(
selected ? "border border-blue-500" : "border dark:border-gray-700", selected ? "border border-blue-500" : "border dark:border-gray-700",
@ -127,37 +138,9 @@ export default function GenericNode({
event.preventDefault(); event.preventDefault();
openPopUp(<NodeModal data={data} />); openPopUp(<NodeModal data={data} />);
}} }}
> ></button>
<div className=" absolute -right-1 -top-2 text-red-600">
{Object.keys(data.node.template).some(
(t) =>
data.node.template[t].advanced &&
data.node.template[t].required
)
? " *"
: ""}
</div> </div>
<Cog6ToothIcon <div className="flex gap-3">
className={classNames(
Object.keys(data.node.template).some(
(t) =>
data.node.template[t].advanced && data.node.template[t].show
)
? ""
: "hidden",
"w-5 h-5 dark:text-gray-300"
)}
></Cog6ToothIcon>
</button>
<button
onClick={() => {
deleteNode(data.id);
}}
>
<TrashIcon className="w-5 h-5 dark:text-gray-300"></TrashIcon>
</button>
<div> <div>
<Tooltip <Tooltip
title={ title={
@ -165,7 +148,9 @@ export default function GenericNode({
"Validating..." "Validating..."
) : ( ) : (
<div className="max-h-96 overflow-auto"> <div className="max-h-96 overflow-auto">
{validationStatus.params.split("\n").map((line, index) => ( {validationStatus.params
.split("\n")
.map((line, index) => (
<div key={index}>{line}</div> <div key={index}>{line}</div>
))} ))}
</div> </div>
@ -282,5 +267,6 @@ export default function GenericNode({
</> </>
</div> </div>
</div> </div>
</>
); );
} }

View file

@ -9,6 +9,7 @@ export default function CodeAreaComponent({
value, value,
onChange, onChange,
disabled, disabled,
editNode = false,
}: TextAreaComponentType) { }: TextAreaComponentType) {
const [myValue, setMyValue] = useState(value); const [myValue, setMyValue] = useState(value);
const { openPopUp } = useContext(PopUpContext); const { openPopUp } = useContext(PopUpContext);
@ -38,7 +39,9 @@ export default function CodeAreaComponent({
); );
}} }}
className={ className={
"truncate block w-full text-gray-500 px-3 py-2 rounded-md border border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" + editNode
? "h-7 truncate placeholder:text-center text-gray-500 border-0 block w-full pt-0.5 pb-0.5 form-input dark:bg-gray-900 dark:text-gray-300 dark:border-gray-600 rounded-md border-gray-300 shadow-sm sm:text-sm focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200"
: "truncate block w-full text-gray-500 px-3 py-2 rounded-md border border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" +
(disabled ? " bg-gray-200" : "") (disabled ? " bg-gray-200" : "")
} }
> >
@ -57,7 +60,9 @@ export default function CodeAreaComponent({
); );
}} }}
> >
{!editNode && (
<ArrowTopRightOnSquareIcon className="w-6 h-6 hover:text-blue-600 dark:text-gray-300" /> <ArrowTopRightOnSquareIcon className="w-6 h-6 hover:text-blue-600 dark:text-gray-300" />
)}
</button> </button>
</div> </div>
</div> </div>

View file

@ -8,10 +8,12 @@ export default function Dropdown({
value, value,
options, options,
onSelect, onSelect,
editNode = false,
}: DropDownComponentType) { }: DropDownComponentType) {
let [internalValue, setInternalValue] = useState( let [internalValue, setInternalValue] = useState(
value === "" || !value ? "Choose an option" : value value === "" || !value ? "Choose an option" : value
); );
return ( return (
<> <>
<Listbox <Listbox
@ -23,12 +25,26 @@ export default function Dropdown({
> >
{({ open }) => ( {({ open }) => (
<> <>
<div className="relative mt-1 w-full"> <div
<Listbox.Button className="relative w-full cursor-default rounded-md border border-gray-300 bg-white dark:bg-gray-900 py-2 pl-3 pr-10 text-left shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 sm:text-sm"> className={
<span className="block truncate w-full dark:text-gray-300"> editNode ? "relative mt-0 w-full" : "relative mt-1 w-full"
{internalValue} }
</span> >
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"> <Listbox.Button
className={
editNode
? "pr-9 arrow-hide placeholder:text-center border-0 block w-full pt-0.5 pb-0.5 form-input dark:bg-gray-900 dark:text-gray-300 dark:border-gray-600 rounded-md border-gray-300 shadow-sm sm:text-sm focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200"
: "relative w-full cursor-default rounded-md border border-gray-300 bg-white dark:bg-gray-900 py-2 pl-3 pr-10 text-left shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 sm:text-sm"
}
>
<span className="block truncate w-full">{internalValue}</span>
<span
className={
editNode
? "hidden"
: "pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"
}
>
<ChevronUpDownIcon <ChevronUpDownIcon
className="h-5 w-5 text-gray-400" className="h-5 w-5 text-gray-400"
aria-hidden="true" aria-hidden="true"
@ -43,16 +59,27 @@ export default function Dropdown({
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
<Listbox.Options className="absolute z-10 mt-1 max-h-60 w-full dark:bg-gray-800 overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"> <Listbox.Options
className={
editNode
? "arrow-hide"
: "absolute z-50 mt-1 max-h-60 w-full dark:bg-gray-800 overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"
}
>
{options.map((option, id) => ( {options.map((option, id) => (
<Listbox.Option <Listbox.Option
key={id} key={id}
className={({ active }) => className={({ active }) =>
classNames( classNames(
active active && !editNode
? "text-white bg-indigo-600 dark:bg-indigo-500" ? "text-white bg-indigo-600 dark:bg-indigo-500"
: "text-gray-900", : "text-gray-900",
"relative cursor-default select-none py-2 pl-3 pr-9 dark:text-gray-300 dark:bg-gray-800" active && editNode
? "text-white bg-gray-400 dark:bg-gray-500"
: "",
editNode
? "relative cursor-default select-none py-0.5 pl-3 pr-12 dark:text-gray-300 dark:bg-gray-800"
: "relative cursor-default select-none py-2 pl-3 pr-9 dark:text-gray-300 dark:bg-gray-800"
) )
} }
value={option} value={option}
@ -71,7 +98,8 @@ export default function Dropdown({
{selected ? ( {selected ? (
<span <span
className={classNames( className={classNames(
active ? "text-white" : "text-indigo-600", editNode ? "text-gray-600" : "text-indigo-600",
active ? "text-white" : "",
"absolute inset-y-0 right-0 flex items-center pr-4" "absolute inset-y-0 right-0 flex items-center pr-4"
)} )}
> >

View file

@ -7,6 +7,7 @@ export default function FloatComponent({
onChange, onChange,
disableCopyPaste = false, disableCopyPaste = false,
disabled, disabled,
editNode = false,
}: FloatComponentType) { }: FloatComponentType) {
const [myValue, setMyValue] = useState(value ?? ""); const [myValue, setMyValue] = useState(value ?? "");
const { setDisableCopyPaste } = useContext(TabsContext); const { setDisableCopyPaste } = useContext(TabsContext);
@ -33,10 +34,14 @@ export default function FloatComponent({
type="number" type="number"
value={myValue} value={myValue}
className={ className={
"block w-full form-input dark:bg-gray-900 arrow-hide dark:text-gray-300 dark:border-gray-600 rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" + editNode
? "text-center arrow-hide placeholder:text-center border-0 block w-full pt-0.5 pb-0.5 form-input dark:bg-gray-900 dark:text-gray-300 dark:border-gray-600 rounded-md border-gray-300 shadow-sm sm:text-sm focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200"
: "block w-full form-input dark:bg-gray-900 arrow-hide dark:text-gray-300 dark:border-gray-600 rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" +
(disabled ? " bg-gray-200 dark:bg-gray-700" : "") (disabled ? " bg-gray-200 dark:bg-gray-700" : "")
} }
placeholder="Type a number from zero to one" placeholder={
editNode ? "Number 0 to 1" : "Type a number from zero to one"
}
onChange={(e) => { onChange={(e) => {
setMyValue(e.target.value); setMyValue(e.target.value);
onChange(e.target.value); onChange(e.target.value);

View file

@ -9,6 +9,7 @@ export default function InputComponent({
disableCopyPaste = false, disableCopyPaste = false,
disabled, disabled,
password, password,
editNode = false,
}: InputComponentType) { }: InputComponentType) {
const [myValue, setMyValue] = useState(value ?? ""); const [myValue, setMyValue] = useState(value ?? "");
const [pwdVisible, setPwdVisible] = useState(false); const [pwdVisible, setPwdVisible] = useState(false);
@ -19,6 +20,7 @@ export default function InputComponent({
onChange(""); onChange("");
} }
}, [disabled, onChange]); }, [disabled, onChange]);
return ( return (
<div <div
className={ className={
@ -36,18 +38,27 @@ export default function InputComponent({
if (disableCopyPaste) setDisableCopyPaste(false); if (disableCopyPaste) setDisableCopyPaste(false);
}} }}
className={classNames( className={classNames(
"block w-full pr-12 form-input dark:bg-gray-900 dark:text-gray-300 dark:border-gray-600 rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm", "block w-full pr-12 form-input dark:bg-gray-900 dark:text-gray-300 dark:border-gray-600 rounded-md border-gray-300 shadow-sm sm:text-sm",
disabled ? " bg-gray-200 dark:bg-gray-700" : "", disabled ? " bg-gray-200 dark:bg-gray-700" : "",
password && !pwdVisible && myValue !== "" ? "password" : "" password && !pwdVisible && myValue !== "" ? "password" : "",
editNode
? "placeholder:text-center border-0 block w-full pt-0.5 pb-0.5 form-input dark:bg-gray-900 dark:text-gray-300 dark:border-gray-600 rounded-md border-gray-300 shadow-sm sm:text-sm focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200 text-center"
: "focus:border-indigo-500 focus:ring-indigo-500",
password && editNode ? "pr-8" : "pr-3"
)} )}
placeholder="Type something..." placeholder={password && editNode ? "Key" : "Type something..."}
onChange={(e) => { onChange={(e) => {
setMyValue(e.target.value); setMyValue(e.target.value);
onChange(e.target.value); onChange(e.target.value);
}} }}
/> />
{password && (
<button <button
className="absolute inset-y-0 right-0 items-center px-4 text-gray-600" className={classNames(
editNode
? "absolute inset-y-0 right-0 pr-2 items-center text-gray-600"
: "absolute inset-y-0 right-0 items-center px-4 text-gray-600"
)}
onClick={() => { onClick={() => {
setPwdVisible(!pwdVisible); setPwdVisible(!pwdVisible);
}} }}
@ -90,6 +101,7 @@ export default function InputComponent({
</svg> </svg>
))} ))}
</button> </button>
)}
</div> </div>
); );
} }

View file

@ -10,6 +10,7 @@ export default function InputFileComponent({
suffixes, suffixes,
fileTypes, fileTypes,
onFileChange, onFileChange,
editNode = false,
}: FileComponentType) { }: FileComponentType) {
const [myValue, setMyValue] = useState(value); const [myValue, setMyValue] = useState(value);
const { setErrorData } = useContext(alertContext); const { setErrorData } = useContext(alertContext);
@ -71,14 +72,18 @@ export default function InputFileComponent({
<span <span
onClick={handleButtonClick} onClick={handleButtonClick}
className={ className={
"truncate block w-full text-gray-500 dark:text-gray-300 px-3 py-2 rounded-md border border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" + editNode
? "placeholder:text-center text-gray-500 border-0 block w-full pt-0.5 pb-0.5 form-input dark:bg-gray-900 dark:text-gray-300 dark:border-gray-600 rounded-md border-gray-300 shadow-sm sm:text-sm focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200"
: "truncate block w-full text-gray-500 dark:text-gray-300 px-3 py-2 rounded-md border border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" +
(disabled ? " bg-gray-200" : "") (disabled ? " bg-gray-200" : "")
} }
> >
{myValue !== "" ? myValue : "No file"} {myValue !== "" ? myValue : "No file"}
</span> </span>
<button onClick={handleButtonClick}> <button onClick={handleButtonClick}>
{!editNode && (
<DocumentMagnifyingGlassIcon className="w-8 h-8 hover:text-blue-600" /> <DocumentMagnifyingGlassIcon className="w-8 h-8 hover:text-blue-600" />
)}
</button> </button>
</div> </div>
</div> </div>

View file

@ -1,12 +1,14 @@
import { useContext, useEffect, useState } from "react"; import { useContext, useEffect, useState } from "react";
import { FloatComponentType } from "../../types/components"; import { FloatComponentType } from "../../types/components";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { classNames } from "../../utils";
export default function IntComponent({ export default function IntComponent({
value, value,
onChange, onChange,
disableCopyPaste = false, disableCopyPaste = false,
disabled, disabled,
editNode = false,
}: FloatComponentType) { }: FloatComponentType) {
const [myValue, setMyValue] = useState(value ?? ""); const [myValue, setMyValue] = useState(value ?? "");
const { setDisableCopyPaste } = useContext(TabsContext); const { setDisableCopyPaste } = useContext(TabsContext);
@ -53,10 +55,12 @@ export default function IntComponent({
type="number" type="number"
value={myValue} value={myValue}
className={ className={
"block w-full form-input dark:bg-gray-900 arrow-hide dark:border-gray-600 dark:text-gray-300 rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" + editNode
? "text-center arrow-hide placeholder:text-center border-0 block w-full pt-0.5 pb-0.5 form-input dark:bg-gray-900 dark:text-gray-300 dark:border-gray-600 rounded-md border-gray-300 shadow-sm sm:text-sm focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200"
: "block w-full form-input dark:bg-gray-900 arrow-hide dark:border-gray-600 dark:text-gray-300 rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" +
(disabled ? " bg-gray-200 dark:bg-gray-700" : "") (disabled ? " bg-gray-200 dark:bg-gray-700" : "")
} }
placeholder="Type a integer number" placeholder={editNode ? "Integer number" : "Type a integer number"}
onChange={(e) => { onChange={(e) => {
setMyValue(e.target.value); setMyValue(e.target.value);
onChange(e.target.value); onChange(e.target.value);

View file

@ -11,6 +11,7 @@ export default function PromptAreaComponent({
value, value,
onChange, onChange,
disabled, disabled,
editNode = false,
}: TextAreaComponentType) { }: TextAreaComponentType) {
const [myValue, setMyValue] = useState(value); const [myValue, setMyValue] = useState(value);
const { openPopUp } = useContext(PopUpContext); const { openPopUp } = useContext(PopUpContext);
@ -43,11 +44,13 @@ export default function PromptAreaComponent({
); );
}} }}
className={ className={
"truncate block w-full text-gray-500 px-3 py-2 rounded-md border border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" + editNode
? "h-7 truncate placeholder:text-center text-gray-500 border-0 block w-full pt-0.5 pb-0.5 form-input dark:bg-gray-900 dark:text-gray-300 dark:border-gray-600 rounded-md border-gray-300 shadow-sm sm:text-sm focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200"
: "truncate block w-full text-gray-500 px-3 py-2 rounded-md border border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" +
(disabled ? " bg-gray-200" : "") (disabled ? " bg-gray-200" : "")
} }
> >
{myValue !== "" ? myValue : "Text empty"} {myValue !== "" ? myValue : "-"}
</span> </span>
<button <button
onClick={() => { onClick={() => {
@ -65,7 +68,9 @@ export default function PromptAreaComponent({
); );
}} }}
> >
{!editNode && (
<ArrowTopRightOnSquareIcon className="w-6 h-6 hover:text-blue-600 dark:text-gray-300" /> <ArrowTopRightOnSquareIcon className="w-6 h-6 hover:text-blue-600 dark:text-gray-300" />
)}
</button> </button>
</div> </div>
</div> </div>

View file

@ -9,6 +9,7 @@ export default function TextAreaComponent({
value, value,
onChange, onChange,
disabled, disabled,
editNode = false,
}: TextAreaComponentType) { }: TextAreaComponentType) {
const [myValue, setMyValue] = useState(value); const [myValue, setMyValue] = useState(value);
const { openPopUp } = useContext(PopUpContext); const { openPopUp } = useContext(PopUpContext);
@ -37,7 +38,9 @@ export default function TextAreaComponent({
); );
}} }}
className={ className={
"truncate block w-full text-gray-500 dark:text-gray-100 px-3 py-2 rounded-md border border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" + editNode
? "h-7 truncate placeholder:text-center text-gray-500 border-0 block w-full pt-0.5 pb-0.5 form-input dark:bg-gray-900 dark:text-gray-300 dark:border-gray-600 rounded-md border-gray-300 shadow-sm sm:text-sm focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200"
: "truncate block w-full text-gray-500 dark:text-gray-100 px-3 py-2 rounded-md border border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" +
(disabled ? " bg-gray-200" : "") (disabled ? " bg-gray-200" : "")
} }
> >
@ -59,7 +62,9 @@ export default function TextAreaComponent({
); );
}} }}
> >
{!editNode && (
<ArrowTopRightOnSquareIcon className="w-6 h-6 hover:text-blue-600 dark:text-gray-300" /> <ArrowTopRightOnSquareIcon className="w-6 h-6 hover:text-blue-600 dark:text-gray-300" />
)}
</button> </button>
</div> </div>
</div> </div>

View file

@ -0,0 +1,29 @@
import { classNames } from "../../utils";
import { useEffect } from "react";
import { ToggleComponentType } from "../../types/components";
import { Switch } from "../ui/switch";
export default function ToggleShadComponent({
enabled,
setEnabled,
disabled,
}: ToggleComponentType) {
useEffect(() => {
if (disabled) {
setEnabled(false);
}
}, [disabled, setEnabled]);
return (
<div className={disabled ? "pointer-events-none cursor-not-allowed" : ""}>
<Switch
style={{
transform: "scaleX(0.6) scaleY(0.6)",
}}
checked={enabled}
onCheckedChange={(x: boolean) => {
setEnabled(x);
}}
></Switch>
</div>
);
}

View file

@ -0,0 +1,29 @@
"use client";
import * as React from "react";
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
import { Check } from "lucide-react";
import { cn } from "../../utils";
const Checkbox = React.forwardRef<
React.ElementRef<typeof CheckboxPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
>(({ className, ...props }, ref) => (
<CheckboxPrimitive.Root
ref={ref}
className={cn(
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
className
)}
{...props}
>
<CheckboxPrimitive.Indicator
className={cn("flex items-center justify-center text-current")}
>
<Check className="h-4 w-4" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
));
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
export { Checkbox };

View file

@ -0,0 +1,125 @@
import * as React from "react";
import * as DialogPrimitive from "@radix-ui/react-dialog";
import { X } from "lucide-react";
import { cn } from "../../utils";
const Dialog = DialogPrimitive.Root;
const DialogTrigger = DialogPrimitive.Trigger;
const DialogPortal = ({
className,
children,
...props
}: DialogPrimitive.DialogPortalProps) => (
<DialogPrimitive.Portal className={cn(className)} {...props}>
<div className="fixed inset-0 z-50 flex items-start justify-center sm:items-center">
{children}
</div>
</DialogPrimitive.Portal>
);
DialogPortal.displayName = DialogPrimitive.Portal.displayName;
const DialogOverlay = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm transition-all duration-100 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in",
className
)}
{...props}
/>
));
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed z-50 grid w-full gap-4 rounded-b-lg border bg-background p-6 shadow-lg animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:max-w-lg sm:rounded-lg sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0",
className
)}
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
));
DialogContent.displayName = DialogPrimitive.Content.displayName;
const DialogHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-1.5 text-center sm:text-left",
className
)}
{...props}
/>
);
DialogHeader.displayName = "DialogHeader";
const DialogFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
)}
{...props}
/>
);
DialogFooter.displayName = "DialogFooter";
const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={cn(
"text-lg font-semibold leading-none tracking-tight",
className
)}
{...props}
/>
));
DialogTitle.displayName = DialogPrimitive.Title.displayName;
const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
));
DialogDescription.displayName = DialogPrimitive.Description.displayName;
export {
Dialog,
DialogTrigger,
DialogContent,
DialogHeader,
DialogFooter,
DialogTitle,
DialogDescription,
};

View file

@ -0,0 +1,24 @@
import * as React from "react";
import { cn } from "../../utils";
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"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}
{...props}
/>
);
}
);
Input.displayName = "Input";
export { Input };

View file

@ -0,0 +1,25 @@
"use client";
import * as React from "react";
import * as LabelPrimitive from "@radix-ui/react-label";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "../../utils";
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
);
const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
));
Label.displayName = LabelPrimitive.Root.displayName;
export { Label };

View file

@ -0,0 +1,28 @@
"use client";
import * as React from "react";
import * as SwitchPrimitives from "@radix-ui/react-switch";
import { cn } from "../../utils";
const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
>(({ className, ...props }, ref) => (
<SwitchPrimitives.Root
className={cn(
"peer inline-flex h-[24px] w-[44px] shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
className
)}
{...props}
ref={ref}
>
<SwitchPrimitives.Thumb
className={cn(
"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
)}
/>
</SwitchPrimitives.Root>
));
Switch.displayName = SwitchPrimitives.Root.displayName;
export { Switch };

View file

@ -0,0 +1,113 @@
import * as React from "react";
import { cn } from "../../utils";
const Table = React.forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<div className="w-full overflow-auto">
<table
ref={ref}
className={cn("w-full caption-bottom text-sm", className)}
{...props}
/>
</div>
));
Table.displayName = "Table";
const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
));
TableHeader.displayName = "TableHeader";
const TableBody = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody
ref={ref}
className={cn("[&_tr:last-child]:border-0", className)}
{...props}
/>
));
TableBody.displayName = "TableBody";
const TableFooter = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={cn("bg-primary font-medium text-primary-foreground", className)}
{...props}
/>
));
TableFooter.displayName = "TableFooter";
const TableRow = React.forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr
ref={ref}
className={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
className
)}
{...props}
/>
));
TableRow.displayName = "TableRow";
const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th
ref={ref}
className={cn(
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
className
)}
{...props}
/>
));
TableHead.displayName = "TableHead";
const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td
ref={ref}
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
{...props}
/>
));
TableCell.displayName = "TableCell";
const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption
ref={ref}
className={cn("mt-4 text-sm text-muted-foreground", className)}
{...props}
/>
));
TableCaption.displayName = "TableCaption";
export {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
};

View file

@ -0,0 +1,23 @@
import * as React from "react";
import { cn } from "../../utils";
export interface TextareaProps
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
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
)}
ref={ref}
{...props}
/>
);
}
);
Textarea.displayName = "Textarea";
export { Textarea };

View file

@ -0,0 +1,39 @@
// src/constants.tsx
/**
* The base text for subtitle of Export Dialog (Toolbar)
* @constant
*/
export const EXPORT_DIALOG_SUBTITLE = "Export your models.";
/**
* The base text for subtitle of Code Dialog (Toolbar)
* @constant
*/
export const CODE_DIALOG_SUBTITLE =
"Export your flow to use it with this code.";
/**
* The base text for subtitle of Edit Node Dialog
* @constant
*/
export const EDIT_DIALOG_SUBTITLE =
"Make configurations changes to your nodes. Click save when you're done.";
/**
* The base text for subtitle of Code Dialog
* @constant
*/
export const CODE_PROMPT_DIALOG_SUBTITLE = "Edit you python code.";
/**
* The base text for subtitle of Prompt Dialog
* @constant
*/
export const PROMPT_DIALOG_SUBTITLE = "Edit you prompt.";
/**
* The base text for subtitle of Text Dialog
* @constant
*/
export const TEXT_DIALOG_SUBTITLE = "Edit you text.";

View file

@ -39,10 +39,13 @@ const TabsContextInitialValue: TabsContextType = {
hardReset: () => {}, hardReset: () => {},
disableCopyPaste: false, disableCopyPaste: false,
setDisableCopyPaste: (state: boolean) => {}, setDisableCopyPaste: (state: boolean) => {},
lastCopiedSelection: null,
setLastCopiedSelection: (selection: any) => {},
getNodeId: () => "", getNodeId: () => "",
paste: ( paste: (
selection: { nodes: any; edges: any }, selection: { nodes: any; edges: any },
position: { x: number; y: number } position: { x: number; y: number; paneX?: number; paneY?: number }
) => {}, ) => {},
}; };
@ -56,6 +59,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
const [flows, setFlows] = useState<Array<FlowType>>([]); const [flows, setFlows] = useState<Array<FlowType>>([]);
const [id, setId] = useState(uuidv4()); const [id, setId] = useState(uuidv4());
const { templates, reactFlowInstance } = useContext(typesContext); const { templates, reactFlowInstance } = useContext(typesContext);
const [lastCopiedSelection, setLastCopiedSelection] = useState(null);
const newNodeId = useRef(uuidv4()); const newNodeId = useRef(uuidv4());
function incrementNodeId() { function incrementNodeId() {
@ -270,7 +274,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
// simulate a click on the link element to trigger the download // simulate a click on the link element to trigger the download
link.click(); link.click();
setNoticeData({ setNoticeData({
title: "Warning: Critical data,JSON file may including API keys.", title: "Warning: Critical data, JSON file may include API keys.",
}); });
} }
@ -334,7 +338,10 @@ export function TabsProvider({ children }: { children: ReactNode }) {
* @param flow Optional flow to add. * @param flow Optional flow to add.
*/ */
function paste(selectionInstance, position) { function paste(
selectionInstance,
position: { x: number; y: number; paneX?: number; paneY?: number }
) {
let minimumX = Infinity; let minimumX = Infinity;
let minimumY = Infinity; let minimumY = Infinity;
let idsMap = {}; let idsMap = {};
@ -349,7 +356,9 @@ export function TabsProvider({ children }: { children: ReactNode }) {
} }
}); });
const insidePosition = reactFlowInstance.project(position); const insidePosition = position.paneX
? { x: position.paneX + position.x, y: position.paneY + position.y }
: reactFlowInstance.project({ x: position.x, y: position.y });
selectionInstance.nodes.forEach((n) => { selectionInstance.nodes.forEach((n) => {
// Generate a unique node ID // Generate a unique node ID
@ -524,6 +533,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
return ( return (
<TabsContext.Provider <TabsContext.Provider
value={{ value={{
lastCopiedSelection,
setLastCopiedSelection,
disableCopyPaste, disableCopyPaste,
setDisableCopyPaste, setDisableCopyPaste,
save, save,

View file

@ -94,6 +94,8 @@ code {
monospace; monospace;
} }
/* 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 { .react-flow__pane {
cursor: default; cursor: default;
} }

View file

@ -1,4 +1,3 @@
import { Dialog, Transition } from "@headlessui/react";
import { IconCheck, IconClipboard, IconDownload } from "@tabler/icons-react"; import { IconCheck, IconClipboard, IconDownload } from "@tabler/icons-react";
import { import {
XMarkIcon, XMarkIcon,
@ -15,13 +14,22 @@ import "ace-builds/src-noconflict/ext-language_tools";
import { darkContext } from "../../contexts/darkContext"; import { darkContext } from "../../contexts/darkContext";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism"; import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "../../components/ui/dialog";
import { Button } from "../../components/ui/button";
export default function ApiModal({ flowName }) { export default function ApiModal({ flowName }) {
const [open, setOpen] = useState(true); const [open, setOpen] = useState(true);
const { dark } = useContext(darkContext); const { dark } = useContext(darkContext);
const { closePopUp } = useContext(PopUpContext); const { closePopUp } = useContext(PopUpContext);
const [activeTab, setActiveTab] = useState(0); const [activeTab, setActiveTab] = useState(0);
const ref = useRef();
const [isCopied, setIsCopied] = useState<Boolean>(false); const [isCopied, setIsCopied] = useState<Boolean>(false);
const copyToClipboard = () => { const copyToClipboard = () => {
@ -40,9 +48,7 @@ export default function ApiModal({ flowName }) {
function setModalOpen(x: boolean) { function setModalOpen(x: boolean) {
setOpen(x); setOpen(x);
if (x === false) { if (x === false) {
setTimeout(() => {
closePopUp(); closePopUp();
}, 300);
} }
} }
@ -82,71 +88,27 @@ flow("Hey, have you heard of LangFlow?")`;
}, },
]; ];
return ( return (
<Transition.Root show={open} appear={true} as={Fragment}> <Dialog open={true} onOpenChange={setModalOpen}>
<Dialog <DialogTrigger></DialogTrigger>
as="div" <DialogContent className="lg:max-w-[800px] sm:max-w-[600px] h-[550px]">
className="relative z-10" <DialogHeader>
onClose={setModalOpen} <DialogTitle className="flex items-center">
initialFocus={ref} <span className="pr-2">Code</span>
>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-gray-500 dark:bg-gray-600 dark:bg-opacity-75 bg-opacity-75 transition-opacity" />
</Transition.Child>
<div className="fixed inset-0 z-10 overflow-y-auto">
<div className="flex h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="relative flex flex-col justify-between transform h-[600px] overflow-hidden rounded-lg bg-white dark:bg-gray-800 text-left shadow-xl transition-all sm:my-8 w-[700px]">
<div className=" z-50 absolute top-0 right-0 hidden pt-4 pr-4 sm:block">
<button
type="button"
className="rounded-md text-gray-400 hover:text-gray-500"
onClick={() => {
setModalOpen(false);
}}
>
<span className="sr-only">Close</span>
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
</button>
</div>
<div className="h-full w-full flex flex-col justify-center items-center">
<div className="flex w-full pb-4 z-10 justify-center shadow-sm">
<div className="mx-auto mt-4 flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-blue-100 dark:bg-gray-900 sm:mx-0 sm:h-10 sm:w-10">
<CodeBracketSquareIcon <CodeBracketSquareIcon
className="h-6 w-6 text-blue-600" className="h-6 w-6 text-gray-800 pl-1 dark:text-white"
aria-hidden="true" aria-hidden="true"
/> />
</div> </DialogTitle>
<div className="mt-4 text-center sm:ml-4 sm:text-left"> <DialogDescription>
<Dialog.Title Export your flow to use it with this code.
as="h3" </DialogDescription>
className="text-lg font-medium dark:text-white leading-10 text-gray-900" </DialogHeader>
>
Code
</Dialog.Title>
</div>
</div>
<div className="h-full w-full bg-gray-200 overflow-auto dark:bg-gray-900 p-4 gap-4 flex flex-row justify-center items-center">
<div className="flex flex-col h-full w-full "> <div className="flex flex-col h-full w-full ">
<div className="flex px-5 z-10"> <div className="flex px-5 z-10">
{tabs.map((tab, index) => ( {tabs.map((tab, index) => (
<button <button
key={index}
onClick={() => { onClick={() => {
setActiveTab(index); setActiveTab(index);
}} }}
@ -162,11 +124,9 @@ flow("Hey, have you heard of LangFlow?")`;
</button> </button>
))} ))}
</div> </div>
<div className="overflow-hidden px-4 py-5 sm:p-6 w-full h-full rounded-lg shadow bg-white dark:bg-gray-800"> <div className="overflow-hidden px-4 sm:p-4 sm:pb-0 sm:pt-0 w-full h-full rounded-lg shadow bg-white dark:bg-gray-800">
<div className="w-full flex items-center justify-between mb-2"> <div className="items-center mb-2">
<span className="text-sm text-gray-500 dark:text-gray-300"> <div className="float-right">
Export your flow to use it with this code.
</span>
<button <button
className="flex gap-1.5 items-center rounded bg-none p-1 text-xs text-gray-500 dark:text-gray-300" className="flex gap-1.5 items-center rounded bg-none p-1 text-xs text-gray-500 dark:text-gray-300"
onClick={copyToClipboard} onClick={copyToClipboard}
@ -179,8 +139,9 @@ flow("Hey, have you heard of LangFlow?")`;
{isCopied ? "Copied!" : "Copy code"} {isCopied ? "Copied!" : "Copy code"}
</button> </button>
</div> </div>
</div>
<SyntaxHighlighter <SyntaxHighlighter
className="h-[370px]" className="h-[370px] w-full"
language={tabs[activeTab].mode} language={tabs[activeTab].mode}
style={oneDark} style={oneDark}
customStyle={{ margin: 0 }} customStyle={{ margin: 0 }}
@ -189,13 +150,7 @@ flow("Hey, have you heard of LangFlow?")`;
</SyntaxHighlighter> </SyntaxHighlighter>
</div> </div>
</div> </div>
</div> </DialogContent>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog> </Dialog>
</Transition.Root>
); );
} }

View file

@ -0,0 +1,335 @@
import {
ChevronDoubleLeftIcon,
ChevronDoubleRightIcon,
PencilSquareIcon,
XMarkIcon,
} from "@heroicons/react/24/outline";
import { Fragment, useContext, useEffect, useRef, useState } from "react";
import { PopUpContext } from "../../contexts/popUpContext";
import { NodeDataType } from "../../types/flow";
import { classNames, limitScrollFieldsModal, nodeIcons } from "../../utils";
import { typesContext } from "../../contexts/typesContext";
import {
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "../../components/ui/table";
import { Switch } from "../../components/ui/switch";
import ToggleShadComponent from "../../components/toggleShadComponent";
import { VariableIcon } from "@heroicons/react/24/outline";
import InputListComponent from "../../components/inputListComponent";
import TextAreaComponent from "../../components/textAreaComponent";
import InputComponent from "../../components/inputComponent";
import ToggleComponent from "../../components/toggleComponent";
import FloatComponent from "../../components/floatComponent";
import Dropdown from "../../components/dropdownComponent";
import IntComponent from "../../components/intComponent";
import InputFileComponent from "../../components/inputFileComponent";
import PromptAreaComponent from "../../components/promptComponent";
import CodeAreaComponent from "../../components/codeAreaComponent";
import { TabsContext } from "../../contexts/tabsContext";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "../../components/ui/dialog";
import { Button } from "../../components/ui/button";
import { EDIT_DIALOG_SUBTITLE } from "../../constants";
export default function EditNodeModal({ data }: { data: NodeDataType }) {
const [open, setOpen] = useState(true);
const [nodeLength, setNodeLength] = useState(
Object.keys(data.node.template).filter(
(t) =>
t.charAt(0) !== "_" &&
data.node.template[t].show &&
(data.node.template[t].type === "str" ||
data.node.template[t].type === "bool" ||
data.node.template[t].type === "float" ||
data.node.template[t].type === "code" ||
data.node.template[t].type === "prompt" ||
data.node.template[t].type === "file" ||
data.node.template[t].type === "int")
).length
);
const [nodeValue, setNodeValue] = useState(true);
const { closePopUp } = useContext(PopUpContext);
const { types } = useContext(typesContext);
const ref = useRef();
const { save } = useContext(TabsContext);
const [enabled, setEnabled] = useState(false);
if (nodeLength == 0) {
closePopUp();
}
function setModalOpen(x: boolean) {
setOpen(x);
if (x === false) {
closePopUp();
}
}
function changeAdvanced(node): void {
Object.keys(data.node.template).filter((n, i) => {
if (n === node.name) {
data.node.template[n].advanced = !data.node.template[n].advanced;
}
return true;
});
setNodeValue(!nodeValue);
}
console.log(data.node.template);
return (
<Dialog open={true} onOpenChange={setModalOpen}>
<DialogTrigger></DialogTrigger>
<DialogContent className="lg:max-w-[700px]">
<DialogHeader>
<DialogTitle className="flex items-center">
<span className="pr-2">Edit Node</span>
<PencilSquareIcon
className="h-6 w-6 text-gray-800 pl-1 dark:text-white"
aria-hidden="true"
/>
</DialogTitle>
<DialogDescription>
{EDIT_DIALOG_SUBTITLE}
<div className="flex pt-3">
<VariableIcon className="w-5 h-5 pe-1 text-gray-700 stroke-2">
&nbsp;
</VariableIcon>
<span className="text-sm font-semibold text-gray-800">
Parameters
</span>
</div>
</DialogDescription>
</DialogHeader>
<div className="flex w-full h-fit max-h-[415px]">
<div
className={classNames(
"w-full rounded-lg bg-white dark:bg-gray-800 shadow",
nodeLength > limitScrollFieldsModal
? "overflow-scroll overflow-x-hidden custom-scroll"
: "overflow-hidden"
)}
>
{nodeLength > 0 && (
<div className="flex flex-col gap-5 h-fit">
<Table className="table-fixed">
<TableHeader className="border-gray-200 text-gray-500 text-xs font-medium">
<TableRow>
<TableHead className="h-7 text-center">PARAM</TableHead>
<TableHead className="p-0 h-7 text-center">
VALUE
</TableHead>
<TableHead className="text-center h-7">SHOW</TableHead>
</TableRow>
</TableHeader>
<TableBody className="p-0">
{Object.keys(data.node.template)
.filter(
(t) =>
t.charAt(0) !== "_" &&
data.node.template[t].show &&
(data.node.template[t].type === "str" ||
data.node.template[t].type === "bool" ||
data.node.template[t].type === "float" ||
data.node.template[t].type === "code" ||
data.node.template[t].type === "prompt" ||
data.node.template[t].type === "file" ||
data.node.template[t].type === "int")
)
.map((n, i) => (
<TableRow key={i} className="h-8">
<TableCell className="p-0 text-center text-gray-900 text-xs dark:text-gray-300">
{data.node.template[n].name
? data.node.template[n].name
: data.node.template[n].display_name}
</TableCell>
<TableCell className="p-0 text-center text-gray-900 text-xs w-[300px] dark:text-gray-300">
{data.node.template[n].type === "str" &&
!data.node.template[n].options ? (
<div className="mx-auto">
{data.node.template[n].list ? (
<InputListComponent
disabled={false}
value={
!data.node.template[n].value ||
data.node.template[n].value === ""
? [""]
: data.node.template[n].value
}
onChange={(t: string[]) => {
data.node.template[n].value = t;
save();
}}
/>
) : data.node.template[n].multiline ? (
<TextAreaComponent
disabled={false}
editNode={true}
value={data.node.template[n].value ?? ""}
onChange={(t: string) => {
data.node.template[n].value = t;
save();
}}
/>
) : (
<InputComponent
editNode={true}
disabled={false}
password={
data.node.template[n].password ?? false
}
value={data.node.template[n].value ?? ""}
onChange={(t) => {
data.node.template[n].value = t;
save();
}}
/>
)}
</div>
) : data.node.template[n].type === "bool" ? (
<div className="ml-auto">
{" "}
<ToggleShadComponent
enabled={data.node.template[n].value}
setEnabled={(e) => {
data.node.template[n].value = e;
setEnabled(e);
save();
}}
disabled={false}
/>
</div>
) : data.node.template[n].type === "float" ? (
<div className="mx-auto">
<FloatComponent
disabled={false}
editNode={true}
value={data.node.template[n].value ?? ""}
onChange={(t) => {
data.node.template[n].value = t;
save();
}}
/>
</div>
) : data.node.template[n].type === "str" &&
data.node.template[n].options ? (
<div className="mx-auto">
<Dropdown
editNode={true}
options={data.node.template[n].options}
onSelect={(newValue) =>
(data.node.template[n].value = newValue)
}
value={
data.node.template[n].value ??
"Choose an option"
}
></Dropdown>
</div>
) : data.node.template[n].type === "int" ? (
<div className="mx-auto">
<IntComponent
disabled={false}
editNode={true}
value={data.node.template[n].value ?? ""}
onChange={(t) => {
data.node.template[n].value = t;
save();
}}
/>
</div>
) : data.node.template[n].type === "file" ? (
<div className="mx-auto">
<InputFileComponent
editNode={true}
disabled={false}
value={data.node.template[n].value ?? ""}
onChange={(t: string) => {
data.node.template[n].value = t;
}}
fileTypes={data.node.template[n].fileTypes}
suffixes={data.node.template[n].suffixes}
onFileChange={(t: string) => {
data.node.template[n].content = t;
save();
}}
></InputFileComponent>
</div>
) : data.node.template[n].type === "prompt" ? (
<div className="mx-auto">
<PromptAreaComponent
editNode={true}
disabled={false}
value={data.node.template[n].value ?? ""}
onChange={(t: string) => {
data.node.template[n].value = t;
save();
}}
/>
</div>
) : data.node.template[n].type === "code" ? (
<div className="mx-auto">
<CodeAreaComponent
disabled={false}
editNode={true}
value={data.node.template[n].value ?? ""}
onChange={(t: string) => {
data.node.template[n].value = t;
save();
}}
/>
</div>
) : data.node.template[n].type === "Any" ? (
"-"
) : (
<div className="hidden"></div>
)}
</TableCell>
<TableCell className="p-0 text-right">
<div className="items-center text-center">
<ToggleShadComponent
enabled={!data.node.template[n].advanced}
setEnabled={(e) =>
changeAdvanced(data.node.template[n])
}
disabled={false}
/>
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
)}
</div>
</div>
<DialogFooter>
<Button
className="mt-3"
onClick={() => {
setModalOpen(false);
}}
type="submit"
>
Save changes
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}

View file

@ -1,4 +1,3 @@
import { Dialog, Transition } from "@headlessui/react";
import { XMarkIcon, CommandLineIcon } from "@heroicons/react/24/outline"; import { XMarkIcon, CommandLineIcon } from "@heroicons/react/24/outline";
import { Fragment, useContext, useRef, useState } from "react"; import { Fragment, useContext, useRef, useState } from "react";
import { PopUpContext } from "../../contexts/popUpContext"; import { PopUpContext } from "../../contexts/popUpContext";
@ -12,6 +11,18 @@ import { darkContext } from "../../contexts/darkContext";
import { checkCode } from "../../controllers/API"; import { checkCode } from "../../controllers/API";
import { alertContext } from "../../contexts/alertContext"; import { alertContext } from "../../contexts/alertContext";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "../../components/ui/dialog";
import { Button } from "../../components/ui/button";
import { CODE_PROMPT_DIALOG_SUBTITLE } from "../../constants";
export default function CodeAreaModal({ export default function CodeAreaModal({
value, value,
setValue, setValue,
@ -34,69 +45,21 @@ export default function CodeAreaModal({
} }
} }
return ( return (
<Transition.Root show={open} appear={true} as={Fragment}> <Dialog open={true} onOpenChange={setModalOpen}>
<Dialog <DialogTrigger></DialogTrigger>
as="div" <DialogContent className="lg:max-w-[700px] h-[500px]">
className="relative z-10" <DialogHeader>
onClose={setModalOpen} <DialogTitle className="flex items-center">
initialFocus={ref} <span className="pr-2">Edit Code</span>
>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-gray-500 dark:bg-gray-600 dark:bg-opacity-75 bg-opacity-75 transition-opacity" />
</Transition.Child>
<div className="fixed inset-0 z-10 overflow-y-auto">
<div className="flex h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="relative flex flex-col justify-between transform h-[600px] overflow-hidden rounded-lg bg-white dark:bg-gray-800 text-left shadow-xl transition-all sm:my-8 w-[700px]">
<div className=" z-50 absolute top-0 right-0 hidden pt-4 pr-4 sm:block">
<button
type="button"
className="rounded-md text-gray-400 hover:text-gray-500"
onClick={() => {
setModalOpen(false);
}}
>
<span className="sr-only">Close</span>
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
</button>
</div>
<div className="h-full w-full flex flex-col justify-center items-center">
<div className="flex w-full pb-4 z-10 justify-center shadow-sm">
<div className="mx-auto mt-4 flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-blue-100 dark:bg-gray-900 sm:mx-0 sm:h-10 sm:w-10">
<CommandLineIcon <CommandLineIcon
className="h-6 w-6 text-blue-600" className="h-6 w-6 text-gray-800 pl-1 dark:text-white"
aria-hidden="true" aria-hidden="true"
/> />
</div> </DialogTitle>
<div className="mt-4 text-center sm:ml-4 sm:text-left"> <DialogDescription>{CODE_PROMPT_DIALOG_SUBTITLE}</DialogDescription>
<Dialog.Title </DialogHeader>
as="h3"
className="text-lg font-medium dark:text-white leading-10 text-gray-900" <div className="flex h-full w-full mt-2">
>
Edit Code
</Dialog.Title>
</div>
</div>
<div className="h-full w-full bg-gray-200 overflow-auto dark:bg-gray-900 p-4 gap-4 flex flex-row justify-center items-center">
<div className="flex h-full w-full">
<div className="overflow-hidden px-4 py-5 sm:p-6 w-full h-full rounded-lg bg-white dark:bg-gray-800 shadow">
<AceEditor <AceEditor
value={code} value={code}
mode="python" mode="python"
@ -110,25 +73,20 @@ export default function CodeAreaModal({
onChange={(value) => { onChange={(value) => {
setCode(value); setCode(value);
}} }}
className="h-full w-full rounded-lg" className="w-full rounded-lg h-[300px] custom-scroll"
/> />
</div> </div>
</div>
</div> <DialogFooter>
<div className="bg-gray-200 dark:bg-gray-900 w-full pb-3 flex flex-row-reverse px-4"> <Button
<button className="mt-3"
type="button"
className="inline-flex w-full justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:ml-3 sm:w-auto sm:text-sm"
onClick={() => { onClick={() => {
checkCode(code) checkCode(code)
.then((apiReturn) => { .then((apiReturn) => {
if (apiReturn.data) { if (apiReturn.data) {
let importsErrors = apiReturn.data.imports.errors; let importsErrors = apiReturn.data.imports.errors;
let funcErrors = apiReturn.data.function.errors; let funcErrors = apiReturn.data.function.errors;
if ( if (funcErrors.length === 0 && importsErrors.length === 0) {
funcErrors.length === 0 &&
importsErrors.length === 0
) {
setSuccessData({ setSuccessData({
title: "Code is ready to run", title: "Code is ready to run",
}); });
@ -161,16 +119,12 @@ export default function CodeAreaModal({
}) })
); );
}} }}
type="submit"
> >
Check & Save Check & Save
</button> </Button>
</div> </DialogFooter>
</div> </DialogContent>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog> </Dialog>
</Transition.Root>
); );
} }

View file

@ -1,4 +1,3 @@
import { Dialog, Transition } from "@headlessui/react";
import { import {
XMarkIcon, XMarkIcon,
ArrowDownTrayIcon, ArrowDownTrayIcon,
@ -10,6 +9,21 @@ import { alertContext } from "../../contexts/alertContext";
import { PopUpContext } from "../../contexts/popUpContext"; import { PopUpContext } from "../../contexts/popUpContext";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { removeApiKeys } from "../../utils"; import { removeApiKeys } from "../../utils";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "../../components/ui/dialog";
import { Button } from "../../components/ui/button";
import { Label } from "@radix-ui/react-label";
import { Checkbox } from "../../components/ui/checkbox";
import { Textarea } from "../../components/ui/textarea";
import { Input } from "../../components/ui/input";
import { EXPORT_DIALOG_SUBTITLE } from "../../constants";
export default function ExportModal() { export default function ExportModal() {
const [open, setOpen] = useState(true); const [open, setOpen] = useState(true);
@ -28,75 +42,25 @@ export default function ExportModal() {
const [checked, setChecked] = useState(true); const [checked, setChecked] = useState(true);
const [name, setName] = useState(flows[tabIndex].name); const [name, setName] = useState(flows[tabIndex].name);
return ( return (
<Transition.Root show={open} appear={true} as={Fragment}> <Dialog open={true} onOpenChange={setModalOpen}>
<Dialog <DialogTrigger asChild></DialogTrigger>
as="div" <DialogContent className="lg:max-w-[600px] h-[420px]">
className="relative z-10" <DialogHeader>
onClose={setModalOpen} <DialogTitle className="flex items-center">
initialFocus={ref} <span className="pr-2">Export</span>
>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-gray-500 dark:bg-gray-600 dark:bg-opacity-75 bg-opacity-75 transition-opacity" />
</Transition.Child>
<div className="fixed inset-0 z-10 overflow-y-auto">
<div className="flex h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="relative flex flex-col justify-between transform h-[600px] overflow-hidden rounded-lg bg-white dark:bg-gray-800 text-left shadow-xl transition-all sm:my-8 w-[700px]">
<div className=" z-50 absolute top-0 right-0 hidden pt-4 pr-4 sm:block">
<button
type="button"
className="rounded-md text-gray-400 hover:text-gray-500"
onClick={() => {
setModalOpen(false);
}}
>
<span className="sr-only">Close</span>
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
</button>
</div>
<div className="h-full w-full flex flex-col justify-center items-center">
<div className="flex w-full pb-4 z-10 justify-center shadow-sm">
<div className="mx-auto mt-4 flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-blue-100 dark:bg-gray-900 sm:mx-0 sm:h-10 sm:w-10">
<ArrowDownTrayIcon <ArrowDownTrayIcon
className="h-6 w-6 text-blue-600" className="h-6 w-6 text-gray-800 pl-1 dark:text-white"
aria-hidden="true" aria-hidden="true"
/> />
</div> </DialogTitle>
<div className="mt-4 text-center sm:ml-4 sm:text-left"> <DialogDescription>{EXPORT_DIALOG_SUBTITLE}</DialogDescription>
<Dialog.Title </DialogHeader>
as="h3"
className="text-lg font-medium dark:text-white leading-10 text-gray-900" <Label>
> <span className="font-medium">Name</span>
Export
</Dialog.Title> <Input
</div> className="mt-2"
</div>
<div className="pt-16 flex flex-col items-start justify-start h-full w-full bg-gray-200 dark:bg-gray-900 p-4 gap-16">
<div className="w-full">
<label
htmlFor="name"
className="block mb-2 font-medium text-gray-700 dark:text-white"
>
Name
</label>
<input
onChange={(event) => { onChange={(event) => {
if (event.target.value != "") { if (event.target.value != "") {
let newFlow = flows[tabIndex]; let newFlow = flows[tabIndex];
@ -112,21 +76,11 @@ export default function ExportModal() {
value={name ?? null} value={name ?? null}
placeholder="File name" placeholder="File name"
id="name" id="name"
className="focus:border focus:border-blue block w-full px-3 py-2 border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-800 dark:border-gray-600 dark:focus:border-blue-500 dark:focus:ring-blue-500 text-gray-900 dark:text-gray-100"
/> />
</div> </Label>
<div className="w-full"> <Label>
<label <span className="font-medium">Description (optional)</span>
htmlFor="description" <Textarea
className="block mb-2 font-medium text-gray-700 dark:text-white"
>
Description{" "}
<span className="text-gray-400 text-sm">
{" "}
(optional)
</span>
</label>
<textarea
name="description" name="description"
id="description" id="description"
onChange={(event) => { onChange={(event) => {
@ -136,45 +90,37 @@ export default function ExportModal() {
}} }}
value={flows[tabIndex].description ?? null} value={flows[tabIndex].description ?? null}
placeholder="Flow description" placeholder="Flow description"
className="max-h-[100px] mt-2"
rows={3} rows={3}
className=" focus:border focus:border-blue block w-full px-3 py-2 border-gray-300 text-gray-900 dark:text-gray-100 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-800 dark:border-gray-600 dark:focus:border-blue-500 dark:focus:ring-blue-500"
></textarea>
</div>
<div>
<label htmlFor="checkbox" className="flex items-center">
<input
onChange={(event) => {
setChecked(event.target.checked);
}}
checked={checked}
id="checkbox"
type="checkbox"
className="h-4 w-4 text-blue-600 border-gray-300 rounded dark:bg-gray-800 dark:border-gray-600 dark:focus:border-blue-500 dark:focus:ring-blue-500"
/> />
<span className="ml-2 font-medium text-gray-700 dark:text-white"> </Label>
<div className="flex items-center space-x-2">
<Checkbox
id="terms"
onCheckedChange={(event: boolean) => {
setChecked(event);
}}
/>
<label
htmlFor="terms"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Save with my API keys Save with my API keys
</span>
</label> </label>
</div> </div>
<div className="w-full flex justify-end">
<button <DialogFooter>
<Button
onClick={() => { onClick={() => {
if (checked) downloadFlow(flows[tabIndex]); if (checked) downloadFlow(flows[tabIndex]);
else downloadFlow(removeApiKeys(flows[tabIndex])); else downloadFlow(removeApiKeys(flows[tabIndex]));
}} }}
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" type="submit"
> >
Download Flow Download Flow
</button> </Button>
</div> </DialogFooter>
</div> </DialogContent>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog> </Dialog>
</Transition.Root>
); );
} }

View file

@ -1,4 +1,3 @@
import { Dialog, Transition } from "@headlessui/react";
import { XMarkIcon, DocumentTextIcon } from "@heroicons/react/24/outline"; import { XMarkIcon, DocumentTextIcon } from "@heroicons/react/24/outline";
import { Fragment, useContext, useRef, useState } from "react"; import { Fragment, useContext, useRef, useState } from "react";
import { PopUpContext } from "../../contexts/popUpContext"; import { PopUpContext } from "../../contexts/popUpContext";
@ -6,6 +5,19 @@ import { darkContext } from "../../contexts/darkContext";
import { checkPrompt } from "../../controllers/API"; import { checkPrompt } from "../../controllers/API";
import { alertContext } from "../../contexts/alertContext"; import { alertContext } from "../../contexts/alertContext";
import { TypeModal } from "../../utils"; import { TypeModal } from "../../utils";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "../../components/ui/dialog";
import { Button } from "../../components/ui/button";
import { Textarea } from "../../components/ui/textarea";
import { PROMPT_DIALOG_SUBTITLE, TEXT_DIALOG_SUBTITLE } from "../../constants";
export default function PromptAreaModal({ export default function PromptAreaModal({
value, value,
setValue, setValue,
@ -31,92 +43,54 @@ export default function PromptAreaModal({
function setModalOpen(x: boolean) { function setModalOpen(x: boolean) {
setOpen(x); setOpen(x);
if (x === false) { if (x === false) {
setTimeout(() => {
closePopUp(); closePopUp();
}, 300);
} }
} }
return ( return (
<Transition.Root show={open} appear={true} as={Fragment}> <Dialog open={true} onOpenChange={setModalOpen}>
<Dialog <DialogTrigger></DialogTrigger>
as="div" <DialogContent className="lg:max-w-[700px]">
className="relative z-10" <DialogHeader>
onClose={setModalOpen} <DialogTitle className="flex items-center">
initialFocus={ref} <span className="pr-2">{myModalTitle}</span>
>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-gray-500 dark:bg-gray-600 dark:bg-opacity-75 bg-opacity-75 transition-opacity" />
</Transition.Child>
<div className="fixed inset-0 z-10 overflow-y-auto">
<div className="flex h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="relative flex flex-col justify-between transform h-[600px] overflow-hidden rounded-lg bg-white dark:bg-gray-800 text-left shadow-xl transition-all sm:my-8 w-[700px]">
<div className=" z-50 absolute top-0 right-0 hidden pt-4 pr-4 sm:block">
<button
type="button"
className="rounded-md text-gray-400 hover:text-gray-500"
onClick={() => {
setModalOpen(false);
}}
>
<span className="sr-only">Close</span>
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
</button>
</div>
<div className="h-full w-full flex flex-col justify-center items-center">
<div className="flex w-full pb-4 z-10 justify-center shadow-sm">
<div className="mx-auto mt-4 flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-blue-100 dark:bg-gray-900 sm:mx-0 sm:h-10 sm:w-10">
<DocumentTextIcon <DocumentTextIcon
className="h-6 w-6 text-blue-600" className="h-6 w-6 text-gray-800 pl-1 dark:text-white"
aria-hidden="true" aria-hidden="true"
/> />
</div> </DialogTitle>
<div className="mt-4 text-center sm:ml-4 sm:text-left"> <DialogDescription>
<Dialog.Title {(() => {
as="h3" switch (myModalTitle) {
className="text-lg font-medium dark:text-white leading-10 text-gray-900" case "Edit Text":
> return TEXT_DIALOG_SUBTITLE;
{myModalTitle}
</Dialog.Title> case "Edit Prompt":
</div> return PROMPT_DIALOG_SUBTITLE;
</div>
<div className="h-full w-full bg-gray-200 overflow-auto dark:bg-gray-900 p-4 gap-4 flex flex-row justify-center items-center"> default:
<div className="flex h-full w-full"> return null;
<div className="overflow-hidden px-4 py-5 sm:p-6 w-full h-full rounded-lg bg-white dark:bg-gray-800 shadow"> }
<textarea })()}
</DialogDescription>
</DialogHeader>
<div className="flex h-full w-full mt-2">
<Textarea
ref={ref} ref={ref}
className="form-input h-full w-full rounded-lg border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-white" className="form-input h-[300px] w-full rounded-lg border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-white"
value={myValue} value={myValue}
onChange={(e) => { onChange={(e) => {
setMyValue(e.target.value); setMyValue(e.target.value);
setValue(e.target.value); setValue(e.target.value);
}} }}
placeholder="Type message here."
/> />
</div> </div>
</div>
</div> <DialogFooter>
<div className="bg-gray-200 dark:bg-gray-900 w-full pb-3 flex flex-row-reverse px-4"> <Button
<button className="mt-3"
type="button"
className="inline-flex w-full justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:ml-3 sm:w-auto sm:text-sm"
onClick={() => { onClick={() => {
switch (myModalType) { switch (myModalType) {
case 1: case 1:
@ -126,8 +100,7 @@ export default function PromptAreaModal({
checkPrompt(myValue) checkPrompt(myValue)
.then((apiReturn) => { .then((apiReturn) => {
if (apiReturn.data) { if (apiReturn.data) {
let inputVariables = let inputVariables = apiReturn.data.input_variables;
apiReturn.data.input_variables;
if (inputVariables.length === 0) { if (inputVariables.length === 0) {
setErrorData({ setErrorData({
title: title:
@ -142,8 +115,7 @@ export default function PromptAreaModal({
} }
} else { } else {
setErrorData({ setErrorData({
title: title: "Something went wrong, please try again",
"Something went wrong, please try again",
}); });
} }
}) })
@ -160,16 +132,12 @@ export default function PromptAreaModal({
break; break;
} }
}} }}
type="submit"
> >
{myButtonText} {myButtonText}
</button> </Button>
</div> </DialogFooter>
</div> </DialogContent>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog> </Dialog>
</Transition.Root>
); );
} }

View file

@ -0,0 +1,195 @@
import React, { useContext, useState } from "react";
import { Menu, Transition } from "@headlessui/react";
import { EllipsisVerticalIcon } from "@heroicons/react/20/solid";
import {
Cog6ToothIcon,
TrashIcon,
PencilSquareIcon,
DocumentDuplicateIcon,
DocumentPlusIcon,
Square2StackIcon,
} from "@heroicons/react/24/outline";
import { classNames } from "../../../../utils";
import { Fragment } from "react";
import NodeModal from "../../../../modals/NodeModal";
import { TabsContext } from "../../../../contexts/tabsContext";
import { useReactFlow } from "reactflow";
import EditNodeModal from "../../../../modals/EditNodeModal";
import TooltipReact from "../../../../components/ReactTooltipComponent";
import ShadTooltip from "../../../../components/ShadTooltipComponent";
const NodeToolbarComponent = (props) => {
const [nodeLength, setNodeLength] = useState(
Object.keys(props.data.node.template).filter(
(t) =>
t.charAt(0) !== "_" &&
props.data.node.template[t].show &&
(props.data.node.template[t].type === "str" ||
props.data.node.template[t].type === "bool" ||
props.data.node.template[t].type === "float" ||
props.data.node.template[t].type === "code" ||
props.data.node.template[t].type === "prompt" ||
props.data.node.template[t].type === "file" ||
props.data.node.template[t].type === "Any" ||
props.data.node.template[t].type === "int")
).length
);
const { setLastCopiedSelection, paste } = useContext(TabsContext);
const reactFlowInstance = useReactFlow();
return (
<>
<div className="h-10 w-26">
<span className="isolate inline-flex rounded-md shadow-sm">
<ShadTooltip delayDuration={1000} content="Delete" side="top">
<button
className="hover:dark:hover:bg-[#242f47] text-gray-700 transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 shadow-md relative inline-flex items-center rounded-l-md bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
onClick={() => {
props.deleteNode(props.data.id);
}}
>
<TrashIcon className="w-5 h-5 dark:text-gray-300"></TrashIcon>
</button>
</ShadTooltip>
<ShadTooltip delayDuration={1000} content="Duplicate" side="top">
<button
className={classNames(
nodeLength > 0
? "hover:dark:hover:bg-[#242f47] text-gray-700 transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 shadow-md relative -ml-px inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
: "hover:dark:hover:bg-[#242f47] text-gray-700 transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 shadow-md relative -ml-px inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10 rounded-r-md"
)}
onClick={(event) => {
event.preventDefault();
console.log(reactFlowInstance.getNode(props.data.id));
paste(
{
nodes: [reactFlowInstance.getNode(props.data.id)],
edges: [],
},
{
x: 50,
y: 10,
paneX: reactFlowInstance.getNode(props.data.id).position.x,
paneY: reactFlowInstance.getNode(props.data.id).position.y,
}
);
}}
>
<Square2StackIcon className="w-5 h-5 dark:text-gray-300"></Square2StackIcon>
</button>
</ShadTooltip>
{nodeLength > 0 && (
<ShadTooltip delayDuration={1000} content="Edit" side="top">
<button
className="hover:dark:hover:bg-[#242f47] text-gray-700 transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 shadow-md relative -ml-px inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10 rounded-r-md"
onClick={(event) => {
event.preventDefault();
props.openPopUp(<EditNodeModal data={props.data} />);
}}
>
<PencilSquareIcon className="w-5 h-5 dark:text-gray-300"></PencilSquareIcon>
</button>
</ShadTooltip>
)}
{/*
<Menu as="div" className="relative inline-block text-left z-100">
<button className="hover:dark:hover:bg-[#242f47] text-gray-700 transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 shadow-md relative -ml-px inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10 rounded-r-md">
<div>
<Menu.Button className="flex items-center">
<EllipsisVerticalIcon
className="w-5 h-5 dark:text-gray-300"
aria-hidden="true"
/>
</Menu.Button>
</div>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="absolute z-40 mt-2 w-56 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none top-[28px]">
<div className="py-1">
<Menu.Item>
{({ active }) => (
<button
onClick={(event) => {
event.preventDefault();
props.openPopUp(
<EditNodeModal data={props.data} />
);
}}
className={classNames(
active
? "bg-gray-100 text-gray-900"
: "text-gray-700",
"w-full group flex items-center px-4 py-2 text-sm"
)}
>
<PencilSquareIcon
className="mr-3 h-5 w-5 text-gray-400 group-hover:text-gray-500"
aria-hidden="true"
/>
Edit
</button>
)}
</Menu.Item>
<Menu.Item>
{({ active }) => (
<button
onClick={(event) => {
event.preventDefault();
console.log(
reactFlowInstance.getNode(props.data.id)
);
paste(
{
nodes: [
reactFlowInstance.getNode(props.data.id),
],
edges: [],
},
{
x: 50,
y: 10,
paneX: reactFlowInstance.getNode(props.data.id)
.position.x,
paneY: reactFlowInstance.getNode(props.data.id)
.position.y,
}
);
}}
className={classNames(
active
? "bg-gray-100 text-gray-900"
: "text-gray-700",
"w-full group flex items-center px-4 py-2 text-sm"
)}
>
<DocumentDuplicateIcon
className="mr-3 h-5 w-5 text-gray-400 group-hover:text-gray-500"
aria-hidden="true"
/>
Duplicate
</button>
)}
</Menu.Item>
</div>
</Menu.Items>
</Transition>
</button>
</Menu> */}
</span>
</div>
</>
);
};
export default NodeToolbarComponent;

View file

@ -38,20 +38,25 @@ const nodeTypes = {
}; };
export default function FlowPage({ flow }: { flow: FlowType }) { export default function FlowPage({ flow }: { flow: FlowType }) {
let { updateFlow, disableCopyPaste, addFlow, getNodeId, paste } = let {
useContext(TabsContext); updateFlow,
disableCopyPaste,
addFlow,
getNodeId,
paste,
lastCopiedSelection,
setLastCopiedSelection,
} = useContext(TabsContext);
const { types, reactFlowInstance, setReactFlowInstance, templates } = const { types, reactFlowInstance, setReactFlowInstance, templates } =
useContext(typesContext); useContext(typesContext);
const reactFlowWrapper = useRef(null); const reactFlowWrapper = useRef(null);
const { undo, redo, canUndo, canRedo, takeSnapshot } = useUndoRedo(); const { takeSnapshot } = useUndoRedo();
const [position, setPosition] = useState({ x: 0, y: 0 }); const [position, setPosition] = useState({ x: 0, y: 0 });
const [lastSelection, setLastSelection] = const [lastSelection, setLastSelection] =
useState<OnSelectionChangeParams>(null); useState<OnSelectionChangeParams>(null);
const [lastCopiedSelection, setLastCopiedSelection] = useState(null);
useEffect(() => { useEffect(() => {
// this effect is used to attach the global event handlers // this effect is used to attach the global event handlers
@ -63,6 +68,7 @@ export default function FlowPage({ flow }: { flow: FlowType }) {
!disableCopyPaste !disableCopyPaste
) { ) {
event.preventDefault(); event.preventDefault();
console.log(_.cloneDeep(lastSelection));
setLastCopiedSelection(_.cloneDeep(lastSelection)); setLastCopiedSelection(_.cloneDeep(lastSelection));
} }
if ( if (

View file

@ -12,6 +12,7 @@ export type InputComponentType = {
onChange: (value: string) => void; onChange: (value: string) => void;
password: boolean; password: boolean;
disableCopyPaste?: boolean; disableCopyPaste?: boolean;
editNode?: boolean;
}; };
export type ToggleComponentType = { export type ToggleComponentType = {
enabled: boolean; enabled: boolean;
@ -22,6 +23,7 @@ export type DropDownComponentType = {
value: string; value: string;
options: string[]; options: string[];
onSelect: (value: string) => void; onSelect: (value: string) => void;
editNode?: boolean;
}; };
export type ParameterComponentType = { export type ParameterComponentType = {
data: NodeDataType; data: NodeDataType;
@ -44,6 +46,7 @@ export type TextAreaComponentType = {
disabled: boolean; disabled: boolean;
onChange: (value: string[] | string) => void; onChange: (value: string[] | string) => void;
value: string; value: string;
editNode?: boolean;
}; };
export type FileComponentType = { export type FileComponentType = {
@ -53,6 +56,7 @@ export type FileComponentType = {
suffixes: Array<string>; suffixes: Array<string>;
fileTypes: Array<string>; fileTypes: Array<string>;
onFileChange: (value: string) => void; onFileChange: (value: string) => void;
editNode?: boolean;
}; };
export type DisclosureComponentType = { export type DisclosureComponentType = {
@ -73,6 +77,7 @@ export type FloatComponentType = {
disabled?: boolean; disabled?: boolean;
disableCopyPaste?: boolean; disableCopyPaste?: boolean;
onChange: (value: string) => void; onChange: (value: string) => void;
editNode?: boolean;
}; };
export type TooltipComponentType = { export type TooltipComponentType = {

View file

@ -18,8 +18,10 @@ export type TabsContextType = {
getNodeId: () => string; getNodeId: () => string;
paste: ( paste: (
selection: { nodes: any; edges: any }, selection: { nodes: any; edges: any },
position: { x: number; y: number } position: { x: number; y: number; paneX?: number; paneY?: number }
) => void; ) => void;
lastCopiedSelection: { nodes: any; edges: any };
setLastCopiedSelection: (selection: { nodes: any; edges: any }) => void;
}; };
export type LangFlowState = { export type LangFlowState = {

View file

@ -535,6 +535,13 @@ export function updateTemplate(
if (objectToUpdate[key] && objectToUpdate[key].value) { if (objectToUpdate[key] && objectToUpdate[key].value) {
clonedObject[key].value = objectToUpdate[key].value; clonedObject[key].value = objectToUpdate[key].value;
} }
if (
objectToUpdate[key] &&
objectToUpdate[key].advanced !== null &&
objectToUpdate[key].advanced !== undefined
) {
clonedObject[key].advanced = objectToUpdate[key].advanced;
}
} }
return clonedObject; return clonedObject;
} }

View file

@ -2,6 +2,81 @@
const { fontFamily } = require("tailwindcss/defaultTheme") const { fontFamily } = require("tailwindcss/defaultTheme")
import plugin from "tailwindcss/plugin"; import plugin from "tailwindcss/plugin";
module.exports = {
darkMode: ["class"],
content: ["app/**/*.{ts,tsx}", "components/**/*.{ts,tsx}"],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
borderRadius: {
lg: `var(--radius)`,
md: `calc(var(--radius) - 2px)`,
sm: "calc(var(--radius) - 4px)",
},
fontFamily: {
sans: ["var(--font-sans)", ...fontFamily.sans],
},
keyframes: {
"accordion-down": {
from: { height: 0 },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: 0 },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
},
},
plugins: [require("tailwindcss-animate")],
}
module.exports = { module.exports = {
content: ["./index.html", "./src/**/*.{js,ts,tsx,jsx}"], content: ["./index.html", "./src/**/*.{js,ts,tsx,jsx}"],
darkMode: "class", darkMode: "class",
@ -89,6 +164,7 @@ module.exports = {
}, },
}, },
plugins: [ plugins: [
require("tailwindcss-animate"),
require("@tailwindcss/forms")({ require("@tailwindcss/forms")({
strategy: "class", // only generate classes strategy: "class", // only generate classes
}), }),