Merge remote-tracking branch 'origin/dev' into authentication

This commit is contained in:
gustavoschaedler 2023-08-09 21:46:08 +01:00
commit f065a46950
36 changed files with 865 additions and 564 deletions

View file

@ -1,7 +1,7 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langflow.utils.logger import logger from langflow.utils.logger import logger
from contextlib import contextmanager from contextlib import contextmanager
from alembic.util.exc import CommandError
from sqlmodel import Session from sqlmodel import Session
if TYPE_CHECKING: if TYPE_CHECKING:
@ -13,7 +13,23 @@ def initialize_database():
from langflow.services import service_manager, ServiceType from langflow.services import service_manager, ServiceType
database_manager = service_manager.get(ServiceType.DATABASE_MANAGER) database_manager = service_manager.get(ServiceType.DATABASE_MANAGER)
try:
database_manager.run_migrations() database_manager.run_migrations()
except CommandError as exc:
if "Can't locate revision identified by" not in str(exc):
raise exc
# This means there's wrong revision in the DB
# We need to delete the alembic_version table
# and run the migrations again
logger.warning(
"Wrong revision in DB, deleting alembic_version table and running migrations again"
)
with session_getter(database_manager) as session:
session.execute("DROP TABLE alembic_version")
database_manager.run_migrations()
except Exception as exc:
logger.error(f"Error running migrations: {exc}")
raise RuntimeError("Error running migrations") from exc
database_manager.create_db_and_tables() database_manager.create_db_and_tables()
logger.debug("Database initialized") logger.debug("Database initialized")

View file

@ -30,7 +30,6 @@ class DocumentLoaderFrontNode(FrontendNode):
"UnstructuredEmailLoader": build_file_field( "UnstructuredEmailLoader": build_file_field(
suffixes=[".eml"], fileTypes=["eml"] suffixes=[".eml"], fileTypes=["eml"]
), ),
"SlackDirectoryLoader": build_file_field(suffixes=[".zip"], fileTypes=["zip"]),
"EverNoteLoader": build_file_field(suffixes=[".xml"], fileTypes=["xml"]), "EverNoteLoader": build_file_field(suffixes=[".xml"], fileTypes=["xml"]),
"FacebookChatLoader": build_file_field(suffixes=[".json"], fileTypes=["json"]), "FacebookChatLoader": build_file_field(suffixes=[".json"], fileTypes=["json"]),
"BSHTMLLoader": build_file_field(suffixes=[".html"], fileTypes=["html"]), "BSHTMLLoader": build_file_field(suffixes=[".html"], fileTypes=["html"]),
@ -105,7 +104,30 @@ class DocumentLoaderFrontNode(FrontendNode):
advanced=False, advanced=False,
) )
) )
elif self.template.type_name in {"SlackDirectoryLoader"}:
self.template.add_field(
TemplateField(
field_type="file",
required=True,
show=True,
name="zip_path",
value="",
display_name="Path to zip file",
suffixes=[".zip"],
file_types=["zip"],
)
)
self.template.add_field(
TemplateField(
field_type="str",
required=False,
show=True,
name="workspace_url",
value="",
display_name="Workspace URL",
advanced=False,
)
)
elif self.template.type_name in self.file_path_templates: elif self.template.type_name in self.file_path_templates:
self.template.add_field(self.file_path_templates[self.template.type_name]) self.template.add_field(self.file_path_templates[self.template.type_name])
elif self.template.type_name in { elif self.template.type_name in {

View file

@ -1,5 +1,11 @@
import { cloneDeep } from "lodash"; import { cloneDeep } from "lodash";
import React, { useContext, useEffect, useRef, useState } from "react"; import React, {
ReactNode,
useContext,
useEffect,
useRef,
useState,
} from "react";
import { Handle, Position, useUpdateNodeInternals } from "reactflow"; import { Handle, Position, useUpdateNodeInternals } from "reactflow";
import ShadTooltip from "../../../../components/ShadTooltipComponent"; import ShadTooltip from "../../../../components/ShadTooltipComponent";
import CodeAreaComponent from "../../../../components/codeAreaComponent"; import CodeAreaComponent from "../../../../components/codeAreaComponent";
@ -17,6 +23,7 @@ import { TOOLTIP_EMPTY } from "../../../../constants/constants";
import { TabsContext } from "../../../../contexts/tabsContext"; import { TabsContext } from "../../../../contexts/tabsContext";
import { typesContext } from "../../../../contexts/typesContext"; import { typesContext } from "../../../../contexts/typesContext";
import { ParameterComponentType } from "../../../../types/components"; import { ParameterComponentType } from "../../../../types/components";
import { TabsState } from "../../../../types/tabs";
import { isValidConnection } from "../../../../utils/reactflowUtils"; import { isValidConnection } from "../../../../utils/reactflowUtils";
import { import {
nodeColors, nodeColors,
@ -38,15 +45,15 @@ export default function ParameterComponent({
required = false, required = false,
optionalHandle = null, optionalHandle = null,
info = "", info = "",
}: ParameterComponentType) { }: ParameterComponentType): JSX.Element {
const ref = useRef(null); const ref = useRef<HTMLDivElement>(null);
const refHtml = useRef(null); const refHtml = useRef<HTMLDivElement & ReactNode>(null);
const infoHtml = useRef(null); const infoHtml = useRef<HTMLDivElement & ReactNode>(null);
const updateNodeInternals = useUpdateNodeInternals(); const updateNodeInternals = useUpdateNodeInternals();
const [position, setPosition] = useState(0); const [position, setPosition] = useState(0);
const { setTabsState, tabId, save, flows } = useContext(TabsContext); const { setTabsState, tabId, save, flows } = useContext(TabsContext);
const flow = flows.find((f) => f.id === tabId).data?.nodes ?? null; const flow = flows.find((flow) => flow.id === tabId)?.data?.nodes ?? null;
// Update component position // Update component position
useEffect(() => { useEffect(() => {
@ -62,16 +69,18 @@ export default function ParameterComponent({
const { reactFlowInstance } = useContext(typesContext); const { reactFlowInstance } = useContext(typesContext);
let disabled = let disabled =
reactFlowInstance?.getEdges().some((e) => e.targetHandle === id) ?? false; reactFlowInstance?.getEdges().some((edge) => edge.targetHandle === id) ??
false;
const { data: myData } = useContext(typesContext); const { data: myData } = useContext(typesContext);
const handleOnNewValue = (newValue: any) => { const handleOnNewValue = (newValue: string | string[] | boolean): void => {
let newData = cloneDeep(data); let newData = cloneDeep(data);
newData.node.template[name].value = newValue; newData.node!.template[name].value = newValue;
setData(newData); setData(newData);
// Set state to pending // Set state to pending
setTabsState((prev) => { //@ts-ignore
setTabsState((prev: TabsState) => {
return { return {
...prev, ...prev,
[tabId]: { [tabId]: {
@ -86,10 +95,11 @@ export default function ParameterComponent({
useEffect(() => { useEffect(() => {
if (name === "openai_api_base") console.log(info); if (name === "openai_api_base") console.log(info);
// @ts-ignore
infoHtml.current = ( infoHtml.current = (
<div className="h-full w-full word-break-break-word"> <div className="h-full w-full break-words">
{info.split("\n").map((line, i) => ( {info.split("\n").map((line, index) => (
<p key={i} className="block"> <p key={index} className="block">
{line} {line}
</p> </p>
))} ))}
@ -98,18 +108,19 @@ export default function ParameterComponent({
}, [info]); }, [info]);
function renderTooltips() { function renderTooltips() {
let groupedObj = groupByFamily(myData, tooltipTitle, left, flow); let groupedObj = groupByFamily(myData, tooltipTitle!, left, flow!);
if (groupedObj && groupedObj.length > 0) { if (groupedObj && groupedObj.length > 0) {
refHtml.current = groupedObj.map((item, i) => { //@ts-ignore
refHtml.current = groupedObj.map((item, index) => {
const Icon: any = const Icon: any =
nodeIconsLucide[item.family] ?? nodeIconsLucide["unknown"]; nodeIconsLucide[item.family] ?? nodeIconsLucide["unknown"];
return ( return (
<span <span
key={i} key={index}
className={classNames( className={classNames(
i > 0 ? "mt-2 flex items-center" : "flex items-center" index > 0 ? "mt-2 flex items-center" : "flex items-center"
)} )}
> >
<div <div
@ -132,10 +143,10 @@ export default function ParameterComponent({
{" "} {" "}
{item.type === "" ? "" : " - "} {item.type === "" ? "" : " - "}
{item.type.split(", ").length > 2 {item.type.split(", ").length > 2
? item.type.split(", ").map((el, i) => ( ? item.type.split(", ").map((el, index) => (
<React.Fragment key={el + i}> <React.Fragment key={el + index}>
<span> <span>
{i === item.type.split(", ").length - 1 {index === item.type.split(", ").length - 1
? el ? el
: (el += `, `)} : (el += `, `)}
</span> </span>
@ -148,6 +159,7 @@ export default function ParameterComponent({
); );
}); });
} else { } else {
//@ts-ignore
refHtml.current = <span>{TOOLTIP_EMPTY}</span>; refHtml.current = <span>{TOOLTIP_EMPTY}</span>;
} }
} }
@ -207,7 +219,7 @@ export default function ParameterComponent({
position={left ? Position.Left : Position.Right} position={left ? Position.Left : Position.Right}
id={id} id={id}
isValidConnection={(connection) => isValidConnection={(connection) =>
isValidConnection(connection, reactFlowInstance) isValidConnection(connection, reactFlowInstance!)
} }
className={classNames( className={classNames(
left ? "-ml-0.5 " : "-mr-0.5 ", left ? "-ml-0.5 " : "-mr-0.5 ",
@ -223,9 +235,9 @@ export default function ParameterComponent({
{left === true && {left === true &&
type === "str" && type === "str" &&
!data.node.template[name].options ? ( !data.node?.template[name].options ? (
<div className="mt-2 w-full"> <div className="mt-2 w-full">
{data.node.template[name].list ? ( {data.node?.template[name].list ? (
<InputListComponent <InputListComponent
disabled={disabled} disabled={disabled}
value={ value={
@ -236,7 +248,7 @@ export default function ParameterComponent({
} }
onChange={handleOnNewValue} onChange={handleOnNewValue}
/> />
) : data.node.template[name].multiline ? ( ) : data.node?.template[name].multiline ? (
<TextAreaComponent <TextAreaComponent
disabled={disabled} disabled={disabled}
value={data.node.template[name].value ?? ""} value={data.node.template[name].value ?? ""}
@ -245,8 +257,8 @@ export default function ParameterComponent({
) : ( ) : (
<InputComponent <InputComponent
disabled={disabled} disabled={disabled}
password={data.node.template[name].password ?? false} password={data.node?.template[name].password ?? false}
value={data.node.template[name].value ?? ""} value={data.node?.template[name].value ?? ""}
onChange={handleOnNewValue} onChange={handleOnNewValue}
/> />
)} )}
@ -255,9 +267,9 @@ export default function ParameterComponent({
<div className="mt-2 w-full"> <div className="mt-2 w-full">
<ToggleShadComponent <ToggleShadComponent
disabled={disabled} disabled={disabled}
enabled={data.node.template[name].value ?? false} enabled={data.node?.template[name].value ?? false}
setEnabled={(t) => { setEnabled={(isEnabled) => {
handleOnNewValue(t); handleOnNewValue(isEnabled);
}} }}
size="large" size="large"
/> />
@ -266,13 +278,13 @@ export default function ParameterComponent({
<div className="mt-2 w-full"> <div className="mt-2 w-full">
<FloatComponent <FloatComponent
disabled={disabled} disabled={disabled}
value={data.node.template[name].value ?? ""} value={data.node?.template[name].value ?? ""}
onChange={handleOnNewValue} onChange={handleOnNewValue}
/> />
</div> </div>
) : left === true && ) : left === true &&
type === "str" && type === "str" &&
data.node.template[name].options ? ( data.node?.template[name].options ? (
<div className="mt-2 w-full"> <div className="mt-2 w-full">
<Dropdown <Dropdown
options={data.node.template[name].options} options={data.node.template[name].options}
@ -283,13 +295,13 @@ export default function ParameterComponent({
) : left === true && type === "code" ? ( ) : left === true && type === "code" ? (
<div className="mt-2 w-full"> <div className="mt-2 w-full">
<CodeAreaComponent <CodeAreaComponent
dynamic={data.node.template[name].dynamic ?? false} dynamic={data.node?.template[name].dynamic ?? false}
setNodeClass={(nodeClass) => { setNodeClass={(nodeClass) => {
data.node = nodeClass; data.node = nodeClass;
}} }}
nodeClass={data.node} nodeClass={data.node}
disabled={disabled} disabled={disabled}
value={data.node.template[name].value ?? ""} value={data.node?.template[name].value ?? ""}
onChange={handleOnNewValue} onChange={handleOnNewValue}
/> />
</div> </div>
@ -297,12 +309,12 @@ export default function ParameterComponent({
<div className="mt-2 w-full"> <div className="mt-2 w-full">
<InputFileComponent <InputFileComponent
disabled={disabled} disabled={disabled}
value={data.node.template[name].value ?? ""} value={data.node?.template[name].value ?? ""}
onChange={handleOnNewValue} onChange={handleOnNewValue}
fileTypes={data.node.template[name].fileTypes} fileTypes={data.node?.template[name].fileTypes}
suffixes={data.node.template[name].suffixes} suffixes={data.node?.template[name].suffixes}
onFileChange={(t: string) => { onFileChange={(filePath: string) => {
data.node.template[name].file_path = t; data.node!.template[name].file_path = filePath;
save(); save();
}} }}
></InputFileComponent> ></InputFileComponent>
@ -311,7 +323,7 @@ export default function ParameterComponent({
<div className="mt-2 w-full"> <div className="mt-2 w-full">
<IntComponent <IntComponent
disabled={disabled} disabled={disabled}
value={data.node.template[name].value ?? ""} value={data.node?.template[name].value ?? ""}
onChange={handleOnNewValue} onChange={handleOnNewValue}
/> />
</div> </div>
@ -324,7 +336,7 @@ export default function ParameterComponent({
}} }}
nodeClass={data.node} nodeClass={data.node}
disabled={disabled} disabled={disabled}
value={data.node.template[name].value ?? ""} value={data.node?.template[name].value ?? ""}
onChange={handleOnNewValue} onChange={handleOnNewValue}
/> />
</div> </div>

View file

@ -159,52 +159,59 @@ export default function GenericNode({
<> <>
{Object.keys(data.node.template) {Object.keys(data.node.template)
.filter((t) => t.charAt(0) !== "_") .filter((templateField) => templateField.charAt(0) !== "_")
.map((t: string, idx) => ( .map((templateField: string, idx) => (
<div key={idx}> <div key={idx}>
{data.node.template[t].show && {data.node.template[templateField].show &&
!data.node.template[t].advanced ? ( !data.node.template[templateField].advanced ? (
<ParameterComponent <ParameterComponent
key={ key={
(data.node.template[t].input_types?.join(";") ?? (data.node.template[templateField].input_types?.join(
data.node.template[t].type) + ";"
) ?? data.node.template[templateField].type) +
"|" + "|" +
t + templateField +
"|" + "|" +
data.id data.id
} }
data={data} data={data}
setData={setData} setData={setData}
color={ color={
nodeColors[types[data.node.template[t].type]] ?? nodeColors[
nodeColors[data.node.template[t].type] ?? types[data.node.template[templateField].type]
] ??
nodeColors[data.node.template[templateField].type] ??
nodeColors.unknown nodeColors.unknown
} }
title={ title={
data.node.template[t].display_name data.node.template[templateField].display_name
? data.node.template[t].display_name ? data.node.template[templateField].display_name
: data.node.template[t].name : data.node.template[templateField].name
? toTitleCase(data.node.template[t].name) ? toTitleCase(data.node.template[templateField].name)
: toTitleCase(t) : toTitleCase(templateField)
} }
info={data.node.template[t].info} info={data.node.template[templateField].info}
name={t} name={templateField}
tooltipTitle={ tooltipTitle={
data.node.template[t].input_types?.join("\n") ?? data.node.template[templateField].input_types?.join(
data.node.template[t].type "\n"
) ?? data.node.template[templateField].type
} }
required={data.node.template[t].required} required={data.node.template[templateField].required}
id={ id={
(data.node.template[t].input_types?.join(";") ?? (data.node.template[templateField].input_types?.join(
data.node.template[t].type) + ";"
) ?? data.node.template[templateField].type) +
"|" + "|" +
t + templateField +
"|" + "|" +
data.id data.id
} }
left={true} left={true}
type={data.node.template[t].type} type={data.node.template[templateField].type}
optionalHandle={data.node.template[t].input_types} optionalHandle={
data.node.template[templateField].input_types
}
/> />
) : ( ) : (
<></> <></>

View file

@ -22,9 +22,9 @@ export default function AlertDropdown({ children }: AlertDropdownType) {
return ( return (
<Popover <Popover
open={open} open={open}
onOpenChange={(k) => { onOpenChange={(target) => {
setOpen(k); setOpen(target);
if (k) setNotificationCenter(false); if (target) setNotificationCenter(false);
}} }}
> >
<PopoverTrigger>{children}</PopoverTrigger> <PopoverTrigger>{children}</PopoverTrigger>

View file

@ -55,12 +55,12 @@ export const EditFlowSettings: React.FC<InputProps> = ({
}; };
const [desc, setDesc] = useState( const [desc, setDesc] = useState(
flows.find((f) => f.id === tabId).description flows.find((flow) => flow.id === tabId).description
); );
const handleDescriptionChange = (event: ChangeEvent<HTMLTextAreaElement>) => { const handleDescriptionChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
flows.find((f) => f.id === tabId).description = event.target.value; flows.find((flow) => flow.id === tabId).description = event.target.value;
setDesc(flows.find((f) => f.id === tabId).description); setDesc(flows.find((flow) => flow.id === tabId).description);
setDescription(event.target.value); setDescription(event.target.value);
}; };

View file

@ -34,9 +34,9 @@ export default function CodeAreaComponent({
value={myValue} value={myValue}
nodeClass={nodeClass} nodeClass={nodeClass}
setNodeClass={setNodeClass} setNodeClass={setNodeClass}
setValue={(t: string) => { setValue={(value: string) => {
setMyValue(t); setMyValue(value);
onChange(t); onChange(value);
}} }}
> >
<div className="flex w-full items-center"> <div className="flex w-full items-center">

View file

@ -205,13 +205,15 @@ export default function CodeTabsComponent({
: "overflow-hidden" : "overflow-hidden"
)} )}
> >
{data.map((t: any, index) => ( {data.map((node: any, index) => (
<div className="px-3" key={index}> <div className="px-3" key={index}>
{tweaks.tweaksList.current.includes(t["data"]["id"]) && ( {tweaks.tweaksList.current.includes(
node["data"]["id"]
) && (
<AccordionComponent <AccordionComponent
trigger={t["data"]["id"]} trigger={node["data"]["id"]}
open={openAccordion} open={openAccordion}
keyValue={t["data"]["id"]} keyValue={node["data"]["id"]}
> >
<div className="api-modal-table-arrangement"> <div className="api-modal-table-arrangement">
<Table className="table-fixed bg-muted outline-1"> <Table className="table-fixed bg-muted outline-1">
@ -226,78 +228,92 @@ export default function CodeTabsComponent({
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody className="p-0"> <TableBody className="p-0">
{Object.keys(t["data"]["node"]["template"]) {Object.keys(node["data"]["node"]["template"])
.filter( .filter(
(n) => (templateField) =>
n.charAt(0) !== "_" && templateField.charAt(0) !== "_" &&
t.data.node.template[n].show && node.data.node.template[templateField]
(t.data.node.template[n].type === "str" || .show &&
t.data.node.template[n].type === (node.data.node.template[templateField]
"bool" || .type === "str" ||
t.data.node.template[n].type === node.data.node.template[templateField]
"float" || .type === "bool" ||
t.data.node.template[n].type === node.data.node.template[templateField]
"code" || .type === "float" ||
t.data.node.template[n].type === node.data.node.template[templateField]
"prompt" || .type === "code" ||
t.data.node.template[n].type === node.data.node.template[templateField]
"file" || .type === "prompt" ||
t.data.node.template[n].type === "int") node.data.node.template[templateField]
.type === "file" ||
node.data.node.template[templateField]
.type === "int")
) )
.map((n, i) => { .map((templateField, index) => {
return ( return (
<TableRow <TableRow
key={i} key={index}
className="h-10 dark:border-b-muted" className="h-10 dark:border-b-muted"
> >
<TableCell className="p-0 text-center text-sm text-foreground"> <TableCell className="p-0 text-center text-sm text-foreground">
{n} {templateField}
</TableCell> </TableCell>
<TableCell className="p-0 text-xs text-foreground"> <TableCell className="p-0 text-xs text-foreground">
<div className="m-auto w-[250px]"> <div className="m-auto w-[250px]">
{t.data.node.template[n].type === {node.data.node.template[
"str" && templateField
!t.data.node.template[n].options ? ( ].type === "str" &&
!node.data.node.template[
templateField
].options ? (
<div className="mx-auto"> <div className="mx-auto">
{t.data.node.template[n] {node.data.node.template[
.list ? ( templateField
].list ? (
<InputListComponent <InputListComponent
editNode={true} editNode={true}
disabled={false} disabled={false}
value={ value={
!t.data.node.template[n] !node.data.node.template[
.value || templateField
t.data.node.template[n] ].value ||
.value === "" node.data.node.template[
templateField
].value === ""
? [""] ? [""]
: t.data.node.template[ : node.data.node
n .template[
templateField
].value ].value
} }
onChange={(k) => { onChange={(target) => {
setData((old) => { setData((old) => {
let newInputList = let newInputList =
cloneDeep(old); cloneDeep(old);
newInputList[ newInputList[
index index
].data.node.template[ ].data.node.template[
n templateField
].value = k; ].value = target;
return newInputList; return newInputList;
}); });
tweaks.buildTweakObject( tweaks.buildTweakObject(
t["data"]["id"], node["data"]["id"],
k, target,
t.data.node.template[n] node.data.node.template[
templateField
]
); );
}} }}
/> />
) : t.data.node.template[n] ) : node.data.node.template[
.multiline ? ( templateField
].multiline ? (
<ShadTooltip <ShadTooltip
content={tweaks.buildContent( content={tweaks.buildContent(
t.data.node.template[n] node.data.node.template[
.value templateField
].value
)} )}
> >
<div> <div>
@ -305,33 +321,38 @@ export default function CodeTabsComponent({
disabled={false} disabled={false}
editNode={true} editNode={true}
value={ value={
!t.data.node.template[ !node.data.node
n .template[
templateField
].value || ].value ||
t.data.node.template[ node.data.node
n .template[
templateField
].value === "" ].value === ""
? "" ? ""
: t.data.node : node.data.node
.template[n] .template[
.value templateField
].value
} }
onChange={(k) => { onChange={(target) => {
setData((old) => { setData((old) => {
let newInputList = let newInputList =
cloneDeep(old); cloneDeep(old);
newInputList[ newInputList[
index index
].data.node.template[ ].data.node.template[
n templateField
].value = k; ].value = target;
return newInputList; return newInputList;
}); });
tweaks.buildTweakObject( tweaks.buildTweakObject(
t["data"]["id"], node["data"]["id"],
k, target,
t.data.node node.data.node
.template[n] .template[
templateField
]
); );
}} }}
/> />
@ -342,47 +363,55 @@ export default function CodeTabsComponent({
editNode={true} editNode={true}
disabled={false} disabled={false}
password={ password={
t.data.node.template[n] node.data.node.template[
.password ?? false templateField
].password ?? false
} }
value={ value={
!t.data.node.template[n] !node.data.node.template[
.value || templateField
t.data.node.template[n] ].value ||
.value === "" node.data.node.template[
templateField
].value === ""
? "" ? ""
: t.data.node.template[ : node.data.node
n .template[
templateField
].value ].value
} }
onChange={(k) => { onChange={(target) => {
setData((old) => { setData((old) => {
let newInputList = let newInputList =
cloneDeep(old); cloneDeep(old);
newInputList[ newInputList[
index index
].data.node.template[ ].data.node.template[
n templateField
].value = k; ].value = target;
return newInputList; return newInputList;
}); });
tweaks.buildTweakObject( tweaks.buildTweakObject(
t["data"]["id"], node["data"]["id"],
k, target,
t.data.node.template[n] node.data.node.template[
templateField
]
); );
}} }}
/> />
)} )}
</div> </div>
) : t.data.node.template[n].type === ) : node.data.node.template[
"bool" ? ( templateField
].type === "bool" ? (
<div className="ml-auto"> <div className="ml-auto">
{" "} {" "}
<ToggleShadComponent <ToggleShadComponent
enabled={ enabled={
t.data.node.template[n] node.data.node.template[
.value templateField
].value
} }
setEnabled={(e) => { setEnabled={(e) => {
setData((old) => { setData((old) => {
@ -391,31 +420,37 @@ export default function CodeTabsComponent({
newInputList[ newInputList[
index index
].data.node.template[ ].data.node.template[
n templateField
].value = e; ].value = e;
return newInputList; return newInputList;
}); });
tweaks.buildTweakObject( tweaks.buildTweakObject(
t["data"]["id"], node["data"]["id"],
e, e,
t.data.node.template[n] node.data.node.template[
templateField
]
); );
}} }}
size="small" size="small"
disabled={false} disabled={false}
/> />
</div> </div>
) : t.data.node.template[n].type === ) : node.data.node.template[
"file" ? ( templateField
].type === "file" ? (
<ShadTooltip <ShadTooltip
content={tweaks.buildContent( content={tweaks.buildContent(
!t.data.node.template[n] !node.data.node.template[
.value || templateField
t.data.node.template[n] ].value ||
.value === "" node.data.node.template[
templateField
].value === ""
? "" ? ""
: t.data.node.template[n] : node.data.node.template[
.value templateField
].value
)} )}
> >
<div className="mx-auto"> <div className="mx-auto">
@ -423,147 +458,176 @@ export default function CodeTabsComponent({
editNode={true} editNode={true}
disabled={false} disabled={false}
value={ value={
t.data.node.template[n] node.data.node.template[
.value ?? "" templateField
].value ?? ""
} }
onChange={(k: any) => {}} onChange={(
target: any
) => {}}
fileTypes={ fileTypes={
t.data.node.template[n] node.data.node.template[
.fileTypes templateField
].fileTypes
} }
suffixes={ suffixes={
t.data.node.template[n] node.data.node.template[
.suffixes templateField
].suffixes
} }
onFileChange={( onFileChange={(
value: any value: any
) => { ) => {
t.data.node.template[ node.data.node.template[
n templateField
].file_path = value; ].file_path = value;
}} }}
></InputFileComponent> ></InputFileComponent>
</div> </div>
</ShadTooltip> </ShadTooltip>
) : t.data.node.template[n].type === ) : node.data.node.template[
"float" ? ( templateField
].type === "float" ? (
<div className="mx-auto"> <div className="mx-auto">
<FloatComponent <FloatComponent
disabled={false} disabled={false}
editNode={true} editNode={true}
value={ value={
!t.data.node.template[n] !node.data.node.template[
.value || templateField
t.data.node.template[n] ].value ||
.value === "" node.data.node.template[
templateField
].value === ""
? "" ? ""
: t.data.node.template[n] : node.data.node.template[
.value templateField
].value
} }
onChange={(k) => { onChange={(target) => {
setData((old) => { setData((old) => {
let newInputList = let newInputList =
cloneDeep(old); cloneDeep(old);
newInputList[ newInputList[
index index
].data.node.template[ ].data.node.template[
n templateField
].value = k; ].value = target;
return newInputList; return newInputList;
}); });
tweaks.buildTweakObject( tweaks.buildTweakObject(
t["data"]["id"], node["data"]["id"],
k, target,
t.data.node.template[n] node.data.node.template[
templateField
]
); );
}} }}
/> />
</div> </div>
) : t.data.node.template[n].type === ) : node.data.node.template[
"str" && templateField
t.data.node.template[n] ].type === "str" &&
.options ? ( node.data.node.template[
templateField
].options ? (
<div className="mx-auto"> <div className="mx-auto">
<Dropdown <Dropdown
editNode={true} editNode={true}
apiModal={true} apiModal={true}
options={ options={
t.data.node.template[n] node.data.node.template[
.options templateField
].options
} }
onSelect={(k) => { onSelect={(target) => {
setData((old) => { setData((old) => {
let newInputList = let newInputList =
cloneDeep(old); cloneDeep(old);
newInputList[ newInputList[
index index
].data.node.template[ ].data.node.template[
n templateField
].value = k; ].value = target;
return newInputList; return newInputList;
}); });
tweaks.buildTweakObject( tweaks.buildTweakObject(
t["data"]["id"], node["data"]["id"],
k, target,
t.data.node.template[n] node.data.node.template[
templateField
]
); );
}} }}
value={ value={
!t.data.node.template[n] !node.data.node.template[
.value || templateField
t.data.node.template[n] ].value ||
.value === "" node.data.node.template[
templateField
].value === ""
? "" ? ""
: t.data.node.template[n] : node.data.node.template[
.value templateField
].value
} }
></Dropdown> ></Dropdown>
</div> </div>
) : t.data.node.template[n].type === ) : node.data.node.template[
"int" ? ( templateField
].type === "int" ? (
<div className="mx-auto"> <div className="mx-auto">
<IntComponent <IntComponent
disabled={false} disabled={false}
editNode={true} editNode={true}
value={ value={
!t.data.node.template[n] !node.data.node.template[
.value || templateField
t.data.node.template[n] ].value ||
.value === "" node.data.node.template[
templateField
].value === ""
? "" ? ""
: t.data.node.template[n] : node.data.node.template[
.value templateField
].value
} }
onChange={(k) => { onChange={(target) => {
setData((old) => { setData((old) => {
let newInputList = let newInputList =
cloneDeep(old); cloneDeep(old);
newInputList[ newInputList[
index index
].data.node.template[ ].data.node.template[
n templateField
].value = k; ].value = target;
return newInputList; return newInputList;
}); });
tweaks.buildTweakObject( tweaks.buildTweakObject(
t["data"]["id"], node["data"]["id"],
k, target,
t.data.node.template[n] node.data.node.template[
templateField
]
); );
}} }}
/> />
</div> </div>
) : t.data.node.template[n].type === ) : node.data.node.template[
"prompt" ? ( templateField
].type === "prompt" ? (
<ShadTooltip <ShadTooltip
content={tweaks.buildContent( content={tweaks.buildContent(
!t.data.node.template[n] !node.data.node.template[
.value || templateField
t.data.node.template[n] ].value ||
.value === "" node.data.node.template[
templateField
].value === ""
? "" ? ""
: t.data.node.template[n] : node.data.node.template[
.value templateField
].value
)} )}
> >
<div className="mx-auto"> <div className="mx-auto">
@ -571,44 +635,53 @@ export default function CodeTabsComponent({
editNode={true} editNode={true}
disabled={false} disabled={false}
value={ value={
!t.data.node.template[n] !node.data.node.template[
.value || templateField
t.data.node.template[n] ].value ||
.value === "" node.data.node.template[
templateField
].value === ""
? "" ? ""
: t.data.node.template[ : node.data.node
n .template[
templateField
].value ].value
} }
onChange={(k) => { onChange={(target) => {
setData((old) => { setData((old) => {
let newInputList = let newInputList =
cloneDeep(old); cloneDeep(old);
newInputList[ newInputList[
index index
].data.node.template[ ].data.node.template[
n templateField
].value = k; ].value = target;
return newInputList; return newInputList;
}); });
tweaks.buildTweakObject( tweaks.buildTweakObject(
t["data"]["id"], node["data"]["id"],
k, target,
t.data.node.template[n] node.data.node.template[
templateField
]
); );
}} }}
/> />
</div> </div>
</ShadTooltip> </ShadTooltip>
) : t.data.node.template[n].type === ) : node.data.node.template[
"code" ? ( templateField
].type === "code" ? (
<ShadTooltip <ShadTooltip
content={tweaks.buildContent( content={tweaks.buildContent(
tweaks.getValue( tweaks.getValue(
t.data.node.template[n] node.data.node.template[
.value, templateField
t.data, ].value,
t.data.node.template[n] node.data,
node.data.node.template[
templateField
]
) )
)} )}
> >
@ -617,37 +690,43 @@ export default function CodeTabsComponent({
disabled={false} disabled={false}
editNode={true} editNode={true}
value={ value={
!t.data.node.template[n] !node.data.node.template[
.value || templateField
t.data.node.template[n] ].value ||
.value === "" node.data.node.template[
templateField
].value === ""
? "" ? ""
: t.data.node.template[ : node.data.node
n .template[
templateField
].value ].value
} }
onChange={(k) => { onChange={(target) => {
setData((old) => { setData((old) => {
let newInputList = let newInputList =
cloneDeep(old); cloneDeep(old);
newInputList[ newInputList[
index index
].data.node.template[ ].data.node.template[
n templateField
].value = k; ].value = target;
return newInputList; return newInputList;
}); });
tweaks.buildTweakObject( tweaks.buildTweakObject(
t["data"]["id"], node["data"]["id"],
k, target,
t.data.node.template[n] node.data.node.template[
templateField
]
); );
}} }}
/> />
</div> </div>
</ShadTooltip> </ShadTooltip>
) : t.data.node.template[n].type === ) : node.data.node.template[
"Any" ? ( templateField
].type === "Any" ? (
"-" "-"
) : ( ) : (
<div className="hidden"></div> <div className="hidden"></div>

View file

@ -25,12 +25,12 @@ export default function FloatComponent({
type="number" type="number"
step={step} step={step}
min={min} min={min}
onInput={(e: React.ChangeEvent<HTMLInputElement>) => { onInput={(event: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.value < min.toString()) { if (event.target.value < min.toString()) {
e.target.value = min.toString(); event.target.value = min.toString();
} }
if (e.target.value > max.toString()) { if (event.target.value > max.toString()) {
e.target.value = max.toString(); event.target.value = max.toString();
} }
}} }}
max={max} max={max}
@ -40,8 +40,8 @@ export default function FloatComponent({
placeholder={ placeholder={
editNode ? "Number 0 to 1" : "Type a number from zero to one" editNode ? "Number 0 to 1" : "Type a number from zero to one"
} }
onChange={(e) => { onChange={(event) => {
onChange(e.target.value); onChange(event.target.value);
}} }}
/> />
</div> </div>

View file

@ -34,9 +34,8 @@ export default function Header() {
<Link to="/"> <Link to="/">
<span className="ml-4 text-2xl">⛓️</span> <span className="ml-4 text-2xl">⛓️</span>
</Link> </Link>
{flows.findIndex((f) => tabId === f.id) !== -1 && tabId !== "" && ( {flows.findIndex((flow) => tabId === flow.id) !== -1 &&
<MenuBar flows={flows} tabId={tabId} /> tabId !== "" && <MenuBar flows={flows} tabId={tabId} />}
)}
</div> </div>
<div className="round-button-div"> <div className="round-button-div">
<Link to="/"> <Link to="/">

View file

@ -31,8 +31,8 @@ export default function InputComponent({
password && !editNode ? "pr-10" : "" password && !editNode ? "pr-10" : ""
)} )}
placeholder={password && editNode ? "Key" : "Type something..."} placeholder={password && editNode ? "Key" : "Type something..."}
onChange={(e) => { onChange={(event) => {
onChange(e.target.value); onChange(event.target.value);
}} }}
/> />
{password && ( {password && (

View file

@ -49,11 +49,11 @@ export default function InputFileComponent({
input.style.display = "none"; // Hidden from view input.style.display = "none"; // Hidden from view
input.multiple = false; // Allow only one file selection input.multiple = false; // Allow only one file selection
input.onchange = (e: Event) => { input.onchange = (event: Event) => {
setLoading(true); setLoading(true);
// Get the selected file // Get the selected file
const file = (e.target as HTMLInputElement).files?.[0]; const file = (event.target as HTMLInputElement).files?.[0];
// Check if the file type is correct // Check if the file type is correct
if (file && checkFileType(file.name)) { if (file && checkFileType(file.name)) {

View file

@ -25,18 +25,18 @@ export default function InputListComponent({
"flex flex-col gap-3" "flex flex-col gap-3"
)} )}
> >
{value.map((i, idx) => { {value.map((singleValue, idx) => {
return ( return (
<div key={idx} className="flex w-full gap-3"> <div key={idx} className="flex w-full gap-3">
<Input <Input
disabled={disabled} disabled={disabled}
type="text" type="text"
value={i} value={singleValue}
className={editNode ? "input-edit-node" : ""} className={editNode ? "input-edit-node" : ""}
placeholder="Type something..." placeholder="Type something..."
onChange={(e) => { onChange={(event) => {
let newInputList = _.cloneDeep(value); let newInputList = _.cloneDeep(value);
newInputList[idx] = e.target.value; newInputList[idx] = event.target.value;
onChange(newInputList); onChange(newInputList);
}} }}
/> />

View file

@ -41,17 +41,17 @@ export default function IntComponent({
type="number" type="number"
step="1" step="1"
min={min} min={min}
onInput={(e: React.ChangeEvent<HTMLInputElement>) => { onInput={(event: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.value < min.toString()) { if (event.target.value < min.toString()) {
e.target.value = min.toString(); event.target.value = min.toString();
} }
}} }}
value={value ?? ""} value={value ?? ""}
className={editNode ? "input-edit-node" : ""} className={editNode ? "input-edit-node" : ""}
disabled={disabled} disabled={disabled}
placeholder={editNode ? "Integer number" : "Type an integer number"} placeholder={editNode ? "Integer number" : "Type an integer number"}
onChange={(e) => { onChange={(event) => {
onChange(e.target.value); onChange(event.target.value);
}} }}
/> />
</div> </div>

View file

@ -39,8 +39,8 @@ export default function PromptAreaComponent({
value={value} value={value}
buttonText="Check & Save" buttonText="Check & Save"
modalTitle="Edit Prompt" modalTitle="Edit Prompt"
setValue={(t: string) => { setValue={(value: string) => {
onChange(t); onChange(value);
}} }}
nodeClass={nodeClass} nodeClass={nodeClass}
setNodeClass={setNodeClass} setNodeClass={setNodeClass}

View file

@ -25,8 +25,8 @@ export default function TextAreaComponent({
disabled={disabled} disabled={disabled}
className={editNode ? "input-edit-node" : ""} className={editNode ? "input-edit-node" : ""}
placeholder={"Type something..."} placeholder={"Type something..."}
onChange={(e) => { onChange={(event) => {
onChange(e.target.value); onChange(event.target.value);
}} }}
/> />
<div> <div>
@ -35,8 +35,8 @@ export default function TextAreaComponent({
buttonText="Finishing Editing" buttonText="Finishing Editing"
modalTitle="Edit Text" modalTitle="Edit Text"
value={value} value={value}
setValue={(t: string) => { setValue={(value: string) => {
onChange(t); onChange(value);
}} }}
> >
{!editNode && ( {!editNode && (

View file

@ -18,8 +18,8 @@ export default function ToggleComponent({
<div className={disabled ? "pointer-events-none cursor-not-allowed" : ""}> <div className={disabled ? "pointer-events-none cursor-not-allowed" : ""}>
<Switch <Switch
checked={enabled} checked={enabled}
onChange={(x: boolean) => { onChange={(isEnabled: boolean) => {
setEnabled(x); setEnabled(isEnabled);
}} }}
className={classNames( className={classNames(
enabled ? "bg-primary" : "bg-input", enabled ? "bg-primary" : "bg-input",

View file

@ -35,8 +35,8 @@ export default function ToggleShadComponent({
disabled={disabled} disabled={disabled}
className="" className=""
checked={enabled} checked={enabled}
onCheckedChange={(x: boolean) => { onCheckedChange={(isEnabled: boolean) => {
setEnabled(x); setEnabled(isEnabled);
}} }}
></Switch> ></Switch>
</div> </div>

View file

@ -15,8 +15,8 @@ export default function RenameLabel(props) {
useEffect(() => { useEffect(() => {
if (isRename) { if (isRename) {
setMyValue(props.value); setMyValue(props.value);
document.addEventListener("keydown", (e) => { document.addEventListener("keydown", (event) => {
if (e.key === "Escape") { if (event.key === "Escape") {
setIsRename(false); setIsRename(false);
props.setValue(""); props.setValue("");
} }
@ -67,8 +67,8 @@ export default function RenameLabel(props) {
} }
}} }}
value={myValue} value={myValue}
onChange={(e) => { onChange={(event) => {
setMyValue(e.target.value); setMyValue(event.target.value);
}} }}
/> />
) : ( ) : (

View file

@ -508,3 +508,84 @@ export const URL_EXCLUDED_FROM_ERROR_RETRIES = [
"/api/v1/custom_component", "/api/v1/custom_component",
"/api/v1/validate/prompt", "/api/v1/validate/prompt",
]; ];
export const tabsCode = [];
export function tabsArray(codes: string[], method: number) {
if (!method) return;
if (method === 0) {
return [
{
name: "cURL",
mode: "bash",
image: "https://curl.se/logo/curl-symbol-transparent.png",
language: "sh",
code: codes[0],
},
{
name: "Python API",
mode: "python",
image:
"https://images.squarespace-cdn.com/content/v1/5df3d8c5d2be5962e4f87890/1628015119369-OY4TV3XJJ53ECO0W2OLQ/Python+API+Training+Logo.png?format=1000w",
language: "py",
code: codes[1],
},
{
name: "Python Code",
mode: "python",
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
language: "py",
code: codes[2],
},
{
name: "Chat Widget HTML",
description:
"Insert this code anywhere in your &lt;body&gt; tag. To use with react and other libs, check our <a class='link-color' href='https://langflow.org/guidelines/widget'>documentation</a>.",
mode: "html",
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
language: "py",
code: codes[3],
},
];
}
return [
{
name: "cURL",
mode: "bash",
image: "https://curl.se/logo/curl-symbol-transparent.png",
language: "sh",
code: codes[0],
},
{
name: "Python API",
mode: "python",
image:
"https://images.squarespace-cdn.com/content/v1/5df3d8c5d2be5962e4f87890/1628015119369-OY4TV3XJJ53ECO0W2OLQ/Python+API+Training+Logo.png?format=1000w",
language: "py",
code: codes[1],
},
{
name: "Python Code",
mode: "python",
language: "py",
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
code: codes[2],
},
{
name: "Chat Widget HTML",
description:
"Insert this code anywhere in your &lt;body&gt; tag. To use with react and other libs, check our <a class='link-color' href='https://langflow.org/guidelines/widget'>documentation</a>.",
mode: "html",
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
language: "py",
code: codes[3],
},
{
name: "Tweaks",
mode: "python",
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
language: "py",
code: codes[4],
},
];
}

View file

@ -316,11 +316,13 @@ export function TabsProvider({ children }: { children: ReactNode }) {
const input = document.createElement("input"); const input = document.createElement("input");
input.type = "file"; input.type = "file";
// add a change event listener to the file input // add a change event listener to the file input
input.onchange = (e: Event) => { input.onchange = (event: Event) => {
// check if the file type is application/json // check if the file type is application/json
if ((e.target as HTMLInputElement).files[0].type === "application/json") { if (
(event.target as HTMLInputElement).files[0].type === "application/json"
) {
// get the file from the file input // get the file from the file input
const file = (e.target as HTMLInputElement).files[0]; const file = (event.target as HTMLInputElement).files[0];
// read the file as text // read the file as text
const formData = new FormData(); const formData = new FormData();
formData.append("file", file); formData.append("file", file);
@ -359,12 +361,12 @@ export function TabsProvider({ children }: { children: ReactNode }) {
let idsMap = {}; let idsMap = {};
let nodes = reactFlowInstance.getNodes(); let nodes = reactFlowInstance.getNodes();
let edges = reactFlowInstance.getEdges(); let edges = reactFlowInstance.getEdges();
selectionInstance.nodes.forEach((n) => { selectionInstance.nodes.forEach((node) => {
if (n.position.y < minimumY) { if (node.position.y < minimumY) {
minimumY = n.position.y; minimumY = node.position.y;
} }
if (n.position.x < minimumX) { if (node.position.x < minimumX) {
minimumX = n.position.x; minimumX = node.position.x;
} }
}); });
@ -372,43 +374,43 @@ export function TabsProvider({ children }: { children: ReactNode }) {
? { x: position.paneX + position.x, y: position.paneY + position.y } ? { x: position.paneX + position.x, y: position.paneY + position.y }
: reactFlowInstance.project({ x: position.x, y: position.y }); : reactFlowInstance.project({ x: position.x, y: position.y });
selectionInstance.nodes.forEach((n: NodeType) => { selectionInstance.nodes.forEach((node: NodeType) => {
// Generate a unique node ID // Generate a unique node ID
let newId = getNodeId(n.data.type); let newId = getNodeId(node.data.type);
idsMap[n.id] = newId; idsMap[node.id] = newId;
// Create a new node object // Create a new node object
const newNode: NodeType = { const newNode: NodeType = {
id: newId, id: newId,
type: "genericNode", type: "genericNode",
position: { position: {
x: insidePosition.x + n.position.x - minimumX, x: insidePosition.x + node.position.x - minimumX,
y: insidePosition.y + n.position.y - minimumY, y: insidePosition.y + node.position.y - minimumY,
}, },
data: { data: {
..._.cloneDeep(n.data), ..._.cloneDeep(node.data),
id: newId, id: newId,
}, },
}; };
// Add the new node to the list of nodes in state // Add the new node to the list of nodes in state
nodes = nodes nodes = nodes
.map((e) => ({ ...e, selected: false })) .map((node) => ({ ...node, selected: false }))
.concat({ ...newNode, selected: false }); .concat({ ...newNode, selected: false });
}); });
reactFlowInstance.setNodes(nodes); reactFlowInstance.setNodes(nodes);
selectionInstance.edges.forEach((e) => { selectionInstance.edges.forEach((edge) => {
let source = idsMap[e.source]; let source = idsMap[edge.source];
let target = idsMap[e.target]; let target = idsMap[edge.target];
let sourceHandleSplitted = e.sourceHandle.split("|"); let sourceHandleSplitted = edge.sourceHandle.split("|");
let sourceHandle = let sourceHandle =
sourceHandleSplitted[0] + sourceHandleSplitted[0] +
"|" + "|" +
source + source +
"|" + "|" +
sourceHandleSplitted.slice(2).join("|"); sourceHandleSplitted.slice(2).join("|");
let targetHandleSplitted = e.targetHandle.split("|"); let targetHandleSplitted = edge.targetHandle.split("|");
let targetHandle = let targetHandle =
targetHandleSplitted.slice(0, -1).join("|") + "|" + target; targetHandleSplitted.slice(0, -1).join("|") + "|" + target;
let id = let id =
@ -433,7 +435,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
animated: targetHandle.split("|")[0] === "Text", animated: targetHandle.split("|")[0] === "Text",
selected: false, selected: false,
}, },
edges.map((e) => ({ ...e, selected: false })) edges.map((edge) => ({ ...edge, selected: false }))
); );
}); });
reactFlowInstance.setEdges(edges); reactFlowInstance.setEdges(edges);

View file

@ -88,12 +88,12 @@ export function TypesProvider({ children }: { children: ReactNode }) {
function deleteNode(idx: string) { function deleteNode(idx: string) {
reactFlowInstance.setNodes( reactFlowInstance.setNodes(
reactFlowInstance.getNodes().filter((n: Node) => n.id !== idx) reactFlowInstance.getNodes().filter((node: Node) => node.id !== idx)
); );
reactFlowInstance.setEdges( reactFlowInstance.setEdges(
reactFlowInstance reactFlowInstance
.getEdges() .getEdges()
.filter((ns) => ns.source !== idx && ns.target !== idx) .filter((edge) => edge.source !== idx && edge.target !== idx)
); );
} }
return ( return (

View file

@ -45,14 +45,18 @@ export function UndoRedoProvider({ children }) {
const [past, setPast] = useState<HistoryItem[][]>(flows.map(() => [])); const [past, setPast] = useState<HistoryItem[][]>(flows.map(() => []));
const [future, setFuture] = useState<HistoryItem[][]>(flows.map(() => [])); const [future, setFuture] = useState<HistoryItem[][]>(flows.map(() => []));
const [tabIndex, setTabIndex] = useState( const [tabIndex, setTabIndex] = useState(
flows.findIndex((f) => f.id === tabId) flows.findIndex((flow) => flow.id === tabId)
); );
useEffect(() => { useEffect(() => {
// whenever the flows variable changes, we need to add one array to the past and future states // whenever the flows variable changes, we need to add one array to the past and future states
setPast((old) => flows.map((f, i) => (old[i] ? old[i] : []))); setPast((old) =>
setFuture((old) => flows.map((f, i) => (old[i] ? old[i] : []))); flows.map((flow, index) => (old[index] ? old[index] : []))
setTabIndex(flows.findIndex((f) => f.id === tabId)); );
setFuture((old) =>
flows.map((flow, index) => (old[index] ? old[index] : []))
);
setTabIndex(flows.findIndex((flow) => flow.id === tabId));
}, [flows, tabId]); }, [flows, tabId]);
const { setNodes, setEdges, getNodes, getEdges } = useReactFlow(); const { setNodes, setEdges, getNodes, getEdges } = useReactFlow();

View file

@ -181,22 +181,22 @@ const ApiModal = forwardRef(
function filterNodes() { function filterNodes() {
let arrNodesWithValues = []; let arrNodesWithValues = [];
flow["data"]["nodes"].forEach((t) => { flow["data"]["nodes"].forEach((node) => {
Object.keys(t["data"]["node"]["template"]) Object.keys(node["data"]["node"]["template"])
.filter( .filter(
(n) => (templateField) =>
n.charAt(0) !== "_" && templateField.charAt(0) !== "_" &&
t.data.node.template[n].show && node.data.node.template[templateField].show &&
(t.data.node.template[n].type === "str" || (node.data.node.template[templateField].type === "str" ||
t.data.node.template[n].type === "bool" || node.data.node.template[templateField].type === "bool" ||
t.data.node.template[n].type === "float" || node.data.node.template[templateField].type === "float" ||
t.data.node.template[n].type === "code" || node.data.node.template[templateField].type === "code" ||
t.data.node.template[n].type === "prompt" || node.data.node.template[templateField].type === "prompt" ||
t.data.node.template[n].type === "file" || node.data.node.template[templateField].type === "file" ||
t.data.node.template[n].type === "int") node.data.node.template[templateField].type === "int")
) )
.map((n, i) => { .map((n, i) => {
arrNodesWithValues.push(t["id"]); arrNodesWithValues.push(node["id"]);
}); });
}); });

View file

@ -49,13 +49,15 @@ const EditNodeModal = forwardRef(
const { reactFlowInstance } = useContext(typesContext); const { reactFlowInstance } = useContext(typesContext);
let disabled = let disabled =
reactFlowInstance?.getEdges().some((e) => e.targetHandle === data.id) ?? reactFlowInstance
false; ?.getEdges()
.some((edge) => edge.targetHandle === data.id) ?? false;
function changeAdvanced(n) { function changeAdvanced(templateParam) {
setMyData((old) => { setMyData((old) => {
let newData = cloneDeep(old); let newData = cloneDeep(old);
newData.node.template[n].advanced = !newData.node.template[n].advanced; newData.node.template[templateParam].advanced =
!newData.node.template[templateParam].advanced;
return newData; return newData;
}); });
} }
@ -112,51 +114,65 @@ const EditNodeModal = forwardRef(
<TableBody className="p-0"> <TableBody className="p-0">
{Object.keys(myData.node.template) {Object.keys(myData.node.template)
.filter( .filter(
(t) => (templateParam) =>
t.charAt(0) !== "_" && templateParam.charAt(0) !== "_" &&
myData.node.template[t].show && myData.node.template[templateParam].show &&
(myData.node.template[t].type === "str" || (myData.node.template[templateParam].type ===
myData.node.template[t].type === "bool" || "str" ||
myData.node.template[t].type === "float" || myData.node.template[templateParam].type ===
myData.node.template[t].type === "code" || "bool" ||
myData.node.template[t].type === "prompt" || myData.node.template[templateParam].type ===
myData.node.template[t].type === "file" || "float" ||
myData.node.template[t].type === "int") myData.node.template[templateParam].type ===
"code" ||
myData.node.template[templateParam].type ===
"prompt" ||
myData.node.template[templateParam].type ===
"file" ||
myData.node.template[templateParam].type ===
"int")
) )
.map((n, i) => ( .map((templateParam, index) => (
<TableRow key={i} className="h-10"> <TableRow key={index} className="h-10">
<TableCell className="truncate p-0 text-center text-sm text-foreground sm:px-3"> <TableCell className="truncate p-0 text-center text-sm text-foreground sm:px-3">
{myData.node.template[n].name {myData.node.template[templateParam].name
? myData.node.template[n].name ? myData.node.template[templateParam].name
: myData.node.template[n].display_name} : myData.node.template[templateParam]
.display_name}
</TableCell> </TableCell>
<TableCell className="w-[300px] p-0 text-center text-xs text-foreground "> <TableCell className="w-[300px] p-0 text-center text-xs text-foreground ">
{myData.node.template[n].type === "str" && {myData.node.template[templateParam].type ===
!myData.node.template[n].options ? ( "str" &&
!myData.node.template[templateParam].options ? (
<div className="mx-auto"> <div className="mx-auto">
{myData.node.template[n].list ? ( {myData.node.template[templateParam].list ? (
<InputListComponent <InputListComponent
editNode={true} editNode={true}
disabled={disabled} disabled={disabled}
value={ value={
!myData.node.template[n].value || !myData.node.template[templateParam]
myData.node.template[n].value === "" .value ||
myData.node.template[templateParam]
.value === ""
? [""] ? [""]
: myData.node.template[n].value : myData.node.template[templateParam]
.value
} }
onChange={(t: string[]) => { onChange={(value: string[]) => {
handleOnNewValue(t, n); handleOnNewValue(value, templateParam);
}} }}
/> />
) : myData.node.template[n].multiline ? ( ) : myData.node.template[templateParam]
.multiline ? (
<TextAreaComponent <TextAreaComponent
disabled={disabled} disabled={disabled}
editNode={true} editNode={true}
value={ value={
myData.node.template[n].value ?? "" myData.node.template[templateParam]
.value ?? ""
} }
onChange={(t: string) => { onChange={(value: string) => {
handleOnNewValue(t, n); handleOnNewValue(value, templateParam);
}} }}
/> />
) : ( ) : (
@ -164,105 +180,142 @@ const EditNodeModal = forwardRef(
editNode={true} editNode={true}
disabled={disabled} disabled={disabled}
password={ password={
myData.node.template[n].password ?? myData.node.template[templateParam]
false .password ?? false
} }
value={ value={
myData.node.template[n].value ?? "" myData.node.template[templateParam]
.value ?? ""
} }
onChange={(t) => { onChange={(value) => {
handleOnNewValue(t, n); handleOnNewValue(value, templateParam);
}} }}
/> />
)} )}
</div> </div>
) : myData.node.template[n].type === "bool" ? ( ) : myData.node.template[templateParam].type ===
"bool" ? (
<div className="ml-auto"> <div className="ml-auto">
{" "} {" "}
<ToggleShadComponent <ToggleShadComponent
disabled={disabled} disabled={disabled}
enabled={myData.node.template[n].value} enabled={
setEnabled={(t) => { myData.node.template[templateParam].value
handleOnNewValue(t, n); }
setEnabled={(isEnabled) => {
handleOnNewValue(
isEnabled,
templateParam
);
}} }}
size="small" size="small"
/> />
</div> </div>
) : myData.node.template[n].type === "float" ? ( ) : myData.node.template[templateParam].type ===
"float" ? (
<div className="mx-auto"> <div className="mx-auto">
<FloatComponent <FloatComponent
disabled={disabled} disabled={disabled}
editNode={true} editNode={true}
value={myData.node.template[n].value ?? ""} value={
onChange={(t) => { myData.node.template[templateParam]
handleOnNewValue(t, n); .value ?? ""
}
onChange={(value) => {
handleOnNewValue(value, templateParam);
}} }}
/> />
</div> </div>
) : myData.node.template[n].type === "str" && ) : myData.node.template[templateParam].type ===
myData.node.template[n].options ? ( "str" &&
myData.node.template[templateParam].options ? (
<div className="mx-auto"> <div className="mx-auto">
<Dropdown <Dropdown
numberOfOptions={nodeLength} numberOfOptions={nodeLength}
editNode={true} editNode={true}
options={myData.node.template[n].options} options={
onSelect={(t) => handleOnNewValue(t, n)} myData.node.template[templateParam]
.options
}
onSelect={(value) =>
handleOnNewValue(value, templateParam)
}
value={ value={
myData.node.template[n].value ?? myData.node.template[templateParam]
"Choose an option" .value ?? "Choose an option"
} }
></Dropdown> ></Dropdown>
</div> </div>
) : myData.node.template[n].type === "int" ? ( ) : myData.node.template[templateParam].type ===
"int" ? (
<div className="mx-auto"> <div className="mx-auto">
<IntComponent <IntComponent
disabled={disabled} disabled={disabled}
editNode={true} editNode={true}
value={myData.node.template[n].value ?? ""} value={
onChange={(t) => { myData.node.template[templateParam]
handleOnNewValue(t, n); .value ?? ""
}
onChange={(value) => {
handleOnNewValue(value, templateParam);
}} }}
/> />
</div> </div>
) : myData.node.template[n].type === "file" ? ( ) : myData.node.template[templateParam].type ===
"file" ? (
<div className="mx-auto"> <div className="mx-auto">
<InputFileComponent <InputFileComponent
editNode={true} editNode={true}
disabled={disabled} disabled={disabled}
value={myData.node.template[n].value ?? ""} value={
onChange={(t: string) => { myData.node.template[templateParam]
handleOnNewValue(t, n); .value ?? ""
}
onChange={(value: string) => {
handleOnNewValue(value, templateParam);
}} }}
fileTypes={ fileTypes={
myData.node.template[n].fileTypes myData.node.template[templateParam]
.fileTypes
} }
suffixes={myData.node.template[n].suffixes} suffixes={
onFileChange={(t: string) => { myData.node.template[templateParam]
data.node.template[n].file_path = t; .suffixes
}
onFileChange={(filePath: string) => {
data.node.template[
templateParam
].file_path = filePath;
}} }}
></InputFileComponent> ></InputFileComponent>
</div> </div>
) : myData.node.template[n].type === "prompt" ? ( ) : myData.node.template[templateParam].type ===
"prompt" ? (
<div className="mx-auto"> <div className="mx-auto">
<PromptAreaComponent <PromptAreaComponent
field_name={n} field_name={templateParam}
editNode={true} editNode={true}
disabled={disabled} disabled={disabled}
nodeClass={myData.node} nodeClass={myData.node}
setNodeClass={(nodeClass) => { setNodeClass={(nodeClass) => {
myData.node = nodeClass; myData.node = nodeClass;
}} }}
value={myData.node.template[n].value ?? ""} value={
onChange={(t: string) => { myData.node.template[templateParam]
handleOnNewValue(t, n); .value ?? ""
}
onChange={(value: string) => {
handleOnNewValue(value, templateParam);
}} }}
/> />
</div> </div>
) : myData.node.template[n].type === "code" ? ( ) : myData.node.template[templateParam].type ===
"code" ? (
<div className="mx-auto"> <div className="mx-auto">
<CodeAreaComponent <CodeAreaComponent
dynamic={ dynamic={
data.node.template[n].dynamic ?? false data.node.template[templateParam]
.dynamic ?? false
} }
setNodeClass={(nodeClass) => { setNodeClass={(nodeClass) => {
data.node = nodeClass; data.node = nodeClass;
@ -270,13 +323,17 @@ const EditNodeModal = forwardRef(
nodeClass={data.node} nodeClass={data.node}
disabled={disabled} disabled={disabled}
editNode={true} editNode={true}
value={myData.node.template[n].value ?? ""} value={
onChange={(t: string) => { myData.node.template[templateParam]
handleOnNewValue(t, n); .value ?? ""
}
onChange={(value: string) => {
handleOnNewValue(value, templateParam);
}} }}
/> />
</div> </div>
) : myData.node.template[n].type === "Any" ? ( ) : myData.node.template[templateParam].type ===
"Any" ? (
"-" "-"
) : ( ) : (
<div className="hidden"></div> <div className="hidden"></div>
@ -285,8 +342,13 @@ const EditNodeModal = forwardRef(
<TableCell className="p-0 text-right"> <TableCell className="p-0 text-right">
<div className="items-center text-center"> <div className="items-center text-center">
<ToggleShadComponent <ToggleShadComponent
enabled={!myData.node.template[n].advanced} enabled={
setEnabled={(e) => changeAdvanced(n)} !myData.node.template[templateParam]
.advanced
}
setEnabled={(e) =>
changeAdvanced(templateParam)
}
disabled={disabled} disabled={disabled}
size="small" size="small"
/> />

View file

@ -12,10 +12,12 @@ const ExportModal = forwardRef((props: { children: ReactNode }, ref) => {
const { flows, tabId, updateFlow, downloadFlow, saveFlow } = const { flows, tabId, updateFlow, downloadFlow, saveFlow } =
useContext(TabsContext); useContext(TabsContext);
const [checked, setChecked] = useState(false); const [checked, setChecked] = useState(false);
const [name, setName] = useState(flows.find((f) => f.id === tabId).name); const [name, setName] = useState(
flows.find((flow) => flow.id === tabId).name
);
const [invalidName, setInvalidName] = useState(false); const [invalidName, setInvalidName] = useState(false);
const [description, setDescription] = useState( const [description, setDescription] = useState(
flows.find((f) => f.id === tabId).description flows.find((flow) => flow.id === tabId).description
); );
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
return ( return (
@ -59,13 +61,13 @@ const ExportModal = forwardRef((props: { children: ReactNode }, ref) => {
onClick={() => { onClick={() => {
if (checked) if (checked)
downloadFlow( downloadFlow(
flows.find((f) => f.id === tabId), flows.find((flow) => flow.id === tabId),
name, name,
description description
); );
else else
downloadFlow( downloadFlow(
removeApiKeys(flows.find((f) => f.id === tabId)), removeApiKeys(flows.find((flow) => flow.id === tabId)),
name, name,
description description
); );

View file

@ -19,14 +19,16 @@ export default function FlowSettingsModal({
const { flows, tabId, updateFlow, setTabsState, saveFlow } = const { flows, tabId, updateFlow, setTabsState, saveFlow } =
useContext(TabsContext); useContext(TabsContext);
const maxLength = 50; const maxLength = 50;
const [name, setName] = useState(flows.find((f) => f.id === tabId).name); const [name, setName] = useState(
flows.find((flow) => flow.id === tabId).name
);
const [description, setDescription] = useState( const [description, setDescription] = useState(
flows.find((f) => f.id === tabId).description flows.find((flow) => flow.id === tabId).description
); );
const [invalidName, setInvalidName] = useState(false); const [invalidName, setInvalidName] = useState(false);
function handleClick() { function handleClick() {
let savedFlow = flows.find((f) => f.id === tabId); let savedFlow = flows.find((flow) => flow.id === tabId);
savedFlow.name = name; savedFlow.name = name;
savedFlow.description = description; savedFlow.description = description;
saveFlow(savedFlow); saveFlow(savedFlow);

View file

@ -46,8 +46,8 @@ export default function ChatInput({
}`, }`,
}} }}
value={lockChat ? "Thinking..." : chatValue} value={lockChat ? "Thinking..." : chatValue}
onChange={(e) => { onChange={(event) => {
setChatValue(e.target.value); setChatValue(event.target.value);
}} }}
className={classNames( className={classNames(
lockChat lockChat

View file

@ -46,7 +46,7 @@ export default function FormModal({
const handleKeys = formKeysData.handle_keys; const handleKeys = formKeysData.handle_keys;
const keyToUse = Object.keys(inputKeys).find( const keyToUse = Object.keys(inputKeys).find(
(k) => !handleKeys.some((j) => j === k) && inputKeys[k] === "" (key) => !handleKeys.some((j) => j === key) && inputKeys[key] === ""
); );
return inputKeys[keyToUse]; return inputKeys[keyToUse];
@ -70,9 +70,9 @@ export default function FormModal({
const [chatKey, setChatKey] = useState(() => { const [chatKey, setChatKey] = useState(() => {
if (tabsState[flow.id]?.formKeysData?.input_keys) { 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(
(k) => (key) =>
!tabsState[flow.id].formKeysData.handle_keys.some((j) => j === k) && !tabsState[flow.id].formKeysData.handle_keys.some((j) => j === key) &&
tabsState[flow.id].formKeysData.input_keys[k] === "" tabsState[flow.id].formKeysData.input_keys[key] === ""
); );
} }
// TODO: return a sensible default // TODO: return a sensible default
@ -425,13 +425,13 @@ export default function FormModal({
{tabsState[id.current]?.formKeysData?.input_keys {tabsState[id.current]?.formKeysData?.input_keys
? Object.keys( ? Object.keys(
tabsState[id.current].formKeysData.input_keys tabsState[id.current].formKeysData.input_keys
).map((i, k) => ( ).map((key, index) => (
<div className="file-component-accordion-div" key={k}> <div className="file-component-accordion-div" key={index}>
<AccordionComponent <AccordionComponent
trigger={ trigger={
<div className="file-component-badge-div"> <div className="file-component-badge-div">
<Badge variant="gray" size="md"> <Badge variant="gray" size="md">
{i} {key}
</Badge> </Badge>
<div <div
@ -441,24 +441,26 @@ export default function FormModal({
}} }}
> >
<ToggleShadComponent <ToggleShadComponent
enabled={chatKey === i} enabled={chatKey === key}
setEnabled={(value) => setEnabled={(value) =>
handleOnCheckedChange(value, i) handleOnCheckedChange(value, key)
} }
size="small" size="small"
disabled={tabsState[ disabled={tabsState[
id.current id.current
].formKeysData.handle_keys.some((t) => t === i)} ].formKeysData.handle_keys.some(
(t) => t === key
)}
/> />
</div> </div>
</div> </div>
} }
key={k} key={index}
keyValue={i} keyValue={key}
> >
<div className="file-component-tab-column"> <div className="file-component-tab-column">
{tabsState[id.current].formKeysData.handle_keys.some( {tabsState[id.current].formKeysData.handle_keys.some(
(t) => t === i (t) => t === key
) && ( ) && (
<div className="font-normal text-muted-foreground "> <div className="font-normal text-muted-foreground ">
Source: Component Source: Component
@ -467,18 +469,18 @@ export default function FormModal({
<Textarea <Textarea
className="custom-scroll" className="custom-scroll"
value={ value={
tabsState[id.current].formKeysData.input_keys[i] tabsState[id.current].formKeysData.input_keys[key]
} }
onChange={(e) => { onChange={(e) => {
setTabsState((old) => { setTabsState((old) => {
let newTabsState = _.cloneDeep(old); let newTabsState = _.cloneDeep(old);
newTabsState[ newTabsState[
id.current id.current
].formKeysData.input_keys[i] = e.target.value; ].formKeysData.input_keys[key] = e.target.value;
return newTabsState; return newTabsState;
}); });
}} }}
disabled={chatKey === i} disabled={chatKey === key}
placeholder="Enter text..." placeholder="Enter text..."
></Textarea> ></Textarea>
</div> </div>
@ -486,17 +488,18 @@ export default function FormModal({
</div> </div>
)) ))
: null} : null}
{tabsState[id.current].formKeysData.memory_keys.map((i, k) => ( {tabsState[id.current].formKeysData.memory_keys.map(
<div className="file-component-accordion-div" key={k}> (key, index) => (
<div className="file-component-accordion-div" key={index}>
<AccordionComponent <AccordionComponent
trigger={ trigger={
<div className="file-component-badge-div"> <div className="file-component-badge-div">
<Badge variant="gray" size="md"> <Badge variant="gray" size="md">
{i} {key}
</Badge> </Badge>
<div className="-mb-1"> <div className="-mb-1">
<ToggleShadComponent <ToggleShadComponent
enabled={chatKey === i} enabled={chatKey === key}
setEnabled={() => {}} setEnabled={() => {}}
size="small" size="small"
disabled={true} disabled={true}
@ -504,8 +507,8 @@ export default function FormModal({
</div> </div>
</div> </div>
} }
key={k} key={index}
keyValue={i} keyValue={key}
> >
<div className="file-component-tab-column"> <div className="file-component-tab-column">
<div className="font-normal text-muted-foreground "> <div className="font-normal text-muted-foreground ">
@ -514,7 +517,8 @@ export default function FormModal({
</div> </div>
</AccordionComponent> </AccordionComponent>
</div> </div>
))} )
)}
</div> </div>
<div className="eraser-column-arrangement"> <div className="eraser-column-arrangement">
<div className="eraser-size"> <div className="eraser-size">
@ -534,14 +538,14 @@ export default function FormModal({
</div> </div>
<div ref={messagesRef} className="chat-message-div"> <div ref={messagesRef} className="chat-message-div">
{chatHistory.length > 0 ? ( {chatHistory.length > 0 ? (
chatHistory.map((c, i) => ( chatHistory.map((chat, index) => (
<ChatMessage <ChatMessage
lockChat={lockChat} lockChat={lockChat}
chat={c} chat={chat}
lastMessage={ lastMessage={
chatHistory.length - 1 === i ? true : false chatHistory.length - 1 === index ? true : false
} }
key={i} key={index}
/> />
)) ))
) : ( ) : (

View file

@ -208,9 +208,9 @@ export default function GenericModal({
setIsEdit(false); setIsEdit(false);
}} }}
autoFocus autoFocus
onChange={(e) => { onChange={(event) => {
setInputValue(e.target.value); setInputValue(event.target.value);
checkVariables(e.target.value); checkVariables(event.target.value);
}} }}
placeholder="Type message here." placeholder="Type message here."
/> />
@ -221,8 +221,8 @@ export default function GenericModal({
ref={ref} ref={ref}
className="form-input h-full w-full rounded-lg focus-visible:ring-1" className="form-input h-full w-full rounded-lg focus-visible:ring-1"
value={inputValue} value={inputValue}
onChange={(e) => { onChange={(event) => {
setInputValue(e.target.value); setInputValue(event.target.value);
}} }}
placeholder="Type message here." placeholder="Type message here."
/> />

View file

@ -18,9 +18,9 @@ export default function DisclosureComponent({
<span className="components-disclosure-title">{title}</span> <span className="components-disclosure-title">{title}</span>
</div> </div>
<div className="components-disclosure-div"> <div className="components-disclosure-div">
{buttons.map((x, index) => ( {buttons.map((btn, index) => (
<button key={index} onClick={x.onClick}> <button key={index} onClick={btn.onClick}>
{x.Icon} {btn.Icon}
</button> </button>
))} ))}
<div> <div>

View file

@ -141,10 +141,10 @@ export default function Page({ flow }: { flow: FlowType }) {
}, [setExtraComponent, setExtraNavigation]); }, [setExtraComponent, setExtraNavigation]);
const onEdgesChangeMod = useCallback( const onEdgesChangeMod = useCallback(
(s: EdgeChange[]) => { (change: EdgeChange[]) => {
onEdgesChange(s); onEdgesChange(change);
setNodes((x) => { setNodes((node) => {
let newX = _.cloneDeep(x); let newX = _.cloneDeep(node);
return newX; return newX;
}); });
setTabsState((prev) => { setTabsState((prev) => {
@ -161,8 +161,8 @@ export default function Page({ flow }: { flow: FlowType }) {
); );
const onNodesChangeMod = useCallback( const onNodesChangeMod = useCallback(
(s: NodeChange[]) => { (change: NodeChange[]) => {
onNodesChange(s); onNodesChange(change);
setTabsState((prev) => { setTabsState((prev) => {
return { return {
...prev, ...prev,
@ -193,8 +193,8 @@ export default function Page({ flow }: { flow: FlowType }) {
eds eds
) )
); );
setNodes((x) => { setNodes((node) => {
let newX = _.cloneDeep(x); let newX = _.cloneDeep(node);
return newX; return newX;
}); });
}, },
@ -219,7 +219,7 @@ export default function Page({ flow }: { flow: FlowType }) {
const onDragOver = useCallback((event: React.DragEvent) => { const onDragOver = useCallback((event: React.DragEvent) => {
event.preventDefault(); event.preventDefault();
if (event.dataTransfer.types.some((t) => t === "nodedata")) { if (event.dataTransfer.types.some((types) => types === "nodedata")) {
event.dataTransfer.dropEffect = "move"; event.dataTransfer.dropEffect = "move";
} else { } else {
event.dataTransfer.dropEffect = "copy"; event.dataTransfer.dropEffect = "copy";
@ -229,7 +229,7 @@ export default function Page({ flow }: { flow: FlowType }) {
const onDrop = useCallback( const onDrop = useCallback(
(event: React.DragEvent) => { (event: React.DragEvent) => {
event.preventDefault(); event.preventDefault();
if (event.dataTransfer.types.some((t) => t === "nodedata")) { if (event.dataTransfer.types.some((types) => types === "nodedata")) {
takeSnapshot(); takeSnapshot();
// Get the current bounds of the ReactFlow wrapper element // Get the current bounds of the ReactFlow wrapper element
@ -281,7 +281,7 @@ export default function Page({ flow }: { flow: FlowType }) {
// Add the new node to the list of nodes in state // Add the new node to the list of nodes in state
} }
setNodes((nds) => nds.concat(newNode)); setNodes((nds) => nds.concat(newNode));
} else if (event.dataTransfer.types.some((t) => t === "Files")) { } else if (event.dataTransfer.types.some((types) => types === "Files")) {
takeSnapshot(); takeSnapshot();
uploadFlow(false, event.dataTransfer.files.item(0)); uploadFlow(false, event.dataTransfer.files.item(0));
} }
@ -303,7 +303,10 @@ export default function Page({ flow }: { flow: FlowType }) {
takeSnapshot(); takeSnapshot();
setEdges( setEdges(
edges.filter( edges.filter(
(ns) => !mynodes.some((n) => ns.source === n.id || ns.target === n.id) (edge) =>
!mynodes.some(
(node) => edge.source === node.id || edge.target === node.id
)
) )
); );
}, },
@ -326,7 +329,7 @@ export default function Page({ flow }: { flow: FlowType }) {
const onEdgeUpdateEnd = useCallback((_, edge) => { const onEdgeUpdateEnd = useCallback((_, edge) => {
if (!edgeUpdateSuccessful.current) { if (!edgeUpdateSuccessful.current) {
setEdges((eds) => eds.filter((e) => e.id !== edge.id)); setEdges((eds) => eds.filter((edg) => edg.id !== edge.id));
} }
edgeUpdateSuccessful.current = true; edgeUpdateSuccessful.current = true;

View file

@ -56,7 +56,7 @@ export default function ExtraSidebar() {
return ret; return ret;
}); });
} }
const flow = flows.find((f) => f.id === tabId); const flow = flows.find((flow) => flow.id === tabId);
useEffect(() => { useEffect(() => {
// show components with error on load // show components with error on load
let errors = []; let errors = [];
@ -143,10 +143,10 @@ export default function ExtraSidebar() {
id="search" id="search"
placeholder="Search" placeholder="Search"
className="nopan nodrag noundo nocopy input-search" className="nopan nodrag noundo nocopy input-search"
onChange={(e) => { onChange={(event) => {
handleSearchInput(e.target.value); handleSearchInput(event.target.value);
// Set search input state // Set search input state
setSearch(e.target.value); setSearch(event.target.value);
}} }}
/> />
<div className="search-icon"> <div className="search-icon">
@ -161,42 +161,43 @@ export default function ExtraSidebar() {
<div className="side-bar-components-div-arrangement"> <div className="side-bar-components-div-arrangement">
{Object.keys(dataFilter) {Object.keys(dataFilter)
.sort() .sort()
.map((d: keyof APIObjectType, i) => .map((SBSectionName: keyof APIObjectType, index) =>
Object.keys(dataFilter[d]).length > 0 ? ( Object.keys(dataFilter[SBSectionName]).length > 0 ? (
<DisclosureComponent <DisclosureComponent
openDisc={search.length == 0 ? false : true} openDisc={search.length == 0 ? false : true}
key={i} key={index}
button={{ button={{
title: nodeNames[d] ?? nodeNames.unknown, title: nodeNames[SBSectionName] ?? nodeNames.unknown,
Icon: nodeIconsLucide[d] ?? nodeIconsLucide.unknown, Icon:
nodeIconsLucide[SBSectionName] ?? nodeIconsLucide.unknown,
}} }}
> >
<div className="side-bar-components-gap"> <div className="side-bar-components-gap">
{Object.keys(dataFilter[d]) {Object.keys(dataFilter[SBSectionName])
.sort() .sort()
.map((t: string, k) => ( .map((SBItemName: string, index) => (
<ShadTooltip <ShadTooltip
content={data[d][t].display_name} content={data[SBSectionName][SBItemName].display_name}
side="right" side="right"
key={k} key={index}
> >
<div key={k} data-tooltip-id={t}> <div key={index} data-tooltip-id={SBItemName}>
<div <div
draggable={!data[d][t].error} draggable={!data[SBSectionName][SBItemName].error}
className={ className={
"side-bar-components-border bg-background" + "side-bar-components-border bg-background" +
(data[d][t].error (data[SBSectionName][SBItemName].error
? " cursor-not-allowed select-none" ? " cursor-not-allowed select-none"
: "") : "")
} }
style={{ style={{
borderLeftColor: borderLeftColor:
nodeColors[d] ?? nodeColors.unknown, nodeColors[SBSectionName] ?? nodeColors.unknown,
}} }}
onDragStart={(event) => onDragStart={(event) =>
onDragStart(event, { onDragStart(event, {
type: t, type: SBItemName,
node: data[d][t], node: data[SBSectionName][SBItemName],
}) })
} }
onDragEnd={() => { onDragEnd={() => {
@ -209,7 +210,7 @@ export default function ExtraSidebar() {
> >
<div className="side-bar-components-div-form"> <div className="side-bar-components-div-form">
<span className="side-bar-components-text"> <span className="side-bar-components-text">
{data[d][t].display_name} {data[SBSectionName][SBItemName].display_name}
</span> </span>
<IconComponent <IconComponent
name="Menu" name="Menu"
@ -223,7 +224,7 @@ export default function ExtraSidebar() {
</div> </div>
</DisclosureComponent> </DisclosureComponent>
) : ( ) : (
<div key={i}></div> <div key={index}></div>
) )
)} )}
</div> </div>

View file

@ -9,17 +9,17 @@ import { classNames } from "../../../../utils/utils";
export default function NodeToolbarComponent({ data, setData, deleteNode }) { export default function NodeToolbarComponent({ data, setData, deleteNode }) {
const [nodeLength, setNodeLength] = useState( const [nodeLength, setNodeLength] = useState(
Object.keys(data.node.template).filter( Object.keys(data.node.template).filter(
(t) => (templateField) =>
t.charAt(0) !== "_" && templateField.charAt(0) !== "_" &&
data.node.template[t].show && data.node.template[templateField].show &&
(data.node.template[t].type === "str" || (data.node.template[templateField].type === "str" ||
data.node.template[t].type === "bool" || data.node.template[templateField].type === "bool" ||
data.node.template[t].type === "float" || data.node.template[templateField].type === "float" ||
data.node.template[t].type === "code" || data.node.template[templateField].type === "code" ||
data.node.template[t].type === "prompt" || data.node.template[templateField].type === "prompt" ||
data.node.template[t].type === "file" || data.node.template[templateField].type === "file" ||
data.node.template[t].type === "Any" || data.node.template[templateField].type === "Any" ||
data.node.template[t].type === "int") data.node.template[templateField].type === "int")
).length ).length
); );

View file

@ -15,7 +15,7 @@ export function cleanEdges({
const sourceNode = nodes.find((node) => node.id === edge.source); const sourceNode = nodes.find((node) => node.id === edge.source);
const targetNode = nodes.find((node) => node.id === edge.target); const targetNode = nodes.find((node) => node.id === edge.target);
if (!sourceNode || !targetNode) { if (!sourceNode || !targetNode) {
newEdges = newEdges.filter((e) => e.id !== edge.id); newEdges = newEdges.filter((edg) => edg.id !== edge.id);
} }
// check if the source and target handle still exists // check if the source and target handle still exists
if (sourceNode && targetNode) { if (sourceNode && targetNode) {
@ -41,7 +41,7 @@ export function cleanEdges({
...sourceNode.data.node.base_classes, ...sourceNode.data.node.base_classes,
].join("|"); ].join("|");
if (id !== sourceHandle) { if (id !== sourceHandle) {
newEdges = newEdges.filter((e) => e.id !== edge.id); newEdges = newEdges.filter((edg) => edg.id !== edge.id);
} }
} }
} }
@ -57,15 +57,15 @@ export function isValidConnection(
targetHandle targetHandle
.split("|")[0] .split("|")[0]
.split(";") .split(";")
.some((n) => n === sourceHandle.split("|")[0]) || .some((target) => target === sourceHandle.split("|")[0]) ||
sourceHandle sourceHandle
.split("|") .split("|")
.slice(2) .slice(2)
.some((t) => .some((target) =>
targetHandle targetHandle
.split("|")[0] .split("|")[0]
.split(";") .split(";")
.some((n) => n === t) .some((n) => n === target)
) || ) ||
targetHandle.split("|")[0] === "str" targetHandle.split("|")[0] === "str"
) { ) {
@ -129,35 +129,35 @@ export function updateTemplate(
export function updateIds(newFlow, getNodeId) { export function updateIds(newFlow, getNodeId) {
let idsMap = {}; let idsMap = {};
newFlow.nodes.forEach((n: NodeType) => { newFlow.nodes.forEach((node: NodeType) => {
// Generate a unique node ID // Generate a unique node ID
let newId = getNodeId(n.data.type); let newId = getNodeId(node.data.type);
idsMap[n.id] = newId; idsMap[node.id] = newId;
n.id = newId; node.id = newId;
n.data.id = newId; node.data.id = newId;
// Add the new node to the list of nodes in state // Add the new node to the list of nodes in state
}); });
newFlow.edges.forEach((e) => { newFlow.edges.forEach((edge) => {
e.source = idsMap[e.source]; edge.source = idsMap[edge.source];
e.target = idsMap[e.target]; edge.target = idsMap[edge.target];
let sourceHandleSplitted = e.sourceHandle.split("|"); let sourceHandleSplitted = edge.sourceHandle.split("|");
e.sourceHandle = edge.sourceHandle =
sourceHandleSplitted[0] + sourceHandleSplitted[0] +
"|" + "|" +
e.source + edge.source +
"|" + "|" +
sourceHandleSplitted.slice(2).join("|"); sourceHandleSplitted.slice(2).join("|");
let targetHandleSplitted = e.targetHandle.split("|"); let targetHandleSplitted = edge.targetHandle.split("|");
e.targetHandle = edge.targetHandle =
targetHandleSplitted.slice(0, -1).join("|") + "|" + e.target; targetHandleSplitted.slice(0, -1).join("|") + "|" + edge.target;
e.id = edge.id =
"reactflow__edge-" + "reactflow__edge-" +
e.source + edge.source +
e.sourceHandle + edge.sourceHandle +
"-" + "-" +
e.target + edge.target +
e.targetHandle; edge.targetHandle;
}); });
} }
@ -169,10 +169,10 @@ export function buildTweaks(flow) {
} }
export function validateNode( export function validateNode(
n: NodeType, node: NodeType,
reactFlowInstance: ReactFlowInstance reactFlowInstance: ReactFlowInstance
): Array<string> { ): Array<string> {
if (!n.data?.node?.template || !Object.keys(n.data.node.template)) { if (!node.data?.node?.template || !Object.keys(node.data.node.template)) {
return [ return [
"We've noticed a potential issue with a node in the flow. Please review it and, if necessary, submit a bug report with your exported flow file. Thank you for your help!", "We've noticed a potential issue with a node in the flow. Please review it and, if necessary, submit a bug report with your exported flow file. Thank you for your help!",
]; ];
@ -181,7 +181,7 @@ export function validateNode(
const { const {
type, type,
node: { template }, node: { template },
} = n.data; } = node.data;
return Object.keys(template).reduce( return Object.keys(template).reduce(
(errors: Array<string>, t) => (errors: Array<string>, t) =>
@ -194,9 +194,9 @@ export function validateNode(
!reactFlowInstance !reactFlowInstance
.getEdges() .getEdges()
.some( .some(
(e) => (edge) =>
e.targetHandle.split("|")[1] === t && edge.targetHandle.split("|")[1] === t &&
e.targetHandle.split("|")[2] === n.id edge.targetHandle.split("|")[2] === node.id
) )
? [ ? [
`${type} is missing ${ `${type} is missing ${

View file

@ -103,11 +103,14 @@ export function groupByFamily(data, baseClasses, left, flow?: NodeType[]) {
"int", "int",
]); ]);
const checkBaseClass = (t: any) => const checkBaseClass = (template: any) =>
t.type && template.type &&
t.show && template.show &&
((!excludeTypes.has(t.type) && baseClassesSet.has(t.type)) || ((!excludeTypes.has(template.type) && baseClassesSet.has(template.type)) ||
(t.input_types && t.input_types.some((x) => baseClassesSet.has(x)))); (template.input_types &&
template.input_types.some((inputType) =>
baseClassesSet.has(inputType)
)));
if (flow) { if (flow) {
for (const node of flow) { for (const node of flow) {
@ -119,7 +122,9 @@ export function groupByFamily(data, baseClasses, left, flow?: NodeType[]) {
Object.values(nodeData.node.template).some(checkBaseClass), Object.values(nodeData.node.template).some(checkBaseClass),
hasBaseClassInBaseClasses: hasBaseClassInBaseClasses:
foundNode?.hasBaseClassInBaseClasses || foundNode?.hasBaseClassInBaseClasses ||
nodeData.node.base_classes.some((t) => baseClassesSet.has(t)), nodeData.node.base_classes.some((baseClass) =>
baseClassesSet.has(baseClass)
),
}); });
} }
} }
@ -135,8 +140,8 @@ export function groupByFamily(data, baseClasses, left, flow?: NodeType[]) {
hasBaseClassInTemplate: Object.values(node.template).some( hasBaseClassInTemplate: Object.values(node.template).some(
checkBaseClass checkBaseClass
), ),
hasBaseClassInBaseClasses: node.base_classes.some((t) => hasBaseClassInBaseClasses: node.base_classes.some((baseClass) =>
baseClassesSet.has(t) baseClassesSet.has(baseClass)
), ),
}; };
checkedNodes.set(n, foundNode); checkedNodes.set(n, foundNode);
@ -162,13 +167,13 @@ export function groupByFamily(data, baseClasses, left, flow?: NodeType[]) {
} }
return left return left
? arrOfPossibleOutputs.map((t) => ({ ? arrOfPossibleOutputs.map((output) => ({
family: t.category, family: output.category,
type: t.full ? "" : t.nodes.join(", "), type: output.full ? "" : output.nodes.join(", "),
})) }))
: arrOfPossibleInputs.map((t) => ({ : arrOfPossibleInputs.map((input) => ({
family: t.category, family: input.category,
type: t.full ? "" : t.nodes.join(", "), type: input.full ? "" : input.nodes.join(", "),
})); }));
} }