Formatting

This commit is contained in:
Gabriel Almeida 2023-05-27 13:01:20 -03:00
commit 276a06976d
65 changed files with 4515 additions and 4390 deletions

View file

@ -34,7 +34,10 @@ export default function SingleAlert({
key={dropItem.id} key={dropItem.id}
> >
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<XCircleIcon className="h-5 w-5 text-red-400 dark:text-red-50" aria-hidden="true" /> <XCircleIcon
className="h-5 w-5 text-red-400 dark:text-red-50"
aria-hidden="true"
/>
</div> </div>
<div className="ml-3"> <div className="ml-3">
<h3 className="text-sm break-words font-medium text-red-800 dark:text-white/80"> <h3 className="text-sm break-words font-medium text-red-800 dark:text-white/80">
@ -44,7 +47,9 @@ export default function SingleAlert({
<div className="mt-2 text-sm text-red-700 dark:text-red-50"> <div className="mt-2 text-sm text-red-700 dark:text-red-50">
<ul className="list-disc space-y-1 pl-5"> <ul className="list-disc space-y-1 pl-5">
{dropItem.list.map((item, idx) => ( {dropItem.list.map((item, idx) => (
<li className="break-words" key={idx}>{item}</li> <li className="break-words" key={idx}>
{item}
</li>
))} ))}
</ul> </ul>
</div> </div>
@ -82,7 +87,9 @@ export default function SingleAlert({
/> />
</div> </div>
<div className="ml-3 flex-1 md:flex md:justify-between"> <div className="ml-3 flex-1 md:flex md:justify-between">
<p className="text-sm text-blue-700 dark:text-white/80">{dropItem.title}</p> <p className="text-sm text-blue-700 dark:text-white/80">
{dropItem.title}
</p>
<p className="mt-3 text-sm md:mt-0 md:ml-6"> <p className="mt-3 text-sm md:mt-0 md:ml-6">
{dropItem.link ? ( {dropItem.link ? (
<Link <Link

View file

@ -43,10 +43,15 @@ export default function ErrorAlert({
> >
<div className="flex"> <div className="flex">
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<XCircleIcon className="h-5 w-5 text-red-400 dark:text-red-50" aria-hidden="true" /> <XCircleIcon
className="h-5 w-5 text-red-400 dark:text-red-50"
aria-hidden="true"
/>
</div> </div>
<div className="ml-3"> <div className="ml-3">
<h3 className="text-sm font-medium text-red-800 dark:text-white/80">{title}</h3> <h3 className="text-sm font-medium text-red-800 dark:text-white/80">
{title}
</h3>
{list.length !== 0 ? ( {list.length !== 0 ? (
<div className="mt-2 text-sm text-red-700 dark:text-red-50"> <div className="mt-2 text-sm text-red-700 dark:text-red-50">
<ul className="list-disc space-y-1 pl-5"> <ul className="list-disc space-y-1 pl-5">

View file

@ -44,7 +44,9 @@ export default function SuccessAlert({
/> />
</div> </div>
<div className="ml-3"> <div className="ml-3">
<p className="text-sm font-medium text-green-800 dark:text-white/80">{title}</p> <p className="text-sm font-medium text-green-800 dark:text-white/80">
{title}
</p>
</div> </div>
</div> </div>
</div> </div>

View file

@ -28,7 +28,6 @@ export default function FloatComponent({
setMyValue(e.target.value); setMyValue(e.target.value);
onChange(e.target.value); onChange(e.target.value);
}} }}
/> />
</div> </div>
); );

View file

@ -27,7 +27,6 @@ export default function InputComponent({
> >
<input <input
value={myValue} value={myValue}
className={classNames( className={classNames(
"block w-full pr-12 form-input dark:bg-gray-900 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: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" : "",

View file

@ -44,9 +44,7 @@ export default function IntComponent({
setMyValue(e.target.value); setMyValue(e.target.value);
onChange(e.target.value); onChange(e.target.value);
}} }}
/> />
</div> </div>
); );
} }

View file

@ -4,7 +4,11 @@ import { PopUpContext } from "../../contexts/popUpContext";
import TextAreaModal from "../../modals/textAreaModal"; import TextAreaModal from "../../modals/textAreaModal";
import { TextAreaComponentType } from "../../types/components"; import { TextAreaComponentType } from "../../types/components";
export default function TextAreaComponent({ value, onChange, disabled }:TextAreaComponentType) { export default function TextAreaComponent({
value,
onChange,
disabled,
}: TextAreaComponentType) {
const [myValue, setMyValue] = useState(value); const [myValue, setMyValue] = useState(value);
const { openPopUp } = useContext(PopUpContext); const { openPopUp } = useContext(PopUpContext);
useEffect(() => { useEffect(() => {
@ -16,15 +20,38 @@ export default function TextAreaComponent({ value, onChange, disabled }:TextArea
return ( return (
<div className={disabled ? "pointer-events-none cursor-not-allowed" : ""}> <div className={disabled ? "pointer-events-none cursor-not-allowed" : ""}>
<div className="w-full flex items-center gap-3"> <div className="w-full flex items-center gap-3">
<span onClick={()=>{openPopUp(<TextAreaModal value={myValue} setValue={(t:string) => {setMyValue(t); onChange(t);}}/>)}} <span
onClick={() => {
openPopUp(
<TextAreaModal
value={myValue}
setValue={(t: string) => {
setMyValue(t);
onChange(t);
}}
/>
);
}}
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" + "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" : "")
} }
> >
{myValue !== "" ? myValue : 'Text empty'} {myValue !== "" ? myValue : "Text empty"}
</span> </span>
<button onClick={()=>{openPopUp(<TextAreaModal value={myValue} setValue={(t:string) => {setMyValue(t); onChange(t);}}/>)}}> <button
onClick={() => {
openPopUp(
<TextAreaModal
value={myValue}
setValue={(t: string) => {
setMyValue(t);
onChange(t);
}}
/>
);
}}
>
<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>

View file

@ -21,8 +21,8 @@ export default function ToggleComponent({
setEnabled(x); setEnabled(x);
}} }}
className={classNames( className={classNames(
enabled ? 'bg-indigo-600' : 'bg-gray-200', enabled ? "bg-indigo-600" : "bg-gray-200",
'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2' "relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2"
)} )}
> >
<span className="sr-only">Use setting</span> <span className="sr-only">Use setting</span>
@ -43,8 +43,7 @@ export default function ToggleComponent({
"absolute inset-0 flex h-full w-full items-center justify-center transition-opacity" "absolute inset-0 flex h-full w-full items-center justify-center transition-opacity"
)} )}
aria-hidden="true" aria-hidden="true"
> ></span>
</span>
<span <span
className={classNames( className={classNames(
enabled enabled
@ -53,8 +52,7 @@ export default function ToggleComponent({
"absolute inset-0 flex h-full w-full items-center justify-center transition-opacity" "absolute inset-0 flex h-full w-full items-center justify-center transition-opacity"
)} )}
aria-hidden="true" aria-hidden="true"
> ></span>
</span>
</span> </span>
</Switch> </Switch>
</div> </div>

View file

@ -27,10 +27,13 @@ const TabsContextInitialValue: TabsContextType = {
downloadFlow: (flow: FlowType) => {}, downloadFlow: (flow: FlowType) => {},
uploadFlow: () => {}, uploadFlow: () => {},
hardReset: () => {}, hardReset: () => {},
disableCopyPaste:false, disableCopyPaste: false,
setDisableCopyPaste:(state:boolean)=>{}, setDisableCopyPaste: (state: boolean) => {},
getNodeId: () => "", getNodeId: () => "",
paste: (selection: {nodes: any, edges: any}, position: {x: number, y: number}) => {}, paste: (
selection: { nodes: any; edges: any },
position: { x: number; y: number }
) => {},
}; };
export const TabsContext = createContext<TabsContextType>( export const TabsContext = createContext<TabsContextType>(
@ -53,20 +56,21 @@ export function TabsProvider({ children }: { children: ReactNode }) {
let Saveflows = [...flows]; let Saveflows = [...flows];
if (Saveflows.length !== 0) if (Saveflows.length !== 0)
Saveflows.forEach((flow) => { Saveflows.forEach((flow) => {
if(flow.data && flow.data?.nodes) flow.data?.nodes.forEach((node) => { if (flow.data && flow.data?.nodes)
console.log(node.data.type) flow.data?.nodes.forEach((node) => {
console.log(node.data.type);
Object.keys(node.data.node.template).forEach((key) => { Object.keys(node.data.node.template).forEach((key) => {
console.log(node.data.node.template[key].type) console.log(node.data.node.template[key].type);
if(node.data.node.template[key].type==="file"){ if (node.data.node.template[key].type === "file") {
console.log(node.data.node.template[key]) console.log(node.data.node.template[key]);
node.data.node.template[key].content = ""; node.data.node.template[key].content = "";
} }
}) });
}) });
}) });
window.localStorage.setItem( window.localStorage.setItem(
"tabsData", "tabsData",
JSON.stringify({ tabIndex, flows:Saveflows, id}) JSON.stringify({ tabIndex, flows: Saveflows, id })
); );
} }
useEffect(() => { useEffect(() => {
@ -184,7 +188,7 @@ 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) {
let minimumX = Infinity; let minimumX = Infinity;
let minimumY = Infinity; let minimumY = Infinity;
let idsMap = {}; let idsMap = {};
@ -223,7 +227,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
// Add the new node to the list of nodes in state // Add the new node to the list of nodes in state
nodes = nodes nodes = nodes
.map((e) => ({ ...e, selected: false })) .map((e) => ({ ...e, selected: false }))
.concat({ ...newNode, selected: false }) .concat({ ...newNode, selected: false });
}); });
reactFlowInstance.setNodes(nodes); reactFlowInstance.setNodes(nodes);
@ -261,7 +265,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
); );
}); });
reactFlowInstance.setEdges(edges); reactFlowInstance.setEdges(edges);
}; }
function addFlow(flow?: FlowType) { function addFlow(flow?: FlowType) {
// Get data from the flow or set it to null if there's no flow provided. // Get data from the flow or set it to null if there's no flow provided.

View file

@ -5,7 +5,7 @@ import reportWebVitals from "./reportWebVitals";
import { BrowserRouter } from "react-router-dom"; import { BrowserRouter } from "react-router-dom";
import ContextWrapper from "./contexts"; import ContextWrapper from "./contexts";
import './index.css'; import "./index.css";
const root = ReactDOM.createRoot( const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement document.getElementById("root") as HTMLElement

View file

@ -1,6 +1,10 @@
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
import { IconCheck, IconClipboard, IconDownload } from '@tabler/icons-react'; import { IconCheck, IconClipboard, IconDownload } from "@tabler/icons-react";
import { XMarkIcon, CommandLineIcon, CodeBracketSquareIcon } from "@heroicons/react/24/outline"; import {
XMarkIcon,
CommandLineIcon,
CodeBracketSquareIcon,
} 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";
import "ace-builds/src-noconflict/mode-python"; import "ace-builds/src-noconflict/mode-python";
@ -9,10 +13,8 @@ import "ace-builds/src-noconflict/theme-twilight";
import "ace-builds/src-noconflict/ext-language_tools"; import "ace-builds/src-noconflict/ext-language_tools";
// import "ace-builds/webpack-resolver"; // import "ace-builds/webpack-resolver";
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";
export default function ApiModal({ flowName }) { export default function ApiModal({ flowName }) {
const [open, setOpen] = useState(true); const [open, setOpen] = useState(true);
@ -68,7 +70,8 @@ flow("Hey, have you heard of LangFlow?")`;
{ {
name: "Python API", name: "Python API",
mode: "python", mode: "python",
image: "https://images.squarespace-cdn.com/content/v1/5df3d8c5d2be5962e4f87890/1628015119369-OY4TV3XJJ53ECO0W2OLQ/Python+API+Training+Logo.png?format=1000w", image:
"https://images.squarespace-cdn.com/content/v1/5df3d8c5d2be5962e4f87890/1628015119369-OY4TV3XJJ53ECO0W2OLQ/Python+API+Training+Logo.png?format=1000w",
code: pythonApiCode, code: pythonApiCode,
}, },
{ {
@ -77,8 +80,7 @@ flow("Hey, have you heard of LangFlow?")`;
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png", image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
code: pythonCode, code: pythonCode,
}, },
];
]
return ( return (
<Transition.Root show={open} appear={true} as={Fragment}> <Transition.Root show={open} appear={true} as={Fragment}>
<Dialog <Dialog
@ -144,9 +146,17 @@ flow("Hey, have you heard of LangFlow?")`;
<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 onClick={() => { <button
onClick={() => {
setActiveTab(index); 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")}> }}
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} {tab.name}
<img src={tab.image} className="w-6" /> <img src={tab.image} className="w-6" />
</button> </button>
@ -161,8 +171,12 @@ flow("Hey, have you heard of LangFlow?")`;
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}
> >
{isCopied ? <IconCheck size={18} /> : <IconClipboard size={18} />} {isCopied ? (
{isCopied ? 'Copied!' : 'Copy code'} <IconCheck size={18} />
) : (
<IconClipboard size={18} />
)}
{isCopied ? "Copied!" : "Copy code"}
</button> </button>
</div> </div>
<SyntaxHighlighter <SyntaxHighlighter

View file

@ -3,7 +3,7 @@ import { useEffect, useRef, useState } from "react";
import { ChatMessageType } from "../../../types/chat"; import { ChatMessageType } from "../../../types/chat";
import { classNames } from "../../../utils"; import { classNames } from "../../../utils";
import AiIcon from "../../../assets/Gooey Ring-5s-271px.svg"; import AiIcon from "../../../assets/Gooey Ring-5s-271px.svg";
import AiIconStill from "../../../assets/froze-flow.png" import AiIconStill from "../../../assets/froze-flow.png";
import { UserIcon } from "@heroicons/react/24/solid"; import { UserIcon } from "@heroicons/react/24/solid";
import FileCard from "../fileComponent"; import FileCard from "../fileComponent";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
@ -13,7 +13,13 @@ import remarkMath from "remark-math";
import { CodeBlock } from "./codeBlock"; import { CodeBlock } from "./codeBlock";
import Convert from "ansi-to-html"; import Convert from "ansi-to-html";
export default function ChatMessage({ chat, lockChat }: { chat: ChatMessageType, lockChat: boolean }) { export default function ChatMessage({
chat,
lockChat,
}: {
chat: ChatMessageType;
lockChat: boolean;
}) {
const convert = new Convert({ newline: true }); const convert = new Convert({ newline: true });
const [message, setMessage] = useState(""); const [message, setMessage] = useState("");
const imgRef = useRef(null); const imgRef = useRef(null);
@ -35,11 +41,27 @@ export default function ChatMessage({ chat, lockChat }: { chat: ChatMessageType,
"rounded-full overflow-hidden w-8 h-8 flex items-center my-3 justify-center" "rounded-full overflow-hidden w-8 h-8 flex items-center my-3 justify-center"
)} )}
> >
{!chat.isSend && <div className="relative w-8 h-8"> {!chat.isSend && (
<img className={"absolute transition-opacity duration-500 scale-150 " + (lockChat ? "opacity-100" : "opacity-0")} src={AiIcon} /> <div className="relative w-8 h-8">
<img className={"absolute transition-opacity duration-500 scale-150 " + (lockChat ? "opacity-0" : "opacity-100")} src={AiIconStill} /> <img
</div>} className={
{chat.isSend && <UserIcon className="w-6 h-6 -mb-1 text-gray-800 dark:text-gray-200" />} "absolute transition-opacity duration-500 scale-150 " +
(lockChat ? "opacity-100" : "opacity-0")
}
src={AiIcon}
/>
<img
className={
"absolute transition-opacity duration-500 scale-150 " +
(lockChat ? "opacity-0" : "opacity-100")
}
src={AiIconStill}
/>
</div>
)}
{chat.isSend && (
<UserIcon className="w-6 h-6 -mb-1 text-gray-800 dark:text-gray-200" />
)}
</div> </div>
{!chat.isSend ? ( {!chat.isSend ? (
<div className="w-full text-start flex items-center"> <div className="w-full text-start flex items-center">

View file

@ -236,11 +236,14 @@ export default function ChatModal({
}, []); }, []);
useEffect(() => { useEffect(() => {
if((ws.current.readyState=== ws.current.CLOSED || ws.current.readyState=== ws.current.CLOSING)){ if (
ws.current.readyState === ws.current.CLOSED ||
ws.current.readyState === ws.current.CLOSING
) {
connectWS(); connectWS();
setLockChat(false); setLockChat(false);
} }
},[lockChat]); }, [lockChat]);
async function sendAll(data: sendAllProps) { async function sendAll(data: sendAllProps) {
try { try {
@ -346,7 +349,7 @@ export default function ChatModal({
function clearChat() { function clearChat() {
setChatHistory([]); setChatHistory([]);
ws.current.send(JSON.stringify({ clear_history: true })); ws.current.send(JSON.stringify({ clear_history: true }));
if(lockChat) setLockChat(false); if (lockChat) setLockChat(false);
} }
function setModalOpen(x: boolean) { function setModalOpen(x: boolean) {

View file

@ -151,7 +151,6 @@ export default function ExportModal() {
id="checkbox" id="checkbox"
type="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" 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"> <span className="ml-2 font-medium text-gray-700 dark:text-white">
Save with my API keys Save with my API keys

View file

@ -75,7 +75,6 @@ export default function ButtonBox({
break; break;
} }
return ( return (
<button disabled={deactivate} onClick={onClick}> <button disabled={deactivate} onClick={onClick}>
<div <div

View file

@ -58,7 +58,9 @@ export default function TabsManagerComponent() {
/> />
<div className="ml-auto mr-2 flex gap-3"> <div className="ml-auto mr-2 flex gap-3">
<button <button
onClick={() => openPopUp(<ApiModal flowName={flows[tabIndex].name} />)} onClick={() =>
openPopUp(<ApiModal flowName={flows[tabIndex].name} />)
}
className="flex items-center gap-1 pr-2 border-gray-400 border-r text-sm text-gray-600 hover:text-gray-500 dark:text-gray-300 dark:hover:text-gray-200" className="flex items-center gap-1 pr-2 border-gray-400 border-r text-sm text-gray-600 hover:text-gray-500 dark:text-gray-300 dark:hover:text-gray-200"
> >
Code <CodeBracketSquareIcon className="w-5 h-5" /> Code <CodeBracketSquareIcon className="w-5 h-5" />

View file

@ -1,7 +1,7 @@
import { useCallback, useContext, useEffect, useState } from 'react'; import { useCallback, useContext, useEffect, useState } from "react";
import { Edge, Node, useReactFlow } from 'reactflow'; import { Edge, Node, useReactFlow } from "reactflow";
import { TabsContext } from '../../../contexts/tabsContext'; import { TabsContext } from "../../../contexts/tabsContext";
import { cloneDeep } from 'lodash'; import { cloneDeep } from "lodash";
type UseUndoRedoOptions = { type UseUndoRedoOptions = {
maxHistorySize: number; maxHistorySize: number;
@ -34,23 +34,25 @@ export const useUndoRedo: UseUndoRedo = ({
// the past and future arrays store the states that we can jump to // the past and future arrays store the states that we can jump to
const { tabIndex, flows } = useContext(TabsContext); const { tabIndex, flows } = useContext(TabsContext);
const [past, setPast] = useState<HistoryItem[][]>(flows.map(()=>[])); const [past, setPast] = useState<HistoryItem[][]>(flows.map(() => []));
const [future, setFuture] = useState<HistoryItem[][]>(flows.map(()=>[])); const [future, setFuture] = useState<HistoryItem[][]>(flows.map(() => []));
useEffect(() => { useEffect(() => {
// whenever the flows variable changes, we need to add one array to the past and future states // whenever the flows variable changes, we need to add one array to the past and future states
setPast((old) => flows.map((f, i)=>old[i] ? old[i] : [])); setPast((old) => flows.map((f, i) => (old[i] ? old[i] : [])));
setFuture((old) => flows.map((f, i)=>old[i] ? old[i] : [])); setFuture((old) => flows.map((f, i) => (old[i] ? old[i] : [])));
}, [flows]); }, [flows]);
const { setNodes, setEdges, getNodes, getEdges } = useReactFlow(); const { setNodes, setEdges, getNodes, getEdges } = useReactFlow();
const takeSnapshot = useCallback(() => { const takeSnapshot = useCallback(() => {
// push the current graph to the past state // push the current graph to the past state
setPast((old) => { setPast((old) => {
let newPast = cloneDeep(old); let newPast = cloneDeep(old);
newPast[tabIndex] = old[tabIndex].slice(old[tabIndex].length - maxHistorySize + 1, old[tabIndex].length); newPast[tabIndex] = old[tabIndex].slice(
old[tabIndex].length - maxHistorySize + 1,
old[tabIndex].length
);
newPast[tabIndex].push({ nodes: getNodes(), edges: getEdges() }); newPast[tabIndex].push({ nodes: getNodes(), edges: getEdges() });
return newPast; return newPast;
}); });
@ -59,9 +61,18 @@ export const useUndoRedo: UseUndoRedo = ({
setFuture((old) => { setFuture((old) => {
let newFuture = cloneDeep(old); let newFuture = cloneDeep(old);
newFuture[tabIndex] = []; newFuture[tabIndex] = [];
return newFuture return newFuture;
}); });
}, [getNodes, getEdges, past, future, tabIndex, setPast, setFuture, maxHistorySize]); }, [
getNodes,
getEdges,
past,
future,
tabIndex,
setPast,
setFuture,
maxHistorySize,
]);
const undo = useCallback(() => { const undo = useCallback(() => {
// get the last state that we want to go back to // get the last state that we want to go back to
@ -72,20 +83,30 @@ export const useUndoRedo: UseUndoRedo = ({
setPast((old) => { setPast((old) => {
let newPast = cloneDeep(old); let newPast = cloneDeep(old);
newPast[tabIndex] = old[tabIndex].slice(0, old[tabIndex].length - 1); newPast[tabIndex] = old[tabIndex].slice(0, old[tabIndex].length - 1);
return newPast return newPast;
}); });
// we store the current graph for the redo operation // we store the current graph for the redo operation
setFuture((old) => { setFuture((old) => {
let newFuture = cloneDeep(old); let newFuture = cloneDeep(old);
newFuture[tabIndex] = old[tabIndex] newFuture[tabIndex] = old[tabIndex];
newFuture[tabIndex].push({ nodes: getNodes(), edges: getEdges() }); newFuture[tabIndex].push({ nodes: getNodes(), edges: getEdges() });
return newFuture return newFuture;
}); });
// now we can set the graph to the past state // now we can set the graph to the past state
setNodes(pastState.nodes); setNodes(pastState.nodes);
setEdges(pastState.edges); setEdges(pastState.edges);
} }
}, [setNodes, setEdges, getNodes, getEdges, future, past, setFuture, setPast, tabIndex]); }, [
setNodes,
setEdges,
getNodes,
getEdges,
future,
past,
setFuture,
setPast,
tabIndex,
]);
const redo = useCallback(() => { const redo = useCallback(() => {
const futureState = future[tabIndex][future[tabIndex].length - 1]; const futureState = future[tabIndex][future[tabIndex].length - 1];
@ -94,18 +115,29 @@ export const useUndoRedo: UseUndoRedo = ({
setFuture((old) => { setFuture((old) => {
let newFuture = cloneDeep(old); let newFuture = cloneDeep(old);
newFuture[tabIndex] = old[tabIndex].slice(0, old[tabIndex].length - 1); newFuture[tabIndex] = old[tabIndex].slice(0, old[tabIndex].length - 1);
return newFuture return newFuture;
}); });
setPast((old) => { setPast((old) => {
let newPast = cloneDeep(old); let newPast = cloneDeep(old);
newPast[tabIndex] = old[tabIndex]; newPast[tabIndex] = old[tabIndex];
newPast[tabIndex].push({ nodes: getNodes(), edges: getEdges() }); newPast[tabIndex].push({ nodes: getNodes(), edges: getEdges() });
return newPast return newPast;
}); });
setNodes(futureState.nodes); setNodes(futureState.nodes);
setEdges(futureState.edges); setEdges(futureState.edges);
} }
}, [future, past, setFuture, setPast, setNodes, setEdges, getNodes, getEdges, future, tabIndex]); }, [
future,
past,
setFuture,
setPast,
setNodes,
setEdges,
getNodes,
getEdges,
future,
tabIndex,
]);
useEffect(() => { useEffect(() => {
// this effect is used to attach the global event handlers // this effect is used to attach the global event handlers
@ -114,21 +146,24 @@ export const useUndoRedo: UseUndoRedo = ({
} }
const keyDownHandler = (event: KeyboardEvent) => { const keyDownHandler = (event: KeyboardEvent) => {
if (event.key === 'z' && (event.ctrlKey || event.metaKey) && event.shiftKey) { if (
event.key === "z" &&
(event.ctrlKey || event.metaKey) &&
event.shiftKey
) {
redo(); redo();
} } else if (event.key === "y" && (event.ctrlKey || event.metaKey)) {
else if (event.key === 'y' && (event.ctrlKey || event.metaKey)) {
event.preventDefault(); // prevent the default action event.preventDefault(); // prevent the default action
redo(); redo();
} else if (event.key === 'z' && (event.ctrlKey || event.metaKey)) { } else if (event.key === "z" && (event.ctrlKey || event.metaKey)) {
undo(); undo();
} }
}; };
document.addEventListener('keydown', keyDownHandler); document.addEventListener("keydown", keyDownHandler);
return () => { return () => {
document.removeEventListener('keydown', keyDownHandler); document.removeEventListener("keydown", keyDownHandler);
}; };
}, [undo, redo, enableShortcuts]); }, [undo, redo, enableShortcuts]);

View file

@ -302,10 +302,10 @@ export default function FlowPage({ flow }: { flow: FlowType }) {
setLastSelection(flow); setLastSelection(flow);
}, []); }, []);
const {setDisableCopyPaste} = useContext(TabsContext) const { setDisableCopyPaste } = useContext(TabsContext);
return ( return (
<div className="w-full h-full" ref={reactFlowWrapper} > <div className="w-full h-full" ref={reactFlowWrapper}>
{Object.keys(templates).length > 0 && Object.keys(types).length > 0 ? ( {Object.keys(templates).length > 0 && Object.keys(types).length > 0 ? (
<> <>
<ReactFlow <ReactFlow
@ -314,9 +314,15 @@ export default function FlowPage({ flow }: { flow: FlowType }) {
updateFlow({ ...flow, data: reactFlowInstance.toObject() }); updateFlow({ ...flow, data: reactFlowInstance.toObject() });
}} }}
edges={edges} edges={edges}
onPaneClick={() => {setDisableCopyPaste(false);}} onPaneClick={() => {
onNodeClick={() => {setDisableCopyPaste(true);}} setDisableCopyPaste(false);
onPaneMouseLeave={() => {setDisableCopyPaste(true);}} }}
onNodeClick={() => {
setDisableCopyPaste(true);
}}
onPaneMouseLeave={() => {
setDisableCopyPaste(true);
}}
onNodesChange={onNodesChange} onNodesChange={onNodesChange}
onEdgesChange={onEdgesChangeMod} onEdgesChange={onEdgesChangeMod}
onConnect={onConnect} onConnect={onConnect}

View file

@ -1,4 +1,9 @@
import { ForwardRefExoticComponent, ReactElement, ReactFragment, ReactNode } from "react"; import {
ForwardRefExoticComponent,
ReactElement,
ReactFragment,
ReactNode,
} from "react";
import { NodeDataType } from "../flow/index"; import { NodeDataType } from "../flow/index";
export type InputComponentType = { export type InputComponentType = {
value: string; value: string;

View file

@ -6,17 +6,20 @@ export type TabsContextType = {
setTabIndex: (index: number) => void; setTabIndex: (index: number) => void;
flows: Array<FlowType>; flows: Array<FlowType>;
removeFlow: (id: string) => void; removeFlow: (id: string) => void;
addFlow: (flowData?: FlowType,newFlow?:boolean) => void; addFlow: (flowData?: FlowType, newFlow?: boolean) => void;
updateFlow: (newFlow: FlowType) => void; updateFlow: (newFlow: FlowType) => void;
incrementNodeId: () => string; incrementNodeId: () => string;
downloadFlow: (flow: FlowType) => void; downloadFlow: (flow: FlowType) => void;
uploadFlow: (newFlow?:boolean) => void; uploadFlow: (newFlow?: boolean) => void;
hardReset: () => void; hardReset: () => void;
//disable CopyPaste //disable CopyPaste
disableCopyPaste: boolean; disableCopyPaste: boolean;
setDisableCopyPaste: (value: boolean) => void; setDisableCopyPaste: (value: boolean) => void;
getNodeId: () => string; getNodeId: () => string;
paste: (selection: {nodes: any, edges: any}, position: {x: number, y: number}) => void; paste: (
selection: { nodes: any; edges: any },
position: { x: number; y: number }
) => void;
}; };
export type LangFlowState = { export type LangFlowState = {

View file

@ -317,11 +317,9 @@ export function toFirstUpperCase(str: string) {
} }
export function snakeToSpaces(str: string) { export function snakeToSpaces(str: string) {
let result = str let result = str.split("_").join(" ");
.split("_")
.join(" ");
return result return result;
} }
export function toNormalCase(str: string) { export function toNormalCase(str: string) {