Merge branch 'uiRefactor' into dev

This commit is contained in:
anovazzi1 2023-03-07 01:01:22 -03:00
commit 6c53c9c30e
27 changed files with 567 additions and 725 deletions

View file

@ -8,7 +8,6 @@ import SuccessAlert from "./alerts/success";
import ExtraSidebar from "./components/ExtraSidebarComponent"; import ExtraSidebar from "./components/ExtraSidebarComponent";
import { alertContext } from "./contexts/alertContext"; import { alertContext } from "./contexts/alertContext";
import { locationContext } from "./contexts/locationContext"; import { locationContext } from "./contexts/locationContext";
import Header from "./components/HeaderComponent";
import TabsManagerComponent from "./pages/FlowPage/components/tabsManagerComponent"; import TabsManagerComponent from "./pages/FlowPage/components/tabsManagerComponent";
export default function App() { export default function App() {
@ -85,7 +84,6 @@ useEffect(() => {
//need parent component with width and height //need parent component with width and height
<div className="h-full flex flex-col"> <div className="h-full flex flex-col">
<div className="flex grow-0 shrink basis-auto"> <div className="flex grow-0 shrink basis-auto">
<Header></Header>
</div> </div>
<div className="flex grow shrink basis-auto min-h-0 flex-1 overflow-hidden"> <div className="flex grow shrink basis-auto min-h-0 flex-1 overflow-hidden">
<ExtraSidebar /> <ExtraSidebar />
@ -97,7 +95,8 @@ useEffect(() => {
</div> </div>
</main> </main>
</div> </div>
<div className="flex z-50 flex-col-reverse fixed bottom-5 left-5"> <div></div>
<div className="flex z-40 flex-col-reverse fixed bottom-5 left-5">
{alertsList.map((alert) => ( {alertsList.map((alert) => (
<div key={alert.id}> <div key={alert.id}>
{alert.type === "error" ? ( {alert.type === "error" ? (
@ -127,6 +126,7 @@ useEffect(() => {
</div> </div>
))} ))}
</div> </div>
<a target={"_blank"} href="https://logspace.ai/" className="absolute bottom-1 left-1 text-gray-500 text-xs cursor-pointer font-sans tracking-wide">Created by Logspace</a>
</div> </div>
); );
} }

View file

@ -1,45 +0,0 @@
import { CheckCircleIcon, TrashIcon } from "@heroicons/react/24/outline";
import { Handle, Position } from "reactflow";
import { isValidConnection, nodeColors } from "../../utils";
import ToggleComponent from "../../components/toggleComponent";
import { useContext, useState } from "react";
import { typesContext } from "../../contexts/typesContext";
import { NodeDataType } from "../../types/flow";
export default function BooleanNode({ data }:{data:NodeDataType}) {
const [enabled, setEnabled] = useState(false);
const {types, deleteNode, reactFlowInstance} = useContext(typesContext);
return (
<div className="prompt-node relative bg-white dark:bg-gray-900 rounded-lg solid border dark:border-gray-700 flex flex-col justify-center">
<div className="w-full flex items-center justify-between gap-8 p-4 bg-gray-50 dark:bg-gray-800 dark:text-white dark:border-b-gray-700 border-b ">
<div className="flex items-center gap-4 text-lg">
<CheckCircleIcon
className="w-10 h-10 p-1 rounded"
style={{ color: nodeColors[types[data.type]] }}
/>
Boolean
</div>
<button
onClick={() => {
deleteNode(data.id);
}}
>
<TrashIcon className="text-gray-600 w-6 h-6 hover:text-red-500"></TrashIcon>
</button>
</div>
<div className="w-full flex justify-center p-5 h-full">
<ToggleComponent enabled={enabled} disabled={false} setEnabled={(x) => {setEnabled(x); data.value = x}} />
</div>
<Handle
type="source"
position={Position.Right}
id={data.type}
isValidConnection={(connection) => isValidConnection(connection,reactFlowInstance)}
className={"-mr-0.5 w-3 h-3 rounded-full border-2 bg-white dark:bg-gray-800"}
style={{
borderColor: nodeColors[types[data.type]],
}}
></Handle>
</div>
);
}

View file

@ -1,55 +0,0 @@
import {
Bars3CenterLeftIcon,
} from "@heroicons/react/24/outline";
import { isValidConnection, nodeColors } from "../../utils";
import { Handle, Position } from "reactflow";
import Tooltip from "../../components/TooltipComponent";
import { typesContext } from "../../contexts/typesContext";
import { useContext } from "react";
import { NodeDataType } from "../../types/flow";
export default function ChatInputNode({ data }:{data:NodeDataType}) {
const { types,reactFlowInstance } = useContext(typesContext);
return (
<div
className="prompt-node relative rounded-lg solid border flex justify-center align-center py-3 px-6 bg-gray-50 dark:bg-gray-800 dark:border-gray-700"
style={{ color: nodeColors[types[data.type]] }}
>
<Tooltip title="Prefix: str">
<Handle
type="target"
isValidConnection={(connection) =>
isValidConnection(connection,reactFlowInstance)
}
position={Position.Left}
id={"str|Prefix|" + data.id}
className={"-ml-0.5 w-3 h-3 rounded-full border-2 bg-white dark:bg-gray-800"}
style={{
borderColor: nodeColors[types[data.type]],
}}
></Handle>
</Tooltip>
<Tooltip title={"Message: str"}>
<Handle
type="source"
isValidConnection={(connection) =>
isValidConnection(connection,reactFlowInstance)
}
position={Position.Right}
id={"str|str|" + data.id}
className={"-mr-0.5 w-3 h-3 rounded-full border-2 bg-white dark:bg-gray-800"}
style={{
borderColor: nodeColors[types[data.type]],
}}
></Handle>
</Tooltip>
<div
className="flex gap-3 text-lg font-medium items-center"
style={{ color: nodeColors[types[data.type]] }}
>
<Bars3CenterLeftIcon className="h-8 w-8 mt-1" />
Input
</div>
</div>
);
}

View file

@ -1,33 +0,0 @@
import { Bars3CenterLeftIcon } from "@heroicons/react/24/outline";
import { Handle, Position } from "reactflow";
import { isValidConnection, nodeColors } from "../../utils";
import Tooltip from "../../components/TooltipComponent";
import { useContext } from "react";
import { typesContext } from "../../contexts/typesContext";
import { NodeDataType } from "../../types/flow";
export default function ChatOutputNode({ data }:{data:NodeDataType}) {
const {types,reactFlowInstance} = useContext(typesContext);
return (
<div className="prompt-node relative rounded-lg solid border flex justify-center align-center py-3 px-6 bg-gray-50 dark:bg-gray-800 dark:border-gray-700" style={{color: nodeColors[types[data.type]]}}>
<Tooltip title="Message: str">
<Handle
type="target"
isValidConnection={(connection) => isValidConnection(connection,reactFlowInstance)}
position={Position.Left}
id={"str|output|"+data.id}
className={"-ml-0.5 w-3 h-3 rounded-full border-2 bg-white dark:bg-gray-800"
}
style={{
borderColor: nodeColors[types[data.type]],
}}
></Handle>
</Tooltip>
<div className="flex gap-3 text-lg font-medium items-center" style={{color: nodeColors[types[data.type]]}}>
Output
<Bars3CenterLeftIcon className="h-8 w-8 mt-1" />
</div>
</div>
);
}

View file

@ -1,8 +1,6 @@
import { Handle, Position, useUpdateNodeInternals } from "reactflow"; import { Handle, Position, useUpdateNodeInternals } from "reactflow";
import Tooltip from "../../../../components/TooltipComponent"; import Tooltip from "../../../../components/TooltipComponent";
import { import { classNames, isValidConnection } from "../../../../utils";
isValidConnection,
} from "../../../../utils";
import { useContext, useEffect, useRef, useState } from "react"; import { useContext, useEffect, useRef, useState } from "react";
import InputComponent from "../../../../components/inputComponent"; import InputComponent from "../../../../components/inputComponent";
import ToggleComponent from "../../../../components/toggleComponent"; import ToggleComponent from "../../../../components/toggleComponent";
@ -10,103 +8,127 @@ import InputListComponent from "../../../../components/inputListComponent";
import TextAreaComponent from "../../../../components/textAreaComponent"; import TextAreaComponent from "../../../../components/textAreaComponent";
import { typesContext } from "../../../../contexts/typesContext"; import { typesContext } from "../../../../contexts/typesContext";
import { ParameterComponentType } from "../../../../types/components"; import { ParameterComponentType } from "../../../../types/components";
import FloatComponent from "../../../../components/floatComponent";
export default function ParameterComponent({ export default function ParameterComponent({
left, left,
id, id,
data, data,
tooltipTitle, tooltipTitle,
title, title,
color, color,
type, type,
name = "", name = "",
required = false, required = false,
}:ParameterComponentType) { }: ParameterComponentType) {
const ref = useRef(null); const ref = useRef(null);
const updateNodeInternals = useUpdateNodeInternals(); const updateNodeInternals = useUpdateNodeInternals();
const [position, setPosition] = useState(0); const [position, setPosition] = useState(0);
useEffect(() => { useEffect(() => {
if (ref.current && ref.current.offsetTop && ref.current.clientHeight) { if (ref.current && ref.current.offsetTop && ref.current.clientHeight) {
setPosition(ref.current.offsetTop + ref.current.clientHeight / 2); setPosition(ref.current.offsetTop + ref.current.clientHeight / 2);
updateNodeInternals(data.id); updateNodeInternals(data.id);
} }
}, [data.id, ref, updateNodeInternals]); }, [data.id, ref, updateNodeInternals]);
useEffect(() => { useEffect(() => {
updateNodeInternals(data.id); updateNodeInternals(data.id);
}, [data.id, position, updateNodeInternals]); }, [data.id, position, updateNodeInternals]);
const [enabled, setEnabled] = useState(data.node.template[name]?.value ?? false); const [enabled, setEnabled] = useState(
const {reactFlowInstance} = useContext(typesContext); data.node.template[name]?.value ?? false
let disabled = reactFlowInstance?.getEdges().some((e) => (e.targetHandle === id)) ?? false; );
const { reactFlowInstance } = useContext(typesContext);
let disabled =
reactFlowInstance?.getEdges().some((e) => e.targetHandle === id) ?? false;
return ( return (
<div ref={ref} className="w-full flex flex-wrap justify-between items-center bg-gray-50 dark:bg-gray-800 dark:text-white mt-1 px-5 py-2"> <div
<> ref={ref}
<div className="text-sm truncate">{title}<span className="text-red-600">{required ? " *" : ""}</span></div> className="w-full flex flex-wrap justify-between items-center bg-gray-50 dark:bg-gray-800 dark:text-white mt-1 px-5 py-2"
<Tooltip title={tooltipTitle + (required ? " (required)" : "")}> >
<Handle <>
type={left ? "target" : "source"} <div className="text-sm truncate">
position={left ? Position.Left : Position.Right} {title}
id={id} <span className="text-red-600">{required ? " *" : ""}</span>
isValidConnection={(connection) => </div>
isValidConnection(connection,reactFlowInstance) <Tooltip title={tooltipTitle + (required ? " (required)" : "")}>
} <Handle
className={ type={left ? "target" : "source"}
(left ? "-ml-0.5 " : "-mr-0.5 ") + position={left ? Position.Left : Position.Right}
"w-3 h-3 rounded-full border-2 bg-white dark:bg-gray-800" id={id}
} isValidConnection={(connection) =>
style={{ isValidConnection(connection, reactFlowInstance)
borderColor: color, }
top: position, className={classNames(
}} left ? "-ml-0.5 " : "-mr-0.5 ",
></Handle> "w-3 h-3 rounded-full border-2 bg-white dark:bg-gray-800",
</Tooltip> left && (type === "str" || type === "bool" || type === "float")
{left === true && type === "str" ? ( ? "hidden"
<div className="mt-2 w-full"> : ""
{data.node.template[name].list ? )}
<InputListComponent style={{
disabled={disabled} borderColor: color,
value={!data.node.template[name].value || data.node.template[name].value === "" ? [""] : data.node.template[name].value} top: position,
onChange={(t:string[]) => { }}
data.node.template[name].value = t; ></Handle>
}} </Tooltip>
/> {left === true && type === "str" ? (
: <div className="mt-2 w-full">
data.node.template[name].multiline ? ( {data.node.template[name].list ? (
<TextAreaComponent <InputListComponent
disabled={disabled} disabled={disabled}
value={data.node.template[name].value ?? ""} value={
onChange={(t:string) => { !data.node.template[name].value ||
data.node.template[name].value = t; data.node.template[name].value === ""
}} ? [""]
/> : data.node.template[name].value
) : }
<InputComponent onChange={(t: string[]) => {
disabled={disabled} data.node.template[name].value = t;
value={data.node.template[name].value ?? ""} }}
onChange={(t) => { />
data.node.template[name].value = t; ) : data.node.template[name].multiline ? (
}} <TextAreaComponent
/> disabled={disabled}
} value={data.node.template[name].value ?? ""}
onChange={(t: string) => {
</div> data.node.template[name].value = t;
) : left === true && type === "bool" ? ( }}
<div className="mt-2"> />
<ToggleComponent ) : (
disabled={disabled} <InputComponent
enabled={enabled} disabled={disabled}
setEnabled={(t) => { value={data.node.template[name].value ?? ""}
data.node.template[name].value = t; onChange={(t) => {
setEnabled(t); data.node.template[name].value = t;
}} }}
/> />
</div> )}
) : ( </div>
<></> ) : left === true && type === "bool" ? (
)} <div className="mt-2">
</> <ToggleComponent
</div> disabled={disabled}
); enabled={enabled}
setEnabled={(t) => {
data.node.template[name].value = t;
setEnabled(t);
}}
/>
</div>
) : left === true && type === "float" ? (
<FloatComponent
disabled={disabled}
value={data.node.template[name].value ?? ""}
onChange={(t) => {
data.node.template[name].value = t;
}}
/>
) : (
<></>
)}
</>
</div>
);
} }

View file

@ -2,6 +2,7 @@ import {
TrashIcon, TrashIcon,
} from "@heroicons/react/24/outline"; } from "@heroicons/react/24/outline";
import { import {
classNames,
nodeColors, nodeColors,
nodeIcons, nodeIcons,
snakeToNormalCase, snakeToNormalCase,
@ -11,13 +12,13 @@ import { typesContext } from "../../contexts/typesContext";
import { useContext } from "react"; import { useContext } from "react";
import { NodeDataType} from "../../types/flow"; import { NodeDataType} from "../../types/flow";
export default function GenericNode({ data}:{data:NodeDataType}) { export default function GenericNode({ data, selected}:{data:NodeDataType,selected:boolean}) {
const {types, deleteNode} = useContext(typesContext); const {types, deleteNode} = useContext(typesContext);
const Icon = nodeIcons[types[data.type]]; const Icon = nodeIcons[types[data.type]];
return ( return (
<div className="prompt-node relative bg-white dark:bg-gray-900 w-96 rounded-lg solid border dark:border-gray-700 flex flex-col justify-center"> <div className={ classNames(selected?"border border-blue-500":"border dark:border-gray-700","prompt-node relative bg-white dark:bg-gray-900 w-96 rounded-lg flex flex-col justify-center")}>
<div className="w-full dark:text-white flex items-center justify-between p-4 gap-8 bg-gray-50 rounded-t-lg dark:bg-gray-800 border-b dark:border-b-gray-700 "> <div className="w-full dark:text-white flex items-center justify-between p-4 gap-8 bg-gray-50 rounded-t-lg dark:bg-gray-800 border-b dark:border-b-gray-700 ">
<div className="w-full flex items-center truncate gap-4 text-lg"> <div className="w-full flex items-center truncate gap-4 text-lg">
<Icon <Icon

View file

@ -1,71 +0,0 @@
import { Bars3CenterLeftIcon, TrashIcon } from "@heroicons/react/24/outline";
import InputComponent from "../../components/inputComponent";
import {
isValidConnection,
nodeColors,
} from "../../utils";
import { Handle, Position } from "reactflow";
import { useContext } from "react";
import Tooltip from "../../components/TooltipComponent";
import { typesContext } from "../../contexts/typesContext";
import { NodeDataType } from "../../types/flow";
export default function InputNode({ data }:{data:NodeDataType}) {
console.log(data)
const {types, deleteNode,reactFlowInstance} = useContext(typesContext);
return (
<div className="prompt-node relative bg-white dark:bg-gray-900 w-96 rounded-lg solid border dark:border-gray-700 flex flex-col justify-center">
<Tooltip title="Prefix: str">
<Handle
type="target"
position={Position.Left}
id={"str|Prefix|" + data.id}
isValidConnection={(connection) =>
isValidConnection(connection,reactFlowInstance)
}
className={"-ml-0.5 w-3 h-3 rounded-full border-2 bg-white dark:bg-gray-800"}
style={{
borderColor: nodeColors[types[data.type]],
}}
></Handle>
</Tooltip>
<div className="w-full flex items-center justify-between p-4 gap-8 bg-gray-50 dark:bg-gray-800 dark:text-white border-b dark:border-b-gray-700 ">
<div className="flex items-center gap-4 text-lg">
<Bars3CenterLeftIcon
className="w-10 h-10 p-1 rounded"
style={{ color: nodeColors[types[data.type]] }}
/>
String
</div>
<button
onClick={() => {
deleteNode(data.id)
}}
>
<TrashIcon className="text-gray-600 w-6 h-6 hover:text-red-500"></TrashIcon>
</button>
</div>
<div className="w-full p-5 h-full">
<InputComponent
disabled={false}
value=""
onChange={(e) => {
data.value = e;
}}
/>
</div>
<Handle
type="source"
position={Position.Right}
id={data.type}
isValidConnection={(connection) => isValidConnection(connection,reactFlowInstance)}
className={"-mr-0.5 w-3 h-3 rounded-full border-2 bg-white dark:bg-gray-800"}
style={{
borderColor: nodeColors[types[data.type]],
}}
></Handle>
</div>
);
}

View file

@ -22,7 +22,7 @@ export default function SingleAlert({ dropItem, removeAlert}:SingleAlertComponen
leaveTo={"transform translate-x-[-100%]"} leaveTo={"transform translate-x-[-100%]"}
> >
{type === "error"? {type === "error"?
<div className="flex bg-red-50 rounded-md p-4 mb-2" key={dropItem.id}> <div className="flex bg-red-50 rounded-md p-3 mb-2 mx-2" key={dropItem.id}>
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<XCircleIcon <XCircleIcon
className="h-5 w-5 text-red-400" className="h-5 w-5 text-red-400"
@ -61,7 +61,7 @@ export default function SingleAlert({ dropItem, removeAlert}:SingleAlertComponen
</div> </div>
</div> </div>
:(type === "notice" ? :(type === "notice" ?
<div className="flex rounded-md p-4 bg-blue-50 mb-2" key={dropItem.id}> <div className="flex rounded-md bg-blue-50 p-3 mb-2 mx-2" key={dropItem.id}>
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<InformationCircleIcon <InformationCircleIcon
className="h-5 w-5 text-blue-400" className="h-5 w-5 text-blue-400"
@ -99,7 +99,7 @@ export default function SingleAlert({ dropItem, removeAlert}:SingleAlertComponen
</div> </div>
</div> </div>
: :
<div className="flex bg-green-50 p-4 mb-2" key={dropItem.id}> <div className="flex bg-green-50 p-3 mb-2 mx-2 rounded-md" key={dropItem.id}>
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<CheckCircleIcon <CheckCircleIcon
className="h-5 w-5 text-green-400" className="h-5 w-5 text-green-400"

View file

@ -3,54 +3,45 @@ import { alertContext } from "../../contexts/alertContext";
import { import {
XMarkIcon, XMarkIcon,
} from "@heroicons/react/24/solid"; } from "@heroicons/react/24/solid";
import { Transition } from "@headlessui/react";
import { TrashIcon } from "@heroicons/react/24/outline"; import { TrashIcon } from "@heroicons/react/24/outline";
import SingleAlert from "./components/singleAlertComponent"; import SingleAlert from "./components/singleAlertComponent";
import { AlertDropdownType } from "../../types/alerts"; import { AlertDropdownType } from "../../types/alerts";
import { PopUpContext } from "../../contexts/popUpContext";
export default function AlertDropdown({closeFunction, open}: AlertDropdownType) { export default function AlertDropdown({}: AlertDropdownType) {
const { const {
notificationList, notificationList,
clearNotificationList, clearNotificationList,
removeFromNotificationList, removeFromNotificationList,
} = useContext(alertContext); } = useContext(alertContext);
const {closePopUp} = useContext(PopUpContext)
return ( return (
<Transition <div className="z-10 py-3 pb-4 rounded-md bg-white ring-1 ring-black ring-opacity-5 shadow-lg focus:outline-none overflow-hidden w-[16rem] h-[28rem] flex flex-col">
show={open} <div className="flex pl-3 flex-row justify-between text-md font-medium text-gray-800">
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"
>
<div className="z-10 px-8 py-6 pb-8 rounded-md bg-white ring-1 ring-black ring-opacity-5 shadow-lg focus:outline-none overflow-hidden w-[36rem] h-[40rem] flex flex-col">
<div className="flex flex-row justify-between text-md font-medium text-gray-800">
Notifications Notifications
<div className="flex gap-4"> <div className="flex gap-2 pr-3 ">
<button <button
className="hover:text-black" className="hover:text-red-500"
onClick={() => {closeFunction(); setTimeout(clearNotificationList, 100)}} onClick={() => {closePopUp(); setTimeout(clearNotificationList, 100)}}
> >
<TrashIcon className="w-5 h-5" /> <TrashIcon className="w-[1.1rem] h-[1.1rem]" />
</button> </button>
<button <button
className="hover:text-black" className="hover:text-red-500"
onClick={closeFunction} onClick={closePopUp}
> >
<XMarkIcon className="h-6 w-6" /> <XMarkIcon className="h-5 w-5" />
</button> </button>
</div> </div>
</div> </div>
<div className="mt-6 flex flex-col overflow-y-scroll w-full h-full scrollbar-hide"> <div className="mt-2 flex flex-col overflow-y-scroll w-full h-full scrollbar-hide">
{notificationList.length !== 0 ? {notificationList.length !== 0 ?
notificationList.map((alertItem, index) => ( notificationList.map((alertItem, index) => (
<SingleAlert key={index} dropItem={alertItem} removeAlert={removeFromNotificationList} /> <SingleAlert key={alertItem.id} dropItem={alertItem} removeAlert={removeFromNotificationList} />
)) ))
: :
<div className="h-full w-full pb-16 text-gray-500 flex justify-center items-center"> <div className="h-full w-full pb-16 text-gray-500 flex justify-center items-center">
@ -59,6 +50,5 @@ export default function AlertDropdown({closeFunction, open}: AlertDropdownType)
} }
</div> </div>
</div> </div>
</Transition>
); );
} }

View file

@ -17,12 +17,12 @@ export default function ExtraSidebar() {
<> <>
<aside <aside
className={` ${ className={` ${
isStackedOpen ? "w-60" : "w-0 " isStackedOpen ? "w-52" : "w-0 "
} flex-shrink-0 flex overflow-hidden flex-col border-r dark:border-r-gray-700 transition-all duration-500`} } flex-shrink-0 flex overflow-hidden flex-col border-r dark:border-r-gray-700 transition-all duration-500`}
> >
<div className="w-60 dark:bg-gray-800 border dark:border-gray-700 overflow-y-auto scrollbar-hide h-full flex flex-col items-start"> <div className="w-52 dark:bg-gray-800 border dark:border-gray-700 overflow-y-auto scrollbar-hide h-full flex flex-col items-start">
<div className="flex pt-4 px-4 justify-between align-middle w-full"> <div className="flex pt-1 px-4 justify-between align-middle w-full">
<span className="text-gray-900 dark:text-white text-lg ml-2 font-semibold"> <span className="text-gray-900 dark:text-white py-[2px] font-medium ">
{extraNavigation.title} {extraNavigation.title}
</span> </span>
</div> </div>

View file

@ -1,51 +0,0 @@
import { useContext, useState } from 'react'
import {
BellIcon,
MoonIcon,
SunIcon,
} from '@heroicons/react/24/outline'
import { alertContext } from '../../contexts/alertContext'
import { useLayer } from 'react-laag'
import AlertDropdown from '../../alerts/alertDropDown'
import { darkContext } from '../../contexts/darkContext'
export default function Header(){
const {notificationCenter, setNotificationCenter} = useContext(alertContext)
const [isOpen,setIsOpen] = useState(false)
const {layerProps,renderLayer, triggerProps} = useLayer({
isOpen,
placement: "left-start",
onOutsideClick:()=>setIsOpen(false),
preferX: "left",
triggerOffset: 10,
containerOffset: 12,
arrowOffset: 4,
})
const {dark, setDark} = useContext(darkContext);
return (
<header className="relative flex h-16 w-full shrink-0 items-center bg-white dark:bg-gray-800">
{/* Desktop nav area */}
<div className="flex min-w-0 flex-1 flex-row-reverse items-center justify-between">
<div className="ml-10 flex shrink-0 items-center space-x-10 pr-4">
<div className="flex items-center space-x-8">
<span className="inline-flex gap-6">
<button className="text-gray-400 hover:text-gray-500 " onClick={()=>{setDark(!dark)}}>
{dark ?
<SunIcon className="h-6 w-6" />
:
<MoonIcon className="h-6 w-6" />
}
</button>
<button type="button" {...triggerProps} className="-mx-1 rounded-full p-1 text-gray-400 hover:text-gray-500 relative" onClick={()=>{setNotificationCenter(false);setIsOpen(true)}}>
<span className="sr-only">View notifications</span>
{notificationCenter&&<div className='absolute top-[2px] w-2 h-2 rounded-full bg-red-600 right-[7px]'></div>}
<BellIcon className="h-6 w-6" aria-hidden="true" />
</button>{renderLayer(<div {...layerProps}><AlertDropdown closeFunction={()=>setIsOpen(false)} open={isOpen}></AlertDropdown></div>)}
</span>
</div>
</div>
</div>
</header>
)
}

View file

@ -0,0 +1,49 @@
import { ChatBubbleLeftEllipsisIcon, ChatBubbleOvalLeftEllipsisIcon, PlusSmallIcon } from "@heroicons/react/24/outline";
import { useState } from "react";
import { ChatMessageType } from "../../../types/chat";
import { nodeColors } from "../../../utils";
export default function ChatMessage({ chat }: { chat: ChatMessageType }) {
const [hidden, setHidden] = useState(true);
return (
<div>
{!chat.isSend ? (
<div className="w-full text-start">
<div
style={{ backgroundColor: nodeColors["chat"] }}
className=" relative text-start inline-block text-white rounded-xl overflow-hidden w-fit max-w-[280px] text-sm font-normal rounded-tl-none"
>
{hidden && chat.thought && chat.thought !== "" && (
<div
onClick={() => setHidden((prev) => !prev)}
className="absolute top-2 right-2 cursor-pointer"
>
<ChatBubbleOvalLeftEllipsisIcon className="w-5 h-5 animate-bounce" />
</div>
)}
{chat.thought && chat.thought !== "" && !hidden && (
<div
onClick={() => setHidden((prev) => !prev)}
style={{ backgroundColor: nodeColors["thought"] }}
className=" text-start inline-block w-full pb-3 pt-3 px-5 cursor-pointer"
dangerouslySetInnerHTML={{
__html: chat.thought.replace(/\n/g, "<br />"),
}}
></div>
)}
{chat.thought && chat.thought !== "" && !hidden && <br></br>}
<div className="w-full rounded-b-md px-4 pb-3 pt-3 pr-8" style={{ backgroundColor: nodeColors["chat"] }}>
{chat.message}
</div>
</div>
</div>
) : (
<div className="w-full text-end">
<div className="text-start inline-block rounded-xl p-3 overflow-hidden w-fit max-w-[280px] px-5 text-sm text-black dark:text-white dark:bg-gray-700 bg-gray-200 font-normal rounded-tr-none">
{chat.message}
</div>
</div>
)}
</div>
);
}

View file

@ -1,182 +1,217 @@
import { Transition } from "@headlessui/react"; import { Transition } from "@headlessui/react";
import { import {
Bars3CenterLeftIcon, Bars3CenterLeftIcon,
PaperAirplaneIcon, LockClosedIcon,
XMarkIcon, PaperAirplaneIcon,
XMarkIcon,
} from "@heroicons/react/24/outline"; } from "@heroicons/react/24/outline";
import { useContext, useEffect, useRef, useState } from "react"; import { useContext, useEffect, useRef, useState } from "react";
import { sendAll } from "../../controllers/NodesServices"; import { sendAll } from "../../controllers/NodesServices";
import { alertContext } from "../../contexts/alertContext"; import { alertContext } from "../../contexts/alertContext";
import { nodeColors } from "../../utils"; import { classNames, nodeColors } from "../../utils";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { ChatType } from "../../types/chat"; import { ChatType } from "../../types/chat";
import ChatMessage from "./chatMessage";
const _ = require("lodash"); const _ = require("lodash");
export default function Chat({flow, reactFlowInstance }:ChatType) { export default function Chat({ flow, reactFlowInstance }: ChatType) {
const {updateFlow} = useContext(TabsContext) const { updateFlow } = useContext(TabsContext);
const [saveChat,setSaveChat] = useState(false) const [saveChat, setSaveChat] = useState(false);
const [open, setOpen] = useState(true); const [lockChat, setLockChat] = useState(false);
const [chatValue, setChatValue] = useState(""); const [open, setOpen] = useState(true);
const [chatHistory, setChatHistory] = useState(flow.chat); const [chatValue, setChatValue] = useState("");
const {setErrorData} = useContext(alertContext); const [chatHistory, setChatHistory] = useState(flow.chat);
const addChatHistory = (message:string, isSend:boolean) => { const { setErrorData } = useContext(alertContext);
setChatHistory((old) => { const addChatHistory = (
let newChat = _.cloneDeep(old); message: string,
newChat.push({ message, isSend }); isSend: boolean,
return newChat; thought?: string
}); ) => {
setSaveChat(chat=>!chat) setChatHistory((old) => {
}; let newChat = _.cloneDeep(old);
useEffect(()=>{ if (thought) {
updateFlow({..._.cloneDeep(flow),chat:chatHistory}) newChat.push({ message, isSend, thought });
// eslint-disable-next-line react-hooks/exhaustive-deps } else {
},[saveChat]) newChat.push({ message, isSend });
useEffect(()=>{ }
setChatHistory(flow.chat) return newChat;
},[flow]) });
useEffect(()=>{ setSaveChat((chat) => !chat);
if(ref.current) };
ref.current.scrollIntoView({behavior: 'smooth'}); useEffect(() => {
}, [chatHistory]) updateFlow({ ..._.cloneDeep(flow), chat: chatHistory });
function validateNodes(){ // eslint-disable-next-line react-hooks/exhaustive-deps
if(reactFlowInstance.getNodes().some((n) => (n.data.node && Object.keys(n.data.node.template).some((t: any) => ((n.data.node.template[t].required && n.data.node.template[t].value === "") && (n.data.node.template[t].required && !reactFlowInstance.getEdges().some((e) => (e.sourceHandle.split('|')[1] === t && e.sourceHandle.split('|')[2] === n.id)))))))){ }, [saveChat]);
return false; useEffect(() => {
} setChatHistory(flow.chat);
return true; }, [flow]);
} useEffect(() => {
function validateChatNodes(){ if (ref.current) ref.current.scrollIntoView({ behavior: "smooth" });
if(!reactFlowInstance.getNodes().some((n)=> (n.type === 'chatOutputNode'))){ }, [chatHistory]);
return false; function validateNodes() {
} if (
return true; reactFlowInstance
} .getNodes()
const ref = useRef(null); .some(
(n) =>
n.data.node &&
Object.keys(n.data.node.template).some(
(t: any) =>
n.data.node.template[t].required &&
n.data.node.template[t].value === "" &&
n.data.node.template[t].required &&
!reactFlowInstance
.getEdges()
.some(
(e) =>
e.sourceHandle.split("|")[1] === t &&
e.sourceHandle.split("|")[2] === n.id
)
)
)
) {
return false;
}
return true;
}
const ref = useRef(null);
function sendMessage(){ function sendMessage() {
if(chatValue !== ""){ if (chatValue !== "") {
if(validateNodes()){ if (validateNodes()) {
if(validateChatNodes()){ setLockChat(true);
let message = chatValue; let message = chatValue;
setChatValue(""); setChatValue("");
addChatHistory(message, true); addChatHistory(message, true);
console.log({...reactFlowInstance.toObject(),message,chatHistory}) console.log({ ...reactFlowInstance.toObject(), message, chatHistory });
sendAll({...reactFlowInstance.toObject(),message,chatHistory}).then((r) => {addChatHistory(r.data.result, false);});
} else {
setErrorData({title: 'Error sending message', list:['Chat nodes are missing.']})
}
} else {
setErrorData({title: 'Error sending message', list:['There are required fields not filled yet.']})
}
} else {
setErrorData({title: 'Error sending message', list:['The message cannot be empty.']})
}
}
return ( sendAll({ ...reactFlowInstance.toObject(), message, chatHistory })
<> .then((r) => {
<Transition console.log(r.data);
show={open} addChatHistory(r.data.result, false,r.data.thought);
appear={true} setLockChat(false);
enter="transition ease-out duration-300" })
enterFrom="translate-y-96" .catch((error) => {
enterTo="translate-y-0" setErrorData({ title: error.message ?? "unknow error" });
leave="transition ease-in duration-300" setLockChat(false);
leaveFrom="translate-y-0" });
leaveTo="translate-y-96" } else {
> setErrorData({
<div className="w-[400px] absolute bottom-0 right-6"> title: "Error sending message",
<div className="border dark:border-gray-700 h-full rounded-xl rounded-b-none bg-white dark:bg-gray-800 shadow"> list: ["There are required fields not filled yet."],
<div className="flex justify-between items-center px-5 py-3 border-b dark:border-b-gray-700"> });
<div className="flex gap-3 text-xl dark:text-white font-medium items-center"> }
<Bars3CenterLeftIcon className="h-8 w-8 mt-1" style={{color: nodeColors['chat']}} /> } else {
Chat setErrorData({
</div> title: "Error sending message",
<button list: ["The message cannot be empty."],
onClick={() => { });
setOpen(false); }
}} }
>
<XMarkIcon className="h-6 w-6 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300" /> return (
</button> <>
</div> <Transition
<div className="w-full h-[400px] flex gap-3 mb-auto overflow-y-auto scrollbar-hide flex-col bg-gray-50 dark:bg-gray-900 p-3 py-5"> show={open}
{chatHistory.map((c, i) => ( appear={true}
<div key={i}> enter="transition ease-out duration-300"
{!c.isSend ? ( enterFrom="translate-y-96"
<div className="w-full text-start"> enterTo="translate-y-0"
<div style={{backgroundColor: nodeColors['chat']}} className="text-start inline-block text-white rounded-xl p-3 overflow-hidden w-fit max-w-[280px] px-5 text-sm font-normal rounded-tl-none"> leave="transition ease-in duration-300"
{c.message} leaveFrom="translate-y-0"
</div> leaveTo="translate-y-96"
</div> >
) : ( <div className="w-[340px] absolute bottom-0 right-1">
<div className="w-full text-end"> <div className="border dark:border-gray-700 h-full rounded-xl rounded-b-none bg-white dark:bg-gray-800 shadow">
<div className="text-start inline-block rounded-xl p-3 overflow-hidden w-fit max-w-[280px] px-5 text-sm text-black dark:text-white dark:bg-gray-700 bg-gray-200 font-normal rounded-tr-none"> <div
{c.message} onClick={() => {
</div> setOpen(false);
</div> }}
)} className="flex justify-between cursor-pointer items-center px-5 py-2 border-b dark:border-b-gray-700"
</div> >
))} <div className="flex gap-3 text-lg dark:text-white font-medium items-center">
<div ref={ref}></div> <Bars3CenterLeftIcon
</div> className="h-5 w-5 mt-1"
<div className="w-full bg-white dark:bg-gray-800 border-t dark:border-t-gray-600 flex items-center justify-between p-3"> style={{ color: nodeColors["chat"] }}
<div className="relative w-full mt-1 rounded-md shadow-sm"> />
<input Chat
onKeyDown={(event)=>{ </div>
if(event.key==='Enter'){ </div>
sendMessage() <div className="w-full h-[400px] flex gap-3 mb-auto overflow-y-auto scrollbar-hide flex-col bg-gray-50 dark:bg-gray-900 p-3 py-5">
} {chatHistory.map((c, i) => (
}} <ChatMessage chat={c} key={i}/>
type="text" ))}
value={chatValue} <div ref={ref}></div>
onChange={(e) => { </div>
setChatValue(e.target.value); <div className="w-full bg-white dark:bg-gray-800 border-t dark:border-t-gray-600 flex items-center justify-between p-3">
}} <div className="relative w-full mt-1 rounded-md shadow-sm">
className="form-input block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white pr-10 sm:text-sm" <input
placeholder="Send a message..." onKeyDown={(event) => {
/> if (event.key === "Enter" && !lockChat) {
<div className="absolute inset-y-0 right-0 flex items-center pr-3"> sendMessage();
<button }
onClick={() => sendMessage()} }}
> type="text"
<PaperAirplaneIcon disabled={lockChat}
className="h-5 w-5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300" value={lockChat?"please wait for the response": chatValue}
aria-hidden="true" onChange={(e) => {
/> setChatValue(e.target.value);
</button> }}
</div> className={classNames(
</div> lockChat ? "bg-gray-500 text-white" : "dark:bg-gray-700",
</div> "form-input block w-full rounded-md border-gray-300 dark:border-gray-600 dark:text-white pr-10 sm:text-sm"
</div> )}
</div> placeholder={"Send a message..."}
</Transition> />
<Transition <div className="absolute inset-y-0 right-0 flex items-center pr-3">
show={!open} <button disabled={lockChat} onClick={() => sendMessage()}>
appear={true} {lockChat ? (
enter="transition ease-out duration-300" <LockClosedIcon
enterFrom="translate-y-96" className="h-5 w-5 text-gray-400 dark:hover:text-gray-300 animate-pulse"
enterTo="translate-y-0" aria-hidden="true"
leave="transition ease-in duration-300" />
leaveFrom="translate-y-0" ) : (
leaveTo="translate-y-96" <PaperAirplaneIcon
> className="h-5 w-5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
<div className="absolute bottom-0 right-6"> aria-hidden="true"
<div className="border flex justify-center align-center py-2 px-4 rounded-xl rounded-b-none bg-white dark:bg-gray-800 dark:border-gray-600 dark:text-white shadow"> />
<button )}
onClick={() => { </button>
setOpen(true); </div>
}} </div>
> </div>
<div className="flex gap-3 text-lg font-medium items-center"> </div>
<Bars3CenterLeftIcon className="h-8 w-8 mt-1" style={{color: nodeColors['chat']}}/> </div>
Chat </Transition>
</div> <Transition
</button> show={!open}
</div> appear={true}
</div> enter="transition ease-out duration-300"
</Transition> enterFrom="translate-y-96"
</> enterTo="translate-y-0"
); leave="transition ease-in duration-300"
leaveFrom="translate-y-0"
leaveTo="translate-y-96"
>
<div className="absolute bottom-0 right-1">
<div className="border flex justify-center align-center py-1 px-3 rounded-xl rounded-b-none bg-white dark:bg-gray-800 dark:border-gray-600 dark:text-white shadow">
<button
onClick={() => {
setOpen(true);
}}
>
<div className="flex gap-3 items-center">
<Bars3CenterLeftIcon
className="h-6 w-6 mt-1"
style={{ color: nodeColors["chat"] }}
/>
Chat
</div>
</button>
</div>
</div>
</Transition>
</>
);
} }

View file

@ -0,0 +1,26 @@
import { useEffect, useState } from "react";
import { FloatComponentType } from "../../types/components";
export default function FloatComponent({value, onChange, disabled}: FloatComponentType){
const [myValue, setMyValue] = useState(value ?? "");
useEffect(()=> {
if(disabled){
setMyValue("");
onChange("");
}
}, [disabled, onChange])
return (
<div className={disabled ? "pointer-events-none cursor-not-allowed" : ""}>
<input
type="number"
value={myValue}
className={"block w-full form-input dark:bg-gray-900 arrow-hide 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="Type a number from zero to one"
onChange={(e) => {
setMyValue(e.target.value);
onChange(e.target.value);
}}
/>
</div>
);
}

View file

@ -6,21 +6,21 @@ import PopUpProvider from "./popUpContext";
import { TabsProvider } from "./tabsContext"; import { TabsProvider } from "./tabsContext";
import { TypesProvider } from "./typesContext"; import { TypesProvider } from "./typesContext";
export default function ContextWrapper({ children }:{children:ReactNode}) { export default function ContextWrapper({ children }: { children: ReactNode }) {
//element to wrap all context //element to wrap all context
return ( return (
<> <>
<DarkProvider> <DarkProvider>
<LocationProvider> <LocationProvider>
<PopUpProvider> <AlertProvider>
<TypesProvider> <PopUpProvider>
<TabsProvider> <TypesProvider>
<AlertProvider>{children}</AlertProvider> <TabsProvider>{children}</TabsProvider>
</TabsProvider> </TypesProvider>
</TypesProvider> </PopUpProvider>
</PopUpProvider> </AlertProvider>
</LocationProvider> </LocationProvider>
</DarkProvider> </DarkProvider>
</> </>
); );
} }

View file

@ -59,17 +59,17 @@ export default function TextAreaModal({value, setValue}:{setValue:(value:string)
</button> </button>
</div> </div>
<div className="h-full w-full flex flex-col justify-center items-center"> <div className="h-full w-full flex flex-col justify-center items-center">
<div className="flex w-full pb-6 z-10 justify-center shadow-sm"> <div className="flex w-full pb-4 z-10 justify-center shadow-sm">
<div className="mx-auto mt-8 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"> <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">
<ClipboardDocumentListIcon <ClipboardDocumentListIcon
className="h-6 w-6 text-blue-600" className="h-6 w-6 text-blue-600"
aria-hidden="true" aria-hidden="true"
/> />
</div> </div>
<div className="mt-10 text-center sm:ml-4 sm:text-left"> <div className="mt-4 text-center sm:ml-4 sm:text-left">
<Dialog.Title <Dialog.Title
as="h3" as="h3"
className="text-lg font-medium dark:text-white leading-6 text-gray-900" className="text-lg font-medium dark:text-white leading-10 text-gray-900"
> >
Edit text Edit text
</Dialog.Title> </Dialog.Title>

View file

@ -1,6 +1,6 @@
import { import {
ChevronRightIcon, ChevronRightIcon,
} from "@heroicons/react/24/outline"; } from "@heroicons/react/24/solid";
import { Disclosure } from "@headlessui/react"; import { Disclosure } from "@headlessui/react";
import { DisclosureComponentType } from "../../../../types/components"; import { DisclosureComponentType } from "../../../../types/components";
@ -31,7 +31,7 @@ export default function DisclosureComponent({
<ChevronRightIcon <ChevronRightIcon
className={`${ className={`${
open ? "rotate-90 transform" : "" open ? "rotate-90 transform" : ""
} h-5 w-5 text-gray-800 dark:text-white`} } h-4 w-4 text-gray-800 dark:text-white`}
/> />
</Disclosure.Button> </Disclosure.Button>
</div> </div>

View file

@ -16,12 +16,6 @@ export default function ExtraSidebar() {
useEffect(() => { useEffect(() => {
async function getTypes():Promise<void>{ async function getTypes():Promise<void>{
// Define an object with initial values for the types.
const initialValue:{[char: string]: string} = {
str: "advanced",
bool: "advanced",
chatOutput: "chat",
}
// Make an asynchronous API call to retrieve all data. // Make an asynchronous API call to retrieve all data.
let result = await getAll(); let result = await getAll();
@ -41,8 +35,7 @@ export default function ExtraSidebar() {
}); });
}); });
return acc; return acc;
}, },{}
initialValue
) )
); );
} }
@ -58,7 +51,7 @@ export default function ExtraSidebar() {
} }
return ( return (
<div className="mt-4 w-full"> <div className="mt-1 w-full">
{Object.keys(data).map((d:keyof APIObjectType, i) => ( {Object.keys(data).map((d:keyof APIObjectType, i) => (
<DisclosureComponent <DisclosureComponent
key={i} key={i}
@ -78,9 +71,9 @@ export default function ExtraSidebar() {
}) })
} }
> >
<div className="flex w-full justify-between text-sm px-4 py-3 items-center border-dashed border-gray-400 dark:border-gray-600 border-l-0 rounded-md rounded-l-none border-2"> <div className="flex w-full justify-between text-sm px-3 py-1 items-center border-dashed border-gray-400 dark:border-gray-600 border-l-0 rounded-md rounded-l-none border">
<span className="text-black dark:text-white w-36 truncate">{t}</span> <span className="text-black dark:text-white w-36 truncate text-xs">{t}</span>
<Bars2Icon className="w-6 h-6 text-gray-400 dark:text-gray-600" /> <Bars2Icon className="w-4 h-6 text-gray-400 dark:text-gray-600" />
</div> </div>
</div> </div>
</div> </div>
@ -88,69 +81,6 @@ export default function ExtraSidebar() {
</div> </div>
</DisclosureComponent> </DisclosureComponent>
))} ))}
<DisclosureComponent
button={{ title: nodeNames["chat"], Icon: nodeIcons["chat"] }}
>
<div className="p-2 flex flex-col gap-2">
<div>
<div
draggable
className={" cursor-grab border-l-8 rounded-l-md"}
style={{ borderLeftColor: nodeColors["chat"] }}
onDragStart={(event) =>
onDragStart(event, {
type: "chatOutput",
})
}
>
<div className="flex w-full justify-between text-sm px-4 py-3 items-center dark:border-gray-600 border-dashed border-gray-400 border-l-0 rounded-md rounded-l-none border-2">
<span className="text-black dark:text-white w-36 truncate">Chat Output</span>
<Bars2Icon className="w-6 h-6 text-gray-400 dark:text-gray-600" />
</div>
</div>
</div>
</div>
</DisclosureComponent>
<DisclosureComponent
button={{ title: nodeNames["advanced"], Icon: nodeIcons["advanced"] }}
>
<div className="p-2 flex flex-col gap-2">
<div>
<div
draggable
className={" cursor-grab border-l-8 rounded-l-md"}
style={{ borderLeftColor: nodeColors["advanced"] }}
onDragStart={(event) =>
onDragStart(event, {
type: "str",
})
}
>
<div className="flex w-full justify-between text-sm px-4 py-3 items-center border-dashed dark:border-gray-600 border-gray-400 border-l-0 rounded-md rounded-l-none border-2">
<span className="text-black dark:text-white w-36 truncate">String</span>
<Bars2Icon className="w-6 h-6 text-gray-400 dark:text-gray-600" />
</div>
</div>
</div>
<div>
<div
draggable
className={" cursor-grab border-l-8 rounded-l-md"}
style={{ borderLeftColor: nodeColors["advanced"] }}
onDragStart={(event) =>
onDragStart(event, {
type: "bool",
})
}
>
<div className="flex w-full justify-between text-sm px-4 py-3 items-center border-dashed dark:border-gray-600 border-gray-400 border-l-0 rounded-md rounded-l-none border-2">
<span className="text-black dark:text-white w-36 truncate">Boolean</span>
<Bars2Icon className="w-6 h-6 text-gray-400 dark:text-gray-600" />
</div>
</div>
</div>
</div>
</DisclosureComponent>
</div> </div>
); );
} }

View file

@ -29,7 +29,7 @@ export default function TabComponent({ selected, flow, onClick }:{flow:FlowType,
</button> </button>
</div> </div>
) : ( ) : (
<div className="bg-white dark:text-white dark:bg-gray-700 flex select-none justify-between w-44 items-center border border-b-0 border-gray-300 dark:border-gray-600 px-4 py-1.5 rounded-t-xl -ml-px"> <div className="bg-white dark:text-white dark:bg-gray-700 flex select-none justify-between w-44 items-center border border-b-0 border-gray-300 dark:border-gray-600 px-4 py-1 rounded-t-xl -ml-px">
{isRename ? ( {isRename ? (
<input <input
autoFocus autoFocus

View file

@ -1,48 +1,83 @@
import { useContext, useEffect } from "react"; import { useContext, useEffect, useState } from "react";
import { ReactFlowProvider } from "reactflow"; import { ReactFlowProvider } from "reactflow";
import TabComponent from "../tabComponent"; import TabComponent from "../tabComponent";
import { TabsContext } from "../../../../contexts/tabsContext"; import { TabsContext } from "../../../../contexts/tabsContext";
import FlowPage from "../.."; import FlowPage from "../..";
import { darkContext } from "../../../../contexts/darkContext";
import { BellIcon, MoonIcon, SunIcon } from "@heroicons/react/24/outline";
import { PopUpContext } from "../../../../contexts/popUpContext";
import AlertDropdown from "../../../../alerts/alertDropDown";
import { alertContext } from "../../../../contexts/alertContext";
export default function TabsManagerComponent() { export default function TabsManagerComponent() {
const { flows, addFlow, tabIndex, setTabIndex } = useContext(TabsContext); const { flows, addFlow, tabIndex, setTabIndex } = useContext(TabsContext);
useEffect(() => { const { openPopUp } = useContext(PopUpContext);
//create the first flow const AlertWidth = 256
if (flows.length === 0) { const { dark, setDark } = useContext(darkContext);
addFlow(); const {notificationCenter, setNotificationCenter} = useContext(alertContext)
} useEffect(() => {
}, [addFlow, flows.length]); //create the first flow
if (flows.length === 0) {
addFlow();
}
}, [addFlow, flows.length]);
return ( return (
<div className="h-full w-full flex flex-col"> <div className="h-full w-full flex flex-col">
<div className="w-full flex pr-2 flex-row text-center items-center bg-gray-100 dark:bg-gray-800 px-2"> <div className="w-full flex pr-2 flex-row text-center items-center bg-gray-100 dark:bg-gray-800 px-2">
{flows.map((flow, index) => { {flows.map((flow, index) => {
return ( return (
<TabComponent <TabComponent
onClick={() => setTabIndex(index)} onClick={() => setTabIndex(index)}
selected={index === tabIndex} selected={index === tabIndex}
key={index} key={index}
flow={flow} flow={flow}
/> />
); );
})} })}
<TabComponent <TabComponent
onClick={() => { onClick={() => {
addFlow(); addFlow();
}} }}
selected={false} selected={false}
flow={null} flow={null}
/> />
</div> <div className="ml-auto mr-2 flex gap-3">
<div className="w-full h-full"> <button
<ReactFlowProvider> className="text-gray-400 hover:text-gray-500 "
{flows[tabIndex] ? ( onClick={() => {
<FlowPage flow={flows[tabIndex]}></FlowPage> setDark(!dark);
) : ( }}
<></> >
)} {dark ? (
</ReactFlowProvider> <SunIcon className="h-5 w-5" />
</div> ) : (
</div> <MoonIcon className="h-5 w-5" />
); )}
</button>
<button
className="text-gray-400 hover:text-gray-500 relative"
onClick={(event: React.MouseEvent<HTMLElement>) => {
setNotificationCenter(false)
const top = (event.target as Element).getBoundingClientRect().top
const left = (event.target as Element).getBoundingClientRect().left
openPopUp(<div className="z-10 absolute" style={{top:top+20, left:left-AlertWidth}}><AlertDropdown/></div>)
}}
>
{notificationCenter&&<div className='absolute w-1.5 h-1.5 rounded-full bg-red-600 right-[3px]'></div>}
<BellIcon className="h-5 w-5" aria-hidden="true" />
</button>
</div>
</div>
<div className="w-full h-full">
<ReactFlowProvider>
{flows[tabIndex] ? (
<FlowPage flow={flows[tabIndex]}></FlowPage>
) : (
<></>
)}
</ReactFlowProvider>
</div>
</div>
);
} }

View file

@ -14,10 +14,6 @@ import { locationContext } from "../../contexts/locationContext";
import ExtraSidebar from "./components/extraSidebarComponent"; import ExtraSidebar from "./components/extraSidebarComponent";
import Chat from "../../components/chatComponent"; import Chat from "../../components/chatComponent";
import GenericNode from "../../CustomNodes/GenericNode"; import GenericNode from "../../CustomNodes/GenericNode";
import ChatInputNode from "../../CustomNodes/ChatInputNode";
import ChatOutputNode from "../../CustomNodes/ChatOutputNode";
import InputNode from "../../CustomNodes/InputNode";
import BooleanNode from "../../CustomNodes/BooleanNode";
import { alertContext } from "../../contexts/alertContext"; import { alertContext } from "../../contexts/alertContext";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { typesContext } from "../../contexts/typesContext"; import { typesContext } from "../../contexts/typesContext";
@ -31,10 +27,6 @@ import { APIClassType } from "../../types/api";
const nodeTypes = { const nodeTypes = {
genericNode: GenericNode, genericNode: GenericNode,
inputNode: InputNode,
chatInputNode: ChatInputNode,
chatOutputNode: ChatOutputNode,
booleanNode: BooleanNode,
}; };
var _ = require("lodash"); var _ = require("lodash");
@ -68,13 +60,13 @@ export default function FlowPage({ flow }:{flow:FlowType}) {
setNodes(flow?.data?.nodes ?? []); setNodes(flow?.data?.nodes ?? []);
setEdges(flow?.data?.edges ?? []); setEdges(flow?.data?.edges ?? []);
if (reactFlowInstance) { if (reactFlowInstance) {
setViewport(flow?.data?.viewport ?? { x: 1, y: 0, zoom: 1 }); setViewport(flow?.data?.viewport ?? { x: 1, y: 0, zoom: 0.5 });
} }
}, [flow, reactFlowInstance, setEdges, setNodes, setViewport]); }, [flow, reactFlowInstance, setEdges, setNodes, setViewport]);
//set extra sidebar //set extra sidebar
useEffect(() => { useEffect(() => {
setExtraComponent(<ExtraSidebar />); setExtraComponent(<ExtraSidebar />);
setExtraNavigation({ title: "Componets" }); setExtraNavigation({ title: "Components" });
}, [setExtraComponent, setExtraNavigation]); }, [setExtraComponent, setExtraNavigation]);
const onEdgesChangeMod = useCallback( const onEdgesChangeMod = useCallback(
@ -139,14 +131,7 @@ export default function FlowPage({ flow }:{flow:FlowType}) {
// Create a new node object // Create a new node object
const newNode:NodeType = { const newNode:NodeType = {
id: newId, id: newId,
type: type: "genericNode",
data.type === "str"
? "inputNode"
: data.type === "chatOutput"
? "chatOutputNode"
: data.type === "bool"
? "booleanNode"
: "genericNode",
position, position,
data: { data: {
...data, ...data,

View file

@ -2,10 +2,7 @@ export type ErrorAlertType = {title:string,list:Array<string>,id:string,removeAl
export type NoticeAlertType = {title:string,link:string,id:string,removeAlert:(id:string)=>void} export type NoticeAlertType = {title:string,link:string,id:string,removeAlert:(id:string)=>void}
export type SuccessAlertType = {title:string,id:string, removeAlert:(id:string)=>void} export type SuccessAlertType = {title:string,id:string, removeAlert:(id:string)=>void}
export type SingleAlertComponentType = {dropItem:AlertItemType,removeAlert:(index:string)=>void} export type SingleAlertComponentType = {dropItem:AlertItemType,removeAlert:(index:string)=>void}
export type AlertDropdownType = { export type AlertDropdownType = {};
closeFunction: () => void;
open?: boolean;
};
export type AlertItemType = { export type AlertItemType = {
type: "notice" | "error" | "success"; type: "notice" | "error" | "success";
title: string; title: string;

View file

@ -1,4 +1,5 @@
import { ReactFlowInstance } from 'reactflow'; import { ReactFlowInstance } from 'reactflow';
import { FlowType } from "../flow"; import { FlowType } from "../flow";
export type ChatType = {flow:FlowType,reactFlowInstance:ReactFlowInstance} export type ChatType = {flow:FlowType,reactFlowInstance:ReactFlowInstance}
export type ChatMessageType = { message: string; isSend: boolean, thought?:string }

View file

@ -1,4 +1,4 @@
import { ForwardRefExoticComponent, ReactElement, ReactNode } from 'react'; import { ForwardRefExoticComponent, ReactElement, ReactNode } from "react";
import { NodeDataType } from "../flow/index"; import { NodeDataType } from "../flow/index";
export type InputComponentType = { export type InputComponentType = {
value: string; value: string;
@ -27,9 +27,17 @@ export type ParameterComponentType = {
name?: string; name?: string;
tooltipTitle: string; tooltipTitle: string;
}; };
export type InputListComponentType = {value:string[],onChange:(value:string[])=>void,disabled:boolean} export type InputListComponentType = {
value: string[];
onChange: (value: string[]) => void;
disabled: boolean;
};
export type TextAreaComponentType = {disabled:boolean,onChange:(value:string[]|string)=>void,value:string[]|string} export type TextAreaComponentType = {
disabled: boolean;
onChange: (value: string[] | string) => void;
value: string[] | string;
};
export type DisclosureComponentType = { export type DisclosureComponentType = {
children: ReactNode; children: ReactNode;
@ -39,6 +47,12 @@ export type DisclosureComponentType = {
buttons?: { buttons?: {
Icon: ReactElement; Icon: ReactElement;
title: string; title: string;
onClick:(event?:React.MouseEvent)=>void; onClick: (event?: React.MouseEvent) => void;
}[]; }[];
}}; };
};
export type FloatComponentType = {
value: string;
disabled?: boolean;
onChange: (value: string) => void;
};

View file

@ -1,3 +1,4 @@
import { ChatMessageType } from './../chat/index';
import { APIClassType } from '../api/index'; import { APIClassType } from '../api/index';
import { ReactFlowJsonObject, XYPosition } from "reactflow"; import { ReactFlowJsonObject, XYPosition } from "reactflow";
@ -5,7 +6,7 @@ export type FlowType = {
name: string; name: string;
id: string; id: string;
data: ReactFlowJsonObject; data: ReactFlowJsonObject;
chat: Array<{ message: string; isSend: boolean }>; chat: Array<ChatMessageType>;
}; };
export type NodeType = {id:string,type:string,position:XYPosition,data:NodeDataType} export type NodeType = {id:string,type:string,position:XYPosition,data:NodeDataType}
export type NodeDataType = {type:string,node?:APIClassType,id:string,value:any} export type NodeDataType = {type:string,node?:APIClassType,id:string,value:any}

View file

@ -68,7 +68,8 @@ export const nodeColors: {[char: string]: string} = {
tools: "#FF3434", tools: "#FF3434",
memories: "#FF9135", memories: "#FF9135",
advanced: "#000000", advanced: "#000000",
chat: "#2563EB", chat: "#454173",
thought:"#272541"
}; };
export const nodeNames:{[char: string]: string} = { export const nodeNames:{[char: string]: string} = {

View file

@ -22,6 +22,16 @@ module.exports = {
'&::-webkit-scrollbar': { '&::-webkit-scrollbar': {
display: 'none' display: 'none'
} }
},
'.arrow-hide':{
'&::-webkit-inner-spin-button':{
'-webkit-appearance': 'none',
'margin': 0
},
'&::-webkit-outer-spin-button':{
'-webkit-appearance': 'none',
'margin': 0
},
} }
} }
) )