Merge branch 'uiRefactor' into dev
This commit is contained in:
commit
6c53c9c30e
27 changed files with 567 additions and 725 deletions
|
|
@ -8,7 +8,6 @@ import SuccessAlert from "./alerts/success";
|
|||
import ExtraSidebar from "./components/ExtraSidebarComponent";
|
||||
import { alertContext } from "./contexts/alertContext";
|
||||
import { locationContext } from "./contexts/locationContext";
|
||||
import Header from "./components/HeaderComponent";
|
||||
import TabsManagerComponent from "./pages/FlowPage/components/tabsManagerComponent";
|
||||
|
||||
export default function App() {
|
||||
|
|
@ -85,7 +84,6 @@ useEffect(() => {
|
|||
//need parent component with width and height
|
||||
<div className="h-full flex flex-col">
|
||||
<div className="flex grow-0 shrink basis-auto">
|
||||
<Header></Header>
|
||||
</div>
|
||||
<div className="flex grow shrink basis-auto min-h-0 flex-1 overflow-hidden">
|
||||
<ExtraSidebar />
|
||||
|
|
@ -97,7 +95,8 @@ useEffect(() => {
|
|||
</div>
|
||||
</main>
|
||||
</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) => (
|
||||
<div key={alert.id}>
|
||||
{alert.type === "error" ? (
|
||||
|
|
@ -127,6 +126,7 @@ useEffect(() => {
|
|||
</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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
import { Handle, Position, useUpdateNodeInternals } from "reactflow";
|
||||
import Tooltip from "../../../../components/TooltipComponent";
|
||||
import {
|
||||
isValidConnection,
|
||||
} from "../../../../utils";
|
||||
import { classNames, isValidConnection } from "../../../../utils";
|
||||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
import InputComponent from "../../../../components/inputComponent";
|
||||
import ToggleComponent from "../../../../components/toggleComponent";
|
||||
|
|
@ -10,103 +8,127 @@ import InputListComponent from "../../../../components/inputListComponent";
|
|||
import TextAreaComponent from "../../../../components/textAreaComponent";
|
||||
import { typesContext } from "../../../../contexts/typesContext";
|
||||
import { ParameterComponentType } from "../../../../types/components";
|
||||
import FloatComponent from "../../../../components/floatComponent";
|
||||
|
||||
export default function ParameterComponent({
|
||||
left,
|
||||
id,
|
||||
data,
|
||||
tooltipTitle,
|
||||
title,
|
||||
color,
|
||||
type,
|
||||
name = "",
|
||||
required = false,
|
||||
}:ParameterComponentType) {
|
||||
const ref = useRef(null);
|
||||
const updateNodeInternals = useUpdateNodeInternals();
|
||||
const [position, setPosition] = useState(0);
|
||||
useEffect(() => {
|
||||
if (ref.current && ref.current.offsetTop && ref.current.clientHeight) {
|
||||
setPosition(ref.current.offsetTop + ref.current.clientHeight / 2);
|
||||
updateNodeInternals(data.id);
|
||||
}
|
||||
}, [data.id, ref, updateNodeInternals]);
|
||||
left,
|
||||
id,
|
||||
data,
|
||||
tooltipTitle,
|
||||
title,
|
||||
color,
|
||||
type,
|
||||
name = "",
|
||||
required = false,
|
||||
}: ParameterComponentType) {
|
||||
const ref = useRef(null);
|
||||
const updateNodeInternals = useUpdateNodeInternals();
|
||||
const [position, setPosition] = useState(0);
|
||||
useEffect(() => {
|
||||
if (ref.current && ref.current.offsetTop && ref.current.clientHeight) {
|
||||
setPosition(ref.current.offsetTop + ref.current.clientHeight / 2);
|
||||
updateNodeInternals(data.id);
|
||||
}
|
||||
}, [data.id, ref, updateNodeInternals]);
|
||||
|
||||
useEffect(() => {
|
||||
updateNodeInternals(data.id);
|
||||
}, [data.id, position, updateNodeInternals]);
|
||||
useEffect(() => {
|
||||
updateNodeInternals(data.id);
|
||||
}, [data.id, position, updateNodeInternals]);
|
||||
|
||||
const [enabled, setEnabled] = useState(data.node.template[name]?.value ?? false);
|
||||
const {reactFlowInstance} = useContext(typesContext);
|
||||
let disabled = reactFlowInstance?.getEdges().some((e) => (e.targetHandle === id)) ?? false;
|
||||
const [enabled, setEnabled] = useState(
|
||||
data.node.template[name]?.value ?? false
|
||||
);
|
||||
const { reactFlowInstance } = useContext(typesContext);
|
||||
let disabled =
|
||||
reactFlowInstance?.getEdges().some((e) => e.targetHandle === id) ?? false;
|
||||
|
||||
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 className="text-sm truncate">{title}<span className="text-red-600">{required ? " *" : ""}</span></div>
|
||||
<Tooltip title={tooltipTitle + (required ? " (required)" : "")}>
|
||||
<Handle
|
||||
type={left ? "target" : "source"}
|
||||
position={left ? Position.Left : Position.Right}
|
||||
id={id}
|
||||
isValidConnection={(connection) =>
|
||||
isValidConnection(connection,reactFlowInstance)
|
||||
}
|
||||
className={
|
||||
(left ? "-ml-0.5 " : "-mr-0.5 ") +
|
||||
"w-3 h-3 rounded-full border-2 bg-white dark:bg-gray-800"
|
||||
}
|
||||
style={{
|
||||
borderColor: color,
|
||||
top: position,
|
||||
}}
|
||||
></Handle>
|
||||
</Tooltip>
|
||||
{left === true && type === "str" ? (
|
||||
<div className="mt-2 w-full">
|
||||
{data.node.template[name].list ?
|
||||
<InputListComponent
|
||||
disabled={disabled}
|
||||
value={!data.node.template[name].value || data.node.template[name].value === "" ? [""] : data.node.template[name].value}
|
||||
onChange={(t:string[]) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
:
|
||||
data.node.template[name].multiline ? (
|
||||
<TextAreaComponent
|
||||
disabled={disabled}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t:string) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
) :
|
||||
<InputComponent
|
||||
disabled={disabled}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
}
|
||||
|
||||
</div>
|
||||
) : left === true && type === "bool" ? (
|
||||
<div className="mt-2">
|
||||
<ToggleComponent
|
||||
disabled={disabled}
|
||||
enabled={enabled}
|
||||
setEnabled={(t) => {
|
||||
data.node.template[name].value = t;
|
||||
setEnabled(t);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
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 className="text-sm truncate">
|
||||
{title}
|
||||
<span className="text-red-600">{required ? " *" : ""}</span>
|
||||
</div>
|
||||
<Tooltip title={tooltipTitle + (required ? " (required)" : "")}>
|
||||
<Handle
|
||||
type={left ? "target" : "source"}
|
||||
position={left ? Position.Left : Position.Right}
|
||||
id={id}
|
||||
isValidConnection={(connection) =>
|
||||
isValidConnection(connection, reactFlowInstance)
|
||||
}
|
||||
className={classNames(
|
||||
left ? "-ml-0.5 " : "-mr-0.5 ",
|
||||
"w-3 h-3 rounded-full border-2 bg-white dark:bg-gray-800",
|
||||
left && (type === "str" || type === "bool" || type === "float")
|
||||
? "hidden"
|
||||
: ""
|
||||
)}
|
||||
style={{
|
||||
borderColor: color,
|
||||
top: position,
|
||||
}}
|
||||
></Handle>
|
||||
</Tooltip>
|
||||
{left === true && type === "str" ? (
|
||||
<div className="mt-2 w-full">
|
||||
{data.node.template[name].list ? (
|
||||
<InputListComponent
|
||||
disabled={disabled}
|
||||
value={
|
||||
!data.node.template[name].value ||
|
||||
data.node.template[name].value === ""
|
||||
? [""]
|
||||
: data.node.template[name].value
|
||||
}
|
||||
onChange={(t: string[]) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
) : data.node.template[name].multiline ? (
|
||||
<TextAreaComponent
|
||||
disabled={disabled}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t: string) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<InputComponent
|
||||
disabled={disabled}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : left === true && type === "bool" ? (
|
||||
<div className="mt-2">
|
||||
<ToggleComponent
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import {
|
|||
TrashIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import {
|
||||
classNames,
|
||||
nodeColors,
|
||||
nodeIcons,
|
||||
snakeToNormalCase,
|
||||
|
|
@ -11,13 +12,13 @@ import { typesContext } from "../../contexts/typesContext";
|
|||
import { useContext } from "react";
|
||||
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 Icon = nodeIcons[types[data.type]];
|
||||
|
||||
|
||||
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 flex items-center truncate gap-4 text-lg">
|
||||
<Icon
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ export default function SingleAlert({ dropItem, removeAlert}:SingleAlertComponen
|
|||
leaveTo={"transform translate-x-[-100%]"}
|
||||
>
|
||||
{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">
|
||||
<XCircleIcon
|
||||
className="h-5 w-5 text-red-400"
|
||||
|
|
@ -61,7 +61,7 @@ export default function SingleAlert({ dropItem, removeAlert}:SingleAlertComponen
|
|||
</div>
|
||||
</div>
|
||||
:(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">
|
||||
<InformationCircleIcon
|
||||
className="h-5 w-5 text-blue-400"
|
||||
|
|
@ -99,7 +99,7 @@ export default function SingleAlert({ dropItem, removeAlert}:SingleAlertComponen
|
|||
</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">
|
||||
<CheckCircleIcon
|
||||
className="h-5 w-5 text-green-400"
|
||||
|
|
|
|||
|
|
@ -3,54 +3,45 @@ import { alertContext } from "../../contexts/alertContext";
|
|||
import {
|
||||
XMarkIcon,
|
||||
} from "@heroicons/react/24/solid";
|
||||
import { Transition } from "@headlessui/react";
|
||||
import { TrashIcon } from "@heroicons/react/24/outline";
|
||||
import SingleAlert from "./components/singleAlertComponent";
|
||||
import { AlertDropdownType } from "../../types/alerts";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
|
||||
|
||||
|
||||
export default function AlertDropdown({closeFunction, open}: AlertDropdownType) {
|
||||
export default function AlertDropdown({}: AlertDropdownType) {
|
||||
const {
|
||||
notificationList,
|
||||
clearNotificationList,
|
||||
removeFromNotificationList,
|
||||
} = useContext(alertContext);
|
||||
|
||||
const {closePopUp} = useContext(PopUpContext)
|
||||
|
||||
|
||||
return (
|
||||
<Transition
|
||||
show={open}
|
||||
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">
|
||||
<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">
|
||||
<div className="flex pl-3 flex-row justify-between text-md font-medium text-gray-800">
|
||||
Notifications
|
||||
<div className="flex gap-4">
|
||||
<div className="flex gap-2 pr-3 ">
|
||||
<button
|
||||
className="hover:text-black"
|
||||
onClick={() => {closeFunction(); setTimeout(clearNotificationList, 100)}}
|
||||
className="hover:text-red-500"
|
||||
onClick={() => {closePopUp(); setTimeout(clearNotificationList, 100)}}
|
||||
>
|
||||
<TrashIcon className="w-5 h-5" />
|
||||
<TrashIcon className="w-[1.1rem] h-[1.1rem]" />
|
||||
</button>
|
||||
<button
|
||||
className="hover:text-black"
|
||||
onClick={closeFunction}
|
||||
className="hover:text-red-500"
|
||||
onClick={closePopUp}
|
||||
>
|
||||
<XMarkIcon className="h-6 w-6" />
|
||||
<XMarkIcon className="h-5 w-5" />
|
||||
</button>
|
||||
</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.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">
|
||||
|
|
@ -59,6 +50,5 @@ export default function AlertDropdown({closeFunction, open}: AlertDropdownType)
|
|||
}
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,12 +17,12 @@ export default function ExtraSidebar() {
|
|||
<>
|
||||
<aside
|
||||
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`}
|
||||
>
|
||||
<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="flex pt-4 px-4 justify-between align-middle w-full">
|
||||
<span className="text-gray-900 dark:text-white text-lg ml-2 font-semibold">
|
||||
<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-1 px-4 justify-between align-middle w-full">
|
||||
<span className="text-gray-900 dark:text-white py-[2px] font-medium ">
|
||||
{extraNavigation.title}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,182 +1,217 @@
|
|||
import { Transition } from "@headlessui/react";
|
||||
import {
|
||||
Bars3CenterLeftIcon,
|
||||
PaperAirplaneIcon,
|
||||
XMarkIcon,
|
||||
Bars3CenterLeftIcon,
|
||||
LockClosedIcon,
|
||||
PaperAirplaneIcon,
|
||||
XMarkIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
import { sendAll } from "../../controllers/NodesServices";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { nodeColors } from "../../utils";
|
||||
import { classNames, nodeColors } from "../../utils";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import { ChatType } from "../../types/chat";
|
||||
import ChatMessage from "./chatMessage";
|
||||
|
||||
const _ = require("lodash");
|
||||
|
||||
export default function Chat({flow, reactFlowInstance }:ChatType) {
|
||||
const {updateFlow} = useContext(TabsContext)
|
||||
const [saveChat,setSaveChat] = useState(false)
|
||||
const [open, setOpen] = useState(true);
|
||||
const [chatValue, setChatValue] = useState("");
|
||||
const [chatHistory, setChatHistory] = useState(flow.chat);
|
||||
const {setErrorData} = useContext(alertContext);
|
||||
const addChatHistory = (message:string, isSend:boolean) => {
|
||||
setChatHistory((old) => {
|
||||
let newChat = _.cloneDeep(old);
|
||||
newChat.push({ message, isSend });
|
||||
return newChat;
|
||||
});
|
||||
setSaveChat(chat=>!chat)
|
||||
};
|
||||
useEffect(()=>{
|
||||
updateFlow({..._.cloneDeep(flow),chat:chatHistory})
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
},[saveChat])
|
||||
useEffect(()=>{
|
||||
setChatHistory(flow.chat)
|
||||
},[flow])
|
||||
useEffect(()=>{
|
||||
if(ref.current)
|
||||
ref.current.scrollIntoView({behavior: 'smooth'});
|
||||
}, [chatHistory])
|
||||
function validateNodes(){
|
||||
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)))))))){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function validateChatNodes(){
|
||||
if(!reactFlowInstance.getNodes().some((n)=> (n.type === 'chatOutputNode'))){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const ref = useRef(null);
|
||||
export default function Chat({ flow, reactFlowInstance }: ChatType) {
|
||||
const { updateFlow } = useContext(TabsContext);
|
||||
const [saveChat, setSaveChat] = useState(false);
|
||||
const [lockChat, setLockChat] = useState(false);
|
||||
const [open, setOpen] = useState(true);
|
||||
const [chatValue, setChatValue] = useState("");
|
||||
const [chatHistory, setChatHistory] = useState(flow.chat);
|
||||
const { setErrorData } = useContext(alertContext);
|
||||
const addChatHistory = (
|
||||
message: string,
|
||||
isSend: boolean,
|
||||
thought?: string
|
||||
) => {
|
||||
setChatHistory((old) => {
|
||||
let newChat = _.cloneDeep(old);
|
||||
if (thought) {
|
||||
newChat.push({ message, isSend, thought });
|
||||
} else {
|
||||
newChat.push({ message, isSend });
|
||||
}
|
||||
return newChat;
|
||||
});
|
||||
setSaveChat((chat) => !chat);
|
||||
};
|
||||
useEffect(() => {
|
||||
updateFlow({ ..._.cloneDeep(flow), chat: chatHistory });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [saveChat]);
|
||||
useEffect(() => {
|
||||
setChatHistory(flow.chat);
|
||||
}, [flow]);
|
||||
useEffect(() => {
|
||||
if (ref.current) ref.current.scrollIntoView({ behavior: "smooth" });
|
||||
}, [chatHistory]);
|
||||
function validateNodes() {
|
||||
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
|
||||
)
|
||||
)
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const ref = useRef(null);
|
||||
|
||||
function sendMessage(){
|
||||
if(chatValue !== ""){
|
||||
if(validateNodes()){
|
||||
if(validateChatNodes()){
|
||||
let message = chatValue;
|
||||
setChatValue("");
|
||||
addChatHistory(message, true);
|
||||
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.']})
|
||||
}
|
||||
}
|
||||
function sendMessage() {
|
||||
if (chatValue !== "") {
|
||||
if (validateNodes()) {
|
||||
setLockChat(true);
|
||||
let message = chatValue;
|
||||
setChatValue("");
|
||||
addChatHistory(message, true);
|
||||
console.log({ ...reactFlowInstance.toObject(), message, chatHistory });
|
||||
|
||||
return (
|
||||
<>
|
||||
<Transition
|
||||
show={open}
|
||||
appear={true}
|
||||
enter="transition ease-out duration-300"
|
||||
enterFrom="translate-y-96"
|
||||
enterTo="translate-y-0"
|
||||
leave="transition ease-in duration-300"
|
||||
leaveFrom="translate-y-0"
|
||||
leaveTo="translate-y-96"
|
||||
>
|
||||
<div className="w-[400px] absolute bottom-0 right-6">
|
||||
<div className="border dark:border-gray-700 h-full rounded-xl rounded-b-none bg-white dark:bg-gray-800 shadow">
|
||||
<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']}} />
|
||||
Chat
|
||||
</div>
|
||||
<button
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<XMarkIcon className="h-6 w-6 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300" />
|
||||
</button>
|
||||
</div>
|
||||
<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) => (
|
||||
<div key={i}>
|
||||
{!c.isSend ? (
|
||||
<div className="w-full text-start">
|
||||
<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">
|
||||
{c.message}
|
||||
</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">
|
||||
{c.message}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<div ref={ref}></div>
|
||||
</div>
|
||||
<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">
|
||||
<input
|
||||
onKeyDown={(event)=>{
|
||||
if(event.key==='Enter'){
|
||||
sendMessage()
|
||||
}
|
||||
}}
|
||||
type="text"
|
||||
value={chatValue}
|
||||
onChange={(e) => {
|
||||
setChatValue(e.target.value);
|
||||
}}
|
||||
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"
|
||||
placeholder="Send a message..."
|
||||
/>
|
||||
<div className="absolute inset-y-0 right-0 flex items-center pr-3">
|
||||
<button
|
||||
onClick={() => sendMessage()}
|
||||
>
|
||||
<PaperAirplaneIcon
|
||||
className="h-5 w-5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
<Transition
|
||||
show={!open}
|
||||
appear={true}
|
||||
enter="transition ease-out duration-300"
|
||||
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-6">
|
||||
<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={() => {
|
||||
setOpen(true);
|
||||
}}
|
||||
>
|
||||
<div className="flex gap-3 text-lg font-medium items-center">
|
||||
<Bars3CenterLeftIcon className="h-8 w-8 mt-1" style={{color: nodeColors['chat']}}/>
|
||||
Chat
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</>
|
||||
);
|
||||
sendAll({ ...reactFlowInstance.toObject(), message, chatHistory })
|
||||
.then((r) => {
|
||||
console.log(r.data);
|
||||
addChatHistory(r.data.result, false,r.data.thought);
|
||||
setLockChat(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
setErrorData({ title: error.message ?? "unknow error" });
|
||||
setLockChat(false);
|
||||
});
|
||||
} 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 (
|
||||
<>
|
||||
<Transition
|
||||
show={open}
|
||||
appear={true}
|
||||
enter="transition ease-out duration-300"
|
||||
enterFrom="translate-y-96"
|
||||
enterTo="translate-y-0"
|
||||
leave="transition ease-in duration-300"
|
||||
leaveFrom="translate-y-0"
|
||||
leaveTo="translate-y-96"
|
||||
>
|
||||
<div className="w-[340px] absolute bottom-0 right-1">
|
||||
<div className="border dark:border-gray-700 h-full rounded-xl rounded-b-none bg-white dark:bg-gray-800 shadow">
|
||||
<div
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
className="flex justify-between cursor-pointer items-center px-5 py-2 border-b dark:border-b-gray-700"
|
||||
>
|
||||
<div className="flex gap-3 text-lg dark:text-white font-medium items-center">
|
||||
<Bars3CenterLeftIcon
|
||||
className="h-5 w-5 mt-1"
|
||||
style={{ color: nodeColors["chat"] }}
|
||||
/>
|
||||
Chat
|
||||
</div>
|
||||
</div>
|
||||
<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}/>
|
||||
))}
|
||||
<div ref={ref}></div>
|
||||
</div>
|
||||
<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">
|
||||
<input
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Enter" && !lockChat) {
|
||||
sendMessage();
|
||||
}
|
||||
}}
|
||||
type="text"
|
||||
disabled={lockChat}
|
||||
value={lockChat?"please wait for the response": chatValue}
|
||||
onChange={(e) => {
|
||||
setChatValue(e.target.value);
|
||||
}}
|
||||
className={classNames(
|
||||
lockChat ? "bg-gray-500 text-white" : "dark:bg-gray-700",
|
||||
"form-input block w-full rounded-md border-gray-300 dark:border-gray-600 dark:text-white pr-10 sm:text-sm"
|
||||
)}
|
||||
placeholder={"Send a message..."}
|
||||
/>
|
||||
<div className="absolute inset-y-0 right-0 flex items-center pr-3">
|
||||
<button disabled={lockChat} onClick={() => sendMessage()}>
|
||||
{lockChat ? (
|
||||
<LockClosedIcon
|
||||
className="h-5 w-5 text-gray-400 dark:hover:text-gray-300 animate-pulse"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
) : (
|
||||
<PaperAirplaneIcon
|
||||
className="h-5 w-5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
<Transition
|
||||
show={!open}
|
||||
appear={true}
|
||||
enter="transition ease-out duration-300"
|
||||
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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
26
langflow/frontend/src/components/floatComponent/index.tsx
Normal file
26
langflow/frontend/src/components/floatComponent/index.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
|
|
@ -6,21 +6,21 @@ import PopUpProvider from "./popUpContext";
|
|||
import { TabsProvider } from "./tabsContext";
|
||||
import { TypesProvider } from "./typesContext";
|
||||
|
||||
export default function ContextWrapper({ children }:{children:ReactNode}) {
|
||||
//element to wrap all context
|
||||
return (
|
||||
<>
|
||||
<DarkProvider>
|
||||
<LocationProvider>
|
||||
<PopUpProvider>
|
||||
<TypesProvider>
|
||||
<TabsProvider>
|
||||
<AlertProvider>{children}</AlertProvider>
|
||||
</TabsProvider>
|
||||
</TypesProvider>
|
||||
</PopUpProvider>
|
||||
</LocationProvider>
|
||||
</DarkProvider>
|
||||
</>
|
||||
);
|
||||
export default function ContextWrapper({ children }: { children: ReactNode }) {
|
||||
//element to wrap all context
|
||||
return (
|
||||
<>
|
||||
<DarkProvider>
|
||||
<LocationProvider>
|
||||
<AlertProvider>
|
||||
<PopUpProvider>
|
||||
<TypesProvider>
|
||||
<TabsProvider>{children}</TabsProvider>
|
||||
</TypesProvider>
|
||||
</PopUpProvider>
|
||||
</AlertProvider>
|
||||
</LocationProvider>
|
||||
</DarkProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,17 +59,17 @@ export default function TextAreaModal({value, setValue}:{setValue:(value:string)
|
|||
</button>
|
||||
</div>
|
||||
<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="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="flex w-full pb-4 z-10 justify-center shadow-sm">
|
||||
<div className="mx-auto mt-4 flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-blue-100 dark:bg-gray-900 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<ClipboardDocumentListIcon
|
||||
className="h-6 w-6 text-blue-600"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</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
|
||||
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
|
||||
</Dialog.Title>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {
|
||||
ChevronRightIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
} from "@heroicons/react/24/solid";
|
||||
import { Disclosure } from "@headlessui/react";
|
||||
import { DisclosureComponentType } from "../../../../types/components";
|
||||
|
||||
|
|
@ -31,7 +31,7 @@ export default function DisclosureComponent({
|
|||
<ChevronRightIcon
|
||||
className={`${
|
||||
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>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -16,12 +16,6 @@ export default function ExtraSidebar() {
|
|||
|
||||
useEffect(() => {
|
||||
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.
|
||||
let result = await getAll();
|
||||
|
|
@ -41,8 +35,7 @@ export default function ExtraSidebar() {
|
|||
});
|
||||
});
|
||||
return acc;
|
||||
},
|
||||
initialValue
|
||||
},{}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
@ -58,7 +51,7 @@ export default function ExtraSidebar() {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="mt-4 w-full">
|
||||
<div className="mt-1 w-full">
|
||||
{Object.keys(data).map((d:keyof APIObjectType, i) => (
|
||||
<DisclosureComponent
|
||||
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">
|
||||
<span className="text-black dark:text-white w-36 truncate">{t}</span>
|
||||
<Bars2Icon className="w-6 h-6 text-gray-400 dark:text-gray-600" />
|
||||
<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 text-xs">{t}</span>
|
||||
<Bars2Icon className="w-4 h-6 text-gray-400 dark:text-gray-600" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -88,69 +81,6 @@ export default function ExtraSidebar() {
|
|||
</div>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export default function TabComponent({ selected, flow, onClick }:{flow:FlowType,
|
|||
</button>
|
||||
</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 ? (
|
||||
<input
|
||||
autoFocus
|
||||
|
|
|
|||
|
|
@ -1,48 +1,83 @@
|
|||
import { useContext, useEffect } from "react";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { ReactFlowProvider } from "reactflow";
|
||||
import TabComponent from "../tabComponent";
|
||||
import { TabsContext } from "../../../../contexts/tabsContext";
|
||||
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() {
|
||||
const { flows, addFlow, tabIndex, setTabIndex } = useContext(TabsContext);
|
||||
useEffect(() => {
|
||||
//create the first flow
|
||||
if (flows.length === 0) {
|
||||
addFlow();
|
||||
}
|
||||
}, [addFlow, flows.length]);
|
||||
const { flows, addFlow, tabIndex, setTabIndex } = useContext(TabsContext);
|
||||
const { openPopUp } = useContext(PopUpContext);
|
||||
const AlertWidth = 256
|
||||
const { dark, setDark } = useContext(darkContext);
|
||||
const {notificationCenter, setNotificationCenter} = useContext(alertContext)
|
||||
useEffect(() => {
|
||||
//create the first flow
|
||||
if (flows.length === 0) {
|
||||
addFlow();
|
||||
}
|
||||
}, [addFlow, flows.length]);
|
||||
|
||||
return (
|
||||
<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">
|
||||
{flows.map((flow, index) => {
|
||||
return (
|
||||
<TabComponent
|
||||
onClick={() => setTabIndex(index)}
|
||||
selected={index === tabIndex}
|
||||
key={index}
|
||||
flow={flow}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<TabComponent
|
||||
onClick={() => {
|
||||
addFlow();
|
||||
}}
|
||||
selected={false}
|
||||
flow={null}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full h-full">
|
||||
<ReactFlowProvider>
|
||||
{flows[tabIndex] ? (
|
||||
<FlowPage flow={flows[tabIndex]}></FlowPage>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</ReactFlowProvider>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<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">
|
||||
{flows.map((flow, index) => {
|
||||
return (
|
||||
<TabComponent
|
||||
onClick={() => setTabIndex(index)}
|
||||
selected={index === tabIndex}
|
||||
key={index}
|
||||
flow={flow}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<TabComponent
|
||||
onClick={() => {
|
||||
addFlow();
|
||||
}}
|
||||
selected={false}
|
||||
flow={null}
|
||||
/>
|
||||
<div className="ml-auto mr-2 flex gap-3">
|
||||
<button
|
||||
className="text-gray-400 hover:text-gray-500 "
|
||||
onClick={() => {
|
||||
setDark(!dark);
|
||||
}}
|
||||
>
|
||||
{dark ? (
|
||||
<SunIcon className="h-5 w-5" />
|
||||
) : (
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,10 +14,6 @@ import { locationContext } from "../../contexts/locationContext";
|
|||
import ExtraSidebar from "./components/extraSidebarComponent";
|
||||
import Chat from "../../components/chatComponent";
|
||||
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 { TabsContext } from "../../contexts/tabsContext";
|
||||
import { typesContext } from "../../contexts/typesContext";
|
||||
|
|
@ -31,10 +27,6 @@ import { APIClassType } from "../../types/api";
|
|||
|
||||
const nodeTypes = {
|
||||
genericNode: GenericNode,
|
||||
inputNode: InputNode,
|
||||
chatInputNode: ChatInputNode,
|
||||
chatOutputNode: ChatOutputNode,
|
||||
booleanNode: BooleanNode,
|
||||
};
|
||||
|
||||
var _ = require("lodash");
|
||||
|
|
@ -68,13 +60,13 @@ export default function FlowPage({ flow }:{flow:FlowType}) {
|
|||
setNodes(flow?.data?.nodes ?? []);
|
||||
setEdges(flow?.data?.edges ?? []);
|
||||
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]);
|
||||
//set extra sidebar
|
||||
useEffect(() => {
|
||||
setExtraComponent(<ExtraSidebar />);
|
||||
setExtraNavigation({ title: "Componets" });
|
||||
setExtraNavigation({ title: "Components" });
|
||||
}, [setExtraComponent, setExtraNavigation]);
|
||||
|
||||
const onEdgesChangeMod = useCallback(
|
||||
|
|
@ -139,14 +131,7 @@ export default function FlowPage({ flow }:{flow:FlowType}) {
|
|||
// Create a new node object
|
||||
const newNode:NodeType = {
|
||||
id: newId,
|
||||
type:
|
||||
data.type === "str"
|
||||
? "inputNode"
|
||||
: data.type === "chatOutput"
|
||||
? "chatOutputNode"
|
||||
: data.type === "bool"
|
||||
? "booleanNode"
|
||||
: "genericNode",
|
||||
type: "genericNode",
|
||||
position,
|
||||
data: {
|
||||
...data,
|
||||
|
|
|
|||
|
|
@ -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 SuccessAlertType = {title:string,id:string, removeAlert:(id:string)=>void}
|
||||
export type SingleAlertComponentType = {dropItem:AlertItemType,removeAlert:(index:string)=>void}
|
||||
export type AlertDropdownType = {
|
||||
closeFunction: () => void;
|
||||
open?: boolean;
|
||||
};
|
||||
export type AlertDropdownType = {};
|
||||
export type AlertItemType = {
|
||||
type: "notice" | "error" | "success";
|
||||
title: string;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { ReactFlowInstance } from 'reactflow';
|
||||
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 }
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { ForwardRefExoticComponent, ReactElement, ReactNode } from 'react';
|
||||
import { ForwardRefExoticComponent, ReactElement, ReactNode } from "react";
|
||||
import { NodeDataType } from "../flow/index";
|
||||
export type InputComponentType = {
|
||||
value: string;
|
||||
|
|
@ -27,9 +27,17 @@ export type ParameterComponentType = {
|
|||
name?: 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 = {
|
||||
children: ReactNode;
|
||||
|
|
@ -39,6 +47,12 @@ export type DisclosureComponentType = {
|
|||
buttons?: {
|
||||
Icon: ReactElement;
|
||||
title: string;
|
||||
onClick:(event?:React.MouseEvent)=>void;
|
||||
onClick: (event?: React.MouseEvent) => void;
|
||||
}[];
|
||||
}};
|
||||
};
|
||||
};
|
||||
export type FloatComponentType = {
|
||||
value: string;
|
||||
disabled?: boolean;
|
||||
onChange: (value: string) => void;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { ChatMessageType } from './../chat/index';
|
||||
import { APIClassType } from '../api/index';
|
||||
import { ReactFlowJsonObject, XYPosition } from "reactflow";
|
||||
|
||||
|
|
@ -5,7 +6,7 @@ export type FlowType = {
|
|||
name: string;
|
||||
id: string;
|
||||
data: ReactFlowJsonObject;
|
||||
chat: Array<{ message: string; isSend: boolean }>;
|
||||
chat: Array<ChatMessageType>;
|
||||
};
|
||||
export type NodeType = {id:string,type:string,position:XYPosition,data:NodeDataType}
|
||||
export type NodeDataType = {type:string,node?:APIClassType,id:string,value:any}
|
||||
|
|
@ -68,7 +68,8 @@ export const nodeColors: {[char: string]: string} = {
|
|||
tools: "#FF3434",
|
||||
memories: "#FF9135",
|
||||
advanced: "#000000",
|
||||
chat: "#2563EB",
|
||||
chat: "#454173",
|
||||
thought:"#272541"
|
||||
};
|
||||
|
||||
export const nodeNames:{[char: string]: string} = {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,16 @@ module.exports = {
|
|||
'&::-webkit-scrollbar': {
|
||||
display: 'none'
|
||||
}
|
||||
},
|
||||
'.arrow-hide':{
|
||||
'&::-webkit-inner-spin-button':{
|
||||
'-webkit-appearance': 'none',
|
||||
'margin': 0
|
||||
},
|
||||
'&::-webkit-outer-spin-button':{
|
||||
'-webkit-appearance': 'none',
|
||||
'margin': 0
|
||||
},
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue