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": [
{
"name": "Python: FastAPI",
"name": "Debug Backend",
"type": "python",
"request": "launch",
"module": "uvicorn",
@ -14,7 +14,7 @@
"debug"
],
"jinja": true,
"justMyCode": false
"justMyCode": true
},
{
"name": "Python: Remote Attach",
@ -30,6 +30,13 @@
"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-slot": "^1.0.2",
"@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",
"@tabler/icons-react": "^2.18.0",
"@tailwindcss/forms": "^0.5.3",
"@tailwindcss/line-clamp": "^0.4.4",
"ace-builds": "^1.16.0",
"add": "^2.0.6",
"ansi-to-html": "^0.7.2",
"axios": "^1.3.2",
"base64-js": "^1.5.1",
"class-variance-authority": "^0.6.0",
"clsx": "^1.2.1",
"esbuild": "^0.17.18",
"lodash": "^4.17.21",
"lucide-react": "^0.233.0",
"react": "^18.2.0",
@ -41,6 +48,9 @@
"rehype-mathjax": "^4.0.2",
"remark-gfm": "^3.0.1",
"remark-math": "^5.1.1",
"shadcn-ui": "^0.1.3",
"switch": "^0.0.0",
"table": "^6.8.1",
"tailwind-merge": "^1.13.0",
"tailwindcss-animate": "^1.0.5",
"uuid": "^9.0.0",
@ -74,6 +84,8 @@
},
"proxy": "http://127.0.0.1:7860",
"devDependencies": {
"@swc/cli": "^0.1.62",
"@swc/core": "^1.3.62",
"@tailwindcss/typography": "^0.5.9",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
@ -91,4 +103,4 @@
"typescript": "^5.0.2",
"vite": "^4.3.5"
}
}
}

View file

@ -123,7 +123,11 @@ export default function App() {
>
<MainPage />
</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) => (
<div key={alert.id}>
{alert.type === "error" ? (

View file

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

View file

@ -29,6 +29,9 @@ import { useCallback } from "react";
import { TabsContext } from "../../contexts/tabsContext";
import { debounce } from "../../utils";
import Tooltip from "../../components/TooltipComponent";
import { NodeToolbar } from "reactflow";
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
import ShadTooltip from "../../components/ShadTooltipComponent";
export default function GenericNode({
data,
@ -97,190 +100,173 @@ export default function GenericNode({
deleteNode(data.id);
return;
}
console.log(data);
return (
<div
className={classNames(
selected ? "border border-blue-500" : "border dark:border-gray-700",
"prompt-node relative flex w-96 flex-col justify-center rounded-lg bg-white dark:bg-gray-900"
)}
>
<div className="flex w-full items-center justify-between gap-8 rounded-t-lg border-b bg-gray-50 p-4 dark:border-b-gray-700 dark:bg-gray-800 dark:text-white ">
<div className="flex w-full items-center gap-2 truncate text-lg">
<Icon
className="h-10 w-10 rounded p-1"
style={{
color: nodeColors[types[data.type]] ?? nodeColors.unknown,
}}
/>
<div className="ml-2 truncate">
<ShadTooltip delayDuration={1500} content={data.type}>
<div className="ml-2 truncate">{data.type}</div>
</ShadTooltip>
</div>
</div>
<div className="flex gap-3">
<button
className="relative"
onClick={(event) => {
event.preventDefault();
openPopUp(<NodeModal data={data} />);
}}
>
<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
)
? " *"
: ""}
<>
<NodeToolbar>
<NodeToolbarComponent
data={data}
openPopUp={openPopUp}
deleteNode={deleteNode}
></NodeToolbarComponent>
</NodeToolbar>
<div
className={classNames(
selected ? "border border-blue-500" : "border dark:border-gray-700",
"prompt-node relative flex w-96 flex-col justify-center rounded-lg bg-white dark:bg-gray-900"
)}
>
<div className="flex w-full items-center justify-between gap-8 rounded-t-lg border-b bg-gray-50 p-4 dark:border-b-gray-700 dark:bg-gray-800 dark:text-white ">
<div className="flex w-full items-center gap-2 truncate text-lg">
<Icon
className="h-10 w-10 rounded p-1"
style={{
color: nodeColors[types[data.type]] ?? nodeColors.unknown,
}}
/>
<div className="ml-2 truncate">
<ShadTooltip delayDuration={1500} content={data.type}>
<div className="ml-2 truncate">{data.type}</div>
</ShadTooltip>
</div>
<Cog6ToothIcon
</div>
<div className="flex gap-3">
<button
className="relative"
onClick={(event) => {
event.preventDefault();
openPopUp(<NodeModal data={data} />);
}}
></button>
</div>
<div className="flex gap-3">
<div>
<Tooltip
title={
!validationStatus ? (
"Validating..."
) : (
<div className="max-h-96 overflow-auto">
{validationStatus.params
.split("\n")
.map((line, index) => (
<div key={index}>{line}</div>
))}
</div>
)
}
>
<div className="w-5 h-5 relative top-[3px]">
<div
className={classNames(
validationStatus && validationStatus.valid
? "w-4 h-4 rounded-full bg-green-500 opacity-100"
: "w-4 h-4 rounded-full bg-gray-500 opacity-0 hidden animate-spin",
"absolute w-4 hover:text-gray-500 hover:dark:text-gray-300 transition-all ease-in-out duration-200"
)}
></div>
<div
className={classNames(
validationStatus && !validationStatus.valid
? "w-4 h-4 rounded-full bg-red-500 opacity-100"
: "w-4 h-4 rounded-full bg-gray-500 opacity-0 hidden animate-spin",
"absolute w-4 hover:text-gray-500 hover:dark:text-gray-300 transition-all ease-in-out duration-200"
)}
></div>
<div
className={classNames(
!validationStatus
? "w-4 h-4 rounded-full bg-yellow-500 opacity-100"
: "w-4 h-4 rounded-full bg-gray-500 opacity-0 hidden animate-spin",
"absolute w-4 hover:text-gray-500 hover:dark:text-gray-300 transition-all ease-in-out duration-200"
)}
></div>
</div>
</Tooltip>
</div>
</div>
</div>
<div className="h-full w-full py-5">
<div className="w-full px-5 pb-3 text-sm text-gray-500 dark:text-gray-300">
{data.node.description}
</div>
<>
{Object.keys(data.node.template)
.filter((t) => t.charAt(0) !== "_")
.map((t: string, idx) => (
<div key={idx}>
{/* {idx === 0 ? (
<div
className={classNames(
"px-5 py-2 mt-2 dark:text-white text-center",
Object.keys(data.node.template).filter(
(key) =>
!key.startsWith("_") &&
data.node.template[key].show &&
!data.node.template[key].advanced
).length === 0
? "hidden"
: ""
)}
>
Inputs
</div>
) : (
<></>
)} */}
{data.node.template[t].show &&
!data.node.template[t].advanced ? (
<ParameterComponent
data={data}
color={
nodeColors[types[data.node.template[t].type]] ??
nodeColors.unknown
}
title={
data.node.template[t].display_name
? data.node.template[t].display_name
: data.node.template[t].name
? toTitleCase(data.node.template[t].name)
: toTitleCase(t)
}
name={t}
tooltipTitle={data.node.template[t].type}
required={data.node.template[t].required}
id={data.node.template[t].type + "|" + t + "|" + data.id}
left={true}
type={data.node.template[t].type}
/>
) : (
<></>
)}
</div>
))}
<div
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"
Object.keys(data.node.template).length < 1 ? "hidden" : "",
"flex w-full justify-center"
)}
></Cog6ToothIcon>
</button>
<button
onClick={() => {
deleteNode(data.id);
}}
>
<TrashIcon className="w-5 h-5 dark:text-gray-300"></TrashIcon>
</button>
<div>
<Tooltip
title={
!validationStatus ? (
"Validating..."
) : (
<div className="max-h-96 overflow-auto">
{validationStatus.params.split("\n").map((line, index) => (
<div key={index}>{line}</div>
))}
</div>
)
}
>
<div className="w-5 h-5 relative top-[3px]">
<div
className={classNames(
validationStatus && validationStatus.valid
? "w-4 h-4 rounded-full bg-green-500 opacity-100"
: "w-4 h-4 rounded-full bg-gray-500 opacity-0 hidden animate-spin",
"absolute w-4 hover:text-gray-500 hover:dark:text-gray-300 transition-all ease-in-out duration-200"
)}
></div>
<div
className={classNames(
validationStatus && !validationStatus.valid
? "w-4 h-4 rounded-full bg-red-500 opacity-100"
: "w-4 h-4 rounded-full bg-gray-500 opacity-0 hidden animate-spin",
"absolute w-4 hover:text-gray-500 hover:dark:text-gray-300 transition-all ease-in-out duration-200"
)}
></div>
<div
className={classNames(
!validationStatus
? "w-4 h-4 rounded-full bg-yellow-500 opacity-100"
: "w-4 h-4 rounded-full bg-gray-500 opacity-0 hidden animate-spin",
"absolute w-4 hover:text-gray-500 hover:dark:text-gray-300 transition-all ease-in-out duration-200"
)}
></div>
</div>
</Tooltip>
</div>
{" "}
</div>
{/* <div className="px-5 py-2 mt-2 dark:text-white text-center">
Output
</div> */}
<ParameterComponent
data={data}
color={nodeColors[types[data.type]] ?? nodeColors.unknown}
title={data.type}
tooltipTitle={`${data.node.base_classes.join("\n")}`}
id={[data.type, data.id, ...data.node.base_classes].join("|")}
type={data.node.base_classes.join("|")}
left={false}
/>
</>
</div>
</div>
<div className="h-full w-full py-5">
<div className="w-full px-5 pb-3 text-sm text-gray-500 dark:text-gray-300">
{data.node.description}
</div>
<>
{Object.keys(data.node.template)
.filter((t) => t.charAt(0) !== "_")
.map((t: string, idx) => (
<div key={idx}>
{/* {idx === 0 ? (
<div
className={classNames(
"px-5 py-2 mt-2 dark:text-white text-center",
Object.keys(data.node.template).filter(
(key) =>
!key.startsWith("_") &&
data.node.template[key].show &&
!data.node.template[key].advanced
).length === 0
? "hidden"
: ""
)}
>
Inputs
</div>
) : (
<></>
)} */}
{data.node.template[t].show &&
!data.node.template[t].advanced ? (
<ParameterComponent
data={data}
color={
nodeColors[types[data.node.template[t].type]] ??
nodeColors.unknown
}
title={
data.node.template[t].display_name
? data.node.template[t].display_name
: data.node.template[t].name
? toTitleCase(data.node.template[t].name)
: toTitleCase(t)
}
name={t}
tooltipTitle={data.node.template[t].type}
required={data.node.template[t].required}
id={data.node.template[t].type + "|" + t + "|" + data.id}
left={true}
type={data.node.template[t].type}
/>
) : (
<></>
)}
</div>
))}
<div
className={classNames(
Object.keys(data.node.template).length < 1 ? "hidden" : "",
"flex w-full justify-center"
)}
>
{" "}
</div>
{/* <div className="px-5 py-2 mt-2 dark:text-white text-center">
Output
</div> */}
<ParameterComponent
data={data}
color={nodeColors[types[data.type]] ?? nodeColors.unknown}
title={data.type}
tooltipTitle={`${data.node.base_classes.join("\n")}`}
id={[data.type, data.id, ...data.node.base_classes].join("|")}
type={data.node.base_classes.join("|")}
left={false}
/>
</>
</div>
</div>
</>
);
}

View file

@ -9,6 +9,7 @@ export default function CodeAreaComponent({
value,
onChange,
disabled,
editNode = false,
}: TextAreaComponentType) {
const [myValue, setMyValue] = useState(value);
const { openPopUp } = useContext(PopUpContext);
@ -38,8 +39,10 @@ export default function CodeAreaComponent({
);
}}
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" +
(disabled ? " bg-gray-200" : "")
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" : "")
}
>
{myValue !== "" ? myValue : "Text empty"}
@ -57,7 +60,9 @@ export default function CodeAreaComponent({
);
}}
>
<ArrowTopRightOnSquareIcon className="w-6 h-6 hover:text-blue-600 dark:text-gray-300" />
{!editNode && (
<ArrowTopRightOnSquareIcon className="w-6 h-6 hover:text-blue-600 dark:text-gray-300" />
)}
</button>
</div>
</div>

View file

@ -8,10 +8,12 @@ export default function Dropdown({
value,
options,
onSelect,
editNode = false,
}: DropDownComponentType) {
let [internalValue, setInternalValue] = useState(
value === "" || !value ? "Choose an option" : value
);
return (
<>
<Listbox
@ -23,12 +25,26 @@ export default function Dropdown({
>
{({ open }) => (
<>
<div className="relative mt-1 w-full">
<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">
<span className="block truncate w-full dark:text-gray-300">
{internalValue}
</span>
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<div
className={
editNode ? "relative mt-0 w-full" : "relative mt-1 w-full"
}
>
<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
className="h-5 w-5 text-gray-400"
aria-hidden="true"
@ -43,16 +59,27 @@ export default function Dropdown({
leaveFrom="opacity-100"
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) => (
<Listbox.Option
key={id}
className={({ active }) =>
classNames(
active
active && !editNode
? "text-white bg-indigo-600 dark:bg-indigo-500"
: "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}
@ -71,7 +98,8 @@ export default function Dropdown({
{selected ? (
<span
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"
)}
>

View file

@ -7,6 +7,7 @@ export default function FloatComponent({
onChange,
disableCopyPaste = false,
disabled,
editNode = false,
}: FloatComponentType) {
const [myValue, setMyValue] = useState(value ?? "");
const { setDisableCopyPaste } = useContext(TabsContext);
@ -33,10 +34,14 @@ export default function FloatComponent({
type="number"
value={myValue}
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" +
(disabled ? " bg-gray-200 dark:bg-gray-700" : "")
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" : "")
}
placeholder={
editNode ? "Number 0 to 1" : "Type a number from zero to one"
}
placeholder="Type a number from zero to one"
onChange={(e) => {
setMyValue(e.target.value);
onChange(e.target.value);

View file

@ -9,6 +9,7 @@ export default function InputComponent({
disableCopyPaste = false,
disabled,
password,
editNode = false,
}: InputComponentType) {
const [myValue, setMyValue] = useState(value ?? "");
const [pwdVisible, setPwdVisible] = useState(false);
@ -19,6 +20,7 @@ export default function InputComponent({
onChange("");
}
}, [disabled, onChange]);
return (
<div
className={
@ -36,60 +38,70 @@ export default function InputComponent({
if (disableCopyPaste) setDisableCopyPaste(false);
}}
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" : "",
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) => {
setMyValue(e.target.value);
onChange(e.target.value);
}}
/>
<button
className="absolute inset-y-0 right-0 items-center px-4 text-gray-600"
onClick={() => {
setPwdVisible(!pwdVisible);
}}
>
{password &&
(pwdVisible ? (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="w-5 h-5"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M3.98 8.223A10.477 10.477 0 001.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.45 10.45 0 0112 4.5c4.756 0 8.773 3.162 10.065 7.498a10.523 10.523 0 01-4.293 5.774M6.228 6.228L3 3m3.228 3.228l3.65 3.65m7.894 7.894L21 21m-3.228-3.228l-3.65-3.65m0 0a3 3 0 10-4.243-4.243m4.242 4.242L9.88 9.88"
/>
</svg>
) : (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="w-5 h-5"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z"
/>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
))}
</button>
{password && (
<button
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={() => {
setPwdVisible(!pwdVisible);
}}
>
{password &&
(pwdVisible ? (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="w-5 h-5"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M3.98 8.223A10.477 10.477 0 001.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.45 10.45 0 0112 4.5c4.756 0 8.773 3.162 10.065 7.498a10.523 10.523 0 01-4.293 5.774M6.228 6.228L3 3m3.228 3.228l3.65 3.65m7.894 7.894L21 21m-3.228-3.228l-3.65-3.65m0 0a3 3 0 10-4.243-4.243m4.242 4.242L9.88 9.88"
/>
</svg>
) : (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="w-5 h-5"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z"
/>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
))}
</button>
)}
</div>
);
}

View file

@ -10,6 +10,7 @@ export default function InputFileComponent({
suffixes,
fileTypes,
onFileChange,
editNode = false,
}: FileComponentType) {
const [myValue, setMyValue] = useState(value);
const { setErrorData } = useContext(alertContext);
@ -71,14 +72,18 @@ export default function InputFileComponent({
<span
onClick={handleButtonClick}
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" +
(disabled ? " bg-gray-200" : "")
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" : "")
}
>
{myValue !== "" ? myValue : "No file"}
</span>
<button onClick={handleButtonClick}>
<DocumentMagnifyingGlassIcon className="w-8 h-8 hover:text-blue-600" />
{!editNode && (
<DocumentMagnifyingGlassIcon className="w-8 h-8 hover:text-blue-600" />
)}
</button>
</div>
</div>

View file

@ -1,12 +1,14 @@
import { useContext, useEffect, useState } from "react";
import { FloatComponentType } from "../../types/components";
import { TabsContext } from "../../contexts/tabsContext";
import { classNames } from "../../utils";
export default function IntComponent({
value,
onChange,
disableCopyPaste = false,
disabled,
editNode = false,
}: FloatComponentType) {
const [myValue, setMyValue] = useState(value ?? "");
const { setDisableCopyPaste } = useContext(TabsContext);
@ -53,10 +55,12 @@ export default function IntComponent({
type="number"
value={myValue}
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" +
(disabled ? " bg-gray-200 dark:bg-gray-700" : "")
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" : "")
}
placeholder="Type a integer number"
placeholder={editNode ? "Integer number" : "Type a integer number"}
onChange={(e) => {
setMyValue(e.target.value);
onChange(e.target.value);

View file

@ -11,6 +11,7 @@ export default function PromptAreaComponent({
value,
onChange,
disabled,
editNode = false,
}: TextAreaComponentType) {
const [myValue, setMyValue] = useState(value);
const { openPopUp } = useContext(PopUpContext);
@ -43,11 +44,13 @@ export default function PromptAreaComponent({
);
}}
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" +
(disabled ? " bg-gray-200" : "")
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" : "")
}
>
{myValue !== "" ? myValue : "Text empty"}
{myValue !== "" ? myValue : "-"}
</span>
<button
onClick={() => {
@ -65,7 +68,9 @@ export default function PromptAreaComponent({
);
}}
>
<ArrowTopRightOnSquareIcon className="w-6 h-6 hover:text-blue-600 dark:text-gray-300" />
{!editNode && (
<ArrowTopRightOnSquareIcon className="w-6 h-6 hover:text-blue-600 dark:text-gray-300" />
)}
</button>
</div>
</div>

View file

@ -9,6 +9,7 @@ export default function TextAreaComponent({
value,
onChange,
disabled,
editNode = false,
}: TextAreaComponentType) {
const [myValue, setMyValue] = useState(value);
const { openPopUp } = useContext(PopUpContext);
@ -37,8 +38,10 @@ export default function TextAreaComponent({
);
}}
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" +
(disabled ? " bg-gray-200" : "")
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" : "")
}
>
{myValue !== "" ? myValue : "Text empty"}
@ -59,7 +62,9 @@ export default function TextAreaComponent({
);
}}
>
<ArrowTopRightOnSquareIcon className="w-6 h-6 hover:text-blue-600 dark:text-gray-300" />
{!editNode && (
<ArrowTopRightOnSquareIcon className="w-6 h-6 hover:text-blue-600 dark:text-gray-300" />
)}
</button>
</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: () => {},
disableCopyPaste: false,
setDisableCopyPaste: (state: boolean) => {},
lastCopiedSelection: null,
setLastCopiedSelection: (selection: any) => {},
getNodeId: () => "",
paste: (
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 [id, setId] = useState(uuidv4());
const { templates, reactFlowInstance } = useContext(typesContext);
const [lastCopiedSelection, setLastCopiedSelection] = useState(null);
const newNodeId = useRef(uuidv4());
function incrementNodeId() {
@ -270,7 +274,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
// simulate a click on the link element to trigger the download
link.click();
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.
*/
function paste(selectionInstance, position) {
function paste(
selectionInstance,
position: { x: number; y: number; paneX?: number; paneY?: number }
) {
let minimumX = Infinity;
let minimumY = Infinity;
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) => {
// Generate a unique node ID
@ -524,6 +533,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
return (
<TabsContext.Provider
value={{
lastCopiedSelection,
setLastCopiedSelection,
disableCopyPaste,
setDisableCopyPaste,
save,

View file

@ -94,6 +94,8 @@ code {
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 {
cursor: default;
}

View file

@ -1,4 +1,3 @@
import { Dialog, Transition } from "@headlessui/react";
import { IconCheck, IconClipboard, IconDownload } from "@tabler/icons-react";
import {
XMarkIcon,
@ -15,13 +14,22 @@ import "ace-builds/src-noconflict/ext-language_tools";
import { darkContext } from "../../contexts/darkContext";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
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 }) {
const [open, setOpen] = useState(true);
const { dark } = useContext(darkContext);
const { closePopUp } = useContext(PopUpContext);
const [activeTab, setActiveTab] = useState(0);
const ref = useRef();
const [isCopied, setIsCopied] = useState<Boolean>(false);
const copyToClipboard = () => {
@ -40,9 +48,7 @@ export default function ApiModal({ flowName }) {
function setModalOpen(x: boolean) {
setOpen(x);
if (x === false) {
setTimeout(() => {
closePopUp();
}, 300);
closePopUp();
}
}
@ -82,120 +88,69 @@ flow("Hey, have you heard of LangFlow?")`;
},
];
return (
<Transition.Root show={open} appear={true} as={Fragment}>
<Dialog
as="div"
className="relative z-10"
onClose={setModalOpen}
initialFocus={ref}
>
<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>
<Dialog open={true} onOpenChange={setModalOpen}>
<DialogTrigger></DialogTrigger>
<DialogContent className="lg:max-w-[800px] sm:max-w-[600px] h-[550px]">
<DialogHeader>
<DialogTitle className="flex items-center">
<span className="pr-2">Code</span>
<CodeBracketSquareIcon
className="h-6 w-6 text-gray-800 pl-1 dark:text-white"
aria-hidden="true"
/>
</DialogTitle>
<DialogDescription>
Export your flow to use it with this code.
</DialogDescription>
</DialogHeader>
<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"
<div className="flex flex-col h-full w-full ">
<div className="flex px-5 z-10">
{tabs.map((tab, index) => (
<button
key={index}
onClick={() => {
setActiveTab(index);
}}
className={
"p-2 rounded-t-lg w-44 border border-b-0 border-gray-300 dark:border-gray-700 dark:text-gray-300 -mr-px flex justify-center items-center gap-4 " +
(activeTab === index
? " bg-white dark:bg-gray-800"
: "bg-gray-100 dark:bg-gray-900")
}
>
{tab.name}
<img src={tab.image} className="w-6" />
</button>
))}
</div>
<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="items-center mb-2">
<div className="float-right">
<button
className="flex gap-1.5 items-center rounded bg-none p-1 text-xs text-gray-500 dark:text-gray-300"
onClick={copyToClipboard}
>
{isCopied ? (
<IconCheck size={18} />
) : (
<IconClipboard size={18} />
)}
{isCopied ? "Copied!" : "Copy code"}
</button>
</div>
</div>
<SyntaxHighlighter
className="h-[370px] w-full"
language={tabs[activeTab].mode}
style={oneDark}
customStyle={{ margin: 0 }}
>
<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
className="h-6 w-6 text-blue-600"
aria-hidden="true"
/>
</div>
<div className="mt-4 text-center sm:ml-4 sm:text-left">
<Dialog.Title
as="h3"
className="text-lg font-medium dark:text-white leading-10 text-gray-900"
>
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 px-5 z-10">
{tabs.map((tab, index) => (
<button
onClick={() => {
setActiveTab(index);
}}
className={
"p-2 rounded-t-lg w-44 border border-b-0 border-gray-300 dark:border-gray-700 dark:text-gray-300 -mr-px flex justify-center items-center gap-4 " +
(activeTab === index
? " bg-white dark:bg-gray-800"
: "bg-gray-100 dark:bg-gray-900")
}
>
{tab.name}
<img src={tab.image} className="w-6" />
</button>
))}
</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="w-full flex items-center justify-between mb-2">
<span className="text-sm text-gray-500 dark:text-gray-300">
Export your flow to use it with this code.
</span>
<button
className="flex gap-1.5 items-center rounded bg-none p-1 text-xs text-gray-500 dark:text-gray-300"
onClick={copyToClipboard}
>
{isCopied ? (
<IconCheck size={18} />
) : (
<IconClipboard size={18} />
)}
{isCopied ? "Copied!" : "Copy code"}
</button>
</div>
<SyntaxHighlighter
className="h-[370px]"
language={tabs[activeTab].mode}
style={oneDark}
customStyle={{ margin: 0 }}
>
{tabs[activeTab].code}
</SyntaxHighlighter>
</div>
</div>
</div>
</div>
</Dialog.Panel>
</Transition.Child>
{tabs[activeTab].code}
</SyntaxHighlighter>
</div>
</div>
</Dialog>
</Transition.Root>
</DialogContent>
</Dialog>
);
}

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 { Fragment, useContext, useRef, useState } from "react";
import { PopUpContext } from "../../contexts/popUpContext";
@ -12,6 +11,18 @@ import { darkContext } from "../../contexts/darkContext";
import { checkCode } from "../../controllers/API";
import { alertContext } from "../../contexts/alertContext";
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({
value,
setValue,
@ -34,143 +45,86 @@ export default function CodeAreaModal({
}
}
return (
<Transition.Root show={open} appear={true} as={Fragment}>
<Dialog
as="div"
className="relative z-10"
onClose={setModalOpen}
initialFocus={ref}
>
<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>
<Dialog open={true} onOpenChange={setModalOpen}>
<DialogTrigger></DialogTrigger>
<DialogContent className="lg:max-w-[700px] h-[500px]">
<DialogHeader>
<DialogTitle className="flex items-center">
<span className="pr-2">Edit Code</span>
<CommandLineIcon
className="h-6 w-6 text-gray-800 pl-1 dark:text-white"
aria-hidden="true"
/>
</DialogTitle>
<DialogDescription>{CODE_PROMPT_DIALOG_SUBTITLE}</DialogDescription>
</DialogHeader>
<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
className="h-6 w-6 text-blue-600"
aria-hidden="true"
/>
</div>
<div className="mt-4 text-center sm:ml-4 sm:text-left">
<Dialog.Title
as="h3"
className="text-lg font-medium dark:text-white leading-10 text-gray-900"
>
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
value={code}
mode="python"
highlightActiveLine={true}
showPrintMargin={false}
fontSize={14}
showGutter
enableLiveAutocompletion
theme={dark ? "twilight" : "github"}
name="CodeEditor"
onChange={(value) => {
setCode(value);
}}
className="h-full w-full rounded-lg"
/>
</div>
</div>
</div>
<div className="bg-gray-200 dark:bg-gray-900 w-full pb-3 flex flex-row-reverse px-4">
<button
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={() => {
checkCode(code)
.then((apiReturn) => {
if (apiReturn.data) {
let importsErrors = apiReturn.data.imports.errors;
let funcErrors = apiReturn.data.function.errors;
if (
funcErrors.length === 0 &&
importsErrors.length === 0
) {
setSuccessData({
title: "Code is ready to run",
});
setModalOpen(false);
setValue(code);
} else {
if (funcErrors.length !== 0) {
setErrorData({
title: "There is an error in your function",
list: funcErrors,
});
}
if (importsErrors.length !== 0) {
setErrorData({
title: "There is an error in your imports",
list: importsErrors,
});
}
}
} else {
setErrorData({
title: "Something went wrong, please try again",
});
}
})
.catch((_) =>
setErrorData({
title:
"There is something wrong with this code, please review it",
})
);
}}
>
Check & Save
</button>
</div>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
<div className="flex h-full w-full mt-2">
<AceEditor
value={code}
mode="python"
highlightActiveLine={true}
showPrintMargin={false}
fontSize={14}
showGutter
enableLiveAutocompletion
theme={dark ? "twilight" : "github"}
name="CodeEditor"
onChange={(value) => {
setCode(value);
}}
className="w-full rounded-lg h-[300px] custom-scroll"
/>
</div>
</Dialog>
</Transition.Root>
<DialogFooter>
<Button
className="mt-3"
onClick={() => {
checkCode(code)
.then((apiReturn) => {
if (apiReturn.data) {
let importsErrors = apiReturn.data.imports.errors;
let funcErrors = apiReturn.data.function.errors;
if (funcErrors.length === 0 && importsErrors.length === 0) {
setSuccessData({
title: "Code is ready to run",
});
setModalOpen(false);
setValue(code);
} else {
if (funcErrors.length !== 0) {
setErrorData({
title: "There is an error in your function",
list: funcErrors,
});
}
if (importsErrors.length !== 0) {
setErrorData({
title: "There is an error in your imports",
list: importsErrors,
});
}
}
} else {
setErrorData({
title: "Something went wrong, please try again",
});
}
})
.catch((_) =>
setErrorData({
title:
"There is something wrong with this code, please review it",
})
);
}}
type="submit"
>
Check & Save
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}

View file

@ -1,4 +1,3 @@
import { Dialog, Transition } from "@headlessui/react";
import {
XMarkIcon,
ArrowDownTrayIcon,
@ -10,6 +9,21 @@ import { alertContext } from "../../contexts/alertContext";
import { PopUpContext } from "../../contexts/popUpContext";
import { TabsContext } from "../../contexts/tabsContext";
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() {
const [open, setOpen] = useState(true);
@ -28,153 +42,85 @@ export default function ExportModal() {
const [checked, setChecked] = useState(true);
const [name, setName] = useState(flows[tabIndex].name);
return (
<Transition.Root show={open} appear={true} as={Fragment}>
<Dialog
as="div"
className="relative z-10"
onClose={setModalOpen}
initialFocus={ref}
>
<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>
<Dialog open={true} onOpenChange={setModalOpen}>
<DialogTrigger asChild></DialogTrigger>
<DialogContent className="lg:max-w-[600px] h-[420px]">
<DialogHeader>
<DialogTitle className="flex items-center">
<span className="pr-2">Export</span>
<ArrowDownTrayIcon
className="h-6 w-6 text-gray-800 pl-1 dark:text-white"
aria-hidden="true"
/>
</DialogTitle>
<DialogDescription>{EXPORT_DIALOG_SUBTITLE}</DialogDescription>
</DialogHeader>
<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
className="h-6 w-6 text-blue-600"
aria-hidden="true"
/>
</div>
<div className="mt-4 text-center sm:ml-4 sm:text-left">
<Dialog.Title
as="h3"
className="text-lg font-medium dark:text-white leading-10 text-gray-900"
>
Export
</Dialog.Title>
</div>
</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) => {
if (event.target.value != "") {
let newFlow = flows[tabIndex];
newFlow.name = event.target.value;
setName(event.target.value);
updateFlow(newFlow);
} else {
setName(event.target.value);
}
}}
type="text"
name="name"
value={name ?? null}
placeholder="File 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>
<div className="w-full">
<label
htmlFor="description"
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"
id="description"
onChange={(event) => {
let newFlow = flows[tabIndex];
newFlow.description = event.target.value;
updateFlow(newFlow);
}}
value={flows[tabIndex].description ?? null}
placeholder="Flow description"
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>
<Label>
<span className="font-medium">Name</span>
<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">
Save with my API keys
</span>
</label>
</div>
<div className="w-full flex justify-end">
<button
onClick={() => {
if (checked) downloadFlow(flows[tabIndex]);
else downloadFlow(removeApiKeys(flows[tabIndex]));
}}
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
>
Download Flow
</button>
</div>
</div>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
<Input
className="mt-2"
onChange={(event) => {
if (event.target.value != "") {
let newFlow = flows[tabIndex];
newFlow.name = event.target.value;
setName(event.target.value);
updateFlow(newFlow);
} else {
setName(event.target.value);
}
}}
type="text"
name="name"
value={name ?? null}
placeholder="File name"
id="name"
/>
</Label>
<Label>
<span className="font-medium">Description (optional)</span>
<Textarea
name="description"
id="description"
onChange={(event) => {
let newFlow = flows[tabIndex];
newFlow.description = event.target.value;
updateFlow(newFlow);
}}
value={flows[tabIndex].description ?? null}
placeholder="Flow description"
className="max-h-[100px] mt-2"
rows={3}
/>
</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
</label>
</div>
</Dialog>
</Transition.Root>
<DialogFooter>
<Button
onClick={() => {
if (checked) downloadFlow(flows[tabIndex]);
else downloadFlow(removeApiKeys(flows[tabIndex]));
}}
type="submit"
>
Download Flow
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}

View file

@ -1,4 +1,3 @@
import { Dialog, Transition } from "@headlessui/react";
import { XMarkIcon, DocumentTextIcon } from "@heroicons/react/24/outline";
import { Fragment, useContext, useRef, useState } from "react";
import { PopUpContext } from "../../contexts/popUpContext";
@ -6,6 +5,19 @@ import { darkContext } from "../../contexts/darkContext";
import { checkPrompt } from "../../controllers/API";
import { alertContext } from "../../contexts/alertContext";
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({
value,
setValue,
@ -31,145 +43,101 @@ export default function PromptAreaModal({
function setModalOpen(x: boolean) {
setOpen(x);
if (x === false) {
setTimeout(() => {
closePopUp();
}, 300);
closePopUp();
}
}
return (
<Transition.Root show={open} appear={true} as={Fragment}>
<Dialog
as="div"
className="relative z-10"
onClose={setModalOpen}
initialFocus={ref}
>
<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>
<Dialog open={true} onOpenChange={setModalOpen}>
<DialogTrigger></DialogTrigger>
<DialogContent className="lg:max-w-[700px]">
<DialogHeader>
<DialogTitle className="flex items-center">
<span className="pr-2">{myModalTitle}</span>
<DocumentTextIcon
className="h-6 w-6 text-gray-800 pl-1 dark:text-white"
aria-hidden="true"
/>
</DialogTitle>
<DialogDescription>
{(() => {
switch (myModalTitle) {
case "Edit Text":
return TEXT_DIALOG_SUBTITLE;
<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
className="h-6 w-6 text-blue-600"
aria-hidden="true"
/>
</div>
<div className="mt-4 text-center sm:ml-4 sm:text-left">
<Dialog.Title
as="h3"
className="text-lg font-medium dark:text-white leading-10 text-gray-900"
>
{myModalTitle}
</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">
<textarea
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"
value={myValue}
onChange={(e) => {
setMyValue(e.target.value);
setValue(e.target.value);
}}
/>
</div>
</div>
</div>
<div className="bg-gray-200 dark:bg-gray-900 w-full pb-3 flex flex-row-reverse px-4">
<button
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={() => {
switch (myModalType) {
case 1:
setModalOpen(false);
break;
case 2:
checkPrompt(myValue)
.then((apiReturn) => {
if (apiReturn.data) {
let inputVariables =
apiReturn.data.input_variables;
if (inputVariables.length === 0) {
setErrorData({
title:
"The template you are attempting to use does not contain any variables for data entry.",
});
} else {
setSuccessData({
title: "Prompt is ready",
});
setModalOpen(false);
setValue(myValue);
}
} else {
setErrorData({
title:
"Something went wrong, please try again",
});
}
})
.catch((error) => {
return setErrorData({
title:
"There is something wrong with this prompt, please review it",
list: [error.response.data.detail],
});
});
break;
case "Edit Prompt":
return PROMPT_DIALOG_SUBTITLE;
default:
break;
}
}}
>
{myButtonText}
</button>
</div>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
default:
return null;
}
})()}
</DialogDescription>
</DialogHeader>
<div className="flex h-full w-full mt-2">
<Textarea
ref={ref}
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}
onChange={(e) => {
setMyValue(e.target.value);
setValue(e.target.value);
}}
placeholder="Type message here."
/>
</div>
</Dialog>
</Transition.Root>
<DialogFooter>
<Button
className="mt-3"
onClick={() => {
switch (myModalType) {
case 1:
setModalOpen(false);
break;
case 2:
checkPrompt(myValue)
.then((apiReturn) => {
if (apiReturn.data) {
let inputVariables = apiReturn.data.input_variables;
if (inputVariables.length === 0) {
setErrorData({
title:
"The template you are attempting to use does not contain any variables for data entry.",
});
} else {
setSuccessData({
title: "Prompt is ready",
});
setModalOpen(false);
setValue(myValue);
}
} else {
setErrorData({
title: "Something went wrong, please try again",
});
}
})
.catch((error) => {
return setErrorData({
title:
"There is something wrong with this prompt, please review it",
list: [error.response.data.detail],
});
});
break;
default:
break;
}
}}
type="submit"
>
{myButtonText}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}

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

View file

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

View file

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

View file

@ -535,6 +535,13 @@ export function updateTemplate(
if (objectToUpdate[key] && 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;
}

View file

@ -2,6 +2,81 @@
const { fontFamily } = require("tailwindcss/defaultTheme")
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 = {
content: ["./index.html", "./src/**/*.{js,ts,tsx,jsx}"],
darkMode: "class",
@ -89,6 +164,7 @@ module.exports = {
},
},
plugins: [
require("tailwindcss-animate"),
require("@tailwindcss/forms")({
strategy: "class", // only generate classes
}),