Formatting
This commit is contained in:
parent
a096e83474
commit
276a06976d
65 changed files with 4515 additions and 4390 deletions
|
|
@ -34,7 +34,10 @@ export default function SingleAlert({
|
|||
key={dropItem.id}
|
||||
>
|
||||
<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 className="ml-3">
|
||||
<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">
|
||||
<ul className="list-disc space-y-1 pl-5">
|
||||
{dropItem.list.map((item, idx) => (
|
||||
<li className="break-words" key={idx}>{item}</li>
|
||||
<li className="break-words" key={idx}>
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -82,7 +87,9 @@ export default function SingleAlert({
|
|||
/>
|
||||
</div>
|
||||
<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">
|
||||
{dropItem.link ? (
|
||||
<Link
|
||||
|
|
|
|||
|
|
@ -43,10 +43,15 @@ export default function ErrorAlert({
|
|||
>
|
||||
<div className="flex">
|
||||
<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 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 ? (
|
||||
<div className="mt-2 text-sm text-red-700 dark:text-red-50">
|
||||
<ul className="list-disc space-y-1 pl-5">
|
||||
|
|
|
|||
|
|
@ -44,7 +44,9 @@ export default function SuccessAlert({
|
|||
/>
|
||||
</div>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ export default function FloatComponent({
|
|||
setMyValue(e.target.value);
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ export default function InputComponent({
|
|||
>
|
||||
<input
|
||||
value={myValue}
|
||||
|
||||
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",
|
||||
disabled ? " bg-gray-200 dark:bg-gray-700" : "",
|
||||
|
|
|
|||
|
|
@ -44,9 +44,7 @@ export default function IntComponent({
|
|||
setMyValue(e.target.value);
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
|
||||
/>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,11 @@ import { PopUpContext } from "../../contexts/popUpContext";
|
|||
import TextAreaModal from "../../modals/textAreaModal";
|
||||
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 { openPopUp } = useContext(PopUpContext);
|
||||
useEffect(() => {
|
||||
|
|
@ -16,15 +20,38 @@ export default function TextAreaComponent({ value, onChange, disabled }:TextArea
|
|||
return (
|
||||
<div className={disabled ? "pointer-events-none cursor-not-allowed" : ""}>
|
||||
<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={
|
||||
"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'}
|
||||
{myValue !== "" ? myValue : "Text empty"}
|
||||
</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" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ export default function ToggleComponent({
|
|||
setEnabled(x);
|
||||
}}
|
||||
className={classNames(
|
||||
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'
|
||||
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"
|
||||
)}
|
||||
>
|
||||
<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"
|
||||
)}
|
||||
aria-hidden="true"
|
||||
>
|
||||
</span>
|
||||
></span>
|
||||
<span
|
||||
className={classNames(
|
||||
enabled
|
||||
|
|
@ -53,8 +52,7 @@ export default function ToggleComponent({
|
|||
"absolute inset-0 flex h-full w-full items-center justify-center transition-opacity"
|
||||
)}
|
||||
aria-hidden="true"
|
||||
>
|
||||
</span>
|
||||
></span>
|
||||
</span>
|
||||
</Switch>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -27,10 +27,13 @@ const TabsContextInitialValue: TabsContextType = {
|
|||
downloadFlow: (flow: FlowType) => {},
|
||||
uploadFlow: () => {},
|
||||
hardReset: () => {},
|
||||
disableCopyPaste:false,
|
||||
setDisableCopyPaste:(state:boolean)=>{},
|
||||
disableCopyPaste: false,
|
||||
setDisableCopyPaste: (state: boolean) => {},
|
||||
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>(
|
||||
|
|
@ -53,20 +56,21 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
let Saveflows = [...flows];
|
||||
if (Saveflows.length !== 0)
|
||||
Saveflows.forEach((flow) => {
|
||||
if(flow.data && flow.data?.nodes) flow.data?.nodes.forEach((node) => {
|
||||
console.log(node.data.type)
|
||||
if (flow.data && flow.data?.nodes)
|
||||
flow.data?.nodes.forEach((node) => {
|
||||
console.log(node.data.type);
|
||||
Object.keys(node.data.node.template).forEach((key) => {
|
||||
console.log(node.data.node.template[key].type)
|
||||
if(node.data.node.template[key].type==="file"){
|
||||
console.log(node.data.node.template[key])
|
||||
console.log(node.data.node.template[key].type);
|
||||
if (node.data.node.template[key].type === "file") {
|
||||
console.log(node.data.node.template[key]);
|
||||
node.data.node.template[key].content = "";
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
window.localStorage.setItem(
|
||||
"tabsData",
|
||||
JSON.stringify({ tabIndex, flows:Saveflows, id})
|
||||
JSON.stringify({ tabIndex, flows: Saveflows, id })
|
||||
);
|
||||
}
|
||||
useEffect(() => {
|
||||
|
|
@ -184,7 +188,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
* @param flow Optional flow to add.
|
||||
*/
|
||||
|
||||
function paste(selectionInstance, position){
|
||||
function paste(selectionInstance, position) {
|
||||
let minimumX = Infinity;
|
||||
let minimumY = Infinity;
|
||||
let idsMap = {};
|
||||
|
|
@ -223,7 +227,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
// Add the new node to the list of nodes in state
|
||||
nodes = nodes
|
||||
.map((e) => ({ ...e, selected: false }))
|
||||
.concat({ ...newNode, selected: false })
|
||||
.concat({ ...newNode, selected: false });
|
||||
});
|
||||
reactFlowInstance.setNodes(nodes);
|
||||
|
||||
|
|
@ -261,7 +265,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
);
|
||||
});
|
||||
reactFlowInstance.setEdges(edges);
|
||||
};
|
||||
}
|
||||
|
||||
function addFlow(flow?: FlowType) {
|
||||
// Get data from the flow or set it to null if there's no flow provided.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import reportWebVitals from "./reportWebVitals";
|
|||
import { BrowserRouter } from "react-router-dom";
|
||||
import ContextWrapper from "./contexts";
|
||||
|
||||
import './index.css';
|
||||
import "./index.css";
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById("root") as HTMLElement
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { IconCheck, IconClipboard, IconDownload } from '@tabler/icons-react';
|
||||
import { XMarkIcon, CommandLineIcon, CodeBracketSquareIcon } from "@heroicons/react/24/outline";
|
||||
import { IconCheck, IconClipboard, IconDownload } from "@tabler/icons-react";
|
||||
import {
|
||||
XMarkIcon,
|
||||
CommandLineIcon,
|
||||
CodeBracketSquareIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { Fragment, useContext, useRef, useState } from "react";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
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/webpack-resolver";
|
||||
import { darkContext } from "../../contexts/darkContext";
|
||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import { oneDark } from 'react-syntax-highlighter/dist/cjs/styles/prism';
|
||||
|
||||
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
||||
|
||||
export default function ApiModal({ flowName }) {
|
||||
const [open, setOpen] = useState(true);
|
||||
|
|
@ -68,7 +70,8 @@ flow("Hey, have you heard of LangFlow?")`;
|
|||
{
|
||||
name: "Python API",
|
||||
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,
|
||||
},
|
||||
{
|
||||
|
|
@ -77,8 +80,7 @@ flow("Hey, have you heard of LangFlow?")`;
|
|||
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
|
||||
code: pythonCode,
|
||||
},
|
||||
|
||||
]
|
||||
];
|
||||
return (
|
||||
<Transition.Root show={open} appear={true} as={Fragment}>
|
||||
<Dialog
|
||||
|
|
@ -144,9 +146,17 @@ flow("Hey, have you heard of LangFlow?")`;
|
|||
<div className="flex flex-col h-full w-full ">
|
||||
<div className="flex px-5 z-10">
|
||||
{tabs.map((tab, index) => (
|
||||
<button onClick={() => {
|
||||
<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")}>
|
||||
}}
|
||||
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>
|
||||
|
|
@ -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"
|
||||
onClick={copyToClipboard}
|
||||
>
|
||||
{isCopied ? <IconCheck size={18} /> : <IconClipboard size={18} />}
|
||||
{isCopied ? 'Copied!' : 'Copy code'}
|
||||
{isCopied ? (
|
||||
<IconCheck size={18} />
|
||||
) : (
|
||||
<IconClipboard size={18} />
|
||||
)}
|
||||
{isCopied ? "Copied!" : "Copy code"}
|
||||
</button>
|
||||
</div>
|
||||
<SyntaxHighlighter
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { useEffect, useRef, useState } from "react";
|
|||
import { ChatMessageType } from "../../../types/chat";
|
||||
import { classNames } from "../../../utils";
|
||||
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 FileCard from "../fileComponent";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
|
|
@ -13,7 +13,13 @@ import remarkMath from "remark-math";
|
|||
import { CodeBlock } from "./codeBlock";
|
||||
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 [message, setMessage] = useState("");
|
||||
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"
|
||||
)}
|
||||
>
|
||||
{!chat.isSend && <div className="relative w-8 h-8">
|
||||
<img className={"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" />}
|
||||
{!chat.isSend && (
|
||||
<div className="relative w-8 h-8">
|
||||
<img
|
||||
className={
|
||||
"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>
|
||||
{!chat.isSend ? (
|
||||
<div className="w-full text-start flex items-center">
|
||||
|
|
|
|||
|
|
@ -236,11 +236,14 @@ export default function ChatModal({
|
|||
}, []);
|
||||
|
||||
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();
|
||||
setLockChat(false);
|
||||
}
|
||||
},[lockChat]);
|
||||
}, [lockChat]);
|
||||
|
||||
async function sendAll(data: sendAllProps) {
|
||||
try {
|
||||
|
|
@ -346,7 +349,7 @@ export default function ChatModal({
|
|||
function clearChat() {
|
||||
setChatHistory([]);
|
||||
ws.current.send(JSON.stringify({ clear_history: true }));
|
||||
if(lockChat) setLockChat(false);
|
||||
if (lockChat) setLockChat(false);
|
||||
}
|
||||
|
||||
function setModalOpen(x: boolean) {
|
||||
|
|
|
|||
|
|
@ -151,7 +151,6 @@ export default function ExportModal() {
|
|||
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
|
||||
|
|
|
|||
|
|
@ -75,7 +75,6 @@ export default function ButtonBox({
|
|||
break;
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<button disabled={deactivate} onClick={onClick}>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -58,7 +58,9 @@ export default function TabsManagerComponent() {
|
|||
/>
|
||||
<div className="ml-auto mr-2 flex gap-3">
|
||||
<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"
|
||||
>
|
||||
Code <CodeBracketSquareIcon className="w-5 h-5" />
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useCallback, useContext, useEffect, useState } from 'react';
|
||||
import { Edge, Node, useReactFlow } from 'reactflow';
|
||||
import { TabsContext } from '../../../contexts/tabsContext';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { useCallback, useContext, useEffect, useState } from "react";
|
||||
import { Edge, Node, useReactFlow } from "reactflow";
|
||||
import { TabsContext } from "../../../contexts/tabsContext";
|
||||
import { cloneDeep } from "lodash";
|
||||
|
||||
type UseUndoRedoOptions = {
|
||||
maxHistorySize: number;
|
||||
|
|
@ -34,23 +34,25 @@ export const useUndoRedo: UseUndoRedo = ({
|
|||
// the past and future arrays store the states that we can jump to
|
||||
const { tabIndex, flows } = useContext(TabsContext);
|
||||
|
||||
const [past, setPast] = useState<HistoryItem[][]>(flows.map(()=>[]));
|
||||
const [future, setFuture] = useState<HistoryItem[][]>(flows.map(()=>[]));
|
||||
const [past, setPast] = useState<HistoryItem[][]>(flows.map(() => []));
|
||||
const [future, setFuture] = useState<HistoryItem[][]>(flows.map(() => []));
|
||||
|
||||
useEffect(() => {
|
||||
// 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] : []));
|
||||
setFuture((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] : [])));
|
||||
}, [flows]);
|
||||
|
||||
|
||||
const { setNodes, setEdges, getNodes, getEdges } = useReactFlow();
|
||||
|
||||
const takeSnapshot = useCallback(() => {
|
||||
// push the current graph to the past state
|
||||
setPast((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() });
|
||||
return newPast;
|
||||
});
|
||||
|
|
@ -59,9 +61,18 @@ export const useUndoRedo: UseUndoRedo = ({
|
|||
setFuture((old) => {
|
||||
let newFuture = cloneDeep(old);
|
||||
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(() => {
|
||||
// get the last state that we want to go back to
|
||||
|
|
@ -72,20 +83,30 @@ export const useUndoRedo: UseUndoRedo = ({
|
|||
setPast((old) => {
|
||||
let newPast = cloneDeep(old);
|
||||
newPast[tabIndex] = old[tabIndex].slice(0, old[tabIndex].length - 1);
|
||||
return newPast
|
||||
return newPast;
|
||||
});
|
||||
// we store the current graph for the redo operation
|
||||
setFuture((old) => {
|
||||
let newFuture = cloneDeep(old);
|
||||
newFuture[tabIndex] = old[tabIndex]
|
||||
newFuture[tabIndex] = old[tabIndex];
|
||||
newFuture[tabIndex].push({ nodes: getNodes(), edges: getEdges() });
|
||||
return newFuture
|
||||
return newFuture;
|
||||
});
|
||||
// now we can set the graph to the past state
|
||||
setNodes(pastState.nodes);
|
||||
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 futureState = future[tabIndex][future[tabIndex].length - 1];
|
||||
|
|
@ -94,18 +115,29 @@ export const useUndoRedo: UseUndoRedo = ({
|
|||
setFuture((old) => {
|
||||
let newFuture = cloneDeep(old);
|
||||
newFuture[tabIndex] = old[tabIndex].slice(0, old[tabIndex].length - 1);
|
||||
return newFuture
|
||||
return newFuture;
|
||||
});
|
||||
setPast((old) => {
|
||||
let newPast = cloneDeep(old);
|
||||
newPast[tabIndex] = old[tabIndex];
|
||||
newPast[tabIndex].push({ nodes: getNodes(), edges: getEdges() });
|
||||
return newPast
|
||||
return newPast;
|
||||
});
|
||||
setNodes(futureState.nodes);
|
||||
setEdges(futureState.edges);
|
||||
}
|
||||
}, [future, past, setFuture, setPast, setNodes, setEdges, getNodes, getEdges, future, tabIndex]);
|
||||
}, [
|
||||
future,
|
||||
past,
|
||||
setFuture,
|
||||
setPast,
|
||||
setNodes,
|
||||
setEdges,
|
||||
getNodes,
|
||||
getEdges,
|
||||
future,
|
||||
tabIndex,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
// this effect is used to attach the global event handlers
|
||||
|
|
@ -114,21 +146,24 @@ export const useUndoRedo: UseUndoRedo = ({
|
|||
}
|
||||
|
||||
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();
|
||||
}
|
||||
else if (event.key === 'y' && (event.ctrlKey || event.metaKey)) {
|
||||
} else if (event.key === "y" && (event.ctrlKey || event.metaKey)) {
|
||||
event.preventDefault(); // prevent the default action
|
||||
redo();
|
||||
} else if (event.key === 'z' && (event.ctrlKey || event.metaKey)) {
|
||||
} else if (event.key === "z" && (event.ctrlKey || event.metaKey)) {
|
||||
undo();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', keyDownHandler);
|
||||
document.addEventListener("keydown", keyDownHandler);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('keydown', keyDownHandler);
|
||||
document.removeEventListener("keydown", keyDownHandler);
|
||||
};
|
||||
}, [undo, redo, enableShortcuts]);
|
||||
|
||||
|
|
|
|||
|
|
@ -302,10 +302,10 @@ export default function FlowPage({ flow }: { flow: FlowType }) {
|
|||
setLastSelection(flow);
|
||||
}, []);
|
||||
|
||||
const {setDisableCopyPaste} = useContext(TabsContext)
|
||||
const { setDisableCopyPaste } = useContext(TabsContext);
|
||||
|
||||
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 ? (
|
||||
<>
|
||||
<ReactFlow
|
||||
|
|
@ -314,9 +314,15 @@ export default function FlowPage({ flow }: { flow: FlowType }) {
|
|||
updateFlow({ ...flow, data: reactFlowInstance.toObject() });
|
||||
}}
|
||||
edges={edges}
|
||||
onPaneClick={() => {setDisableCopyPaste(false);}}
|
||||
onNodeClick={() => {setDisableCopyPaste(true);}}
|
||||
onPaneMouseLeave={() => {setDisableCopyPaste(true);}}
|
||||
onPaneClick={() => {
|
||||
setDisableCopyPaste(false);
|
||||
}}
|
||||
onNodeClick={() => {
|
||||
setDisableCopyPaste(true);
|
||||
}}
|
||||
onPaneMouseLeave={() => {
|
||||
setDisableCopyPaste(true);
|
||||
}}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChangeMod}
|
||||
onConnect={onConnect}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
import { ForwardRefExoticComponent, ReactElement, ReactFragment, ReactNode } from "react";
|
||||
import {
|
||||
ForwardRefExoticComponent,
|
||||
ReactElement,
|
||||
ReactFragment,
|
||||
ReactNode,
|
||||
} from "react";
|
||||
import { NodeDataType } from "../flow/index";
|
||||
export type InputComponentType = {
|
||||
value: string;
|
||||
|
|
|
|||
|
|
@ -6,17 +6,20 @@ export type TabsContextType = {
|
|||
setTabIndex: (index: number) => void;
|
||||
flows: Array<FlowType>;
|
||||
removeFlow: (id: string) => void;
|
||||
addFlow: (flowData?: FlowType,newFlow?:boolean) => void;
|
||||
addFlow: (flowData?: FlowType, newFlow?: boolean) => void;
|
||||
updateFlow: (newFlow: FlowType) => void;
|
||||
incrementNodeId: () => string;
|
||||
downloadFlow: (flow: FlowType) => void;
|
||||
uploadFlow: (newFlow?:boolean) => void;
|
||||
uploadFlow: (newFlow?: boolean) => void;
|
||||
hardReset: () => void;
|
||||
//disable CopyPaste
|
||||
disableCopyPaste: boolean;
|
||||
setDisableCopyPaste: (value: boolean) => void;
|
||||
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 = {
|
||||
|
|
|
|||
|
|
@ -317,11 +317,9 @@ export function toFirstUpperCase(str: string) {
|
|||
}
|
||||
|
||||
export function snakeToSpaces(str: string) {
|
||||
let result = str
|
||||
.split("_")
|
||||
.join(" ");
|
||||
let result = str.split("_").join(" ");
|
||||
|
||||
return result
|
||||
return result;
|
||||
}
|
||||
|
||||
export function toNormalCase(str: string) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue