merging with authentication
This commit is contained in:
commit
bfe8053127
115 changed files with 9363 additions and 3126 deletions
7702
src/frontend/package-lock.json
generated
7702
src/frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -72,7 +72,8 @@
|
|||
"start": "vite",
|
||||
"build": "vite build",
|
||||
"serve": "vite preview",
|
||||
"format": "npx prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\""
|
||||
"format": "npx prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"",
|
||||
"type-check": "tsc --noEmit --pretty --project tsconfig.json && vite"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import ErrorAlert from "./alerts/error";
|
|||
import NoticeAlert from "./alerts/notice";
|
||||
import SuccessAlert from "./alerts/success";
|
||||
import CrashErrorComponent from "./components/CrashErrorComponent";
|
||||
import Header from "./components/headerComponent";
|
||||
import LoadingComponent from "./components/loadingComponent";
|
||||
import { alertContext } from "./contexts/alertContext";
|
||||
import { AuthContext } from "./contexts/authContext";
|
||||
import { locationContext } from "./contexts/locationContext";
|
||||
|
|
@ -27,6 +27,7 @@ export default function App() {
|
|||
setIsStackedOpen(true);
|
||||
}, [location.pathname, setCurrent, setIsStackedOpen, setShowSideBar]);
|
||||
const { hardReset } = useContext(TabsContext);
|
||||
|
||||
const {
|
||||
errorData,
|
||||
errorOpen,
|
||||
|
|
@ -38,6 +39,7 @@ export default function App() {
|
|||
successOpen,
|
||||
setSuccessOpen,
|
||||
setErrorData,
|
||||
loading,
|
||||
} = useContext(alertContext);
|
||||
const navigate = useNavigate();
|
||||
|
||||
|
|
@ -144,7 +146,7 @@ export default function App() {
|
|||
}
|
||||
}).catch((error) => {
|
||||
setAutoLogin(false);
|
||||
if (getAuthentication && !isLoginPage) {
|
||||
if (getAuthentication() && !isLoginPage) {
|
||||
getLoggedUser()
|
||||
.then((user) => {
|
||||
setUserData(user);
|
||||
|
|
@ -167,8 +169,15 @@ export default function App() {
|
|||
}}
|
||||
FallbackComponent={CrashErrorComponent}
|
||||
>
|
||||
{!isLoginPage && !isSignUpPage && <Header />}
|
||||
<Router />
|
||||
{loading ? (
|
||||
<div className="loading-page-panel">
|
||||
<LoadingComponent remSize={50} />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<Router />
|
||||
</>
|
||||
)}
|
||||
</ErrorBoundary>
|
||||
<div></div>
|
||||
<div className="app-div" style={{ zIndex: 999 }}>
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ export default function ParameterComponent({
|
|||
let groupedObj = groupByFamily(myData, tooltipTitle!, left, flow!);
|
||||
|
||||
if (groupedObj && groupedObj.length > 0) {
|
||||
//@ts-ignore
|
||||
//@ts-ignore
|
||||
refHtml.current = groupedObj.map((item, index) => {
|
||||
const Icon: any =
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { useSSE } from "../../contexts/SSEContext";
|
|||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import { typesContext } from "../../contexts/typesContext";
|
||||
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
|
||||
import { validationStatusType } from "../../types/components";
|
||||
import { NodeDataType } from "../../types/flow";
|
||||
import { cleanEdges } from "../../utils/reactflowUtils";
|
||||
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
|
||||
|
|
@ -20,13 +21,14 @@ export default function GenericNode({
|
|||
}: {
|
||||
data: NodeDataType;
|
||||
selected: boolean;
|
||||
}) {
|
||||
}): JSX.Element {
|
||||
const [data, setData] = useState(olddata);
|
||||
const { updateFlow, flows, tabId } = useContext(TabsContext);
|
||||
const updateNodeInternals = useUpdateNodeInternals();
|
||||
const { types, deleteNode, reactFlowInstance } = useContext(typesContext);
|
||||
const name = nodeIconsLucide[data.type] ? data.type : types[data.type];
|
||||
const [validationStatus, setValidationStatus] = useState(null);
|
||||
const [validationStatus, setValidationStatus] =
|
||||
useState<validationStatusType | null>(null);
|
||||
// State for outline color
|
||||
const { sseData, isBuilding } = useSSE();
|
||||
useEffect(() => {
|
||||
|
|
@ -41,7 +43,7 @@ export default function GenericNode({
|
|||
nodes: flow.data.nodes,
|
||||
},
|
||||
updateEdge: (edge) => {
|
||||
flow.data.edges = edge;
|
||||
flow.data!.edges = edge;
|
||||
reactFlowInstance.setEdges(edge);
|
||||
updateNodeInternals(data.id);
|
||||
},
|
||||
|
|
@ -77,7 +79,7 @@ export default function GenericNode({
|
|||
"generic-node-div"
|
||||
)}
|
||||
>
|
||||
{data.node.beta && (
|
||||
{data.node?.beta && (
|
||||
<div className="beta-badge-wrapper">
|
||||
<div className="beta-badge-content">BETA</div>
|
||||
</div>
|
||||
|
|
@ -90,9 +92,9 @@ export default function GenericNode({
|
|||
iconColor={`${nodeColors[types[data.type]]}`}
|
||||
/>
|
||||
<div className="generic-node-tooltip-div">
|
||||
<ShadTooltip content={data.node.display_name}>
|
||||
<ShadTooltip content={data.node?.display_name}>
|
||||
<div className="generic-node-tooltip-div text-primary">
|
||||
{data.node.display_name}
|
||||
{data.node?.display_name}
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
|
|
@ -117,7 +119,9 @@ export default function GenericNode({
|
|||
{typeof validationStatus.params === "string"
|
||||
? validationStatus.params
|
||||
.split("\n")
|
||||
.map((line, index) => <div key={index}>{line}</div>)
|
||||
.map((line: string, index: number) => (
|
||||
<div key={index}>{line}</div>
|
||||
))
|
||||
: ""}
|
||||
</div>
|
||||
)
|
||||
|
|
@ -155,20 +159,20 @@ export default function GenericNode({
|
|||
</div>
|
||||
|
||||
<div className="generic-node-desc">
|
||||
<div className="generic-node-desc-text">{data.node.description}</div>
|
||||
<div className="generic-node-desc-text">{data.node?.description}</div>
|
||||
|
||||
<>
|
||||
{Object.keys(data.node.template)
|
||||
{Object.keys(data.node!.template)
|
||||
.filter((templateField) => templateField.charAt(0) !== "_")
|
||||
.map((templateField: string, idx) => (
|
||||
<div key={idx}>
|
||||
{data.node.template[templateField].show &&
|
||||
!data.node.template[templateField].advanced ? (
|
||||
{data.node!.template[templateField].show &&
|
||||
!data.node!.template[templateField].advanced ? (
|
||||
<ParameterComponent
|
||||
key={
|
||||
(data.node.template[templateField].input_types?.join(
|
||||
(data.node!.template[templateField].input_types?.join(
|
||||
";"
|
||||
) ?? data.node.template[templateField].type) +
|
||||
) ?? data.node!.template[templateField].type) +
|
||||
"|" +
|
||||
templateField +
|
||||
"|" +
|
||||
|
|
@ -178,39 +182,39 @@ export default function GenericNode({
|
|||
setData={setData}
|
||||
color={
|
||||
nodeColors[
|
||||
types[data.node.template[templateField].type]
|
||||
types[data.node?.template[templateField].type!]
|
||||
] ??
|
||||
nodeColors[data.node.template[templateField].type] ??
|
||||
nodeColors[data.node?.template[templateField].type!] ??
|
||||
nodeColors.unknown
|
||||
}
|
||||
title={
|
||||
data.node.template[templateField].display_name
|
||||
data.node?.template[templateField].display_name
|
||||
? data.node.template[templateField].display_name
|
||||
: data.node.template[templateField].name
|
||||
: data.node?.template[templateField].name
|
||||
? toTitleCase(data.node.template[templateField].name)
|
||||
: toTitleCase(templateField)
|
||||
}
|
||||
info={data.node.template[templateField].info}
|
||||
info={data.node?.template[templateField].info}
|
||||
name={templateField}
|
||||
tooltipTitle={
|
||||
data.node.template[templateField].input_types?.join(
|
||||
data.node?.template[templateField].input_types?.join(
|
||||
"\n"
|
||||
) ?? data.node.template[templateField].type
|
||||
) ?? data.node?.template[templateField].type
|
||||
}
|
||||
required={data.node.template[templateField].required}
|
||||
required={data.node?.template[templateField].required}
|
||||
id={
|
||||
(data.node.template[templateField].input_types?.join(
|
||||
(data.node?.template[templateField].input_types?.join(
|
||||
";"
|
||||
) ?? data.node.template[templateField].type) +
|
||||
) ?? data.node?.template[templateField].type) +
|
||||
"|" +
|
||||
templateField +
|
||||
"|" +
|
||||
data.id
|
||||
}
|
||||
left={true}
|
||||
type={data.node.template[templateField].type}
|
||||
type={data.node?.template[templateField].type}
|
||||
optionalHandle={
|
||||
data.node.template[templateField].input_types
|
||||
data.node?.template[templateField].input_types
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
|
|
@ -220,25 +224,25 @@ export default function GenericNode({
|
|||
))}
|
||||
<div
|
||||
className={classNames(
|
||||
Object.keys(data.node.template).length < 1 ? "hidden" : "",
|
||||
Object.keys(data.node!.template).length < 1 ? "hidden" : "",
|
||||
"flex-max-width justify-center"
|
||||
)}
|
||||
>
|
||||
{" "}
|
||||
</div>
|
||||
<ParameterComponent
|
||||
key={[data.type, data.id, ...data.node.base_classes].join("|")}
|
||||
key={[data.type, data.id, ...data.node!.base_classes].join("|")}
|
||||
data={data}
|
||||
setData={setData}
|
||||
color={nodeColors[types[data.type]] ?? nodeColors.unknown}
|
||||
title={
|
||||
data.node.output_types && data.node.output_types.length > 0
|
||||
data.node?.output_types && data.node.output_types.length > 0
|
||||
? data.node.output_types.join("|")
|
||||
: data.type
|
||||
}
|
||||
tooltipTitle={data.node.base_classes.join("\n")}
|
||||
id={[data.type, data.id, ...data.node.base_classes].join("|")}
|
||||
type={data.node.base_classes.join("|")}
|
||||
tooltipTitle={data.node?.base_classes.join("\n")}
|
||||
id={[data.type, data.id, ...data.node!.base_classes].join("|")}
|
||||
type={data.node?.base_classes.join("|")}
|
||||
left={false}
|
||||
/>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { SingleAlertComponentType } from "../../../../types/alerts";
|
|||
export default function SingleAlert({
|
||||
dropItem,
|
||||
removeAlert,
|
||||
}: SingleAlertComponentType) {
|
||||
}: SingleAlertComponentType): JSX.Element {
|
||||
const [show, setShow] = useState(true);
|
||||
const type = dropItem.type;
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@ import { alertContext } from "../../contexts/alertContext";
|
|||
import { AlertDropdownType } from "../../types/alerts";
|
||||
import SingleAlert from "./components/singleAlertComponent";
|
||||
|
||||
export default function AlertDropdown({ children }: AlertDropdownType) {
|
||||
export default function AlertDropdown({
|
||||
children,
|
||||
}: AlertDropdownType): JSX.Element {
|
||||
const {
|
||||
notificationList,
|
||||
clearNotificationList,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ export default function ErrorAlert({
|
|||
list = [],
|
||||
id,
|
||||
removeAlert,
|
||||
}: ErrorAlertType) {
|
||||
}: ErrorAlertType): JSX.Element {
|
||||
const [show, setShow] = useState(true);
|
||||
useEffect(() => {
|
||||
if (show) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { useEffect } from "react";
|
|||
|
||||
export function useOnClickOutside(ref, handler) {
|
||||
useEffect(() => {
|
||||
const listener = (event) => {
|
||||
const listener = (event: Event) => {
|
||||
// Do nothing if clicking ref's element or its children
|
||||
if (!ref.current || ref.current.contains(event.target)) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ export default function NoticeAlert({
|
|||
link = "",
|
||||
id,
|
||||
removeAlert,
|
||||
}: NoticeAlertType) {
|
||||
}: NoticeAlertType): JSX.Element {
|
||||
const [show, setShow] = useState(true);
|
||||
useEffect(() => {
|
||||
if (show) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export default function SuccessAlert({
|
|||
title,
|
||||
id,
|
||||
removeAlert,
|
||||
}: SuccessAlertType) {
|
||||
}: SuccessAlertType): JSX.Element {
|
||||
const [show, setShow] = useState(true);
|
||||
useEffect(() => {
|
||||
if (show) {
|
||||
|
|
|
|||
|
|
@ -12,12 +12,12 @@ export default function AccordionComponent({
|
|||
children,
|
||||
open = [],
|
||||
keyValue,
|
||||
}: AccordionComponentType) {
|
||||
}: AccordionComponentType): JSX.Element {
|
||||
const [value, setValue] = useState(
|
||||
open.length === 0 ? "" : getOpenAccordion()
|
||||
);
|
||||
|
||||
function getOpenAccordion() {
|
||||
function getOpenAccordion(): string {
|
||||
let value = "";
|
||||
open.forEach((el) => {
|
||||
if (el == trigger) {
|
||||
|
|
@ -28,10 +28,17 @@ export default function AccordionComponent({
|
|||
return value;
|
||||
}
|
||||
|
||||
function handleClick() {
|
||||
value === "" ? setValue(keyValue) : setValue("");
|
||||
function handleClick(): void {
|
||||
value === "" ? setValue(keyValue!) : setValue("");
|
||||
}
|
||||
|
||||
const handleKeyDown = (event) => {
|
||||
if (event.key === "Backspace") {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Accordion
|
||||
|
|
@ -39,8 +46,9 @@ export default function AccordionComponent({
|
|||
className="w-full"
|
||||
value={value}
|
||||
onValueChange={setValue}
|
||||
onKeyDown={handleKeyDown}
|
||||
>
|
||||
<AccordionItem value={keyValue} className="border-b">
|
||||
<AccordionItem value={keyValue!} className="border-b">
|
||||
<AccordionTrigger
|
||||
onClick={() => {
|
||||
handleClick();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
export default function CrashErrorComponent({ error, resetErrorBoundary }) {
|
||||
import { crashComponentPropsType } from "../../types/components";
|
||||
|
||||
export default function CrashErrorComponent({
|
||||
error,
|
||||
resetErrorBoundary,
|
||||
}: crashComponentPropsType): JSX.Element {
|
||||
return (
|
||||
<div className="fixed left-0 top-0 z-50 flex h-full w-full items-center justify-center bg-foreground bg-opacity-50">
|
||||
<div className="flex h-1/3 min-h-fit max-w-4xl flex-col justify-evenly rounded-lg bg-background p-8 text-start shadow-lg">
|
||||
|
|
|
|||
|
|
@ -3,19 +3,8 @@ import { Input } from "../../components/ui/input";
|
|||
import { Label } from "../../components/ui/label";
|
||||
import { Textarea } from "../../components/ui/textarea";
|
||||
import { readFlowsFromDatabase } from "../../controllers/API";
|
||||
|
||||
type InputProps = {
|
||||
name: string | null;
|
||||
description: string | null;
|
||||
maxLength?: number;
|
||||
flows: Array<{ id: string; name: string; description: string }>;
|
||||
tabId: string;
|
||||
invalidName: boolean;
|
||||
setInvalidName: (invalidName: boolean) => void;
|
||||
setName: (name: string) => void;
|
||||
setDescription: (description: string) => void;
|
||||
updateFlow: (flow: { id: string; name: string }) => void;
|
||||
};
|
||||
import { InputProps } from "../../types/components";
|
||||
import { FlowType } from "../../types/flow";
|
||||
|
||||
export const EditFlowSettings: React.FC<InputProps> = ({
|
||||
name,
|
||||
|
|
@ -27,13 +16,12 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
|||
tabId,
|
||||
setName,
|
||||
setDescription,
|
||||
updateFlow,
|
||||
}) => {
|
||||
}: InputProps): JSX.Element => {
|
||||
const [isMaxLength, setIsMaxLength] = useState(false);
|
||||
const nameLists = useRef([]);
|
||||
const nameLists = useRef<string[]>([]);
|
||||
useEffect(() => {
|
||||
readFlowsFromDatabase().then((flows) => {
|
||||
flows.forEach((flow) => {
|
||||
flows.forEach((flow: FlowType) => {
|
||||
nameLists.current.push(flow.name);
|
||||
});
|
||||
});
|
||||
|
|
@ -46,21 +34,34 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
|||
} else {
|
||||
setIsMaxLength(false);
|
||||
}
|
||||
if (invalidName !== undefined) {
|
||||
if (!nameLists.current.includes(value)) {
|
||||
setInvalidName(false);
|
||||
} else {
|
||||
setInvalidName(true);
|
||||
}
|
||||
}
|
||||
if (!nameLists.current.includes(value)) {
|
||||
setInvalidName(false);
|
||||
setInvalidName!(false);
|
||||
} else {
|
||||
setInvalidName(true);
|
||||
setInvalidName!(true);
|
||||
}
|
||||
setName(value);
|
||||
setCurrentName(value);
|
||||
};
|
||||
|
||||
const [desc, setDesc] = useState(
|
||||
flows.find((flow) => flow.id === tabId).description
|
||||
);
|
||||
const [currentName, setCurrentName] = useState(name);
|
||||
|
||||
const [currentDescription, setCurrentDescription] = useState(description);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentName(name);
|
||||
setCurrentDescription(description);
|
||||
}, [name, description]);
|
||||
|
||||
const handleDescriptionChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
|
||||
flows.find((flow) => flow.id === tabId).description = event.target.value;
|
||||
setDesc(flows.find((flow) => flow.id === tabId).description);
|
||||
flows.find((f) => f.id === tabId).description = event.target.value;
|
||||
setCurrentDescription(flows.find((f) => f.id === tabId).description);
|
||||
setDescription(event.target.value);
|
||||
};
|
||||
|
||||
|
|
@ -81,7 +82,7 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
|||
onChange={handleNameChange}
|
||||
type="text"
|
||||
name="name"
|
||||
value={name ?? ""}
|
||||
value={currentName ?? ""}
|
||||
placeholder="File name"
|
||||
id="name"
|
||||
maxLength={maxLength}
|
||||
|
|
@ -96,7 +97,7 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
|||
name="description"
|
||||
id="description"
|
||||
onChange={handleDescriptionChange}
|
||||
value={desc}
|
||||
value={currentDescription}
|
||||
placeholder="Flow description"
|
||||
className="mt-2 max-h-[100px] font-normal"
|
||||
rows={3}
|
||||
|
|
|
|||
|
|
@ -3,16 +3,16 @@ import { RadialProgressType } from "../../types/components";
|
|||
export default function RadialProgressComponent({
|
||||
value,
|
||||
color,
|
||||
}: RadialProgressType) {
|
||||
}: RadialProgressType): JSX.Element {
|
||||
const style = {
|
||||
"--value": value * 100,
|
||||
"--value": value! * 100,
|
||||
"--size": "1.5rem",
|
||||
"--thickness": "2px",
|
||||
} as React.CSSProperties;
|
||||
|
||||
return (
|
||||
<div className={"radial-progress " + color} style={style}>
|
||||
<strong className="text-[8px]">{Math.trunc(value * 100)}%</strong>
|
||||
<strong className="text-[8px]">{Math.trunc(value! * 100)}%</strong>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,20 +3,9 @@ import type { FC } from "react";
|
|||
import React from "react";
|
||||
import { Tooltip as ReactTooltip } from "react-tooltip";
|
||||
import "react-tooltip/dist/react-tooltip.css";
|
||||
import { TooltipProps } from "../../types/components";
|
||||
import { classNames } from "../../utils/utils";
|
||||
|
||||
type TooltipProps = {
|
||||
selector: string;
|
||||
content?: string;
|
||||
disabled?: boolean;
|
||||
htmlContent?: React.ReactNode;
|
||||
className?: string; // This should use !impornant to override the default styles eg: '!bg-white'
|
||||
position?: "top" | "right" | "bottom" | "left";
|
||||
clickable?: boolean;
|
||||
children: React.ReactNode;
|
||||
delayShow?: number;
|
||||
};
|
||||
|
||||
const TooltipReact: FC<TooltipProps> = ({
|
||||
selector,
|
||||
content,
|
||||
|
|
@ -27,7 +16,7 @@ const TooltipReact: FC<TooltipProps> = ({
|
|||
className,
|
||||
clickable,
|
||||
delayShow,
|
||||
}) => {
|
||||
}: TooltipProps): JSX.Element => {
|
||||
return (
|
||||
<div className="tooltip-container">
|
||||
{React.cloneElement(children as React.ReactElement, {
|
||||
|
|
@ -38,7 +27,7 @@ const TooltipReact: FC<TooltipProps> = ({
|
|||
content={content}
|
||||
className={classNames(
|
||||
"z-[9999] !bg-white !text-xs !font-normal !text-foreground !opacity-100 !shadow-md",
|
||||
className
|
||||
className!
|
||||
)}
|
||||
place={position}
|
||||
clickable={clickable}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import DOMPurify from "dompurify";
|
||||
import { SanitizedHTMLWrapperType } from "../../types/components";
|
||||
|
||||
const SanitizedHTMLWrapper = ({
|
||||
className,
|
||||
content,
|
||||
onClick,
|
||||
suppressWarning = false,
|
||||
}) => {
|
||||
}: SanitizedHTMLWrapperType): JSX.Element => {
|
||||
const sanitizedHTML = DOMPurify.sanitize(content);
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ export default function ShadTooltip({
|
|||
children,
|
||||
styleClasses,
|
||||
delayDuration = 500,
|
||||
}: ShadToolTipType) {
|
||||
}: ShadToolTipType): JSX.Element {
|
||||
return (
|
||||
<Tooltip delayDuration={delayDuration}>
|
||||
<TooltipTrigger asChild={asChild}>{children}</TooltipTrigger>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ export default function Tooltip({
|
|||
children,
|
||||
title,
|
||||
placement,
|
||||
}: TooltipComponentType) {
|
||||
}: TooltipComponentType): JSX.Element {
|
||||
return (
|
||||
<LightTooltip placement={placement} title={title} arrow>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useContext } from "react";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import { FlowType } from "../../types/flow";
|
||||
import { cardComponentPropsType } from "../../types/components";
|
||||
import { gradients } from "../../utils/styleUtils";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
import {
|
||||
|
|
@ -16,12 +16,7 @@ export const CardComponent = ({
|
|||
id,
|
||||
onDelete,
|
||||
button,
|
||||
}: {
|
||||
flow: FlowType;
|
||||
id: string;
|
||||
onDelete?: () => void;
|
||||
button?: JSX.Element;
|
||||
}) => {
|
||||
}: cardComponentPropsType): JSX.Element => {
|
||||
const { removeFlow } = useContext(TabsContext);
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import { postBuildInit } from "../../../controllers/API";
|
|||
import { FlowType } from "../../../types/flow";
|
||||
|
||||
import { TabsContext } from "../../../contexts/tabsContext";
|
||||
import { parsedDataType } from "../../../types/components";
|
||||
import { TabsState } from "../../../types/tabs";
|
||||
import { validateNodes } from "../../../utils/reactflowUtils";
|
||||
import RadialProgressComponent from "../../RadialProgress";
|
||||
import IconComponent from "../../genericIconComponent";
|
||||
|
|
@ -21,7 +23,7 @@ export default function BuildTrigger({
|
|||
flow: FlowType;
|
||||
setIsBuilt: any;
|
||||
isBuilt: boolean;
|
||||
}) {
|
||||
}): JSX.Element {
|
||||
const { updateSSEData, isBuilding, setIsBuilding, sseData } = useSSE();
|
||||
const { reactFlowInstance } = useContext(typesContext);
|
||||
const { setTabsState } = useContext(TabsContext);
|
||||
|
|
@ -30,12 +32,12 @@ export default function BuildTrigger({
|
|||
const eventClick = isBuilding ? "pointer-events-none" : "";
|
||||
const [progress, setProgress] = useState(0);
|
||||
|
||||
async function handleBuild(flow: FlowType) {
|
||||
async function handleBuild(flow: FlowType): Promise<void> {
|
||||
try {
|
||||
if (isBuilding) {
|
||||
return;
|
||||
}
|
||||
const errors = validateNodes(reactFlowInstance);
|
||||
const errors = validateNodes(reactFlowInstance!);
|
||||
if (errors.length > 0) {
|
||||
setErrorData({
|
||||
title: "Oops! Looks like you missed something",
|
||||
|
|
@ -69,7 +71,7 @@ export default function BuildTrigger({
|
|||
const response = await postBuildInit(flow);
|
||||
const { flowId } = response.data;
|
||||
// Step 2: Use the session ID to establish an SSE connection using EventSource
|
||||
let validationResults = [];
|
||||
let validationResults: boolean[] = [];
|
||||
let finished = false;
|
||||
const apiUrl = `/api/v1/build/stream/${flowId}`;
|
||||
const eventSource = new EventSource(apiUrl);
|
||||
|
|
@ -91,7 +93,8 @@ export default function BuildTrigger({
|
|||
// If the event is a log, log it
|
||||
setSuccessData({ title: parsedData.log });
|
||||
} else if (parsedData.input_keys !== undefined) {
|
||||
setTabsState((old) => {
|
||||
//@ts-ignore
|
||||
setTabsState((old: TabsState) => {
|
||||
return {
|
||||
...old,
|
||||
[flowId]: {
|
||||
|
|
@ -120,13 +123,13 @@ export default function BuildTrigger({
|
|||
// Step 3: Wait for the stream to finish
|
||||
while (!finished) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
finished = validationResults.length === flow.data.nodes.length;
|
||||
finished = validationResults.length === flow.data!.nodes.length;
|
||||
}
|
||||
// Step 4: Return true if all nodes are valid, false otherwise
|
||||
return validationResults.every((result) => result);
|
||||
}
|
||||
|
||||
function processStreamResult(parsedData) {
|
||||
function processStreamResult(parsedData: parsedDataType) {
|
||||
// Process each chunk of data here
|
||||
// Parse the chunk and update the context
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -8,12 +8,18 @@ import {
|
|||
FLOW_NOT_BUILT_TITLE,
|
||||
} from "../../../constants/constants";
|
||||
import { alertContext } from "../../../contexts/alertContext";
|
||||
import { chatTriggerPropType } from "../../../types/components";
|
||||
import IconComponent from "../../genericIconComponent";
|
||||
|
||||
export default function ChatTrigger({ open, setOpen, isBuilt, canOpen }) {
|
||||
export default function ChatTrigger({
|
||||
open,
|
||||
setOpen,
|
||||
isBuilt,
|
||||
canOpen,
|
||||
}: chatTriggerPropType): JSX.Element {
|
||||
const { setErrorData } = useContext(alertContext);
|
||||
|
||||
function handleClick() {
|
||||
function handleClick(): void {
|
||||
if (isBuilt) {
|
||||
if (canOpen) {
|
||||
setOpen(true);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { getBuildStatus } from "../../controllers/API";
|
|||
import FormModal from "../../modals/formModal";
|
||||
import { NodeType } from "../../types/flow";
|
||||
|
||||
export default function Chat({ flow }: ChatType) {
|
||||
export default function Chat({ flow }: ChatType): JSX.Element {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [canOpen, setCanOpen] = useState(false);
|
||||
const { tabsState, isBuilt, setIsBuilt } = useContext(TabsContext);
|
||||
|
|
@ -44,11 +44,11 @@ export default function Chat({ flow }: ChatType) {
|
|||
}, [flow]);
|
||||
|
||||
const prevNodesRef = useRef<any[] | undefined>();
|
||||
const nodes = useNodes();
|
||||
const nodes: NodeType[] = useNodes();
|
||||
useEffect(() => {
|
||||
const prevNodes = prevNodesRef.current;
|
||||
const currentNodes = nodes.map((node: NodeType) =>
|
||||
_.cloneDeep(node.data.node.template)
|
||||
_.cloneDeep(node.data.node?.template)
|
||||
);
|
||||
if (
|
||||
tabsState &&
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export default function CodeAreaComponent({
|
|||
dynamic={dynamic}
|
||||
value={myValue}
|
||||
nodeClass={nodeClass}
|
||||
setNodeClass={setNodeClass}
|
||||
setNodeClass={setNodeClass!}
|
||||
setValue={(value: string) => {
|
||||
setMyValue(value);
|
||||
onChange(value);
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import {
|
|||
TabsTrigger,
|
||||
} from "../../components/ui/tabs";
|
||||
import { darkContext } from "../../contexts/darkContext";
|
||||
import { FlowType } from "../../types/flow/index";
|
||||
import { codeTabsPropsType } from "../../types/components";
|
||||
import { classNames } from "../../utils/utils";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
|
||||
|
|
@ -40,28 +40,15 @@ export default function CodeTabsComponent({
|
|||
setActiveTab,
|
||||
isMessage,
|
||||
tweaks,
|
||||
}: {
|
||||
flow?: FlowType;
|
||||
tabs: any;
|
||||
activeTab: string;
|
||||
setActiveTab: any;
|
||||
isMessage?: boolean;
|
||||
tweaks?: {
|
||||
tweak?: any;
|
||||
tweaksList?: any;
|
||||
buildContent?: any;
|
||||
getValue?: any;
|
||||
buildTweakObject?: any;
|
||||
};
|
||||
}) {
|
||||
}: codeTabsPropsType) {
|
||||
const [isCopied, setIsCopied] = useState<Boolean>(false);
|
||||
const [data, setData] = useState(flow ? flow["data"]["nodes"] : null);
|
||||
const [openAccordion, setOpenAccordion] = useState([]);
|
||||
const [data, setData] = useState(flow ? flow["data"]!["nodes"] : null);
|
||||
const [openAccordion, setOpenAccordion] = useState<string[]>([]);
|
||||
const { dark } = useContext(darkContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (flow && flow["data"]["nodes"]) {
|
||||
setData(flow["data"]["nodes"]);
|
||||
if (flow && flow["data"]!["nodes"]) {
|
||||
setData(flow["data"]!["nodes"]);
|
||||
}
|
||||
}, [flow]);
|
||||
|
||||
|
|
@ -102,8 +89,8 @@ export default function CodeTabsComponent({
|
|||
};
|
||||
|
||||
function openAccordions() {
|
||||
let accordionsToOpen = [];
|
||||
tweaks.tweak.current.forEach((el) => {
|
||||
let accordionsToOpen: string[] = [];
|
||||
tweaks?.tweak!.current.forEach((el) => {
|
||||
Object.keys(el).forEach((key) => {
|
||||
if (Object.keys(el[key]).length > 0) {
|
||||
accordionsToOpen.push(key);
|
||||
|
|
@ -205,9 +192,9 @@ export default function CodeTabsComponent({
|
|||
: "overflow-hidden"
|
||||
)}
|
||||
>
|
||||
{data.map((node: any, index) => (
|
||||
{data?.map((node: any, index) => (
|
||||
<div className="px-3" key={index}>
|
||||
{tweaks.tweaksList.current.includes(
|
||||
{tweaks?.tweaksList!.current.includes(
|
||||
node["data"]["id"]
|
||||
) && (
|
||||
<AccordionComponent
|
||||
|
|
@ -290,14 +277,14 @@ export default function CodeTabsComponent({
|
|||
setData((old) => {
|
||||
let newInputList =
|
||||
cloneDeep(old);
|
||||
newInputList[
|
||||
newInputList![
|
||||
index
|
||||
].data.node.template[
|
||||
templateField
|
||||
].value = target;
|
||||
return newInputList;
|
||||
});
|
||||
tweaks.buildTweakObject(
|
||||
tweaks.buildTweakObject!(
|
||||
node["data"]["id"],
|
||||
target,
|
||||
node.data.node.template[
|
||||
|
|
@ -310,7 +297,7 @@ export default function CodeTabsComponent({
|
|||
templateField
|
||||
].multiline ? (
|
||||
<ShadTooltip
|
||||
content={tweaks.buildContent(
|
||||
content={tweaks.buildContent!(
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].value
|
||||
|
|
@ -339,14 +326,14 @@ export default function CodeTabsComponent({
|
|||
setData((old) => {
|
||||
let newInputList =
|
||||
cloneDeep(old);
|
||||
newInputList[
|
||||
newInputList![
|
||||
index
|
||||
].data.node.template[
|
||||
templateField
|
||||
].value = target;
|
||||
return newInputList;
|
||||
});
|
||||
tweaks.buildTweakObject(
|
||||
tweaks.buildTweakObject!(
|
||||
node["data"]["id"],
|
||||
target,
|
||||
node.data.node
|
||||
|
|
@ -384,14 +371,14 @@ export default function CodeTabsComponent({
|
|||
setData((old) => {
|
||||
let newInputList =
|
||||
cloneDeep(old);
|
||||
newInputList[
|
||||
newInputList![
|
||||
index
|
||||
].data.node.template[
|
||||
templateField
|
||||
].value = target;
|
||||
return newInputList;
|
||||
});
|
||||
tweaks.buildTweakObject(
|
||||
tweaks.buildTweakObject!(
|
||||
node["data"]["id"],
|
||||
target,
|
||||
node.data.node.template[
|
||||
|
|
@ -417,14 +404,14 @@ export default function CodeTabsComponent({
|
|||
setData((old) => {
|
||||
let newInputList =
|
||||
cloneDeep(old);
|
||||
newInputList[
|
||||
newInputList![
|
||||
index
|
||||
].data.node.template[
|
||||
templateField
|
||||
].value = e;
|
||||
return newInputList;
|
||||
});
|
||||
tweaks.buildTweakObject(
|
||||
tweaks.buildTweakObject!(
|
||||
node["data"]["id"],
|
||||
e,
|
||||
node.data.node.template[
|
||||
|
|
@ -440,7 +427,7 @@ export default function CodeTabsComponent({
|
|||
templateField
|
||||
].type === "file" ? (
|
||||
<ShadTooltip
|
||||
content={tweaks.buildContent(
|
||||
content={tweaks.buildContent!(
|
||||
!node.data.node.template[
|
||||
templateField
|
||||
].value ||
|
||||
|
|
@ -508,14 +495,14 @@ export default function CodeTabsComponent({
|
|||
setData((old) => {
|
||||
let newInputList =
|
||||
cloneDeep(old);
|
||||
newInputList[
|
||||
newInputList![
|
||||
index
|
||||
].data.node.template[
|
||||
templateField
|
||||
].value = target;
|
||||
return newInputList;
|
||||
});
|
||||
tweaks.buildTweakObject(
|
||||
tweaks.buildTweakObject!(
|
||||
node["data"]["id"],
|
||||
target,
|
||||
node.data.node.template[
|
||||
|
|
@ -544,14 +531,14 @@ export default function CodeTabsComponent({
|
|||
setData((old) => {
|
||||
let newInputList =
|
||||
cloneDeep(old);
|
||||
newInputList[
|
||||
newInputList![
|
||||
index
|
||||
].data.node.template[
|
||||
templateField
|
||||
].value = target;
|
||||
return newInputList;
|
||||
});
|
||||
tweaks.buildTweakObject(
|
||||
tweaks.buildTweakObject!(
|
||||
node["data"]["id"],
|
||||
target,
|
||||
node.data.node.template[
|
||||
|
|
@ -596,14 +583,14 @@ export default function CodeTabsComponent({
|
|||
setData((old) => {
|
||||
let newInputList =
|
||||
cloneDeep(old);
|
||||
newInputList[
|
||||
newInputList![
|
||||
index
|
||||
].data.node.template[
|
||||
templateField
|
||||
].value = target;
|
||||
return newInputList;
|
||||
});
|
||||
tweaks.buildTweakObject(
|
||||
tweaks.buildTweakObject!(
|
||||
node["data"]["id"],
|
||||
target,
|
||||
node.data.node.template[
|
||||
|
|
@ -617,7 +604,7 @@ export default function CodeTabsComponent({
|
|||
templateField
|
||||
].type === "prompt" ? (
|
||||
<ShadTooltip
|
||||
content={tweaks.buildContent(
|
||||
content={tweaks.buildContent!(
|
||||
!node.data.node.template[
|
||||
templateField
|
||||
].value ||
|
||||
|
|
@ -651,14 +638,14 @@ export default function CodeTabsComponent({
|
|||
setData((old) => {
|
||||
let newInputList =
|
||||
cloneDeep(old);
|
||||
newInputList[
|
||||
newInputList![
|
||||
index
|
||||
].data.node.template[
|
||||
templateField
|
||||
].value = target;
|
||||
return newInputList;
|
||||
});
|
||||
tweaks.buildTweakObject(
|
||||
tweaks.buildTweakObject!(
|
||||
node["data"]["id"],
|
||||
target,
|
||||
node.data.node.template[
|
||||
|
|
@ -673,8 +660,8 @@ export default function CodeTabsComponent({
|
|||
templateField
|
||||
].type === "code" ? (
|
||||
<ShadTooltip
|
||||
content={tweaks.buildContent(
|
||||
tweaks.getValue(
|
||||
content={tweaks.buildContent!(
|
||||
tweaks.getValue!(
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].value,
|
||||
|
|
@ -706,14 +693,14 @@ export default function CodeTabsComponent({
|
|||
setData((old) => {
|
||||
let newInputList =
|
||||
cloneDeep(old);
|
||||
newInputList[
|
||||
newInputList![
|
||||
index
|
||||
].data.node.template[
|
||||
templateField
|
||||
].value = target;
|
||||
return newInputList;
|
||||
});
|
||||
tweaks.buildTweakObject(
|
||||
tweaks.buildTweakObject!(
|
||||
node["data"]["id"],
|
||||
target,
|
||||
node.data.node.template[
|
||||
|
|
@ -742,7 +729,7 @@ export default function CodeTabsComponent({
|
|||
</AccordionComponent>
|
||||
)}
|
||||
|
||||
{tweaks.tweaksList.current.length === 0 && (
|
||||
{tweaks?.tweaksList!.current.length === 0 && (
|
||||
<>
|
||||
<div className="pt-3">
|
||||
No tweaks are available for this flow.
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export default function Dropdown({
|
|||
editNode = false,
|
||||
numberOfOptions = 0,
|
||||
apiModal = false,
|
||||
}: DropDownComponentType) {
|
||||
}: DropDownComponentType): JSX.Element {
|
||||
let [internalValue, setInternalValue] = useState(
|
||||
value === "" || !value ? "Choose an option" : value
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useEffect } from "react";
|
||||
import { FloatComponentType } from "../../types/components";
|
||||
import { handleKeyDown } from "../../utils/reactflowUtils";
|
||||
import { Input } from "../ui/input";
|
||||
|
||||
export default function FloatComponent({
|
||||
|
|
@ -7,7 +8,7 @@ export default function FloatComponent({
|
|||
onChange,
|
||||
disabled,
|
||||
editNode = false,
|
||||
}: FloatComponentType) {
|
||||
}: FloatComponentType): JSX.Element {
|
||||
const step = 0.1;
|
||||
const min = 0;
|
||||
const max = 1;
|
||||
|
|
@ -43,6 +44,9 @@ export default function FloatComponent({
|
|||
onChange={(event) => {
|
||||
onChange(event.target.value);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
handleKeyDown(e, value, "0");
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -12,10 +12,11 @@ import { Link, useNavigate } from "react-router-dom";
|
|||
import { alertContext } from "../../../../contexts/alertContext";
|
||||
import { undoRedoContext } from "../../../../contexts/undoRedoContext";
|
||||
import FlowSettingsModal from "../../../../modals/flowSettingsModal";
|
||||
import { menuBarPropsType } from "../../../../types/components";
|
||||
import IconComponent from "../../../genericIconComponent";
|
||||
import { Button } from "../../../ui/button";
|
||||
|
||||
export const MenuBar = ({ flows, tabId }) => {
|
||||
export const MenuBar = ({ flows, tabId }: menuBarPropsType): JSX.Element => {
|
||||
const { addFlow } = useContext(TabsContext);
|
||||
const { setErrorData } = useContext(alertContext);
|
||||
const { undo, redo } = useContext(undoRedoContext);
|
||||
|
|
@ -25,12 +26,12 @@ export const MenuBar = ({ flows, tabId }) => {
|
|||
|
||||
function handleAddFlow() {
|
||||
try {
|
||||
addFlow(null, true).then((id) => {
|
||||
addFlow(undefined, true).then((id) => {
|
||||
navigate("/flow/" + id);
|
||||
});
|
||||
// saveFlowStyleInDataBase();
|
||||
} catch (err) {
|
||||
setErrorData(err);
|
||||
setErrorData(err as { title: string; list?: Array<string> });
|
||||
}
|
||||
}
|
||||
let current_flow = flows.find((flow) => flow.id === tabId);
|
||||
|
|
@ -45,7 +46,9 @@ export const MenuBar = ({ flows, tabId }) => {
|
|||
<DropdownMenuTrigger asChild>
|
||||
<Button asChild variant="primary" size="sm">
|
||||
<div className="header-menu-bar-display">
|
||||
<div className="header-menu-flow-name">{current_flow.name}</div>
|
||||
<div className="header-menu-flow-name">
|
||||
{current_flow!.name}
|
||||
</div>
|
||||
<IconComponent name="ChevronDown" className="h-4 w-4" />
|
||||
</div>
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import { Button } from "../ui/button";
|
|||
import { Separator } from "../ui/separator";
|
||||
import MenuBar from "./components/menuBar";
|
||||
|
||||
export default function Header() {
|
||||
export default function Header(): JSX.Element {
|
||||
const { flows, tabId } = useContext(TabsContext);
|
||||
const { dark, setDark } = useContext(darkContext);
|
||||
const { notificationCenter } = useContext(alertContext);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import * as Form from "@radix-ui/react-form";
|
||||
import { useEffect, useState } from "react";
|
||||
import { InputComponentType } from "../../types/components";
|
||||
import { handleKeyDown } from "../../utils/reactflowUtils";
|
||||
import { classNames } from "../../utils/utils";
|
||||
import { Input } from "../ui/input";
|
||||
|
||||
|
|
@ -14,7 +15,7 @@ export default function InputComponent({
|
|||
editNode = false,
|
||||
placeholder = "Type something...",
|
||||
className,
|
||||
}: InputComponentType) {
|
||||
}: InputComponentType): JSX.Element {
|
||||
const [pwdVisible, setPwdVisible] = useState(false);
|
||||
|
||||
// Clear component state
|
||||
|
|
@ -39,12 +40,15 @@ export default function InputComponent({
|
|||
editNode ? " input-edit-node " : "",
|
||||
password && editNode ? "pr-8" : "",
|
||||
password && !editNode ? "pr-10" : "",
|
||||
className
|
||||
className!
|
||||
)}
|
||||
placeholder={password && editNode ? "Key" : placeholder}
|
||||
onChange={(e) => {
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
handleKeyDown(e, value, "");
|
||||
}}
|
||||
/>
|
||||
</Form.Control>
|
||||
) : (
|
||||
|
|
@ -59,12 +63,15 @@ export default function InputComponent({
|
|||
editNode ? " input-edit-node " : "",
|
||||
password && editNode ? "pr-8" : "",
|
||||
password && !editNode ? "pr-10" : "",
|
||||
className
|
||||
className!
|
||||
)}
|
||||
placeholder={password && editNode ? "Key" : placeholder}
|
||||
onChange={(e) => {
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
handleKeyDown(e, value, "");
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{password && (
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export default function InputFileComponent({
|
|||
fileTypes,
|
||||
onFileChange,
|
||||
editNode = false,
|
||||
}: FileComponentType) {
|
||||
}: FileComponentType): JSX.Element {
|
||||
const [myValue, setMyValue] = useState(value);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { setErrorData } = useContext(alertContext);
|
||||
|
|
@ -41,7 +41,7 @@ export default function InputFileComponent({
|
|||
setMyValue(value);
|
||||
}, [value]);
|
||||
|
||||
const handleButtonClick = () => {
|
||||
const handleButtonClick = (): void => {
|
||||
// Create a file input element
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
|
|
@ -49,7 +49,7 @@ export default function InputFileComponent({
|
|||
input.style.display = "none"; // Hidden from view
|
||||
input.multiple = false; // Allow only one file selection
|
||||
|
||||
input.onchange = (event: Event) => {
|
||||
input.onchange = (event: Event): void => {
|
||||
setLoading(true);
|
||||
|
||||
// Get the selected file
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export default function InputListComponent({
|
|||
onChange,
|
||||
disabled,
|
||||
editNode = false,
|
||||
}: InputListComponentType) {
|
||||
}: InputListComponentType): JSX.Element {
|
||||
useEffect(() => {
|
||||
if (disabled) {
|
||||
onChange([""]);
|
||||
|
|
@ -39,6 +39,12 @@ export default function InputListComponent({
|
|||
newInputList[idx] = event.target.value;
|
||||
onChange(newInputList);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.ctrlKey && e.key === "Backspace") {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{idx === value.length - 1 ? (
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useEffect } from "react";
|
||||
import { FloatComponentType } from "../../types/components";
|
||||
import { handleKeyDown } from "../../utils/reactflowUtils";
|
||||
import { Input } from "../ui/input";
|
||||
|
||||
export default function IntComponent({
|
||||
|
|
@ -7,7 +8,7 @@ export default function IntComponent({
|
|||
onChange,
|
||||
disabled,
|
||||
editNode = false,
|
||||
}: FloatComponentType) {
|
||||
}: FloatComponentType): JSX.Element {
|
||||
const min = 0;
|
||||
|
||||
// Clear component state
|
||||
|
|
@ -37,6 +38,7 @@ export default function IntComponent({
|
|||
) {
|
||||
event.preventDefault();
|
||||
}
|
||||
handleKeyDown(event, value, "0");
|
||||
}}
|
||||
type="number"
|
||||
step="1"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
type LoadingComponentProps = {
|
||||
remSize: number;
|
||||
};
|
||||
import { LoadingComponentProps } from "../../types/components";
|
||||
|
||||
export default function LoadingComponent({ remSize }: LoadingComponentProps) {
|
||||
export default function LoadingComponent({
|
||||
remSize,
|
||||
}: LoadingComponentProps): JSX.Element {
|
||||
return (
|
||||
<div role="status" className="m-auto w-min">
|
||||
<svg
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export default function PromptAreaComponent({
|
|||
onChange,
|
||||
disabled,
|
||||
editNode = false,
|
||||
}: TextAreaComponentType) {
|
||||
}: TextAreaComponentType): JSX.Element {
|
||||
useEffect(() => {
|
||||
if (disabled) {
|
||||
onChange("");
|
||||
|
|
@ -23,9 +23,9 @@ export default function PromptAreaComponent({
|
|||
|
||||
useEffect(() => {
|
||||
if (value !== "" && !editNode) {
|
||||
postValidatePrompt(field_name, value, nodeClass).then((apiReturn) => {
|
||||
postValidatePrompt(field_name!, value, nodeClass!).then((apiReturn) => {
|
||||
if (apiReturn.data) {
|
||||
setNodeClass(apiReturn.data.frontend_node);
|
||||
setNodeClass!(apiReturn.data.frontend_node);
|
||||
// need to update reactFlowInstance to re-render the nodes.
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export default function TextAreaComponent({
|
|||
onChange,
|
||||
disabled,
|
||||
editNode = false,
|
||||
}: TextAreaComponentType) {
|
||||
}: TextAreaComponentType): JSX.Element {
|
||||
// Clear text area
|
||||
useEffect(() => {
|
||||
if (disabled) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export default function ToggleComponent({
|
|||
enabled,
|
||||
setEnabled,
|
||||
disabled,
|
||||
}: ToggleComponentType) {
|
||||
}: ToggleComponentType): JSX.Element {
|
||||
// set component state as disabled
|
||||
useEffect(() => {
|
||||
if (disabled) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ export default function ToggleShadComponent({
|
|||
setEnabled,
|
||||
disabled,
|
||||
size,
|
||||
}: ToggleComponentType) {
|
||||
}: ToggleComponentType): JSX.Element {
|
||||
let scaleX, scaleY;
|
||||
switch (size) {
|
||||
case "small":
|
||||
|
|
|
|||
|
|
@ -23,14 +23,14 @@ export default function RenameLabel(props) {
|
|||
});
|
||||
if (inputRef.current) {
|
||||
setTimeout(() => {
|
||||
inputRef.current.focus();
|
||||
inputRef.current?.focus();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
resizeInput();
|
||||
}, [isRename]);
|
||||
|
||||
const inputRef = useRef(null);
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
const resizeInput = () => {
|
||||
const input = inputRef.current;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ export const INVALID_CHARACTERS = [
|
|||
*/
|
||||
|
||||
export const regexHighlight = /\{([^}]+)\}/g;
|
||||
export const specialCharsRegex = /[!@#$%^&*()\-_=+[\]{}|;:'",.<>/?\\`´]/;
|
||||
|
||||
export const programmingLanguages: languageMap = {
|
||||
javascript: ".js",
|
||||
|
|
@ -510,6 +511,8 @@ export const URL_EXCLUDED_FROM_ERROR_RETRIES = [
|
|||
"http://localhost:7860/login",
|
||||
];
|
||||
|
||||
export const skipNodeUpdate = ["CustomComponent"];
|
||||
|
||||
export const CONTROL_INPUT_STATE = {
|
||||
password: "",
|
||||
cnfPassword: "",
|
||||
|
|
|
|||
|
|
@ -1,34 +1,15 @@
|
|||
import { createContext, ReactNode, useState } from "react";
|
||||
import { AlertItemType } from "../types/alerts";
|
||||
import { alertContextType } from "../types/typesContext";
|
||||
|
||||
import _ from "lodash";
|
||||
|
||||
//types for alertContextType
|
||||
type alertContextType = {
|
||||
errorData: { title: string; list?: Array<string> };
|
||||
setErrorData: (newState: { title: string; list?: Array<string> }) => void;
|
||||
errorOpen: boolean;
|
||||
setErrorOpen: (newState: boolean) => void;
|
||||
noticeData: { title: string; link?: string };
|
||||
setNoticeData: (newState: { title: string; link?: string }) => void;
|
||||
noticeOpen: boolean;
|
||||
setNoticeOpen: (newState: boolean) => void;
|
||||
successData: { title: string };
|
||||
setSuccessData: (newState: { title: string }) => void;
|
||||
successOpen: boolean;
|
||||
setSuccessOpen: (newState: boolean) => void;
|
||||
notificationCenter: boolean;
|
||||
setNotificationCenter: (newState: boolean) => void;
|
||||
notificationList: Array<AlertItemType>;
|
||||
pushNotificationList: (Object: AlertItemType) => void;
|
||||
clearNotificationList: () => void;
|
||||
removeFromNotificationList: (index: string) => void;
|
||||
};
|
||||
|
||||
//initial values to alertContextType
|
||||
const initialValue: alertContextType = {
|
||||
errorData: { title: "", list: [] },
|
||||
setErrorData: () => {},
|
||||
loading: true,
|
||||
setLoading: () => {},
|
||||
errorOpen: false,
|
||||
setErrorOpen: () => {},
|
||||
noticeData: { title: "", link: "" },
|
||||
|
|
@ -55,6 +36,7 @@ export function AlertProvider({ children }: { children: ReactNode }) {
|
|||
list?: Array<string>;
|
||||
}>({ title: "", list: [] });
|
||||
const [errorOpen, setErrorOpen] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [noticeData, setNoticeDataState] = useState<{
|
||||
title: string;
|
||||
link?: string;
|
||||
|
|
@ -65,7 +47,7 @@ export function AlertProvider({ children }: { children: ReactNode }) {
|
|||
});
|
||||
const [successOpen, setSuccessOpen] = useState(false);
|
||||
const [notificationCenter, setNotificationCenter] = useState(false);
|
||||
const [notificationList, setNotificationList] = useState([]);
|
||||
const [notificationList, setNotificationList] = useState<AlertItemType[]>([]);
|
||||
const pushNotificationList = (notification: AlertItemType) => {
|
||||
setNotificationList((old) => {
|
||||
let newNotificationList = _.cloneDeep(old);
|
||||
|
|
@ -141,6 +123,8 @@ export function AlertProvider({ children }: { children: ReactNode }) {
|
|||
removeFromNotificationList,
|
||||
clearNotificationList,
|
||||
notificationList,
|
||||
loading,
|
||||
setLoading,
|
||||
pushNotificationList,
|
||||
setNotificationCenter,
|
||||
notificationCenter,
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export function AuthProvider({ children }): React.ReactElement {
|
|||
const [refreshToken, setRefreshToken] = useState<string | null>(null);
|
||||
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
|
||||
const [isAdmin, setIsAdmin] = useState<boolean>(false);
|
||||
const [userData, setUserData] = useState<Users>(null);
|
||||
const [userData, setUserData] = useState<Users | null>(null);
|
||||
const [autoLogin, setAutoLogin] = useState<boolean>(false);
|
||||
const cookies = new Cookies();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
import { createContext, useEffect, useState } from "react";
|
||||
|
||||
type darkContextType = {
|
||||
dark: {};
|
||||
setDark: (newState: {}) => void;
|
||||
};
|
||||
import { darkContextType } from "../types/typesContext";
|
||||
|
||||
const initialValue = {
|
||||
dark: {},
|
||||
|
|
@ -14,13 +10,13 @@ export const darkContext = createContext<darkContextType>(initialValue);
|
|||
|
||||
export function DarkProvider({ children }) {
|
||||
const [dark, setDark] = useState(
|
||||
JSON.parse(window.localStorage.getItem("isDark")) ?? false
|
||||
JSON.parse(window.localStorage.getItem("isDark")!) ?? false
|
||||
);
|
||||
useEffect(() => {
|
||||
if (dark) {
|
||||
document.getElementById("body").classList.add("dark");
|
||||
document.getElementById("body")!.classList.add("dark");
|
||||
} else {
|
||||
document.getElementById("body").classList.remove("dark");
|
||||
document.getElementById("body")!.classList.remove("dark");
|
||||
}
|
||||
window.localStorage.setItem("isDark", dark.toString());
|
||||
}, [dark]);
|
||||
|
|
|
|||
|
|
@ -1,34 +1,5 @@
|
|||
import { createContext, ReactNode, useState } from "react";
|
||||
|
||||
//types for location context
|
||||
type locationContextType = {
|
||||
current: Array<string>;
|
||||
setCurrent: (newState: Array<string>) => void;
|
||||
isStackedOpen: boolean;
|
||||
setIsStackedOpen: (newState: boolean) => void;
|
||||
showSideBar: boolean;
|
||||
setShowSideBar: (newState: boolean) => void;
|
||||
extraNavigation: {
|
||||
title: string;
|
||||
options?: Array<{
|
||||
name: string;
|
||||
href: string;
|
||||
icon: any;
|
||||
children?: Array<any>;
|
||||
}>;
|
||||
};
|
||||
setExtraNavigation: (newState: {
|
||||
title: string;
|
||||
options?: Array<{
|
||||
name: string;
|
||||
href: string;
|
||||
icon: any;
|
||||
children?: Array<any>;
|
||||
}>;
|
||||
}) => void;
|
||||
extraComponent: any;
|
||||
setExtraComponent: (newState: any) => void;
|
||||
};
|
||||
import { locationContextType } from "../types/typesContext";
|
||||
|
||||
//initial value for location context
|
||||
const initialValue = {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@ import {
|
|||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { addEdge } from "reactflow";
|
||||
import { Edge, Node, ReactFlowJsonObject, addEdge } from "reactflow";
|
||||
import ShortUniqueId from "short-unique-id";
|
||||
import { skipNodeUpdate } from "../constants/constants";
|
||||
import {
|
||||
deleteFlowFromDatabase,
|
||||
downloadFlowsFromDatabase,
|
||||
|
|
@ -18,8 +19,9 @@ import {
|
|||
uploadFlowsToDatabase,
|
||||
} from "../controllers/API";
|
||||
import { APIClassType, APITemplateType } from "../types/api";
|
||||
import { FlowType, NodeType } from "../types/flow";
|
||||
import { TabsContextType, TabsState } from "../types/tabs";
|
||||
import { tweakType } from "../types/components";
|
||||
import { FlowType, NodeDataType, NodeType } from "../types/flow";
|
||||
import { TabsContextType, TabsState, errorsVarType } from "../types/tabs";
|
||||
import {
|
||||
addVersionToDuplicates,
|
||||
updateIds,
|
||||
|
|
@ -73,9 +75,12 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
const [flows, setFlows] = useState<Array<FlowType>>([]);
|
||||
const [id, setId] = useState(uid());
|
||||
const { templates, reactFlowInstance } = useContext(typesContext);
|
||||
const [lastCopiedSelection, setLastCopiedSelection] = useState(null);
|
||||
const [lastCopiedSelection, setLastCopiedSelection] = useState<{
|
||||
nodes: any;
|
||||
edges: any;
|
||||
} | null>(null);
|
||||
const [tabsState, setTabsState] = useState<TabsState>({});
|
||||
const [getTweak, setTweak] = useState([]);
|
||||
const [getTweak, setTweak] = useState<tweakType>([]);
|
||||
|
||||
const newNodeId = useRef(uid());
|
||||
function incrementNodeId() {
|
||||
|
|
@ -130,8 +135,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
//get tabs from db
|
||||
return readFlowsFromDatabase();
|
||||
}
|
||||
function processDBData(DbData) {
|
||||
DbData.forEach((flow) => {
|
||||
function processDBData(DbData: FlowType[]) {
|
||||
DbData.forEach((flow: FlowType) => {
|
||||
try {
|
||||
if (!flow.data) {
|
||||
return;
|
||||
|
|
@ -144,7 +149,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
});
|
||||
}
|
||||
|
||||
function processFlowEdges(flow) {
|
||||
function processFlowEdges(flow: FlowType) {
|
||||
if (!flow.data || !flow.data.edges) return;
|
||||
flow.data.edges.forEach((edge) => {
|
||||
edge.className = "";
|
||||
|
|
@ -153,16 +158,17 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
}
|
||||
|
||||
function updateDisplay_name(node: NodeType, template: APIClassType) {
|
||||
node.data.node.display_name = template["display_name"] || node.data.type;
|
||||
node.data.node!.display_name = template["display_name"] || node.data.type;
|
||||
}
|
||||
|
||||
function updateNodeDocumentation(node: NodeType, template: APIClassType) {
|
||||
node.data.node.documentation = template["documentation"];
|
||||
node.data.node!.documentation = template["documentation"];
|
||||
}
|
||||
|
||||
function processFlowNodes(flow) {
|
||||
function processFlowNodes(flow: FlowType) {
|
||||
if (!flow.data || !flow.data.nodes) return;
|
||||
flow.data.nodes.forEach((node: NodeType) => {
|
||||
if (skipNodeUpdate.includes(node.data.type)) return;
|
||||
const template = templates[node.data.type];
|
||||
if (!template) {
|
||||
setErrorData({ title: `Unknown node type: ${node.data.type}` });
|
||||
|
|
@ -180,7 +186,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
}
|
||||
|
||||
function updateNodeBaseClasses(node: NodeType, template: APIClassType) {
|
||||
node.data.node.base_classes = template["base_classes"];
|
||||
node.data.node!.base_classes = template["base_classes"];
|
||||
}
|
||||
|
||||
function updateNodeEdges(
|
||||
|
|
@ -188,10 +194,10 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
node: NodeType,
|
||||
template: APIClassType
|
||||
) {
|
||||
flow.data.edges.forEach((edge) => {
|
||||
flow.data!.edges.forEach((edge) => {
|
||||
if (edge.source === node.id) {
|
||||
edge.sourceHandle = edge.sourceHandle
|
||||
.split("|")
|
||||
?.split("|")
|
||||
.slice(0, 2)
|
||||
.concat(template["base_classes"])
|
||||
.join("|");
|
||||
|
|
@ -200,17 +206,17 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
}
|
||||
|
||||
function updateNodeDescription(node: NodeType, template: APIClassType) {
|
||||
node.data.node.description = template["description"];
|
||||
node.data.node!.description = template["description"];
|
||||
}
|
||||
|
||||
function updateNodeTemplate(node: NodeType, template: APIClassType) {
|
||||
node.data.node.template = updateTemplate(
|
||||
node.data.node!.template = updateTemplate(
|
||||
template["template"] as unknown as APITemplateType,
|
||||
node.data.node.template as APITemplateType
|
||||
node.data.node!.template as APITemplateType
|
||||
);
|
||||
}
|
||||
|
||||
function updateStateWithDbData(tabsData) {
|
||||
function updateStateWithDbData(tabsData: FlowType[]) {
|
||||
setFlows(tabsData);
|
||||
}
|
||||
|
||||
|
|
@ -241,7 +247,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
link.download = `${
|
||||
flowName && flowName != ""
|
||||
? flowName
|
||||
: flows.find((f) => f.id === tabId).name
|
||||
: flows.find((f) => f.id === tabId)!.name
|
||||
}.json`;
|
||||
|
||||
// simulate a click on the link element to trigger the download
|
||||
|
|
@ -293,10 +299,10 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
input.onchange = (e: Event) => {
|
||||
// check if the file type is application/json
|
||||
if (
|
||||
(e.target as HTMLInputElement).files[0].type === "application/json"
|
||||
(e.target as HTMLInputElement).files![0].type === "application/json"
|
||||
) {
|
||||
// get the file from the file input
|
||||
const currentfile = (e.target as HTMLInputElement).files[0];
|
||||
const currentfile = (e.target as HTMLInputElement).files![0];
|
||||
// read the file as text
|
||||
currentfile.text().then((text) => {
|
||||
// parse the text into a JSON object
|
||||
|
|
@ -319,10 +325,10 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
input.onchange = (event: Event) => {
|
||||
// check if the file type is application/json
|
||||
if (
|
||||
(event.target as HTMLInputElement).files[0].type === "application/json"
|
||||
(event.target as HTMLInputElement).files![0].type === "application/json"
|
||||
) {
|
||||
// get the file from the file input
|
||||
const file = (event.target as HTMLInputElement).files[0];
|
||||
const file = (event.target as HTMLInputElement).files![0];
|
||||
// read the file as text
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
|
|
@ -353,15 +359,15 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
*/
|
||||
|
||||
function paste(
|
||||
selectionInstance,
|
||||
selectionInstance: { nodes: Node[]; edges: Edge[] },
|
||||
position: { x: number; y: number; paneX?: number; paneY?: number }
|
||||
) {
|
||||
let minimumX = Infinity;
|
||||
let minimumY = Infinity;
|
||||
let idsMap = {};
|
||||
let nodes = reactFlowInstance.getNodes();
|
||||
let edges = reactFlowInstance.getEdges();
|
||||
selectionInstance.nodes.forEach((node) => {
|
||||
let nodes: Node<NodeDataType>[] = reactFlowInstance!.getNodes();
|
||||
let edges = reactFlowInstance!.getEdges();
|
||||
selectionInstance.nodes.forEach((node: Node) => {
|
||||
if (node.position.y < minimumY) {
|
||||
minimumY = node.position.y;
|
||||
}
|
||||
|
|
@ -371,8 +377,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
});
|
||||
|
||||
const insidePosition = position.paneX
|
||||
? { x: position.paneX + position.x, y: position.paneY + position.y }
|
||||
: reactFlowInstance.project({ x: position.x, y: position.y });
|
||||
? { x: position.paneX + position.x, y: position.paneY! + position.y }
|
||||
: reactFlowInstance!.project({ x: position.x, y: position.y });
|
||||
|
||||
selectionInstance.nodes.forEach((node: NodeType) => {
|
||||
// Generate a unique node ID
|
||||
|
|
@ -384,8 +390,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
id: newId,
|
||||
type: "genericNode",
|
||||
position: {
|
||||
x: insidePosition.x + node.position.x - minimumX,
|
||||
y: insidePosition.y + node.position.y - minimumY,
|
||||
x: insidePosition.x + node.position!.x - minimumX,
|
||||
y: insidePosition.y + node.position!.y - minimumY,
|
||||
},
|
||||
data: {
|
||||
..._.cloneDeep(node.data),
|
||||
|
|
@ -398,19 +404,19 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
.map((node) => ({ ...node, selected: false }))
|
||||
.concat({ ...newNode, selected: false });
|
||||
});
|
||||
reactFlowInstance.setNodes(nodes);
|
||||
reactFlowInstance!.setNodes(nodes);
|
||||
|
||||
selectionInstance.edges.forEach((edge) => {
|
||||
let source = idsMap[edge.source];
|
||||
let target = idsMap[edge.target];
|
||||
let sourceHandleSplitted = edge.sourceHandle.split("|");
|
||||
let sourceHandleSplitted = edge.sourceHandle!.split("|");
|
||||
let sourceHandle =
|
||||
sourceHandleSplitted[0] +
|
||||
"|" +
|
||||
source +
|
||||
"|" +
|
||||
sourceHandleSplitted.slice(2).join("|");
|
||||
let targetHandleSplitted = edge.targetHandle.split("|");
|
||||
let targetHandleSplitted = edge.targetHandle!.split("|");
|
||||
let targetHandle =
|
||||
targetHandleSplitted.slice(0, -1).join("|") + "|" + target;
|
||||
let id =
|
||||
|
|
@ -438,21 +444,21 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
edges.map((edge) => ({ ...edge, selected: false }))
|
||||
);
|
||||
});
|
||||
reactFlowInstance.setEdges(edges);
|
||||
reactFlowInstance!.setEdges(edges);
|
||||
}
|
||||
|
||||
const addFlow = async (
|
||||
flow?: FlowType,
|
||||
newProject?: Boolean
|
||||
): Promise<String> => {
|
||||
): Promise<String | undefined> => {
|
||||
if (newProject) {
|
||||
let flowData = extractDataFromFlow(flow);
|
||||
let flowData = extractDataFromFlow(flow!);
|
||||
if (flowData.description == "") {
|
||||
flowData.description = getRandomDescription();
|
||||
}
|
||||
|
||||
// Create a new flow with a default name if no flow is provided.
|
||||
const newFlow = createNewFlow(flowData, flow);
|
||||
const newFlow = createNewFlow(flowData, flow!);
|
||||
processFlowEdges(newFlow);
|
||||
processFlowNodes(newFlow);
|
||||
|
||||
|
|
@ -477,13 +483,13 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
}
|
||||
} else {
|
||||
paste(
|
||||
{ nodes: flow.data.nodes, edges: flow.data.edges },
|
||||
{ nodes: flow!.data!.nodes, edges: flow!.data!.edges },
|
||||
{ x: 10, y: 10 }
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const extractDataFromFlow = (flow) => {
|
||||
const extractDataFromFlow = (flow: FlowType) => {
|
||||
let data = flow?.data ? flow.data : null;
|
||||
const description = flow?.description ? flow.description : "";
|
||||
|
||||
|
|
@ -496,18 +502,19 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
return { data, description };
|
||||
};
|
||||
|
||||
const updateEdges = (edges) => {
|
||||
const updateEdges = (edges: Edge[]) => {
|
||||
edges.forEach((edge) => {
|
||||
edge.className =
|
||||
(edge.targetHandle.split("|")[0] === "Text"
|
||||
(edge.targetHandle!.split("|")[0] === "Text"
|
||||
? "stroke-gray-800 "
|
||||
: "stroke-gray-900 ") + " stroke-connection";
|
||||
edge.animated = edge.targetHandle.split("|")[0] === "Text";
|
||||
edge.animated = edge.targetHandle!.split("|")[0] === "Text";
|
||||
});
|
||||
};
|
||||
|
||||
const updateNodes = (nodes, edges) => {
|
||||
const updateNodes = (nodes: Node[], edges: Edge[]) => {
|
||||
nodes.forEach((node) => {
|
||||
if (skipNodeUpdate.includes(node.data.type)) return;
|
||||
const template = templates[node.data.type];
|
||||
if (!template) {
|
||||
setErrorData({ title: `Unknown node type: ${node.data.type}` });
|
||||
|
|
@ -517,8 +524,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
node.data.node.base_classes = template["base_classes"];
|
||||
edges.forEach((edge) => {
|
||||
if (edge.source === node.id) {
|
||||
edge.sourceHandle = edge.sourceHandle
|
||||
.split("|")
|
||||
edge.sourceHandle = edge
|
||||
.sourceHandle!.split("|")
|
||||
.slice(0, 2)
|
||||
.concat(template["base_classes"])
|
||||
.join("|");
|
||||
|
|
@ -533,14 +540,17 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
});
|
||||
};
|
||||
|
||||
const createNewFlow = (flowData, flow) => ({
|
||||
const createNewFlow = (
|
||||
flowData: { data: ReactFlowJsonObject | null; description: string },
|
||||
flow: FlowType
|
||||
) => ({
|
||||
description: flowData.description,
|
||||
name: flow?.name ?? getRandomName(),
|
||||
data: flowData.data,
|
||||
id: "",
|
||||
});
|
||||
|
||||
const addFlowToLocalState = (newFlow) => {
|
||||
const addFlowToLocalState = (newFlow: FlowType) => {
|
||||
setFlows((prevState) => {
|
||||
return [...prevState, newFlow];
|
||||
});
|
||||
|
|
@ -591,7 +601,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
});
|
||||
}
|
||||
} catch (err) {
|
||||
setErrorData(err);
|
||||
setErrorData(err as errorsVarType);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,21 @@
|
|||
import { createContext, ReactNode, useEffect, useState } from "react";
|
||||
import { Node } from "reactflow";
|
||||
import {
|
||||
createContext,
|
||||
ReactNode,
|
||||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Node, ReactFlowInstance } from "reactflow";
|
||||
import { getAll } from "../controllers/API";
|
||||
import { APIKindType } from "../types/api";
|
||||
import { typesContextType } from "../types/typesContext";
|
||||
import { alertContext } from "./alertContext";
|
||||
|
||||
//context to share types adn functions from nodes to flow
|
||||
|
||||
const initialValue: typesContextType = {
|
||||
reactFlowInstance: null,
|
||||
setReactFlowInstance: () => {},
|
||||
setReactFlowInstance: (newState: ReactFlowInstance) => {},
|
||||
deleteNode: () => {},
|
||||
types: {},
|
||||
setTypes: () => {},
|
||||
|
|
@ -22,13 +29,15 @@ export const typesContext = createContext<typesContextType>(initialValue);
|
|||
|
||||
export function TypesProvider({ children }: { children: ReactNode }) {
|
||||
const [types, setTypes] = useState({});
|
||||
const [reactFlowInstance, setReactFlowInstance] = useState(null);
|
||||
const [reactFlowInstance, setReactFlowInstance] =
|
||||
useState<ReactFlowInstance | null>(null);
|
||||
const [templates, setTemplates] = useState({});
|
||||
const [data, setData] = useState({});
|
||||
const { setLoading } = useContext(alertContext);
|
||||
|
||||
useEffect(() => {
|
||||
let delay = 1000; // Start delay of 1 second
|
||||
let intervalId = null;
|
||||
let intervalId: NodeJS.Timer;
|
||||
let retryCount = 0; // Count of retry attempts
|
||||
const maxRetryCount = 5; // Max retry attempts
|
||||
|
||||
|
|
@ -40,6 +49,7 @@ export function TypesProvider({ children }: { children: ReactNode }) {
|
|||
const result = await getAll();
|
||||
// Make sure to only update the state if the component is still mounted.
|
||||
if (isMounted) {
|
||||
setLoading(false);
|
||||
setData(result.data);
|
||||
setTemplates(
|
||||
Object.keys(result.data).reduce((acc, curr) => {
|
||||
|
|
@ -69,7 +79,7 @@ export function TypesProvider({ children }: { children: ReactNode }) {
|
|||
);
|
||||
}
|
||||
// Clear the interval if successful.
|
||||
clearInterval(intervalId);
|
||||
clearInterval(intervalId!);
|
||||
} catch (error) {
|
||||
console.error("An error has occurred while fetching types.");
|
||||
}
|
||||
|
|
@ -77,21 +87,20 @@ export function TypesProvider({ children }: { children: ReactNode }) {
|
|||
|
||||
// Start the initial interval.
|
||||
intervalId = setInterval(getTypes, delay);
|
||||
|
||||
return () => {
|
||||
// This will clear the interval when the component unmounts, or when the dependencies of the useEffect hook change.
|
||||
clearInterval(intervalId);
|
||||
clearInterval(intervalId!);
|
||||
// Indicate that the component has been unmounted.
|
||||
isMounted = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
function deleteNode(idx: string) {
|
||||
reactFlowInstance.setNodes(
|
||||
reactFlowInstance.getNodes().filter((node: Node) => node.id !== idx)
|
||||
reactFlowInstance!.setNodes(
|
||||
reactFlowInstance!.getNodes().filter((node: Node) => node.id !== idx)
|
||||
);
|
||||
reactFlowInstance.setEdges(
|
||||
reactFlowInstance
|
||||
reactFlowInstance!.setEdges(
|
||||
reactFlowInstance!
|
||||
.getEdges()
|
||||
.filter((edge) => edge.source !== idx && edge.target !== idx)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,26 +6,15 @@ import {
|
|||
useEffect,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Edge, Node, useReactFlow } from "reactflow";
|
||||
import { useReactFlow } from "reactflow";
|
||||
import {
|
||||
HistoryItem,
|
||||
UseUndoRedoOptions,
|
||||
undoRedoContextType,
|
||||
} from "../types/typesContext";
|
||||
import { isWrappedWithClass } from "../utils/utils";
|
||||
import { TabsContext } from "./tabsContext";
|
||||
|
||||
type undoRedoContextType = {
|
||||
undo: () => void;
|
||||
redo: () => void;
|
||||
takeSnapshot: () => void;
|
||||
};
|
||||
|
||||
type UseUndoRedoOptions = {
|
||||
maxHistorySize: number;
|
||||
enableShortcuts: boolean;
|
||||
};
|
||||
|
||||
type HistoryItem = {
|
||||
nodes: Node[];
|
||||
edges: Edge[];
|
||||
};
|
||||
|
||||
const initialValue = {
|
||||
undo: () => {},
|
||||
redo: () => {},
|
||||
|
|
|
|||
|
|
@ -36,13 +36,14 @@ function ApiInterceptor() {
|
|||
const res = await renewAccessToken(refreshToken);
|
||||
login(res.data.access_token, res.data.refresh_token);
|
||||
try {
|
||||
const accessToken = cookies.get("access_token");
|
||||
delete error.config.headers["Authorization"];
|
||||
error.config.headers["Authorization"] = `Bearer ${accessToken}`;
|
||||
const response = await axios.request(error.config);
|
||||
return response;
|
||||
if (error?.config?.headers) {
|
||||
delete error.config.headers["Authorization"];
|
||||
error.config.headers["Authorization"] = `Bearer ${accessToken}`;
|
||||
const response = await axios.request(error.config);
|
||||
return response;
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.response?.status === 401) {
|
||||
if (axios.isAxiosError(error) && error.response?.status === 401) {
|
||||
logout();
|
||||
navigate("/login");
|
||||
}
|
||||
|
|
@ -103,7 +104,7 @@ function ApiInterceptor() {
|
|||
}
|
||||
|
||||
// Function to sleep for a given duration in milliseconds
|
||||
function sleep(ms) {
|
||||
function sleep(ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ export async function getAll(): Promise<AxiosResponse<APIObjectType>> {
|
|||
|
||||
const GITHUB_API_URL = "https://api.github.com";
|
||||
|
||||
export async function getRepoStars(owner, repo) {
|
||||
export async function getRepoStars(owner: string, repo: string) {
|
||||
try {
|
||||
const response = await api.get(`${GITHUB_API_URL}/repos/${owner}/${repo}`);
|
||||
return response.data.stargazers_count;
|
||||
|
|
@ -107,7 +107,7 @@ export async function getExamples(): Promise<FlowType[]> {
|
|||
export async function saveFlowToDatabase(newFlow: {
|
||||
name: string;
|
||||
id: string;
|
||||
data: ReactFlowJsonObject;
|
||||
data: ReactFlowJsonObject | null;
|
||||
description: string;
|
||||
style?: FlowStyleType;
|
||||
}): Promise<FlowType> {
|
||||
|
|
@ -186,7 +186,7 @@ export async function downloadFlowsFromDatabase() {
|
|||
}
|
||||
}
|
||||
|
||||
export async function uploadFlowsToDatabase(flows) {
|
||||
export async function uploadFlowsToDatabase(flows: FormData) {
|
||||
try {
|
||||
const response = await api.post(`/api/v1/flows/upload/`, flows);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import React, { forwardRef } from "react";
|
||||
import { ReactComponent as PowerPointSVG } from "./PowerPoint.svg";
|
||||
import SvgPowerPoint from "./PowerPoint";
|
||||
|
||||
export const PowerPointIcon = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return <PowerPointSVG ref={ref} {...props} />;
|
||||
return <SvgPowerPoint ref={ref} {...props} />;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,7 +15,9 @@ import CodeTabsComponent from "../../components/codeTabsComponent";
|
|||
import IconComponent from "../../components/genericIconComponent";
|
||||
import { EXPORT_CODE_DIALOG } from "../../constants/constants";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import { FlowType } from "../../types/flow/index";
|
||||
import { TemplateVariableType } from "../../types/api";
|
||||
import { tweakType, uniqueTweakType } from "../../types/components";
|
||||
import { FlowType, NodeType } from "../../types/flow/index";
|
||||
import { buildTweaks } from "../../utils/reactflowUtils";
|
||||
import {
|
||||
getCurlCode,
|
||||
|
|
@ -41,8 +43,8 @@ const ApiModal = forwardRef(
|
|||
) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState("0");
|
||||
const tweak = useRef([]);
|
||||
const tweaksList = useRef([]);
|
||||
const tweak = useRef<tweakType>([]);
|
||||
const tweaksList = useRef<string[]>([]);
|
||||
const { setTweak, getTweak, tabsState } = useContext(TabsContext);
|
||||
const pythonApiCode = getPythonApiCode(flow, tweak.current, tabsState);
|
||||
const curl_code = getCurlCode(flow, tweak.current, tabsState);
|
||||
|
|
@ -65,7 +67,7 @@ const ApiModal = forwardRef(
|
|||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (flow["data"]["nodes"].length == 0) {
|
||||
if (flow["data"]!["nodes"].length == 0) {
|
||||
startState();
|
||||
} else {
|
||||
tweak.current = [];
|
||||
|
|
@ -81,12 +83,12 @@ const ApiModal = forwardRef(
|
|||
} else {
|
||||
setTabs(tabsArray(codesArray, 1));
|
||||
}
|
||||
}, [flow["data"]["nodes"], open]);
|
||||
}, [flow["data"]!["nodes"], open]);
|
||||
|
||||
function filterNodes() {
|
||||
let arrNodesWithValues = [];
|
||||
let arrNodesWithValues: string[] = [];
|
||||
|
||||
flow["data"]["nodes"].forEach((node) => {
|
||||
flow["data"]!["nodes"].forEach((node) => {
|
||||
Object.keys(node["data"]["node"]["template"])
|
||||
.filter(
|
||||
(templateField) =>
|
||||
|
|
@ -109,11 +111,15 @@ const ApiModal = forwardRef(
|
|||
return self.indexOf(value) === index;
|
||||
});
|
||||
}
|
||||
function buildTweakObject(tw, changes, template) {
|
||||
if (template.type === "float") {
|
||||
function buildTweakObject(
|
||||
tw: string,
|
||||
changes: string | string[] | boolean | number,
|
||||
template: TemplateVariableType
|
||||
) {
|
||||
if (typeof changes === "string" && template.type === "float") {
|
||||
changes = parseFloat(changes);
|
||||
}
|
||||
if (template.type === "int") {
|
||||
if (typeof changes === "string" && template.type === "int") {
|
||||
changes = parseInt(changes);
|
||||
}
|
||||
if (template.list === true && Array.isArray(changes)) {
|
||||
|
|
@ -125,7 +131,7 @@ const ApiModal = forwardRef(
|
|||
);
|
||||
|
||||
if (existingTweak) {
|
||||
existingTweak[tw][template["name"]] = changes;
|
||||
existingTweak[tw][template["name"]] = changes as string;
|
||||
|
||||
if (existingTweak[tw][template["name"]] == template.value) {
|
||||
tweak.current.forEach((element) => {
|
||||
|
|
@ -142,7 +148,7 @@ const ApiModal = forwardRef(
|
|||
[tw]: {
|
||||
[template["name"]]: changes,
|
||||
},
|
||||
};
|
||||
} as uniqueTweakType;
|
||||
tweak.current.push(newTweak);
|
||||
}
|
||||
|
||||
|
|
@ -151,15 +157,15 @@ const ApiModal = forwardRef(
|
|||
const pythonCode = getPythonCode(flow, tweak.current, tabsState);
|
||||
const widgetCode = getWidgetCode(flow, tabsState);
|
||||
|
||||
tabs[0].code = curl_code;
|
||||
tabs[1].code = pythonApiCode;
|
||||
tabs[2].code = pythonCode;
|
||||
tabs[3].code = widgetCode;
|
||||
tabs![0].code = curl_code;
|
||||
tabs![1].code = pythonApiCode;
|
||||
tabs![2].code = pythonCode;
|
||||
tabs![3].code = widgetCode;
|
||||
|
||||
setTweak(tweak.current);
|
||||
}
|
||||
|
||||
function buildContent(value) {
|
||||
function buildContent(value: string) {
|
||||
const htmlContent = (
|
||||
<div className="w-[200px]">
|
||||
<span>{value != null && value != "" ? value : "None"}</span>
|
||||
|
|
@ -168,7 +174,11 @@ const ApiModal = forwardRef(
|
|||
return htmlContent;
|
||||
}
|
||||
|
||||
function getValue(value, node, template) {
|
||||
function getValue(
|
||||
value: string,
|
||||
node: NodeType,
|
||||
template: TemplateVariableType
|
||||
) {
|
||||
let returnValue = value ?? "";
|
||||
|
||||
if (getTweak.length > 0) {
|
||||
|
|
@ -204,7 +214,7 @@ const ApiModal = forwardRef(
|
|||
<BaseModal.Content>
|
||||
<CodeTabsComponent
|
||||
flow={flow}
|
||||
tabs={tabs}
|
||||
tabs={tabs!}
|
||||
activeTab={activeTab}
|
||||
setActiveTab={setActiveTab}
|
||||
tweaks={{
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import { limitScrollFieldsModal } from "../../constants/constants";
|
|||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import { typesContext } from "../../contexts/typesContext";
|
||||
import { NodeDataType } from "../../types/flow";
|
||||
import { TabsState } from "../../types/tabs";
|
||||
import { classNames } from "../../utils/utils";
|
||||
import BaseModal from "../baseModal";
|
||||
|
||||
|
|
@ -53,19 +54,22 @@ const EditNodeModal = forwardRef(
|
|||
?.getEdges()
|
||||
.some((edge) => edge.targetHandle === data.id) ?? false;
|
||||
|
||||
function changeAdvanced(templateParam) {
|
||||
function changeAdvanced(templateParam: string): void {
|
||||
setMyData((old) => {
|
||||
let newData = cloneDeep(old);
|
||||
newData.node.template[templateParam].advanced =
|
||||
!newData.node.template[templateParam].advanced;
|
||||
newData.node!.template[templateParam].advanced =
|
||||
!newData.node!.template[templateParam].advanced;
|
||||
return newData;
|
||||
});
|
||||
}
|
||||
|
||||
const handleOnNewValue = (newValue: any, name) => {
|
||||
const handleOnNewValue = (
|
||||
newValue: string | string[] | boolean,
|
||||
name: string
|
||||
) => {
|
||||
setMyData((old) => {
|
||||
let newData = cloneDeep(old);
|
||||
newData.node.template[name].value = newValue;
|
||||
newData.node!.template[name].value = newValue;
|
||||
return newData;
|
||||
});
|
||||
};
|
||||
|
|
@ -77,7 +81,7 @@ const EditNodeModal = forwardRef(
|
|||
return (
|
||||
<BaseModal size="large-h-full" open={modalOpen} setOpen={setModalOpen}>
|
||||
<BaseModal.Trigger>{children}</BaseModal.Trigger>
|
||||
<BaseModal.Header description={myData.node?.description}>
|
||||
<BaseModal.Header description={myData.node?.description!}>
|
||||
<span className="pr-2">{myData.type}</span>
|
||||
<Badge variant="secondary">ID: {myData.id}</Badge>
|
||||
</BaseModal.Header>
|
||||
|
|
@ -112,11 +116,11 @@ const EditNodeModal = forwardRef(
|
|||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody className="p-0">
|
||||
{Object.keys(myData.node.template)
|
||||
{Object.keys(myData.node!.template)
|
||||
.filter(
|
||||
(templateParam) =>
|
||||
templateParam.charAt(0) !== "_" &&
|
||||
myData.node.template[templateParam].show &&
|
||||
myData.node?.template[templateParam].show &&
|
||||
(myData.node.template[templateParam].type ===
|
||||
"str" ||
|
||||
myData.node.template[templateParam].type ===
|
||||
|
|
@ -135,13 +139,13 @@ const EditNodeModal = forwardRef(
|
|||
.map((templateParam, index) => (
|
||||
<TableRow key={index} className="h-10">
|
||||
<TableCell className="truncate p-0 text-center text-sm text-foreground sm:px-3">
|
||||
{myData.node.template[templateParam].name
|
||||
{myData.node?.template[templateParam].name
|
||||
? myData.node.template[templateParam].name
|
||||
: myData.node.template[templateParam]
|
||||
: myData.node?.template[templateParam]
|
||||
.display_name}
|
||||
</TableCell>
|
||||
<TableCell className="w-[300px] p-0 text-center text-xs text-foreground ">
|
||||
{myData.node.template[templateParam].type ===
|
||||
{myData.node?.template[templateParam].type ===
|
||||
"str" &&
|
||||
!myData.node.template[templateParam].options ? (
|
||||
<div className="mx-auto">
|
||||
|
|
@ -171,7 +175,7 @@ const EditNodeModal = forwardRef(
|
|||
myData.node.template[templateParam]
|
||||
.value ?? ""
|
||||
}
|
||||
onChange={(value: string) => {
|
||||
onChange={(value: string | string[]) => {
|
||||
handleOnNewValue(value, templateParam);
|
||||
}}
|
||||
/>
|
||||
|
|
@ -193,7 +197,7 @@ const EditNodeModal = forwardRef(
|
|||
/>
|
||||
)}
|
||||
</div>
|
||||
) : myData.node.template[templateParam].type ===
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"bool" ? (
|
||||
<div className="ml-auto">
|
||||
{" "}
|
||||
|
|
@ -211,7 +215,7 @@ const EditNodeModal = forwardRef(
|
|||
size="small"
|
||||
/>
|
||||
</div>
|
||||
) : myData.node.template[templateParam].type ===
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"float" ? (
|
||||
<div className="mx-auto">
|
||||
<FloatComponent
|
||||
|
|
@ -226,7 +230,7 @@ const EditNodeModal = forwardRef(
|
|||
}}
|
||||
/>
|
||||
</div>
|
||||
) : myData.node.template[templateParam].type ===
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"str" &&
|
||||
myData.node.template[templateParam].options ? (
|
||||
<div className="mx-auto">
|
||||
|
|
@ -246,7 +250,7 @@ const EditNodeModal = forwardRef(
|
|||
}
|
||||
></Dropdown>
|
||||
</div>
|
||||
) : myData.node.template[templateParam].type ===
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"int" ? (
|
||||
<div className="mx-auto">
|
||||
<IntComponent
|
||||
|
|
@ -261,7 +265,7 @@ const EditNodeModal = forwardRef(
|
|||
}}
|
||||
/>
|
||||
</div>
|
||||
) : myData.node.template[templateParam].type ===
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"file" ? (
|
||||
<div className="mx-auto">
|
||||
<InputFileComponent
|
||||
|
|
@ -271,7 +275,7 @@ const EditNodeModal = forwardRef(
|
|||
myData.node.template[templateParam]
|
||||
.value ?? ""
|
||||
}
|
||||
onChange={(value: string) => {
|
||||
onChange={(value: string | string[]) => {
|
||||
handleOnNewValue(value, templateParam);
|
||||
}}
|
||||
fileTypes={
|
||||
|
|
@ -283,13 +287,13 @@ const EditNodeModal = forwardRef(
|
|||
.suffixes
|
||||
}
|
||||
onFileChange={(filePath: string) => {
|
||||
data.node.template[
|
||||
data.node!.template[
|
||||
templateParam
|
||||
].file_path = filePath;
|
||||
}}
|
||||
></InputFileComponent>
|
||||
</div>
|
||||
) : myData.node.template[templateParam].type ===
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"prompt" ? (
|
||||
<div className="mx-auto">
|
||||
<PromptAreaComponent
|
||||
|
|
@ -304,17 +308,17 @@ const EditNodeModal = forwardRef(
|
|||
myData.node.template[templateParam]
|
||||
.value ?? ""
|
||||
}
|
||||
onChange={(value: string) => {
|
||||
onChange={(value: string | string[]) => {
|
||||
handleOnNewValue(value, templateParam);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : myData.node.template[templateParam].type ===
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"code" ? (
|
||||
<div className="mx-auto">
|
||||
<CodeAreaComponent
|
||||
dynamic={
|
||||
data.node.template[templateParam]
|
||||
data.node!.template[templateParam]
|
||||
.dynamic ?? false
|
||||
}
|
||||
setNodeClass={(nodeClass) => {
|
||||
|
|
@ -327,12 +331,12 @@ const EditNodeModal = forwardRef(
|
|||
myData.node.template[templateParam]
|
||||
.value ?? ""
|
||||
}
|
||||
onChange={(value: string) => {
|
||||
onChange={(value: string | string[]) => {
|
||||
handleOnNewValue(value, templateParam);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : myData.node.template[templateParam].type ===
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"Any" ? (
|
||||
"-"
|
||||
) : (
|
||||
|
|
@ -343,7 +347,7 @@ const EditNodeModal = forwardRef(
|
|||
<div className="items-center text-center">
|
||||
<ToggleShadComponent
|
||||
enabled={
|
||||
!myData.node.template[templateParam]
|
||||
!myData.node?.template[templateParam]
|
||||
.advanced
|
||||
}
|
||||
setEnabled={(e) =>
|
||||
|
|
@ -369,7 +373,8 @@ const EditNodeModal = forwardRef(
|
|||
className="mt-3"
|
||||
onClick={() => {
|
||||
setData(cloneDeep(myData)); //saves data with actual state of modal
|
||||
setTabsState((prev) => {
|
||||
//@ts-ignore
|
||||
setTabsState((prev: TabsState) => {
|
||||
return {
|
||||
...prev,
|
||||
[tabId]: {
|
||||
|
|
|
|||
|
|
@ -1,180 +0,0 @@
|
|||
import { useState } from "react";
|
||||
import CodeAreaComponent from "../../../../components/codeAreaComponent";
|
||||
import Dropdown from "../../../../components/dropdownComponent";
|
||||
import FloatComponent from "../../../../components/floatComponent";
|
||||
import InputComponent from "../../../../components/inputComponent";
|
||||
import InputFileComponent from "../../../../components/inputFileComponent";
|
||||
import InputListComponent from "../../../../components/inputListComponent";
|
||||
import IntComponent from "../../../../components/intComponent";
|
||||
import PromptAreaComponent from "../../../../components/promptComponent";
|
||||
import TextAreaComponent from "../../../../components/textAreaComponent";
|
||||
import ToggleComponent from "../../../../components/toggleComponent";
|
||||
import { classNames } from "../../../../utils/utils";
|
||||
|
||||
export default function ModalField({
|
||||
data,
|
||||
title,
|
||||
required,
|
||||
id,
|
||||
name,
|
||||
type,
|
||||
index,
|
||||
}) {
|
||||
const [enabled, setEnabled] = useState(
|
||||
data.node.template[name]?.value ?? false
|
||||
);
|
||||
const display =
|
||||
type === "str" ||
|
||||
type === "int" ||
|
||||
type === "prompt" ||
|
||||
type === "bool" ||
|
||||
type === "float" ||
|
||||
type === "file" ||
|
||||
type === "code";
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
"flex w-full flex-row items-center justify-between",
|
||||
display ? "" : "hidden",
|
||||
Object.keys(data.node.template).filter(
|
||||
(t) =>
|
||||
t.charAt(0) !== "_" &&
|
||||
data.node.template[t].advanced &&
|
||||
data.node.template[t].show
|
||||
).length -
|
||||
1 ===
|
||||
index
|
||||
? "pb-4"
|
||||
: ""
|
||||
)}
|
||||
>
|
||||
{display && (
|
||||
<div>
|
||||
<span className="mx-2">{title}</span>
|
||||
<span className="text-status-red">{required ? " *" : ""}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{type === "str" && !data.node.template[name].options ? (
|
||||
<div className="w-1/2">
|
||||
{data.node.template[name].list ? (
|
||||
<InputListComponent
|
||||
disabled={false}
|
||||
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={false}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t: string) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<InputComponent
|
||||
disabled={false}
|
||||
password={data.node.template[name].password ?? false}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : type === "bool" ? (
|
||||
<div className="ml-auto">
|
||||
{" "}
|
||||
<ToggleComponent
|
||||
disabled={false}
|
||||
enabled={enabled}
|
||||
setEnabled={(t) => {
|
||||
data.node.template[name].value = t;
|
||||
setEnabled(t);
|
||||
}}
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
) : type === "float" ? (
|
||||
<div className="w-1/2">
|
||||
<FloatComponent
|
||||
disabled={false}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : type === "str" && data.node.template[name].options ? (
|
||||
<div className="w-1/2">
|
||||
<Dropdown
|
||||
options={data.node.template[name].options}
|
||||
onSelect={(newValue) => (data.node.template[name].value = newValue)}
|
||||
value={data.node.template[name].value ?? "Choose an option"}
|
||||
></Dropdown>
|
||||
</div>
|
||||
) : type === "int" ? (
|
||||
<div className="w-1/2">
|
||||
<IntComponent
|
||||
disabled={false}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : type === "file" ? (
|
||||
<div className="w-1/2">
|
||||
<InputFileComponent
|
||||
disabled={false}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t: string) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
fileTypes={data.node.template[name].fileTypes}
|
||||
suffixes={data.node.template[name].suffixes}
|
||||
onFileChange={(t: string) => {
|
||||
data.node.template[name].file_path = t;
|
||||
}}
|
||||
></InputFileComponent>
|
||||
</div>
|
||||
) : type === "prompt" ? (
|
||||
<div className="w-1/2">
|
||||
<PromptAreaComponent
|
||||
field_name={name}
|
||||
disabled={false}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t: string) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : type === "code" ? (
|
||||
<div className="w-1/2">
|
||||
<CodeAreaComponent
|
||||
dynamic={data.node.template[name].dynamic ?? false}
|
||||
setNodeClass={(nodeClass) => {
|
||||
data.node = nodeClass;
|
||||
}}
|
||||
nodeClass={data.node}
|
||||
disabled={false}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t: string) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="hidden"></div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,151 +0,0 @@
|
|||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { Fragment, useContext, useRef, useState } from "react";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import { limitScrollFieldsModal } from "../../constants/constants";
|
||||
import { typesContext } from "../../contexts/typesContext";
|
||||
import { NodeDataType } from "../../types/flow";
|
||||
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
|
||||
import { classNames, toTitleCase } from "../../utils/utils";
|
||||
import ModalField from "./components/ModalField";
|
||||
|
||||
export default function NodeModal({ data }: { data: NodeDataType }) {
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const { types } = useContext(typesContext);
|
||||
const ref = useRef();
|
||||
// any to avoid type conflict
|
||||
const Icon: any = nodeIconsLucide[types[data.type]];
|
||||
return (
|
||||
<Dialog
|
||||
open={modalOpen}
|
||||
as="div"
|
||||
className="relative z-10"
|
||||
onClose={setModalOpen}
|
||||
initialFocus={ref}
|
||||
>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="node-modal-div" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="node-modal-dialog-arrangement">
|
||||
<div className="node-modal-dialog-div">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<Dialog.Panel className="node-modal-dialog-panel">
|
||||
<div className=" node-modal-dialog-panel-div">
|
||||
<button
|
||||
type="button"
|
||||
className="node-modal-dialog-button"
|
||||
onClick={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
>
|
||||
<span className="sr-only">Close</span>
|
||||
<IconComponent
|
||||
name="X"
|
||||
className="h-6 w-6"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div className="node-modal-dialog-icon-div">
|
||||
<div className="node-modal-icon-arrangement">
|
||||
<Icon
|
||||
strokeWidth={1.5}
|
||||
className="node-modal-icon"
|
||||
style={{
|
||||
color: nodeColors[types[data.type]] ?? nodeColors.unknown,
|
||||
}}
|
||||
/>
|
||||
<div className="node-modal-title-div">
|
||||
<Dialog.Title as="h3" className="node-modal-title">
|
||||
{data.type}
|
||||
</Dialog.Title>
|
||||
</div>
|
||||
</div>
|
||||
<div className="node-modal-template-div">
|
||||
<div className="flex-max-width h-[445px]">
|
||||
<div
|
||||
className={classNames(
|
||||
"node-modal-template",
|
||||
Object.keys(data.node.template).filter(
|
||||
(t) =>
|
||||
t.charAt(0) !== "_" &&
|
||||
data.node.template[t].advanced &&
|
||||
data.node.template[t].show
|
||||
).length > limitScrollFieldsModal
|
||||
? "overflow-scroll overflow-x-hidden custom-scroll"
|
||||
: "overflow-hidden"
|
||||
)}
|
||||
>
|
||||
<div className="node-modal-template-column">
|
||||
{Object.keys(data.node.template)
|
||||
.filter(
|
||||
(t) =>
|
||||
t.charAt(0) !== "_" &&
|
||||
data.node.template[t].advanced &&
|
||||
data.node.template[t].show
|
||||
)
|
||||
.map((t: string, idx) => {
|
||||
return (
|
||||
<ModalField
|
||||
key={idx}
|
||||
data={data}
|
||||
title={
|
||||
data.node.template[t].display_name
|
||||
? data.node.template[t].display_name
|
||||
: data.node.template[t].name
|
||||
? toTitleCase(data.node.template[t].name)
|
||||
: toTitleCase(t)
|
||||
}
|
||||
required={data.node.template[t].required}
|
||||
id={
|
||||
data.node.template[t].type +
|
||||
"|" +
|
||||
t +
|
||||
"|" +
|
||||
data.id
|
||||
}
|
||||
name={t}
|
||||
type={data.node.template[t].type}
|
||||
index={idx}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="node-modal-button-box">
|
||||
<button
|
||||
type="button"
|
||||
className="node-modal-button"
|
||||
onClick={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
>
|
||||
Done
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import {
|
|||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "../../components/ui/dialog";
|
||||
import { modalHeaderType } from "../../types/components";
|
||||
|
||||
type ContentProps = { children: ReactNode };
|
||||
type HeaderProps = { children: ReactNode; description: string };
|
||||
|
|
@ -22,10 +23,10 @@ const Trigger: React.FC<ContentProps> = ({ children }) => {
|
|||
return <>{children}</>;
|
||||
};
|
||||
|
||||
const Header: React.FC<{ children: ReactNode; description: string }> = ({
|
||||
const Header: React.FC<{ children: ReactNode; description: string | null }> = ({
|
||||
children,
|
||||
description,
|
||||
}) => {
|
||||
}: modalHeaderType): JSX.Element => {
|
||||
return (
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center">{children}</DialogTitle>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import "ace-builds/src-noconflict/mode-python";
|
|||
import "ace-builds/src-noconflict/theme-github";
|
||||
import "ace-builds/src-noconflict/theme-twilight";
|
||||
// import "ace-builds/webpack-resolver";
|
||||
import { ReactNode, useContext, useEffect, useState } from "react";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import AceEditor from "react-ace";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
|
|
@ -13,7 +13,7 @@ import { alertContext } from "../../contexts/alertContext";
|
|||
import { darkContext } from "../../contexts/darkContext";
|
||||
import { typesContext } from "../../contexts/typesContext";
|
||||
import { postCustomComponent, postValidateCode } from "../../controllers/API";
|
||||
import { APIClassType } from "../../types/api";
|
||||
import { codeAreaModalPropsType } from "../../types/components";
|
||||
import BaseModal from "../baseModal";
|
||||
|
||||
export default function CodeAreaModal({
|
||||
|
|
@ -23,27 +23,20 @@ export default function CodeAreaModal({
|
|||
setNodeClass,
|
||||
children,
|
||||
dynamic,
|
||||
}: {
|
||||
setValue: (value: string) => void;
|
||||
value: string;
|
||||
nodeClass?: APIClassType;
|
||||
children: ReactNode;
|
||||
setNodeClass?: (Class: APIClassType) => void;
|
||||
dynamic?: boolean;
|
||||
}) {
|
||||
}: codeAreaModalPropsType): JSX.Element {
|
||||
const [code, setCode] = useState(value);
|
||||
const { dark } = useContext(darkContext);
|
||||
const { reactFlowInstance } = useContext(typesContext);
|
||||
const [height, setHeight] = useState(null);
|
||||
const [height, setHeight] = useState<string | null>(null);
|
||||
const { setErrorData, setSuccessData } = useContext(alertContext);
|
||||
const [error, setError] = useState<{
|
||||
detail: { error: string; traceback: string };
|
||||
}>(null);
|
||||
detail: { error: string | undefined; traceback: string | undefined };
|
||||
} | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// if nodeClass.template has more fields other than code and dynamic is true
|
||||
// do not run handleClick
|
||||
if (dynamic && Object.keys(nodeClass.template).length > 2) {
|
||||
if (dynamic && Object.keys(nodeClass!.template).length > 2) {
|
||||
return;
|
||||
}
|
||||
processCode();
|
||||
|
|
@ -90,7 +83,7 @@ export default function CodeAreaModal({
|
|||
}
|
||||
|
||||
function processDynamicField() {
|
||||
postCustomComponent(code, nodeClass)
|
||||
postCustomComponent(code, nodeClass!)
|
||||
.then((apiReturn) => {
|
||||
const { data } = apiReturn;
|
||||
if (data) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { ReactNode, forwardRef, useContext, useState } from "react";
|
||||
import { ReactNode, forwardRef, useContext, useEffect, useState } from "react";
|
||||
import EditFlowSettings from "../../components/EditFlowSettingsComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
|
|
@ -8,77 +8,77 @@ import { TabsContext } from "../../contexts/tabsContext";
|
|||
import { removeApiKeys } from "../../utils/reactflowUtils";
|
||||
import BaseModal from "../baseModal";
|
||||
|
||||
const ExportModal = forwardRef((props: { children: ReactNode }, ref) => {
|
||||
const { flows, tabId, updateFlow, downloadFlow, saveFlow } =
|
||||
useContext(TabsContext);
|
||||
const [checked, setChecked] = useState(false);
|
||||
const [name, setName] = useState(
|
||||
flows.find((flow) => flow.id === tabId).name
|
||||
);
|
||||
const [invalidName, setInvalidName] = useState(false);
|
||||
const [description, setDescription] = useState(
|
||||
flows.find((flow) => flow.id === tabId).description
|
||||
);
|
||||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<BaseModal size="smaller" open={open} setOpen={setOpen}>
|
||||
<BaseModal.Trigger>{props.children}</BaseModal.Trigger>
|
||||
<BaseModal.Header description={EXPORT_DIALOG_SUBTITLE}>
|
||||
<span className="pr-2">Export</span>
|
||||
<IconComponent
|
||||
name="Download"
|
||||
className="h-6 w-6 pl-1 text-foreground"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</BaseModal.Header>
|
||||
<BaseModal.Content>
|
||||
<EditFlowSettings
|
||||
invalidName={invalidName}
|
||||
setInvalidName={setInvalidName}
|
||||
name={name}
|
||||
description={description}
|
||||
flows={flows}
|
||||
tabId={tabId}
|
||||
setName={setName}
|
||||
setDescription={setDescription}
|
||||
updateFlow={updateFlow}
|
||||
/>
|
||||
<div className="mt-3 flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="terms"
|
||||
onCheckedChange={(event: boolean) => {
|
||||
setChecked(event);
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="terms" className="export-modal-save-api text-sm ">
|
||||
Save with my API keys
|
||||
</label>
|
||||
</div>
|
||||
</BaseModal.Content>
|
||||
const ExportModal = forwardRef(
|
||||
(props: { children: ReactNode }, ref): JSX.Element => {
|
||||
const { flows, tabId, updateFlow, downloadFlow } = useContext(TabsContext);
|
||||
const [checked, setChecked] = useState(false);
|
||||
const flow = flows.find((f) => f.id === tabId);
|
||||
useEffect(() => {
|
||||
setName(flow.name);
|
||||
setDescription(flow.description);
|
||||
}, [flow.name, flow.description]);
|
||||
const [name, setName] = useState(flow.name);
|
||||
const [description, setDescription] = useState(flow.description);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
<BaseModal.Footer>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (checked)
|
||||
downloadFlow(
|
||||
flows.find((flow) => flow.id === tabId),
|
||||
name,
|
||||
description
|
||||
);
|
||||
else
|
||||
downloadFlow(
|
||||
removeApiKeys(flows.find((flow) => flow.id === tabId)),
|
||||
name,
|
||||
description
|
||||
);
|
||||
setOpen(false);
|
||||
}}
|
||||
type="submit"
|
||||
>
|
||||
Download Flow
|
||||
</Button>
|
||||
</BaseModal.Footer>
|
||||
</BaseModal>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<BaseModal size="smaller" open={open} setOpen={setOpen}>
|
||||
<BaseModal.Trigger>{props.children}</BaseModal.Trigger>
|
||||
<BaseModal.Header description={EXPORT_DIALOG_SUBTITLE}>
|
||||
<span className="pr-2">Export</span>
|
||||
<IconComponent
|
||||
name="Download"
|
||||
className="h-6 w-6 pl-1 text-foreground"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</BaseModal.Header>
|
||||
<BaseModal.Content>
|
||||
<EditFlowSettings
|
||||
name={name}
|
||||
description={description}
|
||||
flows={flows}
|
||||
tabId={tabId}
|
||||
setName={setName}
|
||||
setDescription={setDescription}
|
||||
updateFlow={updateFlow}
|
||||
/>
|
||||
<div className="mt-3 flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="terms"
|
||||
onCheckedChange={(event: boolean) => {
|
||||
setChecked(event);
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="terms" className="export-modal-save-api text-sm ">
|
||||
Save with my API keys
|
||||
</label>
|
||||
</div>
|
||||
</BaseModal.Content>
|
||||
|
||||
<BaseModal.Footer>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (checked)
|
||||
downloadFlow(
|
||||
flows.find((flow) => flow.id === tabId)!,
|
||||
name!,
|
||||
description
|
||||
);
|
||||
else
|
||||
downloadFlow(
|
||||
removeApiKeys(flows.find((flow) => flow.id === tabId)!),
|
||||
name!,
|
||||
description
|
||||
);
|
||||
setOpen(false);
|
||||
}}
|
||||
type="submit"
|
||||
>
|
||||
Download Flow
|
||||
</Button>
|
||||
</BaseModal.Footer>
|
||||
</BaseModal>
|
||||
);
|
||||
}
|
||||
);
|
||||
export default ExportModal;
|
||||
|
|
|
|||
|
|
@ -1,37 +1,33 @@
|
|||
import { useContext, useRef, useState } from "react";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import EditFlowSettings from "../../components/EditFlowSettingsComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { SETTINGS_DIALOG_SUBTITLE } from "../../constants/constants";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import { FlowSettingsPropsType } from "../../types/components";
|
||||
import BaseModal from "../baseModal";
|
||||
|
||||
export default function FlowSettingsModal({
|
||||
open,
|
||||
setOpen,
|
||||
}: {
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
}) {
|
||||
const { setErrorData, setSuccessData } = useContext(alertContext);
|
||||
const ref = useRef();
|
||||
const { flows, tabId, updateFlow, setTabsState, saveFlow } =
|
||||
useContext(TabsContext);
|
||||
const maxLength = 50;
|
||||
const [name, setName] = useState(
|
||||
flows.find((flow) => flow.id === tabId).name
|
||||
);
|
||||
const [description, setDescription] = useState(
|
||||
flows.find((flow) => flow.id === tabId).description
|
||||
);
|
||||
}: FlowSettingsPropsType): JSX.Element {
|
||||
const { setSuccessData } = useContext(alertContext);
|
||||
const { flows, tabId, updateFlow, saveFlow } = useContext(TabsContext);
|
||||
const flow = flows.find((f) => f.id === tabId);
|
||||
useEffect(() => {
|
||||
setName(flow.name);
|
||||
setDescription(flow.description);
|
||||
}, [flow.name, flow.description]);
|
||||
const [name, setName] = useState(flow.name);
|
||||
const [description, setDescription] = useState(flow.description);
|
||||
const [invalidName, setInvalidName] = useState(false);
|
||||
|
||||
function handleClick() {
|
||||
function handleClick(): void {
|
||||
let savedFlow = flows.find((flow) => flow.id === tabId);
|
||||
savedFlow.name = name;
|
||||
savedFlow.description = description;
|
||||
saveFlow(savedFlow);
|
||||
savedFlow!.name = name;
|
||||
savedFlow!.description = description;
|
||||
saveFlow(savedFlow!);
|
||||
setSuccessData({ title: "Changes saved successfully" });
|
||||
setOpen(false);
|
||||
}
|
||||
|
|
@ -51,7 +47,6 @@ export default function FlowSettingsModal({
|
|||
tabId={tabId}
|
||||
setName={setName}
|
||||
setDescription={setDescription}
|
||||
updateFlow={updateFlow}
|
||||
/>
|
||||
</BaseModal.Content>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useEffect } from "react";
|
||||
import IconComponent from "../../../components/genericIconComponent";
|
||||
import { Textarea } from "../../../components/ui/textarea";
|
||||
import { chatInputType } from "../../../types/components";
|
||||
import { classNames } from "../../../utils/utils";
|
||||
|
||||
export default function ChatInput({
|
||||
|
|
@ -10,7 +11,7 @@ export default function ChatInput({
|
|||
setChatValue,
|
||||
inputRef,
|
||||
noInput,
|
||||
}) {
|
||||
}: chatInputType): JSX.Element {
|
||||
useEffect(() => {
|
||||
if (!lockChat && inputRef.current) {
|
||||
inputRef.current.focus();
|
||||
|
|
@ -46,7 +47,7 @@ export default function ChatInput({
|
|||
}`,
|
||||
}}
|
||||
value={lockChat ? "Thinking..." : chatValue}
|
||||
onChange={(event) => {
|
||||
onChange={(event): void => {
|
||||
setChatValue(event.target.value);
|
||||
}}
|
||||
className={classNames(
|
||||
|
|
@ -75,7 +76,7 @@ export default function ChatInput({
|
|||
: "bg-chat-send text-background"
|
||||
)}
|
||||
disabled={lockChat}
|
||||
onClick={() => sendMessage()}
|
||||
onClick={(): void => sendMessage()}
|
||||
>
|
||||
{lockChat ? (
|
||||
<IconComponent
|
||||
|
|
|
|||
|
|
@ -3,16 +3,12 @@ import { useState } from "react";
|
|||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
||||
import { programmingLanguages } from "../../../../constants/constants";
|
||||
import { Props } from "../../../../types/components";
|
||||
|
||||
interface Props {
|
||||
language: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export function CodeBlock({ language, value }) {
|
||||
export function CodeBlock({ language, value }: Props): JSX.Element {
|
||||
const [isCopied, setIsCopied] = useState<Boolean>(false);
|
||||
|
||||
const copyToClipboard = () => {
|
||||
const copyToClipboard = (): void => {
|
||||
if (!navigator.clipboard || !navigator.clipboard.writeText) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -25,7 +21,7 @@ export function CodeBlock({ language, value }) {
|
|||
}, 2000);
|
||||
});
|
||||
};
|
||||
const downloadAsFile = () => {
|
||||
const downloadAsFile = (): void => {
|
||||
const fileExtension = programmingLanguages[language] || ".file";
|
||||
const suggestedFileName = `${"generated-code"}${fileExtension}`;
|
||||
const fileName = window.prompt("enter file name", suggestedFileName);
|
||||
|
|
|
|||
|
|
@ -9,18 +9,15 @@ import Robot from "../../../assets/robot.png";
|
|||
import SanitizedHTMLWrapper from "../../../components/SanitizedHTMLWrapper";
|
||||
import CodeTabsComponent from "../../../components/codeTabsComponent";
|
||||
import IconComponent from "../../../components/genericIconComponent";
|
||||
import { ChatMessageType } from "../../../types/chat";
|
||||
import { chatMessagePropsType } from "../../../types/components";
|
||||
import { classNames } from "../../../utils/utils";
|
||||
import FileCard from "../fileComponent";
|
||||
|
||||
export default function ChatMessage({
|
||||
chat,
|
||||
lockChat,
|
||||
lastMessage,
|
||||
}: {
|
||||
chat: ChatMessageType;
|
||||
lockChat: boolean;
|
||||
lastMessage: boolean;
|
||||
}) {
|
||||
}: chatMessagePropsType): JSX.Element {
|
||||
const convert = new Convert({ newline: true });
|
||||
const [hidden, setHidden] = useState(true);
|
||||
const template = chat.template;
|
||||
|
|
@ -57,7 +54,7 @@ export default function ChatMessage({
|
|||
<div className="form-modal-chat-text">
|
||||
{hidden && chat.thought && chat.thought !== "" && (
|
||||
<div
|
||||
onClick={() => setHidden((prev) => !prev)}
|
||||
onClick={(): void => setHidden((prev) => !prev)}
|
||||
className="form-modal-chat-icon-div"
|
||||
>
|
||||
<IconComponent
|
||||
|
|
@ -192,7 +189,7 @@ export default function ChatMessage({
|
|||
? template?.split("\n")?.map((line, index) => {
|
||||
const regex = /{([^}]+)}/g;
|
||||
let match;
|
||||
let parts = [];
|
||||
let parts: Array<JSX.Element | string> = [];
|
||||
let lastIndex = 0;
|
||||
while ((match = regex.exec(line)) !== null) {
|
||||
// Push text up to the match
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
import * as base64js from "base64-js";
|
||||
import { useState } from "react";
|
||||
import IconComponent from "../../../components/genericIconComponent";
|
||||
import { fileCardPropsType } from "../../../types/components";
|
||||
|
||||
export default function FileCard({ fileName, content, fileType }) {
|
||||
const handleDownload = () => {
|
||||
export default function FileCard({
|
||||
fileName,
|
||||
content,
|
||||
fileType,
|
||||
}: fileCardPropsType): JSX.Element {
|
||||
const handleDownload = (): void => {
|
||||
const byteArray = new Uint8Array(base64js.toByteArray(content));
|
||||
const blob = new Blob([byteArray], { type: "application/octet-stream" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
|
@ -16,10 +21,10 @@ export default function FileCard({ fileName, content, fileType }) {
|
|||
URL.revokeObjectURL(url);
|
||||
};
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
function handleMouseEnter() {
|
||||
function handleMouseEnter(): void {
|
||||
setIsHovered(true);
|
||||
}
|
||||
function handleMouseLeave() {
|
||||
function handleMouseLeave(): void {
|
||||
setIsHovered(false);
|
||||
}
|
||||
|
||||
|
|
@ -55,7 +60,7 @@ export default function FileCard({ fileName, content, fileType }) {
|
|||
return (
|
||||
<button onClick={handleDownload} className="file-card-modal-button">
|
||||
<div className="file-card-modal-div">
|
||||
{" "}
|
||||
ooooooooooooooo{" "}
|
||||
{fileType === "image" ? (
|
||||
<img
|
||||
src={`data:image/png;base64,${content}`}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import {
|
|||
import { Textarea } from "../../components/ui/textarea";
|
||||
import { CHAT_FORM_DIALOG_SUBTITLE } from "../../constants/constants";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import { TabsState } from "../../types/tabs";
|
||||
import { validateNodes } from "../../utils/reactflowUtils";
|
||||
|
||||
export default function FormModal({
|
||||
|
|
@ -34,7 +35,7 @@ export default function FormModal({
|
|||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
flow: FlowType;
|
||||
}) {
|
||||
}): JSX.Element {
|
||||
const { tabsState, setTabsState } = useContext(TabsContext);
|
||||
const [chatValue, setChatValue] = useState(() => {
|
||||
try {
|
||||
|
|
@ -45,11 +46,11 @@ export default function FormModal({
|
|||
const inputKeys = formKeysData.input_keys;
|
||||
const handleKeys = formKeysData.handle_keys;
|
||||
|
||||
const keyToUse = Object.keys(inputKeys).find(
|
||||
(key) => !handleKeys.some((j) => j === key) && inputKeys[key] === ""
|
||||
const keyToUse = Object.keys(inputKeys!).find(
|
||||
(key) => !handleKeys?.some((j) => j === key) && inputKeys![key] === ""
|
||||
);
|
||||
|
||||
return inputKeys[keyToUse];
|
||||
return inputKeys![keyToUse!];
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
// return a sensible default or `undefined` if no default is possible
|
||||
|
|
@ -63,16 +64,17 @@ export default function FormModal({
|
|||
const ws = useRef<WebSocket | null>(null);
|
||||
const [lockChat, setLockChat] = useState(false);
|
||||
const isOpen = useRef(open);
|
||||
const messagesRef = useRef(null);
|
||||
const messagesRef = useRef<HTMLDivElement | null>(null);
|
||||
const id = useRef(flow.id);
|
||||
const tabsStateFlowId = tabsState[flow.id];
|
||||
const tabsStateFlowIdFormKeysData = tabsStateFlowId.formKeysData;
|
||||
const [chatKey, setChatKey] = useState(() => {
|
||||
if (tabsState[flow.id]?.formKeysData?.input_keys) {
|
||||
return Object.keys(tabsState[flow.id].formKeysData.input_keys).find(
|
||||
return Object.keys(tabsState[flow.id].formKeysData.input_keys!).find(
|
||||
(key) =>
|
||||
!tabsState[flow.id].formKeysData.handle_keys.some((j) => j === key) &&
|
||||
tabsState[flow.id].formKeysData.input_keys[key] === ""
|
||||
!tabsState[flow.id].formKeysData.handle_keys!.some(
|
||||
(j) => j === key
|
||||
) && tabsState[flow.id].formKeysData.input_keys![key] === ""
|
||||
);
|
||||
}
|
||||
// TODO: return a sensible default
|
||||
|
|
@ -149,7 +151,7 @@ export default function FormModal({
|
|||
});
|
||||
}
|
||||
|
||||
function handleOnClose(event: CloseEvent) {
|
||||
function handleOnClose(event: CloseEvent): void {
|
||||
if (isOpen.current) {
|
||||
setErrorData({ title: event.reason });
|
||||
setTimeout(() => {
|
||||
|
|
@ -159,7 +161,10 @@ export default function FormModal({
|
|||
}
|
||||
}
|
||||
|
||||
function getWebSocketUrl(chatId, isDevelopment = false) {
|
||||
function getWebSocketUrl(
|
||||
chatId: string,
|
||||
isDevelopment: boolean = false
|
||||
): string {
|
||||
const isSecureProtocol =
|
||||
window.location.protocol === "https:" || window.location.port === "443";
|
||||
const webSocketProtocol = isSecureProtocol ? "wss" : "ws";
|
||||
|
|
@ -212,7 +217,7 @@ export default function FormModal({
|
|||
});
|
||||
}
|
||||
if (data.type === "start") {
|
||||
addChatHistory("", false, chatKey);
|
||||
addChatHistory("", false, chatKey!);
|
||||
isStream = true;
|
||||
}
|
||||
if (data.type === "end") {
|
||||
|
|
@ -241,7 +246,7 @@ export default function FormModal({
|
|||
}
|
||||
}
|
||||
|
||||
function connectWS() {
|
||||
function connectWS(): void {
|
||||
try {
|
||||
const urlWs = getWebSocketUrl(
|
||||
id.current,
|
||||
|
|
@ -308,15 +313,15 @@ export default function FormModal({
|
|||
// do not add connectWS on dependencies array
|
||||
}, [lockChat]);
|
||||
|
||||
async function sendAll(data: sendAllProps) {
|
||||
async function sendAll(data: sendAllProps): Promise<void> {
|
||||
try {
|
||||
if (ws) {
|
||||
ws.current.send(JSON.stringify(data));
|
||||
ws.current?.send(JSON.stringify(data));
|
||||
}
|
||||
} catch (error) {
|
||||
setErrorData({
|
||||
title: "There was an error sending the message",
|
||||
list: [error.message],
|
||||
list: [(error as { message: string }).message],
|
||||
});
|
||||
setChatValue(data.inputs);
|
||||
connectWS();
|
||||
|
|
@ -327,7 +332,7 @@ export default function FormModal({
|
|||
if (ref.current) ref.current.scrollIntoView({ behavior: "smooth" });
|
||||
}, [chatHistory]);
|
||||
|
||||
const ref = useRef(null);
|
||||
const ref = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (open && ref.current) {
|
||||
|
|
@ -335,30 +340,31 @@ export default function FormModal({
|
|||
}
|
||||
}, [open]);
|
||||
|
||||
function sendMessage() {
|
||||
let nodeValidationErrors = validateNodes(reactFlowInstance);
|
||||
function sendMessage(): void {
|
||||
let nodeValidationErrors = validateNodes(reactFlowInstance!);
|
||||
if (nodeValidationErrors.length === 0) {
|
||||
setLockChat(true);
|
||||
let inputs = tabsState[id.current].formKeysData.input_keys;
|
||||
setChatValue("");
|
||||
const message = inputs;
|
||||
addChatHistory(
|
||||
message,
|
||||
message!,
|
||||
true,
|
||||
chatKey,
|
||||
chatKey!,
|
||||
tabsState[flow.id].formKeysData.template
|
||||
);
|
||||
sendAll({
|
||||
...reactFlowInstance.toObject(),
|
||||
inputs: inputs,
|
||||
...reactFlowInstance?.toObject()!,
|
||||
inputs: inputs!,
|
||||
chatHistory,
|
||||
name: flow.name,
|
||||
description: flow.description,
|
||||
});
|
||||
setTabsState((old) => {
|
||||
//@ts-ignore
|
||||
setTabsState((old: TabsState) => {
|
||||
if (!chatKey) return old;
|
||||
let newTabsState = _.cloneDeep(old);
|
||||
newTabsState[id.current].formKeysData.input_keys[chatKey] = "";
|
||||
newTabsState[id.current].formKeysData.input_keys![chatKey] = "";
|
||||
return newTabsState;
|
||||
});
|
||||
} else {
|
||||
|
|
@ -368,18 +374,18 @@ export default function FormModal({
|
|||
});
|
||||
}
|
||||
}
|
||||
function clearChat() {
|
||||
function clearChat(): void {
|
||||
setChatHistory([]);
|
||||
ws.current.send(JSON.stringify({ clear_history: true }));
|
||||
ws.current?.send(JSON.stringify({ clear_history: true }));
|
||||
if (lockChat) setLockChat(false);
|
||||
}
|
||||
|
||||
function handleOnCheckedChange(checked: boolean, i: string) {
|
||||
if (checked === true) {
|
||||
setChatKey(i);
|
||||
setChatValue(tabsState[flow.id].formKeysData.input_keys[i]);
|
||||
setChatValue(tabsState[flow.id].formKeysData.input_keys![i]);
|
||||
} else {
|
||||
setChatKey(null);
|
||||
setChatKey(null!);
|
||||
setChatValue("");
|
||||
}
|
||||
}
|
||||
|
|
@ -425,7 +431,7 @@ export default function FormModal({
|
|||
|
||||
{tabsState[id.current]?.formKeysData?.input_keys
|
||||
? Object.keys(
|
||||
tabsState[id.current].formKeysData.input_keys
|
||||
tabsState[id.current].formKeysData.input_keys!
|
||||
).map((key, index) => (
|
||||
<div className="file-component-accordion-div" key={index}>
|
||||
<AccordionComponent
|
||||
|
|
@ -449,7 +455,7 @@ export default function FormModal({
|
|||
size="small"
|
||||
disabled={tabsState[
|
||||
id.current
|
||||
].formKeysData.handle_keys.some(
|
||||
].formKeysData.handle_keys!.some(
|
||||
(t) => t === key
|
||||
)}
|
||||
/>
|
||||
|
|
@ -460,7 +466,7 @@ export default function FormModal({
|
|||
keyValue={key}
|
||||
>
|
||||
<div className="file-component-tab-column">
|
||||
{tabsState[id.current].formKeysData.handle_keys.some(
|
||||
{tabsState[id.current].formKeysData.handle_keys!.some(
|
||||
(t) => t === key
|
||||
) && (
|
||||
<div className="font-normal text-muted-foreground ">
|
||||
|
|
@ -470,14 +476,18 @@ export default function FormModal({
|
|||
<Textarea
|
||||
className="custom-scroll"
|
||||
value={
|
||||
tabsState[id.current].formKeysData.input_keys[key]
|
||||
tabsState[id.current].formKeysData.input_keys![
|
||||
key
|
||||
]
|
||||
}
|
||||
onChange={(e) => {
|
||||
setTabsState((old) => {
|
||||
//@ts-ignore
|
||||
setTabsState((old: TabsState) => {
|
||||
let newTabsState = _.cloneDeep(old);
|
||||
newTabsState[
|
||||
id.current
|
||||
].formKeysData.input_keys[key] = e.target.value;
|
||||
].formKeysData.input_keys![key] =
|
||||
e.target.value;
|
||||
return newTabsState;
|
||||
});
|
||||
}}
|
||||
|
|
@ -489,7 +499,7 @@ export default function FormModal({
|
|||
</div>
|
||||
))
|
||||
: null}
|
||||
{tabsState[id.current].formKeysData.memory_keys.map(
|
||||
{tabsState[id.current].formKeysData.memory_keys!.map(
|
||||
(key, index) => (
|
||||
<div className="file-component-accordion-div" key={index}>
|
||||
<AccordionComponent
|
||||
|
|
@ -583,10 +593,11 @@ export default function FormModal({
|
|||
sendMessage={sendMessage}
|
||||
setChatValue={(value) => {
|
||||
setChatValue(value);
|
||||
setTabsState((old) => {
|
||||
//@ts-ignore
|
||||
setTabsState((old: TabsState) => {
|
||||
let newTabsState = _.cloneDeep(old);
|
||||
newTabsState[id.current].formKeysData.input_keys[
|
||||
chatKey
|
||||
newTabsState[id.current].formKeysData.input_keys![
|
||||
chatKey!
|
||||
] = value;
|
||||
return newTabsState;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { ReactNode, useContext, useEffect, useRef, useState } from "react";
|
||||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
import SanitizedHTMLWrapper from "../../components/SanitizedHTMLWrapper";
|
||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
|
|
@ -15,7 +15,8 @@ import {
|
|||
import { TypeModal } from "../../constants/enums";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { postValidatePrompt } from "../../controllers/API";
|
||||
import { APIClassType } from "../../types/api";
|
||||
import { genericModalPropsType } from "../../types/components";
|
||||
import { handleKeyDown } from "../../utils/reactflowUtils";
|
||||
import {
|
||||
classNames,
|
||||
getRandomKeyByssmm,
|
||||
|
|
@ -33,39 +34,29 @@ export default function GenericModal({
|
|||
nodeClass,
|
||||
setNodeClass,
|
||||
children,
|
||||
}: {
|
||||
field_name?: string;
|
||||
setValue: (value: string) => void;
|
||||
value: string;
|
||||
buttonText: string;
|
||||
modalTitle: string;
|
||||
type: number;
|
||||
children: ReactNode;
|
||||
nodeClass?: APIClassType;
|
||||
setNodeClass?: (Class: APIClassType) => void;
|
||||
}) {
|
||||
}: genericModalPropsType): JSX.Element {
|
||||
const [myButtonText] = useState(buttonText);
|
||||
const [myModalTitle] = useState(modalTitle);
|
||||
const [myModalType] = useState(type);
|
||||
const [inputValue, setInputValue] = useState(value);
|
||||
const [isEdit, setIsEdit] = useState(true);
|
||||
const [wordsHighlight, setWordsHighlight] = useState([]);
|
||||
const [wordsHighlight, setWordsHighlight] = useState<string[]>([]);
|
||||
const { setErrorData, setSuccessData, setNoticeData } =
|
||||
useContext(alertContext);
|
||||
const ref = useRef();
|
||||
const divRef = useRef(null);
|
||||
const divRefPrompt = useRef(null);
|
||||
|
||||
function checkVariables(valueToCheck) {
|
||||
function checkVariables(valueToCheck: string): void {
|
||||
const regex = /\{([^{}]+)\}/g;
|
||||
const matches = [];
|
||||
const matches: string[] = [];
|
||||
let match;
|
||||
while ((match = regex.exec(valueToCheck))) {
|
||||
matches.push(`{${match[1]}}`);
|
||||
}
|
||||
|
||||
let invalid_chars = [];
|
||||
let fixed_variables = [];
|
||||
let invalid_chars: string[] = [];
|
||||
let fixed_variables: string[] = [];
|
||||
let input_variables = matches;
|
||||
for (let variable of input_variables) {
|
||||
let new_var = variable;
|
||||
|
|
@ -106,7 +97,7 @@ export default function GenericModal({
|
|||
.replace(regexHighlight, varHighlightHTML({ name: "$1" }))
|
||||
.replace(/\n/g, "<br />");
|
||||
|
||||
const TextAreaContentView = () => {
|
||||
const TextAreaContentView = (): JSX.Element => {
|
||||
return (
|
||||
<SanitizedHTMLWrapper
|
||||
className={getClassByNumberLength()}
|
||||
|
|
@ -119,7 +110,7 @@ export default function GenericModal({
|
|||
);
|
||||
};
|
||||
|
||||
function getClassByNumberLength() {
|
||||
function getClassByNumberLength(): string {
|
||||
let sumOfCaracteres: number = 0;
|
||||
wordsHighlight.forEach((element) => {
|
||||
sumOfCaracteres = sumOfCaracteres + element.replace(/[{}]/g, "").length;
|
||||
|
|
@ -129,8 +120,8 @@ export default function GenericModal({
|
|||
: "code-nohighlight";
|
||||
}
|
||||
|
||||
function validatePrompt(closeModal: boolean) {
|
||||
postValidatePrompt(field_name, inputValue, nodeClass)
|
||||
function validatePrompt(closeModal: boolean): void {
|
||||
postValidatePrompt(field_name, inputValue, nodeClass!)
|
||||
.then((apiReturn) => {
|
||||
if (apiReturn.data) {
|
||||
let inputVariables = apiReturn.data.input_variables ?? [];
|
||||
|
|
@ -144,7 +135,7 @@ export default function GenericModal({
|
|||
setSuccessData({
|
||||
title: "Prompt is ready",
|
||||
});
|
||||
setNodeClass(apiReturn.data?.frontend_node);
|
||||
setNodeClass!(apiReturn.data?.frontend_node);
|
||||
setModalOpen(closeModal);
|
||||
setValue(inputValue);
|
||||
}
|
||||
|
|
@ -213,11 +204,15 @@ export default function GenericModal({
|
|||
checkVariables(event.target.value);
|
||||
}}
|
||||
placeholder="Type message here."
|
||||
onKeyDown={(e) => {
|
||||
handleKeyDown(e, inputValue, "");
|
||||
}}
|
||||
/>
|
||||
) : type === TypeModal.PROMPT && !isEdit ? (
|
||||
<TextAreaContentView />
|
||||
) : type !== TypeModal.PROMPT ? (
|
||||
<Textarea
|
||||
//@ts-ignore
|
||||
ref={ref}
|
||||
className="form-input h-full w-full rounded-lg focus-visible:ring-1"
|
||||
value={inputValue}
|
||||
|
|
@ -225,6 +220,9 @@ export default function GenericModal({
|
|||
setInputValue(event.target.value);
|
||||
}}
|
||||
placeholder="Type message here."
|
||||
onKeyDown={(e) => {
|
||||
handleKeyDown(e, value, "");
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { ReactNode } from "react";
|
||||
import { buttonBoxPropsType } from "../../../types/components";
|
||||
import { classNames } from "../../../utils/utils";
|
||||
|
||||
export default function ButtonBox({
|
||||
|
|
@ -10,16 +10,7 @@ export default function ButtonBox({
|
|||
textColor,
|
||||
deactivate,
|
||||
size,
|
||||
}: {
|
||||
onClick: () => void;
|
||||
title: string;
|
||||
description: string;
|
||||
icon: ReactNode;
|
||||
bgColor: string;
|
||||
textColor: string;
|
||||
deactivate?: boolean;
|
||||
size: "small" | "medium" | "big";
|
||||
}) {
|
||||
}: buttonBoxPropsType): JSX.Element {
|
||||
let bigCircle: string;
|
||||
let smallCircle: string;
|
||||
let titleFontSize: string;
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import { FlowType } from "../../types/flow";
|
|||
import { classNames } from "../../utils/utils";
|
||||
import ButtonBox from "./buttonBox";
|
||||
|
||||
export default function ImportModal() {
|
||||
export default function ImportModal(): JSX.Element {
|
||||
const [open, setOpen] = useState(true);
|
||||
const { setErrorData } = useContext(alertContext);
|
||||
const ref = useRef();
|
||||
|
|
@ -32,7 +32,7 @@ export default function ImportModal() {
|
|||
const [examples, setExamples] = useState<FlowType[]>([]);
|
||||
const { uploadFlow, addFlow } = useContext(TabsContext);
|
||||
|
||||
function handleExamples() {
|
||||
function handleExamples(): void {
|
||||
setLoadingExamples(true);
|
||||
getExamples()
|
||||
.then((result) => {
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@ import { TabsContext } from "../../contexts/tabsContext";
|
|||
import { useNavigate } from "react-router-dom";
|
||||
import { CardComponent } from "../../components/cardComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import Header from "../../components/headerComponent";
|
||||
import { getExamples } from "../../controllers/API";
|
||||
import { FlowType } from "../../types/flow";
|
||||
export default function CommunityPage() {
|
||||
export default function CommunityPage(): JSX.Element {
|
||||
const { flows, setTabId, downloadFlows, uploadFlows, addFlow } =
|
||||
useContext(TabsContext);
|
||||
|
||||
|
|
@ -21,7 +22,7 @@ export default function CommunityPage() {
|
|||
const [examples, setExamples] = useState<FlowType[]>([]);
|
||||
|
||||
// Show community examples on screen
|
||||
function handleExamples() {
|
||||
function handleExamples(): void {
|
||||
setLoadingExamples(true);
|
||||
getExamples()
|
||||
.then((result) => {
|
||||
|
|
@ -42,61 +43,65 @@ export default function CommunityPage() {
|
|||
handleExamples();
|
||||
}, []);
|
||||
return (
|
||||
<div className="community-page-arrangement">
|
||||
<div className="community-page-nav-arrangement">
|
||||
<span className="community-page-nav-title">
|
||||
<IconComponent name="Users2" className="w-6" />
|
||||
Community Examples
|
||||
<>
|
||||
<Header />
|
||||
|
||||
<div className="community-page-arrangement">
|
||||
<div className="community-page-nav-arrangement">
|
||||
<span className="community-page-nav-title">
|
||||
<IconComponent name="Users2" className="w-6" />
|
||||
Community Examples
|
||||
</span>
|
||||
<div className="community-page-nav-button">
|
||||
<a
|
||||
href="https://github.com/logspace-ai/langflow_examples"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<Button variant="primary">
|
||||
<IconComponent
|
||||
name="GithubIcon"
|
||||
className="main-page-nav-button"
|
||||
/>
|
||||
Add Your Example
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<span className="community-page-description-text">
|
||||
Discover and learn from shared examples by the Langflow community. We
|
||||
welcome new example contributions that can help our community explore
|
||||
new and powerful features.
|
||||
</span>
|
||||
<div className="community-page-nav-button">
|
||||
<a
|
||||
href="https://github.com/logspace-ai/langflow_examples"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<Button variant="primary">
|
||||
<IconComponent
|
||||
name="GithubIcon"
|
||||
className="main-page-nav-button"
|
||||
<div className="community-pages-flows-panel">
|
||||
{!loadingExamples &&
|
||||
examples.map((flow, idx) => (
|
||||
<CardComponent
|
||||
key={idx}
|
||||
flow={flow}
|
||||
id={flow.id}
|
||||
button={
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="whitespace-nowrap "
|
||||
onClick={() => {
|
||||
addFlow(flow, true).then((id) => {
|
||||
navigate("/flow/" + id);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<IconComponent
|
||||
name="GitFork"
|
||||
className="main-page-nav-button"
|
||||
/>
|
||||
Fork Example
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
Add Your Example
|
||||
</Button>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<span className="community-page-description-text">
|
||||
Discover and learn from shared examples by the Langflow community. We
|
||||
welcome new example contributions that can help our community explore
|
||||
new and powerful features.
|
||||
</span>
|
||||
<div className="community-pages-flows-panel">
|
||||
{!loadingExamples &&
|
||||
examples.map((flow, idx) => (
|
||||
<CardComponent
|
||||
key={idx}
|
||||
flow={flow}
|
||||
id={flow.id}
|
||||
button={
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="whitespace-nowrap "
|
||||
onClick={() => {
|
||||
addFlow(flow, true).then((id) => {
|
||||
navigate("/flow/" + id);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<IconComponent
|
||||
name="GitFork"
|
||||
className="main-page-nav-button"
|
||||
/>
|
||||
Fork Example
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ const ConnectionLineComponent = ({
|
|||
toX,
|
||||
toY,
|
||||
connectionLineStyle = {}, // provide a default value for connectionLineStyle
|
||||
}: ConnectionLineComponentProps) => {
|
||||
}: ConnectionLineComponentProps): JSX.Element => {
|
||||
return (
|
||||
<g>
|
||||
<path
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ export default function DisclosureComponent({
|
|||
button: { title, Icon, buttons = [] },
|
||||
children,
|
||||
openDisc,
|
||||
}: DisclosureComponentType) {
|
||||
}: DisclosureComponentType): JSX.Element {
|
||||
return (
|
||||
<Disclosure as="div" key={title}>
|
||||
{({ open }) => (
|
||||
|
|
|
|||
|
|
@ -1,11 +1,19 @@
|
|||
import _ from "lodash";
|
||||
import { useCallback, useContext, useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
MouseEvent,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import ReactFlow, {
|
||||
Background,
|
||||
Connection,
|
||||
Controls,
|
||||
Edge,
|
||||
EdgeChange,
|
||||
Node,
|
||||
NodeChange,
|
||||
NodeDragHandler,
|
||||
OnEdgesDelete,
|
||||
|
|
@ -26,6 +34,7 @@ import { typesContext } from "../../../../contexts/typesContext";
|
|||
import { undoRedoContext } from "../../../../contexts/undoRedoContext";
|
||||
import { APIClassType } from "../../../../types/api";
|
||||
import { FlowType, NodeType } from "../../../../types/flow";
|
||||
import { TabsState } from "../../../../types/tabs";
|
||||
import { isValidConnection } from "../../../../utils/reactflowUtils";
|
||||
import { isWrappedWithClass } from "../../../../utils/utils";
|
||||
import ConnectionLineComponent from "../ConnectionLineComponent";
|
||||
|
|
@ -35,7 +44,13 @@ const nodeTypes = {
|
|||
genericNode: GenericNode,
|
||||
};
|
||||
|
||||
export default function Page({ flow }: { flow: FlowType }) {
|
||||
export default function Page({
|
||||
flow,
|
||||
view,
|
||||
}: {
|
||||
flow: FlowType;
|
||||
view?: boolean;
|
||||
}): JSX.Element {
|
||||
let {
|
||||
updateFlow,
|
||||
uploadFlow,
|
||||
|
|
@ -51,13 +66,13 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
} = useContext(TabsContext);
|
||||
const { types, reactFlowInstance, setReactFlowInstance, templates } =
|
||||
useContext(typesContext);
|
||||
const reactFlowWrapper = useRef(null);
|
||||
const reactFlowWrapper = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { takeSnapshot } = useContext(undoRedoContext);
|
||||
|
||||
const [position, setPosition] = useState({ x: 0, y: 0 });
|
||||
const [lastSelection, setLastSelection] =
|
||||
useState<OnSelectionChangeParams>(null);
|
||||
useState<OnSelectionChangeParams | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// this effect is used to attach the global event handlers
|
||||
|
|
@ -78,10 +93,10 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
lastCopiedSelection
|
||||
) {
|
||||
event.preventDefault();
|
||||
let bounds = reactFlowWrapper.current.getBoundingClientRect();
|
||||
let bounds = reactFlowWrapper.current?.getBoundingClientRect();
|
||||
paste(lastCopiedSelection, {
|
||||
x: position.x - bounds.left,
|
||||
y: position.y - bounds.top,
|
||||
x: position.x - bounds!.left,
|
||||
y: position.y - bounds!.top,
|
||||
});
|
||||
}
|
||||
if (
|
||||
|
|
@ -147,7 +162,8 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
let newX = _.cloneDeep(node);
|
||||
return newX;
|
||||
});
|
||||
setTabsState((prev) => {
|
||||
//@ts-ignore
|
||||
setTabsState((prev: TabsState) => {
|
||||
return {
|
||||
...prev,
|
||||
[tabId]: {
|
||||
|
|
@ -163,7 +179,8 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
const onNodesChangeMod = useCallback(
|
||||
(change: NodeChange[]) => {
|
||||
onNodesChange(change);
|
||||
setTabsState((prev) => {
|
||||
//@ts-ignore
|
||||
setTabsState((prev: TabsState) => {
|
||||
return {
|
||||
...prev,
|
||||
[tabId]: {
|
||||
|
|
@ -185,10 +202,10 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
...params,
|
||||
style: { stroke: "#555" },
|
||||
className:
|
||||
(params.targetHandle.split("|")[0] === "Text"
|
||||
(params.targetHandle?.split("|")[0] === "Text"
|
||||
? "stroke-foreground "
|
||||
: "stroke-foreground ") + " stroke-connection",
|
||||
animated: params.targetHandle.split("|")[0] === "Text",
|
||||
animated: params.targetHandle?.split("|")[0] === "Text",
|
||||
},
|
||||
eds
|
||||
)
|
||||
|
|
@ -234,7 +251,7 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
|
||||
// Get the current bounds of the ReactFlow wrapper element
|
||||
const reactflowBounds =
|
||||
reactFlowWrapper.current.getBoundingClientRect();
|
||||
reactFlowWrapper.current?.getBoundingClientRect();
|
||||
|
||||
// Extract the data from the drag event and parse it as a JSON object
|
||||
let data: { type: string; node?: APIClassType } = JSON.parse(
|
||||
|
|
@ -243,9 +260,9 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
|
||||
// If data type is not "chatInput" or if there are no "chatInputNode" nodes present in the ReactFlow instance, create a new node
|
||||
// Calculate the position where the node should be created
|
||||
const position = reactFlowInstance.project({
|
||||
x: event.clientX - reactflowBounds.left,
|
||||
y: event.clientY - reactflowBounds.top,
|
||||
const position = reactFlowInstance!.project({
|
||||
x: event.clientX - reactflowBounds!.left,
|
||||
y: event.clientY - reactflowBounds!.top,
|
||||
});
|
||||
|
||||
// Generate a unique node ID
|
||||
|
|
@ -262,7 +279,6 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
data: {
|
||||
...data,
|
||||
id: newId,
|
||||
value: null,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
|
|
@ -274,7 +290,6 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
data: {
|
||||
...data,
|
||||
id: newId,
|
||||
value: null,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -283,7 +298,7 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
setNodes((nds) => nds.concat(newNode));
|
||||
} else if (event.dataTransfer.types.some((types) => types === "Files")) {
|
||||
takeSnapshot();
|
||||
uploadFlow(false, event.dataTransfer.files.item(0));
|
||||
uploadFlow(false, event.dataTransfer.files.item(0)!);
|
||||
}
|
||||
},
|
||||
// Specify dependencies for useCallback
|
||||
|
|
@ -299,7 +314,7 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
}, []);
|
||||
|
||||
const onDelete = useCallback(
|
||||
(mynodes) => {
|
||||
(mynodes: Node[]) => {
|
||||
takeSnapshot();
|
||||
setEdges(
|
||||
edges.filter(
|
||||
|
|
@ -319,7 +334,7 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
|
||||
const onEdgeUpdate = useCallback(
|
||||
(oldEdge: Edge, newConnection: Connection) => {
|
||||
if (isValidConnection(newConnection, reactFlowInstance)) {
|
||||
if (isValidConnection(newConnection, reactFlowInstance!)) {
|
||||
edgeUpdateSuccessful.current = true;
|
||||
setEdges((els) => updateEdge(oldEdge, newConnection, els));
|
||||
}
|
||||
|
|
@ -327,11 +342,10 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
[reactFlowInstance, setEdges]
|
||||
);
|
||||
|
||||
const onEdgeUpdateEnd = useCallback((_, edge) => {
|
||||
const onEdgeUpdateEnd = useCallback((_, edge: Edge): void => {
|
||||
if (!edgeUpdateSuccessful.current) {
|
||||
setEdges((eds) => eds.filter((edg) => edg.id !== edge.id));
|
||||
}
|
||||
|
||||
edgeUpdateSuccessful.current = true;
|
||||
}, []);
|
||||
|
||||
|
|
@ -340,7 +354,7 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
const onSelectionEnd = useCallback(() => {
|
||||
setSelectionEnded(true);
|
||||
}, []);
|
||||
const onSelectionStart = useCallback((event) => {
|
||||
const onSelectionStart = useCallback((event: MouseEvent) => {
|
||||
event.preventDefault();
|
||||
setSelectionEnded(false);
|
||||
}, []);
|
||||
|
|
@ -354,13 +368,16 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
}
|
||||
}, [selectionEnded, lastSelection]);
|
||||
|
||||
const onSelectionChange = useCallback((flow) => {
|
||||
setLastSelection(flow);
|
||||
}, []);
|
||||
const onSelectionChange = useCallback(
|
||||
(flow: OnSelectionChangeParams): void => {
|
||||
setLastSelection(flow);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex h-full overflow-hidden">
|
||||
<ExtraSidebar />
|
||||
{!view && <ExtraSidebar />}
|
||||
{/* Main area */}
|
||||
<main className="flex flex-1">
|
||||
{/* Primary column */}
|
||||
|
|
@ -383,7 +400,6 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
onEdgesChange={onEdgesChangeMod}
|
||||
onConnect={onConnect}
|
||||
disableKeyboardA11y={true}
|
||||
onLoad={setReactFlowInstance}
|
||||
onInit={setReactFlowInstance}
|
||||
nodeTypes={nodeTypes}
|
||||
onEdgeUpdate={onEdgeUpdate}
|
||||
|
|
@ -402,14 +418,21 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
className="theme-attribution"
|
||||
minZoom={0.01}
|
||||
maxZoom={8}
|
||||
zoomOnScroll={!view}
|
||||
zoomOnPinch={!view}
|
||||
panOnDrag={!view}
|
||||
>
|
||||
<Background className="" />
|
||||
<Controls
|
||||
className="bg-muted fill-foreground stroke-foreground text-primary
|
||||
{!view && (
|
||||
<Controls
|
||||
className="bg-muted fill-foreground stroke-foreground text-primary
|
||||
[&>button]:border-b-border hover:[&>button]:bg-border"
|
||||
></Controls>
|
||||
></Controls>
|
||||
)}
|
||||
</ReactFlow>
|
||||
<Chat flow={flow} reactFlowInstance={reactFlowInstance} />
|
||||
{!view && (
|
||||
<Chat flow={flow} reactFlowInstance={reactFlowInstance!} />
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import {
|
|||
import { classNames } from "../../../../utils/utils";
|
||||
import DisclosureComponent from "../DisclosureComponent";
|
||||
|
||||
export default function ExtraSidebar() {
|
||||
export default function ExtraSidebar(): JSX.Element {
|
||||
const { data, templates } = useContext(typesContext);
|
||||
const { flows, tabId, uploadFlow, tabsState, saveFlow, isBuilt } =
|
||||
useContext(TabsContext);
|
||||
|
|
@ -28,7 +28,7 @@ export default function ExtraSidebar() {
|
|||
function onDragStart(
|
||||
event: React.DragEvent<any>,
|
||||
data: { type: string; node?: APIClassType }
|
||||
) {
|
||||
): void {
|
||||
//start drag event
|
||||
var crt = event.currentTarget.cloneNode(true);
|
||||
crt.style.position = "absolute";
|
||||
|
|
@ -59,7 +59,7 @@ export default function ExtraSidebar() {
|
|||
const flow = flows.find((flow) => flow.id === tabId);
|
||||
useEffect(() => {
|
||||
// show components with error on load
|
||||
let errors = [];
|
||||
let errors: string[] = [];
|
||||
Object.keys(templates).forEach((component) => {
|
||||
if (templates[component].error) {
|
||||
errors.push(component);
|
||||
|
|
@ -120,7 +120,7 @@ export default function ExtraSidebar() {
|
|||
"extra-side-bar-buttons " + (isPending ? "" : "button-disable")
|
||||
}
|
||||
onClick={(event) => {
|
||||
saveFlow(flow);
|
||||
saveFlow(flow!);
|
||||
setSuccessData({ title: "Changes saved successfully" });
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -4,14 +4,19 @@ import ShadTooltip from "../../../../components/ShadTooltipComponent";
|
|||
import IconComponent from "../../../../components/genericIconComponent";
|
||||
import { TabsContext } from "../../../../contexts/tabsContext";
|
||||
import EditNodeModal from "../../../../modals/EditNodeModal";
|
||||
import { nodeToolbarPropsType } from "../../../../types/components";
|
||||
import { classNames } from "../../../../utils/utils";
|
||||
|
||||
export default function NodeToolbarComponent({ data, setData, deleteNode }) {
|
||||
export default function NodeToolbarComponent({
|
||||
data,
|
||||
setData,
|
||||
deleteNode,
|
||||
}: nodeToolbarPropsType): JSX.Element {
|
||||
const [nodeLength, setNodeLength] = useState(
|
||||
Object.keys(data.node.template).filter(
|
||||
Object.keys(data.node!.template).filter(
|
||||
(templateField) =>
|
||||
templateField.charAt(0) !== "_" &&
|
||||
data.node.template[templateField].show &&
|
||||
data.node?.template[templateField].show &&
|
||||
(data.node.template[templateField].type === "str" ||
|
||||
data.node.template[templateField].type === "bool" ||
|
||||
data.node.template[templateField].type === "float" ||
|
||||
|
|
@ -55,8 +60,8 @@ export default function NodeToolbarComponent({ data, setData, deleteNode }) {
|
|||
{
|
||||
x: 50,
|
||||
y: 10,
|
||||
paneX: reactFlowInstance.getNode(data.id).position.x,
|
||||
paneY: reactFlowInstance.getNode(data.id).position.y,
|
||||
paneX: reactFlowInstance.getNode(data.id)?.position.x,
|
||||
paneY: reactFlowInstance.getNode(data.id)?.position.y,
|
||||
}
|
||||
);
|
||||
}}
|
||||
|
|
@ -67,23 +72,23 @@ export default function NodeToolbarComponent({ data, setData, deleteNode }) {
|
|||
|
||||
<ShadTooltip
|
||||
content={
|
||||
data.node.documentation === "" ? "Coming Soon" : "Documentation"
|
||||
data.node?.documentation === "" ? "Coming Soon" : "Documentation"
|
||||
}
|
||||
side="top"
|
||||
>
|
||||
<a
|
||||
className={classNames(
|
||||
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10" +
|
||||
(data.node.documentation === ""
|
||||
(data.node?.documentation === ""
|
||||
? " text-muted-foreground"
|
||||
: " text-foreground")
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={data.node.documentation}
|
||||
href={data.node?.documentation}
|
||||
// deactivate link if no documentation is provided
|
||||
onClick={(event) => {
|
||||
if (data.node.documentation === "") {
|
||||
if (data.node?.documentation === "") {
|
||||
event.preventDefault();
|
||||
}
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
import { useContext, useEffect, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import Header from "../../components/headerComponent";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import { getVersion } from "../../controllers/API";
|
||||
import Page from "./components/PageComponent";
|
||||
|
||||
export default function FlowPage() {
|
||||
export default function FlowPage(): JSX.Element {
|
||||
const { flows, tabId, setTabId } = useContext(TabsContext);
|
||||
const { id } = useParams();
|
||||
|
||||
// Set flow tab id
|
||||
useEffect(() => {
|
||||
setTabId(id);
|
||||
setTabId(id!);
|
||||
}, [id]);
|
||||
|
||||
// Initialize state variable for the version
|
||||
|
|
@ -22,20 +23,23 @@ export default function FlowPage() {
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flow-page-positioning">
|
||||
{flows.length > 0 &&
|
||||
tabId !== "" &&
|
||||
flows.findIndex((flow) => flow.id === tabId) !== -1 && (
|
||||
<Page flow={flows.find((flow) => flow.id === tabId)} />
|
||||
)}
|
||||
<a
|
||||
target={"_blank"}
|
||||
href="https://logspace.ai/"
|
||||
className="logspace-page-icon"
|
||||
>
|
||||
{version && <div className="mt-1">⛓️ Langflow v{version}</div>}
|
||||
<div className={version ? "mt-2" : "mt-1"}>Created by Logspace</div>
|
||||
</a>
|
||||
</div>
|
||||
<>
|
||||
<Header />
|
||||
<div className="flow-page-positioning">
|
||||
{flows.length > 0 &&
|
||||
tabId !== "" &&
|
||||
flows.findIndex((flow) => flow.id === tabId) !== -1 && (
|
||||
<Page flow={flows.find((flow) => flow.id === tabId)!} />
|
||||
)}
|
||||
<a
|
||||
target={"_blank"}
|
||||
href="https://logspace.ai/"
|
||||
className="logspace-page-icon"
|
||||
>
|
||||
{version && <div className="mt-1">⛓️ Langflow v{version}</div>}
|
||||
<div className={version ? "mt-2" : "mt-1"}>Created by Logspace</div>
|
||||
</a>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@ import { useContext, useEffect } from "react";
|
|||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { CardComponent } from "../../components/cardComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import Header from "../../components/headerComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { USER_PROJECTS_HEADER } from "../../constants/constants";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
export default function HomePage() {
|
||||
export default function HomePage(): JSX.Element {
|
||||
const { flows, setTabId, downloadFlows, uploadFlows, addFlow, removeFlow } =
|
||||
useContext(TabsContext);
|
||||
|
||||
|
|
@ -17,74 +18,77 @@ export default function HomePage() {
|
|||
|
||||
// Personal flows display
|
||||
return (
|
||||
<div className="main-page-panel">
|
||||
<div className="main-page-nav-arrangement">
|
||||
<span className="main-page-nav-title">
|
||||
<IconComponent name="Home" className="w-6" />
|
||||
{USER_PROJECTS_HEADER}
|
||||
<>
|
||||
<Header />
|
||||
<div className="main-page-panel">
|
||||
<div className="main-page-nav-arrangement">
|
||||
<span className="main-page-nav-title">
|
||||
<IconComponent name="Home" className="w-6" />
|
||||
{USER_PROJECTS_HEADER}
|
||||
</span>
|
||||
<div className="button-div-style">
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
downloadFlows();
|
||||
}}
|
||||
>
|
||||
<IconComponent name="Download" className="main-page-nav-button" />
|
||||
Download Collection
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
uploadFlows();
|
||||
}}
|
||||
>
|
||||
<IconComponent name="Upload" className="main-page-nav-button" />
|
||||
Upload Collection
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
addFlow(null!, true).then((id) => {
|
||||
navigate("/flow/" + id);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<IconComponent name="Plus" className="main-page-nav-button" />
|
||||
New Project
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<span className="main-page-description-text">
|
||||
Manage your personal projects. Download or upload your collection.
|
||||
</span>
|
||||
<div className="button-div-style">
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
downloadFlows();
|
||||
}}
|
||||
>
|
||||
<IconComponent name="Download" className="main-page-nav-button" />
|
||||
Download Collection
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
uploadFlows();
|
||||
}}
|
||||
>
|
||||
<IconComponent name="Upload" className="main-page-nav-button" />
|
||||
Upload Collection
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
addFlow(null, true).then((id) => {
|
||||
navigate("/flow/" + id);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<IconComponent name="Plus" className="main-page-nav-button" />
|
||||
New Project
|
||||
</Button>
|
||||
<div className="main-page-flows-display">
|
||||
{flows.map((flow, idx) => (
|
||||
<CardComponent
|
||||
key={idx}
|
||||
flow={flow}
|
||||
id={flow.id}
|
||||
button={
|
||||
<Link to={"/flow/" + flow.id}>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="whitespace-nowrap "
|
||||
>
|
||||
<IconComponent
|
||||
name="ExternalLink"
|
||||
className="main-page-nav-button"
|
||||
/>
|
||||
Edit Flow
|
||||
</Button>
|
||||
</Link>
|
||||
}
|
||||
onDelete={() => {
|
||||
removeFlow(flow.id);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<span className="main-page-description-text">
|
||||
Manage your personal projects. Download or upload your collection.
|
||||
</span>
|
||||
<div className="main-page-flows-display">
|
||||
{flows.map((flow, idx) => (
|
||||
<CardComponent
|
||||
key={idx}
|
||||
flow={flow}
|
||||
id={flow.id}
|
||||
button={
|
||||
<Link to={"/flow/" + flow.id}>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="whitespace-nowrap "
|
||||
>
|
||||
<IconComponent
|
||||
name="ExternalLink"
|
||||
className="main-page-nav-button"
|
||||
/>
|
||||
Edit Flow
|
||||
</Button>
|
||||
</Link>
|
||||
}
|
||||
onDelete={() => {
|
||||
removeFlow(flow.id);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
33
src/frontend/src/pages/ViewPage/index.tsx
Normal file
33
src/frontend/src/pages/ViewPage/index.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { useContext, useEffect, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import { getVersion } from "../../controllers/API";
|
||||
import Page from "../FlowPage/components/PageComponent";
|
||||
|
||||
export default function ViewPage() {
|
||||
const { flows, tabId, setTabId } = useContext(TabsContext);
|
||||
const { id } = useParams();
|
||||
|
||||
// Set flow tab id
|
||||
useEffect(() => {
|
||||
setTabId(id);
|
||||
}, [id]);
|
||||
|
||||
// Initialize state variable for the version
|
||||
const [version, setVersion] = useState("");
|
||||
useEffect(() => {
|
||||
getVersion().then((data) => {
|
||||
setVersion(data.version);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flow-page-positioning">
|
||||
{flows.length > 0 &&
|
||||
tabId !== "" &&
|
||||
flows.findIndex((flow) => flow.id === tabId) !== -1 && (
|
||||
<Page view flow={flows.find((flow) => flow.id === tabId)} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -49,7 +49,7 @@ export default function LoginPage(): JSX.Element {
|
|||
}
|
||||
|
||||
function getUser() {
|
||||
if (getAuthentication) {
|
||||
if (getAuthentication()) {
|
||||
setTimeout(() => {
|
||||
getLoggedUser()
|
||||
.then((user) => {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import LoginAdminPage from "./pages/AdminPage/LoginPage";
|
|||
import CommunityPage from "./pages/CommunityPage";
|
||||
import FlowPage from "./pages/FlowPage";
|
||||
import HomePage from "./pages/MainPage";
|
||||
import ViewPage from "./pages/ViewPage";
|
||||
import DeleteAccountPage from "./pages/deleteAccountPage";
|
||||
import LoginPage from "./pages/loginPage";
|
||||
import SignUp from "./pages/signUpPage";
|
||||
|
|
@ -41,6 +42,11 @@ const Router = () => {
|
|||
</ProtectedRoute>
|
||||
}
|
||||
/>
|
||||
<Route path="view" element={
|
||||
<ProtectedRoute>
|
||||
<ViewPage />
|
||||
</ProtectedRoute>
|
||||
} />
|
||||
</Route>
|
||||
<Route
|
||||
path="*"
|
||||
|
|
|
|||
|
|
@ -192,6 +192,10 @@
|
|||
@apply flex w-full;
|
||||
}
|
||||
|
||||
.loading-page-panel {
|
||||
@apply h-full w-full flex justify-center items-center bg-muted;
|
||||
}
|
||||
|
||||
.main-page-panel {
|
||||
@apply flex-max-width h-full flex-col overflow-auto bg-muted px-16;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
export type ErrorAlertType = {
|
||||
title: string;
|
||||
list: Array<string>;
|
||||
list: Array<string> | undefined;
|
||||
id: string;
|
||||
removeAlert: (id: string) => void;
|
||||
};
|
||||
export type NoticeAlertType = {
|
||||
title: string;
|
||||
link: string;
|
||||
link: string | undefined;
|
||||
id: string;
|
||||
removeAlert: (id: string) => void;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Edge, Node, Viewport } from "reactflow";
|
||||
//kind and class are just representative names to represent the actual structure of the object received by the API
|
||||
|
||||
export type APIDataType = { [key: string]: APIKindType };
|
||||
export type APIObjectType = { kind: APIKindType; [key: string]: APIKindType };
|
||||
export type APIKindType = { class: APIClassType; [key: string]: APIClassType };
|
||||
export type APITemplateType = {
|
||||
|
|
@ -17,7 +17,7 @@ export type APIClassType = {
|
|||
beta?: boolean;
|
||||
documentation: string;
|
||||
error?: string;
|
||||
[key: string]: Array<string> | string | APITemplateType | boolean;
|
||||
[key: string]: Array<string> | string | APITemplateType | boolean | undefined;
|
||||
};
|
||||
|
||||
export type TemplateVariableType = {
|
||||
|
|
@ -37,7 +37,7 @@ export type sendAllProps = {
|
|||
name: string;
|
||||
description: string;
|
||||
viewport: Viewport;
|
||||
inputs: any;
|
||||
inputs: { text?: string };
|
||||
|
||||
chatHistory: { message: string | object; isSend: boolean }[];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import { ReactElement, ReactNode } from "react";
|
||||
import { APIClassType } from "../api";
|
||||
import { NodeDataType } from "../flow/index";
|
||||
import { ReactFlowJsonObject } from "reactflow";
|
||||
import { APIClassType, APITemplateType, TemplateVariableType } from "../api";
|
||||
import { ChatMessageType } from "../chat";
|
||||
import { FlowStyleType, FlowType, NodeDataType, NodeType } from "../flow/index";
|
||||
import { typesContextType } from "../typesContext";
|
||||
|
||||
export type InputComponentType = {
|
||||
value: string;
|
||||
disabled?: boolean;
|
||||
|
|
@ -18,7 +21,7 @@ export type InputComponentType = {
|
|||
export type ToggleComponentType = {
|
||||
enabled: boolean;
|
||||
setEnabled: (state: boolean) => void;
|
||||
disabled: boolean;
|
||||
disabled: boolean | undefined;
|
||||
size: "small" | "medium" | "large";
|
||||
};
|
||||
export type DropDownComponentType = {
|
||||
|
|
@ -36,12 +39,12 @@ export type ParameterComponentType = {
|
|||
id: string;
|
||||
color: string;
|
||||
left: boolean;
|
||||
type: string;
|
||||
type: string | undefined;
|
||||
required?: boolean;
|
||||
name?: string;
|
||||
tooltipTitle: string;
|
||||
tooltipTitle: string | undefined;
|
||||
dataContext?: typesContextType;
|
||||
optionalHandle?: Array<String>;
|
||||
optionalHandle?: Array<String> | null;
|
||||
info?: string;
|
||||
};
|
||||
export type InputListComponentType = {
|
||||
|
|
@ -86,7 +89,7 @@ export type DisclosureComponentType = {
|
|||
openDisc: boolean;
|
||||
button: {
|
||||
title: string;
|
||||
Icon: any;
|
||||
Icon: React.ElementType;
|
||||
buttons?: {
|
||||
Icon: ReactElement;
|
||||
title: string;
|
||||
|
|
@ -146,7 +149,7 @@ export type ShadTooltipProps = {
|
|||
style?: string;
|
||||
};
|
||||
export type ShadToolTipType = {
|
||||
content?: ReactNode;
|
||||
content?: ReactNode | null;
|
||||
side?: "top" | "right" | "bottom" | "left";
|
||||
asChild?: boolean;
|
||||
children?: ReactElement;
|
||||
|
|
@ -172,6 +175,37 @@ export type IconComponentProps = {
|
|||
iconColor?: string;
|
||||
};
|
||||
|
||||
export type InputProps = {
|
||||
name: string | null;
|
||||
description: string | null;
|
||||
maxLength?: number;
|
||||
flows: Array<{ id: string; name: string; description: string }>;
|
||||
tabId: string;
|
||||
invalidName?: boolean;
|
||||
setName: (name: string) => void;
|
||||
setDescription: (description: string) => void;
|
||||
setInvalidName?: (invalidName: boolean) => void;
|
||||
};
|
||||
|
||||
export type TooltipProps = {
|
||||
selector: string;
|
||||
content?: string;
|
||||
disabled?: boolean;
|
||||
htmlContent?: React.ReactNode;
|
||||
className?: string; // This should use !impornant to override the default styles eg: '!bg-white'
|
||||
position?: "top" | "right" | "bottom" | "left";
|
||||
clickable?: boolean;
|
||||
children: React.ReactNode;
|
||||
delayShow?: number;
|
||||
};
|
||||
|
||||
export type LoadingComponentProps = {
|
||||
remSize: number;
|
||||
};
|
||||
|
||||
export type ContentProps = { children: ReactNode };
|
||||
export type HeaderProps = { children: ReactNode; description: string };
|
||||
|
||||
export interface languageMap {
|
||||
[key: string]: string | undefined;
|
||||
}
|
||||
|
|
@ -245,4 +279,251 @@ export type ApiKeyType = {
|
|||
|
||||
export type ApiKeyInputType = {
|
||||
apikeyname: string;
|
||||
};
|
||||
};
|
||||
export type groupedObjType = {
|
||||
family: string;
|
||||
type: string;
|
||||
};
|
||||
|
||||
type test = {
|
||||
[char: string]: string;
|
||||
};
|
||||
|
||||
export type tweakType = Array<{
|
||||
[key: string]: {
|
||||
[char: string]: string;
|
||||
} & FlowStyleType;
|
||||
}>;
|
||||
|
||||
export type uniqueTweakType = {
|
||||
[key: string]: {
|
||||
[char: string]: string;
|
||||
} & FlowStyleType;
|
||||
};
|
||||
|
||||
export type apiModalTweakType = {
|
||||
current: Array<{
|
||||
[key: string]: {
|
||||
[char: string]: string | number;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
|
||||
export type nodeToolbarType = {
|
||||
data: {
|
||||
id: string;
|
||||
type: string;
|
||||
node: {
|
||||
base_classes: string[];
|
||||
description: string;
|
||||
display_name: string;
|
||||
documentation: string;
|
||||
template: APITemplateType;
|
||||
};
|
||||
value: void;
|
||||
};
|
||||
deleteNode: (idx: string) => void;
|
||||
openPopUp: (element: JSX.Element) => JSX.Element;
|
||||
};
|
||||
|
||||
export type chatTriggerPropType = {
|
||||
open: boolean;
|
||||
isBuilt: boolean;
|
||||
canOpen: boolean;
|
||||
setOpen: (can: boolean) => void;
|
||||
};
|
||||
|
||||
export type headerFlowsType = {
|
||||
data: ReactFlowJsonObject | null;
|
||||
description: string;
|
||||
id: string;
|
||||
name: string;
|
||||
style?: FlowStyleType;
|
||||
};
|
||||
|
||||
export type menuBarPropsType = {
|
||||
flows: Array<headerFlowsType>;
|
||||
tabId: string;
|
||||
};
|
||||
|
||||
export type chatInputType = {
|
||||
chatValue: string;
|
||||
inputRef: {
|
||||
current: any;
|
||||
};
|
||||
lockChat: boolean;
|
||||
noInput: boolean;
|
||||
sendMessage: () => void;
|
||||
setChatValue: (value: string) => void;
|
||||
};
|
||||
|
||||
export type editNodeToggleType = {
|
||||
advanced?: boolean;
|
||||
info?: string;
|
||||
list: boolean;
|
||||
multiline?: boolean;
|
||||
name?: string;
|
||||
password?: boolean;
|
||||
placeholder?: string;
|
||||
required: boolean;
|
||||
show: boolean;
|
||||
type: string;
|
||||
};
|
||||
|
||||
export interface Props {
|
||||
language: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export type fileCardPropsType = {
|
||||
fileName: string;
|
||||
content: string;
|
||||
fileType: string;
|
||||
};
|
||||
|
||||
export type nodeToolbarPropsType = {
|
||||
data: NodeDataType;
|
||||
deleteNode: (idx: string) => void;
|
||||
setData: (newState: NodeDataType) => void;
|
||||
};
|
||||
|
||||
export type parsedDataType = {
|
||||
id: string;
|
||||
params: string;
|
||||
progress: number;
|
||||
valid: boolean;
|
||||
};
|
||||
|
||||
export type SanitizedHTMLWrapperType = {
|
||||
className: string;
|
||||
content: string;
|
||||
onClick: () => void;
|
||||
suppressWarning?: boolean;
|
||||
};
|
||||
|
||||
export type iconsType = {
|
||||
[key: string]: React.ElementType;
|
||||
};
|
||||
|
||||
export type modalHeaderType = {
|
||||
children: ReactNode;
|
||||
description: string | null;
|
||||
};
|
||||
|
||||
export type codeAreaModalPropsType = {
|
||||
setValue: (value: string) => void;
|
||||
value: string;
|
||||
nodeClass: APIClassType | undefined;
|
||||
setNodeClass: (Class: APIClassType) => void | undefined;
|
||||
children: ReactNode;
|
||||
dynamic?: boolean;
|
||||
};
|
||||
|
||||
export type chatMessagePropsType = {
|
||||
chat: ChatMessageType;
|
||||
lockChat: boolean;
|
||||
lastMessage: boolean;
|
||||
};
|
||||
|
||||
export type formModalPropsType = {
|
||||
open: boolean;
|
||||
setOpen: Function;
|
||||
flow: FlowType;
|
||||
};
|
||||
|
||||
export type genericModalPropsType = {
|
||||
field_name?: string;
|
||||
setValue: (value: string) => void;
|
||||
value: string;
|
||||
buttonText: string;
|
||||
modalTitle: string;
|
||||
type: number;
|
||||
nodeClass?: APIClassType;
|
||||
setNodeClass?: (Class: APIClassType) => void;
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export type buttonBoxPropsType = {
|
||||
onClick: () => void;
|
||||
title: string;
|
||||
description: string;
|
||||
icon: ReactNode;
|
||||
bgColor: string;
|
||||
textColor: string;
|
||||
deactivate?: boolean;
|
||||
size: "small" | "medium" | "big";
|
||||
};
|
||||
|
||||
export type FlowSettingsPropsType = {
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
};
|
||||
|
||||
export type groupDataType = {
|
||||
[char: string]: string;
|
||||
};
|
||||
|
||||
export type cardComponentPropsType = {
|
||||
flow: FlowType;
|
||||
id: string;
|
||||
onDelete?: () => void;
|
||||
button?: JSX.Element;
|
||||
};
|
||||
|
||||
type tabsArrayType = {
|
||||
code: string;
|
||||
image: string;
|
||||
language: string;
|
||||
mode: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
type getValueNodeType = {
|
||||
id: string;
|
||||
node: NodeType;
|
||||
type: string;
|
||||
value: null;
|
||||
};
|
||||
|
||||
type codeTabsFuncTempType = {
|
||||
[key: string]: string | boolean;
|
||||
};
|
||||
|
||||
export type codeTabsPropsType = {
|
||||
flow?: FlowType;
|
||||
tabs: Array<tabsArrayType>;
|
||||
activeTab: string;
|
||||
setActiveTab: (value: string) => void;
|
||||
isMessage?: boolean;
|
||||
tweaks?: {
|
||||
tweak?: { current: tweakType };
|
||||
tweaksList?: { current: Array<string> };
|
||||
buildContent?: (value: string) => ReactNode;
|
||||
getValue?: (
|
||||
value: string,
|
||||
node: NodeType,
|
||||
template: TemplateVariableType
|
||||
) => string;
|
||||
buildTweakObject?: (
|
||||
tw: string,
|
||||
changes: string | string[] | boolean | number,
|
||||
template: TemplateVariableType
|
||||
) => string | void;
|
||||
};
|
||||
};
|
||||
|
||||
export type crashComponentPropsType = {
|
||||
error: {
|
||||
message: string;
|
||||
stack: string;
|
||||
};
|
||||
resetErrorBoundary: (args) => void;
|
||||
};
|
||||
|
||||
export type validationStatusType = {
|
||||
id: string;
|
||||
params: string;
|
||||
progress: number;
|
||||
valid: boolean;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { APIClassType } from "../api/index";
|
|||
export type FlowType = {
|
||||
name: string;
|
||||
id: string;
|
||||
data: ReactFlowJsonObject;
|
||||
data: ReactFlowJsonObject | null;
|
||||
description: string;
|
||||
style?: FlowStyleType;
|
||||
};
|
||||
|
|
@ -14,11 +14,11 @@ export type NodeType = {
|
|||
position: XYPosition;
|
||||
data: NodeDataType;
|
||||
};
|
||||
|
||||
export type NodeDataType = {
|
||||
type: string;
|
||||
node?: APIClassType;
|
||||
id: string;
|
||||
value: any;
|
||||
};
|
||||
// FlowStyleType is the type of the style object that is used to style the
|
||||
// Flow card with an emoji and a color.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Dispatch, SetStateAction } from "react";
|
||||
import { FlowType, TweaksType } from "../flow";
|
||||
import { tweakType } from "../components";
|
||||
import { FlowType } from "../flow";
|
||||
|
||||
export type TabsContextType = {
|
||||
saveFlow: (flow: FlowType) => Promise<void>;
|
||||
|
|
@ -8,7 +8,10 @@ export type TabsContextType = {
|
|||
setTabId: (index: string) => void;
|
||||
flows: Array<FlowType>;
|
||||
removeFlow: (id: string) => void;
|
||||
addFlow: (flowData?: FlowType, newProject?: boolean) => Promise<String>;
|
||||
addFlow: (
|
||||
flow?: FlowType,
|
||||
newProject?: Boolean
|
||||
) => Promise<String | undefined>;
|
||||
updateFlow: (newFlow: FlowType) => void;
|
||||
incrementNodeId: () => string;
|
||||
downloadFlow: (
|
||||
|
|
@ -24,15 +27,15 @@ export type TabsContextType = {
|
|||
hardReset: () => void;
|
||||
getNodeId: (nodeType: string) => string;
|
||||
tabsState: TabsState;
|
||||
setTabsState: Dispatch<SetStateAction<TabsState>>;
|
||||
setTabsState: (state: TabsState) => void;
|
||||
paste: (
|
||||
selection: { nodes: any; edges: any },
|
||||
position: { x: number; y: number; paneX?: number; paneY?: number }
|
||||
) => void;
|
||||
lastCopiedSelection: { nodes: any; edges: any };
|
||||
lastCopiedSelection: { nodes: any; edges: any } | null;
|
||||
setLastCopiedSelection: (selection: { nodes: any; edges: any }) => void;
|
||||
setTweak: (tweak: TweaksType) => void;
|
||||
getTweak: TweaksType[];
|
||||
setTweak: (tweak: tweakType) => tweakType | void;
|
||||
getTweak: tweakType;
|
||||
};
|
||||
|
||||
export type TabsState = {
|
||||
|
|
@ -46,3 +49,8 @@ export type TabsState = {
|
|||
};
|
||||
};
|
||||
};
|
||||
|
||||
export type errorsVarType = {
|
||||
title: string;
|
||||
list?: Array<string>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { ReactFlowInstance } from "reactflow";
|
||||
import { APIClassType } from "../api";
|
||||
import { Edge, Node, ReactFlowInstance } from "reactflow";
|
||||
import { AlertItemType } from "../alerts";
|
||||
import { APIClassType, APIDataType } from "../api";
|
||||
|
||||
const types: { [char: string]: string } = {};
|
||||
const template: { [char: string]: APIClassType } = {};
|
||||
|
|
@ -7,12 +8,93 @@ const data: { [char: string]: string } = {};
|
|||
|
||||
export type typesContextType = {
|
||||
reactFlowInstance: ReactFlowInstance | null;
|
||||
setReactFlowInstance: any;
|
||||
setReactFlowInstance: (newState: ReactFlowInstance) => void;
|
||||
deleteNode: (idx: string) => void;
|
||||
types: typeof types;
|
||||
setTypes: (newState: {}) => void;
|
||||
templates: typeof template;
|
||||
setTemplates: (newState: {}) => void;
|
||||
data: typeof data;
|
||||
data: APIDataType;
|
||||
setData: (newState: {}) => void;
|
||||
};
|
||||
|
||||
export type alertContextType = {
|
||||
errorData: { title: string; list?: Array<string> };
|
||||
setErrorData: (newState: { title: string; list?: Array<string> }) => void;
|
||||
errorOpen: boolean;
|
||||
setErrorOpen: (newState: boolean) => void;
|
||||
noticeData: { title: string; link?: string };
|
||||
setNoticeData: (newState: { title: string; link?: string }) => void;
|
||||
noticeOpen: boolean;
|
||||
setNoticeOpen: (newState: boolean) => void;
|
||||
successData: { title: string };
|
||||
setSuccessData: (newState: { title: string }) => void;
|
||||
successOpen: boolean;
|
||||
setSuccessOpen: (newState: boolean) => void;
|
||||
notificationCenter: boolean;
|
||||
setNotificationCenter: (newState: boolean) => void;
|
||||
notificationList: Array<AlertItemType>;
|
||||
pushNotificationList: (Object: AlertItemType) => void;
|
||||
clearNotificationList: () => void;
|
||||
removeFromNotificationList: (index: string) => void;
|
||||
loading: boolean;
|
||||
setLoading: (newState: boolean) => void;
|
||||
};
|
||||
|
||||
export type darkContextType = {
|
||||
dark: {};
|
||||
setDark: (newState: {}) => void;
|
||||
};
|
||||
|
||||
export type locationContextType = {
|
||||
current: Array<string>;
|
||||
setCurrent: (newState: Array<string>) => void;
|
||||
isStackedOpen: boolean;
|
||||
setIsStackedOpen: (newState: boolean) => void;
|
||||
showSideBar: boolean;
|
||||
setShowSideBar: (newState: boolean) => void;
|
||||
extraNavigation: {
|
||||
title: string;
|
||||
options?: Array<{
|
||||
name: string;
|
||||
href: string;
|
||||
icon: React.ElementType;
|
||||
children?: Array<JSX.Element>;
|
||||
}>;
|
||||
};
|
||||
setExtraNavigation: (newState: {
|
||||
title: string;
|
||||
options?: Array<{
|
||||
name: string;
|
||||
href: string;
|
||||
icon: React.ElementType;
|
||||
children?: Array<JSX.Element>;
|
||||
}>;
|
||||
}) => void;
|
||||
extraComponent: any;
|
||||
setExtraComponent: (newState: JSX.Element) => void;
|
||||
};
|
||||
|
||||
export type undoRedoContextType = {
|
||||
undo: () => void;
|
||||
redo: () => void;
|
||||
takeSnapshot: () => void;
|
||||
};
|
||||
|
||||
export type UseUndoRedoOptions = {
|
||||
maxHistorySize: number;
|
||||
enableShortcuts: boolean;
|
||||
};
|
||||
|
||||
export type UseUndoRedo = (options?: UseUndoRedoOptions) => {
|
||||
undo: () => void;
|
||||
redo: () => void;
|
||||
takeSnapshot: () => void;
|
||||
canUndo: boolean;
|
||||
canRedo: boolean;
|
||||
};
|
||||
|
||||
export type HistoryItem = {
|
||||
nodes: Node[];
|
||||
edges: Edge[];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
import _ from "lodash";
|
||||
import { Connection, Edge, ReactFlowInstance } from "reactflow";
|
||||
import {
|
||||
Connection,
|
||||
Edge,
|
||||
ReactFlowInstance,
|
||||
ReactFlowJsonObject,
|
||||
} from "reactflow";
|
||||
import { specialCharsRegex } from "../constants/constants";
|
||||
import { APITemplateType } from "../types/api";
|
||||
import { FlowType, NodeType } from "../types/flow";
|
||||
import { cleanEdgesType } from "../types/utils/reactflowUtils";
|
||||
|
|
@ -24,8 +30,8 @@ export function cleanEdges({
|
|||
if (targetHandle) {
|
||||
const field = targetHandle.split("|")[1];
|
||||
const id =
|
||||
(targetNode.data.node.template[field]?.input_types?.join(";") ??
|
||||
targetNode.data.node.template[field]?.type) +
|
||||
(targetNode.data.node?.template[field]?.input_types?.join(";") ??
|
||||
targetNode.data.node?.template[field]?.type) +
|
||||
"|" +
|
||||
field +
|
||||
"|" +
|
||||
|
|
@ -38,7 +44,7 @@ export function cleanEdges({
|
|||
const id = [
|
||||
sourceNode.data.type,
|
||||
sourceNode.data.id,
|
||||
...sourceNode.data.node.base_classes,
|
||||
...sourceNode.data.node?.base_classes!,
|
||||
].join("|");
|
||||
if (id !== sourceHandle) {
|
||||
newEdges = newEdges.filter((edg) => edg.id !== edge.id);
|
||||
|
|
@ -55,21 +61,21 @@ export function isValidConnection(
|
|||
) {
|
||||
if (
|
||||
targetHandle
|
||||
.split("|")[0]
|
||||
?.split("|")[0]
|
||||
.split(";")
|
||||
.some((target) => target === sourceHandle.split("|")[0]) ||
|
||||
.some((target) => target === sourceHandle?.split("|")[0]) ||
|
||||
sourceHandle
|
||||
.split("|")
|
||||
?.split("|")
|
||||
.slice(2)
|
||||
.some((target) =>
|
||||
targetHandle
|
||||
.split("|")[0]
|
||||
?.split("|")[0]
|
||||
.split(";")
|
||||
.some((n) => n === target)
|
||||
) ||
|
||||
targetHandle.split("|")[0] === "str"
|
||||
targetHandle?.split("|")[0] === "str"
|
||||
) {
|
||||
let targetNode = reactFlowInstance?.getNode(target)?.data?.node;
|
||||
let targetNode = reactFlowInstance?.getNode(target!)?.data?.node;
|
||||
if (!targetNode) {
|
||||
if (
|
||||
!reactFlowInstance
|
||||
|
|
@ -79,11 +85,11 @@ export function isValidConnection(
|
|||
return true;
|
||||
}
|
||||
} else if (
|
||||
(!targetNode.template[targetHandle.split("|")[1]].list &&
|
||||
(!targetNode.template[targetHandle?.split("|")[1]!].list &&
|
||||
!reactFlowInstance
|
||||
.getEdges()
|
||||
.find((e) => e.targetHandle === targetHandle)) ||
|
||||
targetNode.template[targetHandle.split("|")[1]].list
|
||||
targetNode.template[targetHandle?.split("|")[1]!].list
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -93,7 +99,7 @@ export function isValidConnection(
|
|||
|
||||
export function removeApiKeys(flow: FlowType): FlowType {
|
||||
let cleanFLow = _.cloneDeep(flow);
|
||||
cleanFLow.data.nodes.forEach((node) => {
|
||||
cleanFLow.data!.nodes.forEach((node) => {
|
||||
for (const key in node.data.node.template) {
|
||||
if (node.data.node.template[key].password) {
|
||||
node.data.node.template[key].value = "";
|
||||
|
|
@ -126,7 +132,10 @@ export function updateTemplate(
|
|||
return clonedObject;
|
||||
}
|
||||
|
||||
export function updateIds(newFlow, getNodeId) {
|
||||
export function updateIds(
|
||||
newFlow: ReactFlowJsonObject,
|
||||
getNodeId: (type: string) => string
|
||||
) {
|
||||
let idsMap = {};
|
||||
|
||||
newFlow.nodes.forEach((node: NodeType) => {
|
||||
|
|
@ -141,14 +150,14 @@ export function updateIds(newFlow, getNodeId) {
|
|||
newFlow.edges.forEach((edge) => {
|
||||
edge.source = idsMap[edge.source];
|
||||
edge.target = idsMap[edge.target];
|
||||
let sourceHandleSplitted = edge.sourceHandle.split("|");
|
||||
let sourceHandleSplitted = edge.sourceHandle!.split("|");
|
||||
edge.sourceHandle =
|
||||
sourceHandleSplitted[0] +
|
||||
"|" +
|
||||
edge.source +
|
||||
"|" +
|
||||
sourceHandleSplitted.slice(2).join("|");
|
||||
let targetHandleSplitted = edge.targetHandle.split("|");
|
||||
let targetHandleSplitted = edge.targetHandle!.split("|");
|
||||
edge.targetHandle =
|
||||
targetHandleSplitted.slice(0, -1).join("|") + "|" + edge.target;
|
||||
edge.id =
|
||||
|
|
@ -161,8 +170,8 @@ export function updateIds(newFlow, getNodeId) {
|
|||
});
|
||||
}
|
||||
|
||||
export function buildTweaks(flow) {
|
||||
return flow.data.nodes.reduce((acc, node) => {
|
||||
export function buildTweaks(flow: FlowType) {
|
||||
return flow.data!.nodes.reduce((acc, node) => {
|
||||
acc[node.data.id] = {};
|
||||
return acc;
|
||||
}, {});
|
||||
|
|
@ -195,7 +204,7 @@ export function validateNode(
|
|||
.getEdges()
|
||||
.some(
|
||||
(edge) =>
|
||||
edge.targetHandle.split("|")[1] === t &&
|
||||
edge.targetHandle?.split("|")[1] === t &&
|
||||
edge.targetHandle.split("|")[2] === node.id
|
||||
)
|
||||
? [
|
||||
|
|
@ -233,6 +242,34 @@ export function addVersionToDuplicates(flow: FlowType, flows: FlowType[]) {
|
|||
return newName;
|
||||
}
|
||||
|
||||
export function handleKeyDown(
|
||||
e: React.KeyboardEvent<HTMLInputElement>,
|
||||
inputValue: string | string[] | null,
|
||||
block: string
|
||||
) {
|
||||
console.log(e, inputValue, block);
|
||||
//condition to fix bug control+backspace on Windows/Linux
|
||||
if (
|
||||
(typeof inputValue === "string" &&
|
||||
(e.metaKey === true || e.ctrlKey === true) &&
|
||||
e.key === "Backspace" &&
|
||||
(inputValue === block ||
|
||||
inputValue?.charAt(inputValue?.length - 1) === " " ||
|
||||
specialCharsRegex.test(inputValue?.charAt(inputValue?.length - 1)))) ||
|
||||
(navigator.userAgent.toUpperCase().includes("MAC") &&
|
||||
e.ctrlKey === true &&
|
||||
e.key === "Backspace")
|
||||
) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
if (e.ctrlKey === true && e.key === "Backspace" && inputValue === block) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
export function getConnectedNodes(
|
||||
edge: Edge,
|
||||
nodes: Array<NodeType>
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ import SvgWikipedia from "../icons/Wikipedia/Wikipedia";
|
|||
import SvgWolfram from "../icons/Wolfram/Wolfram";
|
||||
import { HackerNewsIcon } from "../icons/hackerNews";
|
||||
import { SupabaseIcon } from "../icons/supabase";
|
||||
import { iconsType } from "../types/components";
|
||||
|
||||
export const gradients = [
|
||||
"bg-gradient-to-br from-gray-800 via-rose-700 to-violet-900",
|
||||
|
|
@ -182,7 +183,7 @@ export const nodeNames: { [char: string]: string } = {
|
|||
unknown: "Other",
|
||||
};
|
||||
|
||||
export const nodeIconsLucide = {
|
||||
export const nodeIconsLucide: iconsType = {
|
||||
Chroma: ChromaIcon,
|
||||
AirbyteJSONLoader: AirbyteIcon,
|
||||
Anthropic: AnthropicIcon,
|
||||
|
|
|
|||
|
|
@ -1,20 +1,25 @@
|
|||
import clsx, { ClassValue } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { ADJECTIVES, DESCRIPTIONS, NOUNS } from "../flow_constants";
|
||||
import { IVarHighlightType } from "../types/components";
|
||||
import { APIDataType, TemplateVariableType } from "../types/api";
|
||||
import {
|
||||
IVarHighlightType,
|
||||
groupedObjType,
|
||||
tweakType,
|
||||
} from "../types/components";
|
||||
import { FlowType, NodeType } from "../types/flow";
|
||||
import { TabsState } from "../types/tabs";
|
||||
import { buildTweaks } from "./reactflowUtils";
|
||||
|
||||
export function classNames(...classes: Array<string>) {
|
||||
export function classNames(...classes: Array<string>): string {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
}
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
export function cn(...inputs: ClassValue[]): string {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
export function toNormalCase(str: string) {
|
||||
export function toNormalCase(str: string): string {
|
||||
let result = str
|
||||
.split("_")
|
||||
.map((word, index) => {
|
||||
|
|
@ -36,7 +41,7 @@ export function toNormalCase(str: string) {
|
|||
.join(" ");
|
||||
}
|
||||
|
||||
export function normalCaseToSnakeCase(str: string) {
|
||||
export function normalCaseToSnakeCase(str: string): string {
|
||||
return str
|
||||
.split(" ")
|
||||
.map((word, index) => {
|
||||
|
|
@ -48,7 +53,7 @@ export function normalCaseToSnakeCase(str: string) {
|
|||
.join("_");
|
||||
}
|
||||
|
||||
export function toTitleCase(str: string) {
|
||||
export function toTitleCase(str: string): string {
|
||||
let result = str
|
||||
.split("_")
|
||||
.map((word, index) => {
|
||||
|
|
@ -75,7 +80,7 @@ export function toTitleCase(str: string) {
|
|||
}
|
||||
|
||||
export const upperCaseWords: string[] = ["llm", "uri"];
|
||||
export function checkUpperWords(str: string) {
|
||||
export function checkUpperWords(str: string): string {
|
||||
const words = str.split(" ").map((word) => {
|
||||
return upperCaseWords.includes(word.toLowerCase())
|
||||
? word.toUpperCase()
|
||||
|
|
@ -88,10 +93,23 @@ export function checkUpperWords(str: string) {
|
|||
export const isWrappedWithClass = (event: any, className: string | undefined) =>
|
||||
event.target.closest(`.${className}`);
|
||||
|
||||
export function groupByFamily(data, baseClasses, left, flow?: NodeType[]) {
|
||||
export function groupByFamily(
|
||||
data: APIDataType,
|
||||
baseClasses: string,
|
||||
left: boolean,
|
||||
flow?: NodeType[]
|
||||
): groupedObjType[] {
|
||||
const baseClassesSet = new Set(baseClasses.split("\n"));
|
||||
let arrOfPossibleInputs = [];
|
||||
let arrOfPossibleOutputs = [];
|
||||
let arrOfPossibleInputs: Array<{
|
||||
category: string;
|
||||
nodes: string[];
|
||||
full: boolean;
|
||||
}> = [];
|
||||
let arrOfPossibleOutputs: Array<{
|
||||
category: string;
|
||||
nodes: string[];
|
||||
full: boolean;
|
||||
}> = [];
|
||||
let checkedNodes = new Map();
|
||||
const excludeTypes = new Set([
|
||||
"str",
|
||||
|
|
@ -103,7 +121,7 @@ export function groupByFamily(data, baseClasses, left, flow?: NodeType[]) {
|
|||
"int",
|
||||
]);
|
||||
|
||||
const checkBaseClass = (template: any) =>
|
||||
const checkBaseClass = (template: TemplateVariableType) =>
|
||||
template.type &&
|
||||
template.show &&
|
||||
((!excludeTypes.has(template.type) && baseClassesSet.has(template.type)) ||
|
||||
|
|
@ -119,10 +137,10 @@ export function groupByFamily(data, baseClasses, left, flow?: NodeType[]) {
|
|||
checkedNodes.set(nodeData.type, {
|
||||
hasBaseClassInTemplate:
|
||||
foundNode?.hasBaseClassInTemplate ||
|
||||
Object.values(nodeData.node.template).some(checkBaseClass),
|
||||
Object.values(nodeData.node!.template).some(checkBaseClass),
|
||||
hasBaseClassInBaseClasses:
|
||||
foundNode?.hasBaseClassInBaseClasses ||
|
||||
nodeData.node.base_classes.some((baseClass) =>
|
||||
nodeData.node!.base_classes.some((baseClass) =>
|
||||
baseClassesSet.has(baseClass)
|
||||
),
|
||||
});
|
||||
|
|
@ -130,17 +148,17 @@ export function groupByFamily(data, baseClasses, left, flow?: NodeType[]) {
|
|||
}
|
||||
|
||||
for (const [d, nodes] of Object.entries(data)) {
|
||||
let tempInputs = [],
|
||||
tempOutputs = [];
|
||||
let tempInputs: string[] = [],
|
||||
tempOutputs: string[] = [];
|
||||
|
||||
for (const [n, node] of Object.entries(nodes)) {
|
||||
for (const [n, node] of Object.entries(nodes!)) {
|
||||
let foundNode = checkedNodes.get(n);
|
||||
if (!foundNode) {
|
||||
foundNode = {
|
||||
hasBaseClassInTemplate: Object.values(node.template).some(
|
||||
hasBaseClassInTemplate: Object.values(node!.template).some(
|
||||
checkBaseClass
|
||||
),
|
||||
hasBaseClassInBaseClasses: node.base_classes.some((baseClass) =>
|
||||
hasBaseClassInBaseClasses: node!.base_classes.some((baseClass) =>
|
||||
baseClassesSet.has(baseClass)
|
||||
),
|
||||
};
|
||||
|
|
@ -151,7 +169,7 @@ export function groupByFamily(data, baseClasses, left, flow?: NodeType[]) {
|
|||
if (foundNode.hasBaseClassInBaseClasses) tempOutputs.push(n);
|
||||
}
|
||||
|
||||
const totalNodes = Object.keys(nodes).length;
|
||||
const totalNodes = Object.keys(nodes!).length;
|
||||
if (tempInputs.length)
|
||||
arrOfPossibleInputs.push({
|
||||
category: d,
|
||||
|
|
@ -177,12 +195,12 @@ export function groupByFamily(data, baseClasses, left, flow?: NodeType[]) {
|
|||
}));
|
||||
}
|
||||
|
||||
export function buildInputs(tabsState, id) {
|
||||
export function buildInputs(tabsState: TabsState, id: string): string {
|
||||
return tabsState &&
|
||||
tabsState[id] &&
|
||||
tabsState[id].formKeysData &&
|
||||
tabsState[id].formKeysData.input_keys &&
|
||||
Object.keys(tabsState[id].formKeysData.input_keys).length > 0
|
||||
Object.keys(tabsState[id].formKeysData.input_keys!).length > 0
|
||||
? JSON.stringify(tabsState[id].formKeysData.input_keys)
|
||||
: '{"input": "message"}';
|
||||
}
|
||||
|
|
@ -238,7 +256,7 @@ export function varHighlightHTML({ name }: IVarHighlightType): string {
|
|||
return html;
|
||||
}
|
||||
|
||||
export function buildTweakObject(tweak) {
|
||||
export function buildTweakObject(tweak: tweakType) {
|
||||
tweak.forEach((el) => {
|
||||
Object.keys(el).forEach((key) => {
|
||||
for (let kp in el[key]) {
|
||||
|
|
@ -248,7 +266,6 @@ export function buildTweakObject(tweak) {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
const tweakString = JSON.stringify(tweak.at(-1), null, 2);
|
||||
return tweakString;
|
||||
}
|
||||
|
|
@ -263,12 +280,13 @@ export function getChatInputField(flow: FlowType, tabsState?: TabsState) {
|
|||
let chat_input_field = "text";
|
||||
|
||||
if (
|
||||
tabsState &&
|
||||
tabsState[flow.id] &&
|
||||
tabsState[flow.id].formKeysData &&
|
||||
tabsState[flow.id].formKeysData.input_keys
|
||||
) {
|
||||
chat_input_field = Object.keys(
|
||||
tabsState[flow.id].formKeysData.input_keys
|
||||
tabsState[flow.id].formKeysData.input_keys!
|
||||
)[0];
|
||||
}
|
||||
return chat_input_field;
|
||||
|
|
@ -291,7 +309,7 @@ export function getPythonApiCode(
|
|||
// node.data.id
|
||||
// }
|
||||
const tweaks = buildTweaks(flow);
|
||||
const inputs = buildInputs(tabsState, flow.id);
|
||||
const inputs = buildInputs(tabsState!, flow.id);
|
||||
return `import requests
|
||||
from typing import Optional
|
||||
|
||||
|
|
@ -343,7 +361,7 @@ export function getCurlCode(
|
|||
): string {
|
||||
const flowId = flow.id;
|
||||
const tweaks = buildTweaks(flow);
|
||||
const inputs = buildInputs(tabsState, flow.id);
|
||||
const inputs = buildInputs(tabsState!, flow.id);
|
||||
|
||||
return `curl -X POST \\
|
||||
${window.location.protocol}//${
|
||||
|
|
@ -369,7 +387,7 @@ export function getPythonCode(
|
|||
): string {
|
||||
const flowName = flow.name;
|
||||
const tweaks = buildTweaks(flow);
|
||||
const inputs = buildInputs(tabsState, flow.id);
|
||||
const inputs = buildInputs(tabsState!, flow.id);
|
||||
return `from langflow import load_flow_from_json
|
||||
TWEAKS = ${
|
||||
tweak && tweak.length > 0
|
||||
|
|
@ -390,7 +408,7 @@ flow(inputs)`;
|
|||
export function getWidgetCode(flow: FlowType, tabsState?: TabsState): string {
|
||||
const flowId = flow.id;
|
||||
const flowName = flow.name;
|
||||
const inputs = buildInputs(tabsState, flow.id);
|
||||
const inputs = buildInputs(tabsState!, flow.id);
|
||||
let chat_input_field = getChatInputField(flow, tabsState);
|
||||
|
||||
return `<script src="https://cdn.jsdelivr.net/gh/logspace-ai/langflow-embedded-chat@main/dist/build/static/js/bundle.min.js"></script>
|
||||
|
|
@ -402,7 +420,7 @@ chat_input_field: Input key that you want the chat to send the user message with
|
|||
window_title="${flowName}"
|
||||
flow_id="${flowId}"
|
||||
${
|
||||
tabsState[flow.id] && tabsState[flow.id].formKeysData
|
||||
tabsState![flow.id] && tabsState![flow.id].formKeysData
|
||||
? `chat_inputs='${inputs}'
|
||||
chat_input_field="${chat_input_field}"
|
||||
`
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": false,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "esnext",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue