>(({ className, ...props }, ref) => (
-
,
+ React.ComponentPropsWithoutRef
+>(({ className, value, ...props }, ref) => (
+
+
+
+));
+Progress.displayName = ProgressPrimitive.Root.displayName;
+
+export { Progress };
diff --git a/src/frontend/src/components/ui/rename-label.tsx b/src/frontend/src/components/ui/rename-label.tsx
index 1ee79808b..e5fef11bf 100644
--- a/src/frontend/src/components/ui/rename-label.tsx
+++ b/src/frontend/src/components/ui/rename-label.tsx
@@ -1,4 +1,4 @@
-import { useState, useEffect, useRef } from "react";
+import { useEffect, useRef, useState } from "react";
import { cn } from "../../utils";
export default function RenameLabel(props) {
@@ -57,7 +57,7 @@ export default function RenameLabel(props) {
ref={inputRef}
onInput={resizeInput}
className={cn(
- "px-2 bg-transparent focus:border-none active:outline hover:outline focus:outline outline-gray-300 rounded-md",
+ "rounded-md bg-transparent px-2 outline-ring hover:outline focus:border-none focus:outline active:outline",
props.className
)}
onBlur={() => {
@@ -74,7 +74,7 @@ export default function RenameLabel(props) {
) : (
{
setIsRename(true);
setMyValue(props.value);
diff --git a/src/frontend/src/components/ui/separator.tsx b/src/frontend/src/components/ui/separator.tsx
index 84a16676d..1fa0287d8 100644
--- a/src/frontend/src/components/ui/separator.tsx
+++ b/src/frontend/src/components/ui/separator.tsx
@@ -1,7 +1,7 @@
"use client";
-import * as React from "react";
import * as SeparatorPrimitive from "@radix-ui/react-separator";
+import * as React from "react";
import { cn } from "../../utils";
const Separator = React.forwardRef<
@@ -17,7 +17,7 @@ const Separator = React.forwardRef<
decorative={decorative}
orientation={orientation}
className={cn(
- "shrink-0 bg-border",
+ "shrink-0 bg-ring/40",
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
className
)}
diff --git a/src/frontend/src/components/ui/switch.tsx b/src/frontend/src/components/ui/switch.tsx
index 122057cfb..8ec7c3c80 100644
--- a/src/frontend/src/components/ui/switch.tsx
+++ b/src/frontend/src/components/ui/switch.tsx
@@ -1,7 +1,7 @@
"use client";
-import * as React from "react";
import * as SwitchPrimitives from "@radix-ui/react-switch";
+import * as React from "react";
import { cn } from "../../utils";
const Switch = React.forwardRef<
@@ -10,15 +10,16 @@ const Switch = React.forwardRef<
>(({ className, ...props }, ref) => (
diff --git a/src/frontend/src/components/ui/table.tsx b/src/frontend/src/components/ui/table.tsx
index f08ce3f6b..9b7bdd8ce 100644
--- a/src/frontend/src/components/ui/table.tsx
+++ b/src/frontend/src/components/ui/table.tsx
@@ -5,7 +5,7 @@ const Table = React.forwardRef<
HTMLTableElement,
React.HTMLAttributes
>(({ className, ...props }, ref) => (
-
+
,
React.ComponentPropsWithoutRef
>(({ className, sideOffset = 4, ...props }, ref) => (
-
+
+
+
));
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
diff --git a/src/frontend/src/constants.tsx b/src/frontend/src/constants.tsx
index 7017ac27e..108b6780b 100644
--- a/src/frontend/src/constants.tsx
+++ b/src/frontend/src/constants.tsx
@@ -1,7 +1,15 @@
// src/constants.tsx
+import { MessageSquare } from "lucide-react";
import { FlowType } from "./types/flow";
-import { buildTweaks } from "./utils";
+import { TabsState } from "./types/tabs";
+import { buildInputs, buildTweaks } from "./utils";
+
+/**
+ * Number maximum of components to scroll on tooltips
+ * @constant
+ */
+export const MAX_LENGTH_TO_SCROLL_TOOLTIP = 200;
/**
* The base text for subtitle of Export Dialog (Toolbar)
@@ -22,6 +30,13 @@ export const SETTINGS_DIALOG_SUBTITLE = "Edit details about your project.";
export const CODE_DIALOG_SUBTITLE =
"Export your flow to use it with this code.";
+/**
+ * The base text for subtitle of Chat Form
+ * @constant
+ */
+export const CHAT_FORM_DIALOG_SUBTITLE =
+ "Set up the input variables defined in prompt templates. Interact with agents and chains.";
+
/**
* The base text for subtitle of Edit Node Dialog
* @constant
@@ -43,6 +58,17 @@ export const CODE_PROMPT_DIALOG_SUBTITLE =
export const PROMPT_DIALOG_SUBTITLE =
"Create your prompt. Prompts can help guide the behavior of a Language Model.";
+export const CHAT_CANNOT_OPEN_TITLE = "Chat Cannot Open";
+
+export const CHAT_CANNOT_OPEN_DESCRIPTION = "This is not a chat flow.";
+
+export const FLOW_NOT_BUILT_TITLE = "Flow not built";
+
+export const THOUGHTS_ICON = MessageSquare;
+
+export const FLOW_NOT_BUILT_DESCRIPTION =
+ "Please build the flow before chatting.";
+
/**
* The base text for subtitle of Text Dialog
* @constant
@@ -54,7 +80,11 @@ export const TEXT_DIALOG_SUBTITLE = "Edit your text.";
* @param {string} flowId - The id of the flow
* @returns {string} - The python code
*/
-export const getPythonApiCode = (flow: FlowType): string => {
+export const getPythonApiCode = (
+ flow: FlowType,
+ tweak?: any[],
+ tabsState?: TabsState
+): string => {
const flowId = flow.id;
// create a dictionary of node ids and the values is an empty dictionary
@@ -62,17 +92,23 @@ export const getPythonApiCode = (flow: FlowType): string => {
// node.data.id
// }
const tweaks = buildTweaks(flow);
+ const inputs = buildInputs(tabsState, flow.id);
return `import requests
+from typing import Optional
BASE_API_URL = "${window.location.protocol}//${
window.location.host
- }/api/v1/predict"
+ }/api/v1/process"
FLOW_ID = "${flowId}"
# You can tweak the flow by adding a tweaks dictionary
# e.g {"OpenAI-XXXXX": {"model_name": "gpt-4"}}
-TWEAKS = ${JSON.stringify(tweaks, null, 2)}
+TWEAKS = ${
+ tweak && tweak.length > 0
+ ? buildTweakObject(tweak)
+ : JSON.stringify(tweaks, null, 2)
+ }
-def run_flow(message: str, flow_id: str, tweaks: dict = None) -> dict:
+def run_flow(inputs: dict, flow_id: str, tweaks: Optional[dict] = None) -> dict:
"""
Run a flow with a given message and optional tweaks.
@@ -83,7 +119,7 @@ def run_flow(message: str, flow_id: str, tweaks: dict = None) -> dict:
"""
api_url = f"{BASE_API_URL}/{flow_id}"
- payload = {"message": message}
+ payload = {"inputs": inputs}
if tweaks:
payload["tweaks"] = tweaks
@@ -92,43 +128,74 @@ def run_flow(message: str, flow_id: str, tweaks: dict = None) -> dict:
return response.json()
# Setup any tweaks you want to apply to the flow
-
-print(run_flow("Your message", flow_id=FLOW_ID, tweaks=TWEAKS))`;
+inputs = ${inputs}
+print(run_flow(inputs, flow_id=FLOW_ID, tweaks=TWEAKS))`;
};
/**
* Function to get the curl code for the API
* @param {string} flowId - The id of the flow
* @returns {string} - The curl code
*/
-export const getCurlCode = (flow: FlowType): string => {
+export const getCurlCode = (
+ flow: FlowType,
+ tweak?: any[],
+ tabsState?: TabsState
+): string => {
const flowId = flow.id;
const tweaks = buildTweaks(flow);
+ const inputs = buildInputs(tabsState, flow.id);
+
return `curl -X POST \\
${window.location.protocol}//${
window.location.host
- }/api/v1/predict/${flowId} \\
+ }/api/v1/process/${flowId} \\
-H 'Content-Type: application/json' \\
- -d '{"message": "Your message", "tweaks": ${JSON.stringify(
- tweaks,
- null,
- 2
- )}}'`;
+ -d '{"inputs": ${inputs}, "tweaks": ${
+ tweak && tweak.length > 0
+ ? buildTweakObject(tweak)
+ : JSON.stringify(tweaks, null, 2)
+ }}'`;
};
/**
* Function to get the python code for the API
* @param {string} flowName - The name of the flow
* @returns {string} - The python code
*/
-export const getPythonCode = (flow: FlowType): string => {
+export const getPythonCode = (
+ flow: FlowType,
+ tweak?: any[],
+ tabsState?: TabsState
+): string => {
const flowName = flow.name;
const tweaks = buildTweaks(flow);
+ const inputs = buildInputs(tabsState, flow.id);
return `from langflow import load_flow_from_json
-TWEAKS = ${JSON.stringify(tweaks, null, 2)}
+TWEAKS = ${
+ tweak && tweak.length > 0
+ ? buildTweakObject(tweak)
+ : JSON.stringify(tweaks, null, 2)
+ }
flow = load_flow_from_json("${flowName}.json", tweaks=TWEAKS)
# Now you can use it like any chain
-flow("Hey, have you heard of LangFlow?")`;
+inputs = ${inputs}
+flow(inputs)`;
};
+function buildTweakObject(tweak) {
+ tweak.forEach((el) => {
+ Object.keys(el).forEach((key) => {
+ for (let kp in el[key]) {
+ try {
+ el[key][kp] = JSON.parse(el[key][kp]);
+ } catch {}
+ }
+ });
+ });
+
+ const tweakString = JSON.stringify(tweak, null, 2);
+ return tweakString;
+}
+
/**
* The base text for subtitle of Import Dialog
* @constant
@@ -147,11 +214,14 @@ export const EXPORT_CODE_DIALOG =
* The base text for subtitle of code dialog
* @constant
*/
-export const INPUT_STYLE =
- " focus:ring-1 focus:ring-offset-1 focus:ring-ring focus:outline-none ";
+export const COLUMN_DIV_STYLE =
+ " w-full h-full flex overflow-auto flex-col bg-muted px-16 ";
+
+export const NAV_DISPLAY_STYLE =
+ " w-full flex justify-between py-12 pb-2 px-6 ";
/**
- * Default description for the flow
+ * The base text for subtitle of code dialog
* @constant
*/
export const DESCRIPTIONS: string[] = [
@@ -177,11 +247,12 @@ export const DESCRIPTIONS: string[] = [
"Conversational Cartography Unlocked.",
"Design, Develop, Dialogize.",
];
+export const BUTTON_DIV_STYLE =
+ " flex gap-2 focus:ring-1 focus:ring-offset-1 focus:ring-ring focus:outline-none ";
/**
- * Adjectives for the name of the flow
+ * The base text for subtitle of code dialog
* @constant
- *
*/
export const ADJECTIVES: string[] = [
"admiring",
@@ -249,8 +320,49 @@ export const ADJECTIVES: string[] = [
"thirsty",
"tiny",
"trusting",
+ "bubbly",
+ "charming",
+ "cheerful",
+ "comical",
+ "dazzling",
+ "delighted",
+ "dynamic",
+ "effervescent",
+ "enthusiastic",
+ "exuberant",
+ "fluffy",
+ "friendly",
+ "funky",
+ "giddy",
+ "giggly",
+ "gleeful",
+ "goofy",
+ "graceful",
+ "grinning",
+ "hilarious",
+ "inquisitive",
+ "joyous",
+ "jubilant",
+ "lively",
+ "mirthful",
+ "mischievous",
+ "optimistic",
+ "peppy",
+ "perky",
+ "playful",
+ "quirky",
+ "radiant",
+ "sassy",
+ "silly",
+ "spirited",
+ "sprightly",
+ "twinkly",
+ "upbeat",
+ "vibrant",
+ "witty",
+ "zany",
+ "zealous",
];
-
/**
* Nouns for the name of the flow
* @constant
@@ -399,6 +511,38 @@ export const NOUNS: string[] = [
"wright",
"yalow",
"yonath",
+ "coulomb",
+ "degrasse",
+ "dewey",
+ "edison",
+ "eratosthenes",
+ "faraday",
+ "galton",
+ "gauss",
+ "herschel",
+ "hubble",
+ "joule",
+ "kaku",
+ "kepler",
+ "khayyam",
+ "lavoisier",
+ "maxwell",
+ "mendel",
+ "mendeleev",
+ "ohm",
+ "pascal",
+ "planck",
+ "riemann",
+ "schrodinger",
+ "sagan",
+ "tesla",
+ "tyson",
+ "volta",
+ "watt",
+ "weber",
+ "wien",
+ "zoBell",
+ "zuse",
];
/**
diff --git a/src/frontend/src/contexts/SSEContext.tsx b/src/frontend/src/contexts/SSEContext.tsx
index 83b1d4c89..9db32310b 100644
--- a/src/frontend/src/contexts/SSEContext.tsx
+++ b/src/frontend/src/contexts/SSEContext.tsx
@@ -1,10 +1,4 @@
-import {
- createContext,
- useContext,
- useState,
- useEffect,
- useCallback,
-} from "react";
+import { createContext, useCallback, useContext, useState } from "react";
const initialValue = {
updateSSEData: ({}) => {},
diff --git a/src/frontend/src/contexts/alertContext.tsx b/src/frontend/src/contexts/alertContext.tsx
index ed31c03a9..62ae2e396 100644
--- a/src/frontend/src/contexts/alertContext.tsx
+++ b/src/frontend/src/contexts/alertContext.tsx
@@ -78,9 +78,9 @@ export function AlertProvider({ children }: { children: ReactNode }) {
* @param newState An object containing the new error data, including title and optional list of error messages
*/
function setErrorData(newState: { title: string; list?: Array }) {
- setErrorDataState(newState);
- setErrorOpen(true);
if (newState.title && newState.title !== "") {
+ setErrorDataState(newState);
+ setErrorOpen(true);
setNotificationCenter(true);
pushNotificationList({
type: "error",
@@ -95,9 +95,9 @@ export function AlertProvider({ children }: { children: ReactNode }) {
* @param newState An object containing the title of the notice and optionally a link.
*/
function setNoticeData(newState: { title: string; link?: string }) {
- setNoticeDataState(newState);
- setNoticeOpen(true);
if (newState.title && newState.title !== "") {
+ setNoticeDataState(newState);
+ setNoticeOpen(true);
// Add new notice to notification center
setNotificationCenter(true);
pushNotificationList({
@@ -113,11 +113,10 @@ export function AlertProvider({ children }: { children: ReactNode }) {
* @param newState - A state object with a "title" property to set in the success data state.
*/
function setSuccessData(newState: { title: string }) {
- setSuccessDataState(newState); // update the success data state with the provided new state
- setSuccessOpen(true); // open the success alert
-
// If the new state has a "title" property, add a new success notification to the list
if (newState.title && newState.title !== "") {
+ setSuccessDataState(newState); // update the success data state with the provided new state
+ setSuccessOpen(true); // open the success alert
setNotificationCenter(true); // show the notification center
pushNotificationList({
// add the new notification to the list
diff --git a/src/frontend/src/contexts/index.tsx b/src/frontend/src/contexts/index.tsx
index 750df65d2..70b7da07c 100644
--- a/src/frontend/src/contexts/index.tsx
+++ b/src/frontend/src/contexts/index.tsx
@@ -1,35 +1,38 @@
import { ReactNode } from "react";
+import { ReactFlowProvider } from "reactflow";
+import { TooltipProvider } from "../components/ui/tooltip";
+import { SSEProvider } from "./SSEContext";
import { AlertProvider } from "./alertContext";
import { DarkProvider } from "./darkContext";
import { LocationProvider } from "./locationContext";
import PopUpProvider from "./popUpContext";
import { TabsProvider } from "./tabsContext";
import { TypesProvider } from "./typesContext";
-import { ReactFlowProvider } from "reactflow";
import { UndoRedoProvider } from "./undoRedoContext";
-import { SSEProvider } from "./SSEContext";
export default function ContextWrapper({ children }: { children: ReactNode }) {
//element to wrap all context
return (
<>
-
-
-
-
-
-
-
-
- {children}
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ {children}
+
+
+
+
+
+
+
+
+
>
);
}
diff --git a/src/frontend/src/contexts/popUpContext.tsx b/src/frontend/src/contexts/popUpContext.tsx
index 371aeefce..8da50278c 100644
--- a/src/frontend/src/contexts/popUpContext.tsx
+++ b/src/frontend/src/contexts/popUpContext.tsx
@@ -1,10 +1,11 @@
-import { createContext } from "react";
-import React, { useState } from "react";
+import React, { createContext, useState } from "react";
// context to set JSX element on the DOM
export const PopUpContext = createContext({
openPopUp: (popUpElement: JSX.Element) => {},
closePopUp: () => {},
+ setCloseEdit: (value: string) => {},
+ closeEdit: "",
});
interface PopUpProviderProps {
@@ -22,8 +23,12 @@ const PopUpProvider = ({ children }: PopUpProviderProps) => {
setPopUpElements((prevPopUps) => prevPopUps.slice(1));
};
+ const [closeEdit, setCloseEdit] = useState("");
+
return (
-
+
{children}
{popUpElements[0]}
diff --git a/src/frontend/src/contexts/tabsContext.tsx b/src/frontend/src/contexts/tabsContext.tsx
index ba31a5621..a7ddf11f8 100644
--- a/src/frontend/src/contexts/tabsContext.tsx
+++ b/src/frontend/src/contexts/tabsContext.tsx
@@ -1,33 +1,33 @@
+import _ from "lodash";
import {
- createContext,
- useEffect,
- useState,
- useRef,
ReactNode,
+ createContext,
useContext,
+ useEffect,
+ useRef,
+ useState,
} from "react";
+import { addEdge } from "reactflow";
+import ShortUniqueId from "short-unique-id";
+import {
+ deleteFlowFromDatabase,
+ downloadFlowsFromDatabase,
+ readFlowsFromDatabase,
+ saveFlowToDatabase,
+ updateFlowInDatabase,
+ uploadFlowsToDatabase,
+} from "../controllers/API";
+import { APIClassType, APITemplateType } from "../types/api";
import { FlowType, NodeType } from "../types/flow";
import { TabsContextType, TabsState } from "../types/tabs";
import {
- updateIds,
- updateTemplate,
getRandomDescription,
getRandomName,
+ updateIds,
+ updateTemplate,
} from "../utils";
import { alertContext } from "./alertContext";
import { typesContext } from "./typesContext";
-import { APITemplateType } from "../types/api";
-import ShortUniqueId from "short-unique-id";
-import { addEdge } from "reactflow";
-import {
- readFlowsFromDatabase,
- deleteFlowFromDatabase,
- saveFlowToDatabase,
- downloadFlowsFromDatabase,
- uploadFlowsToDatabase,
- updateFlowInDatabase,
-} from "../controllers/API";
-import _ from "lodash";
const uid = new ShortUniqueId({ length: 5 });
@@ -53,6 +53,8 @@ const TabsContextInitialValue: TabsContextType = {
tabsState: {},
setTabsState: (state: TabsState) => {},
getNodeId: (nodeType: string) => "",
+ setTweak: (tweak: any) => {},
+ getTweak: [],
paste: (
selection: { nodes: any; edges: any },
position: { x: number; y: number; paneX?: number; paneY?: number }
@@ -73,6 +75,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
const { templates, reactFlowInstance } = useContext(typesContext);
const [lastCopiedSelection, setLastCopiedSelection] = useState(null);
const [tabsState, setTabsState] = useState({});
+ const [getTweak, setTweak] = useState([]);
const newNodeId = useRef(uid());
function incrementNodeId() {
@@ -87,12 +90,9 @@ export function TabsProvider({ children }: { children: ReactNode }) {
Saveflows.forEach((flow) => {
if (flow.data && flow.data?.nodes)
flow.data?.nodes.forEach((node) => {
- // console.log(node.data.type);
//looking for file fields to prevent saving the content and breaking the flow for exceeding the the data limite for local storage
Object.keys(node.data.node.template).forEach((key) => {
- // console.log(node.data.node.template[key].type);
if (node.data.node.template[key].type === "file") {
- // console.log(node.data.node.template[key]);
node.data.node.template[key].content = null;
node.data.node.template[key].value = "";
}
@@ -108,7 +108,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
// function loadCookie(cookie: string) {
// if (cookie && Object.keys(templates).length > 0) {
- // let cookieObject: LangFlowState = JSON.parse(cookie);
+ // let cookieObject: LangflowState = JSON.parse(cookie);
// try {
// cookieObject.flows.forEach((flow) => {
// if (!flow.data) {
@@ -192,33 +192,49 @@ export function TabsProvider({ children }: { children: ReactNode }) {
}
function processFlowEdges(flow) {
+ if (!flow.data || !flow.data.edges) return;
flow.data.edges.forEach((edge) => {
edge.className = "";
edge.style = { stroke: "#555555" };
});
}
+ function updateDisplay_name(node: NodeType, template: APIClassType) {
+ node.data.node.display_name = template["display_name"] || node.data.type;
+ }
+
+ function updateNodeDocumentation(node: NodeType, template: APIClassType) {
+ node.data.node.documentation = template["documentation"];
+ }
+
function processFlowNodes(flow) {
- flow.data.nodes.forEach((node) => {
+ if (!flow.data || !flow.data.nodes) return;
+ flow.data.nodes.forEach((node: NodeType) => {
const template = templates[node.data.type];
if (!template) {
setErrorData({ title: `Unknown node type: ${node.data.type}` });
return;
}
if (Object.keys(template["template"]).length > 0) {
+ updateDisplay_name(node, template);
updateNodeBaseClasses(node, template);
updateNodeEdges(flow, node, template);
updateNodeDescription(node, template);
updateNodeTemplate(node, template);
+ updateNodeDocumentation(node, template);
}
});
}
- function updateNodeBaseClasses(node, template) {
+ function updateNodeBaseClasses(node: NodeType, template: APIClassType) {
node.data.node.base_classes = template["base_classes"];
}
- function updateNodeEdges(flow, node, template) {
+ function updateNodeEdges(
+ flow: FlowType,
+ node: NodeType,
+ template: APIClassType
+ ) {
flow.data.edges.forEach((edge) => {
if (edge.source === node.id) {
edge.sourceHandle = edge.sourceHandle
@@ -230,11 +246,11 @@ export function TabsProvider({ children }: { children: ReactNode }) {
});
}
- function updateNodeDescription(node, template) {
+ function updateNodeDescription(node: NodeType, template: APIClassType) {
node.data.node.description = template["description"];
}
- function updateNodeTemplate(node, template) {
+ function updateNodeTemplate(node: NodeType, template: APIClassType) {
node.data.node.template = updateTemplate(
template["template"] as unknown as APITemplateType,
node.data.node.template as APITemplateType
@@ -256,16 +272,24 @@ export function TabsProvider({ children }: { children: ReactNode }) {
/**
* Downloads the current flow as a JSON file
*/
- function downloadFlow(flow: FlowType) {
+ function downloadFlow(
+ flow: FlowType,
+ flowName: string,
+ flowDescription?: string
+ ) {
// create a data URI with the current flow data
const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent(
- JSON.stringify(flow)
+ JSON.stringify({ ...flow, name: flowName, description: flowDescription })
)}`;
// create a link element and set its properties
const link = document.createElement("a");
link.href = jsonString;
- link.download = `${flows.find((f) => f.id === tabId).name}.json`;
+ link.download = `${
+ flowName && flowName != ""
+ ? flowName
+ : flows.find((f) => f.id === tabId).name
+ }.json`;
// simulate a click on the link element to trigger the download
link.click();
@@ -299,28 +323,39 @@ export function TabsProvider({ children }: { children: ReactNode }) {
* If the file type is application/json, the file is read and parsed into a JSON object.
* The resulting JSON object is passed to the addFlow function.
*/
- function uploadFlow(newProject?: boolean) {
- // create a file input
- const input = document.createElement("input");
- input.type = "file";
- input.accept = ".json";
- // add a change event listener to the file input
- input.onchange = (e: Event) => {
- // check if the file type is application/json
- if ((e.target as HTMLInputElement).files[0].type === "application/json") {
- // get the file from the file input
- const file = (e.target as HTMLInputElement).files[0];
- // read the file as text
- file.text().then((text) => {
- // parse the text into a JSON object
- let flow: FlowType = JSON.parse(text);
+ function uploadFlow(newProject?: boolean, file?: File) {
+ if (file) {
+ file.text().then((text) => {
+ // parse the text into a JSON object
+ let flow: FlowType = JSON.parse(text);
- addFlow(flow, newProject);
- });
- }
- };
- // trigger the file input click event to open the file dialog
- input.click();
+ addFlow(flow, newProject);
+ });
+ } else {
+ // create a file input
+ const input = document.createElement("input");
+ input.type = "file";
+ input.accept = ".json";
+ // add a change event listener to the file input
+ input.onchange = (e: Event) => {
+ // check if the file type is application/json
+ if (
+ (e.target as HTMLInputElement).files[0].type === "application/json"
+ ) {
+ // get the file from the file input
+ const currentfile = (e.target as HTMLInputElement).files[0];
+ // read the file as text
+ currentfile.text().then((text) => {
+ // parse the text into a JSON object
+ let flow: FlowType = JSON.parse(text);
+
+ addFlow(flow, newProject);
+ });
+ }
+ };
+ // trigger the file input click event to open the file dialog
+ input.click();
+ }
}
function uploadFlows() {
@@ -398,7 +433,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
y: insidePosition.y + n.position.y - minimumY,
},
data: {
- ...n.data,
+ ..._.cloneDeep(n.data),
id: newId,
},
};
@@ -440,8 +475,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
style: { stroke: "inherit" },
className:
targetHandle.split("|")[0] === "Text"
- ? "stroke-gray-800 dark:stroke-gray-300"
- : "stroke-gray-900 dark:stroke-gray-200",
+ ? "stroke-gray-800 "
+ : "stroke-gray-900 ",
animated: targetHandle.split("|")[0] === "Text",
selected: false,
},
@@ -463,6 +498,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
// Create a new flow with a default name if no flow is provided.
const newFlow = createNewFlow(flowData, flow);
+ processFlowEdges(newFlow);
+ processFlowNodes(newFlow);
try {
const { id } = await saveFlowToDatabase(newFlow);
@@ -502,11 +539,10 @@ export function TabsProvider({ children }: { children: ReactNode }) {
const updateEdges = (edges) => {
edges.forEach((edge) => {
- edge.style = { stroke: "inherit" };
edge.className =
- edge.targetHandle.split("|")[0] === "Text"
- ? "stroke-gray-800 dark:stroke-gray-300"
- : "stroke-gray-900 dark:stroke-gray-200";
+ (edge.targetHandle.split("|")[0] === "Text"
+ ? "stroke-gray-800 "
+ : "stroke-gray-900 ") + " stroke-connection";
edge.animated = edge.targetHandle.split("|")[0] === "Text";
});
};
@@ -589,6 +625,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
return {
...prev,
[tabId]: {
+ ...prev[tabId],
isPending: false,
},
};
@@ -626,6 +663,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
tabsState,
setTabsState,
paste,
+ getTweak,
+ setTweak,
}}
>
{children}
diff --git a/src/frontend/src/contexts/typesContext.tsx b/src/frontend/src/contexts/typesContext.tsx
index 3179e582e..ff4cc1c29 100644
--- a/src/frontend/src/contexts/typesContext.tsx
+++ b/src/frontend/src/contexts/typesContext.tsx
@@ -1,8 +1,8 @@
import { createContext, ReactNode, useEffect, useState } from "react";
import { Node } from "reactflow";
-import { typesContextType } from "../types/typesContext";
import { getAll } from "../controllers/API";
import { APIKindType } from "../types/api";
+import { typesContextType } from "../types/typesContext";
//context to share types adn functions from nodes to flow
diff --git a/src/frontend/src/contexts/undoRedoContext.tsx b/src/frontend/src/contexts/undoRedoContext.tsx
index b0f8ca6b9..3df201082 100644
--- a/src/frontend/src/contexts/undoRedoContext.tsx
+++ b/src/frontend/src/contexts/undoRedoContext.tsx
@@ -1,3 +1,4 @@
+import { cloneDeep } from "lodash";
import {
createContext,
useCallback,
@@ -6,7 +7,6 @@ import {
useState,
} from "react";
import { Edge, Node, useReactFlow } from "reactflow";
-import { cloneDeep } from "lodash";
import { TabsContext } from "./tabsContext";
type undoRedoContextType = {
diff --git a/src/frontend/src/controllers/API/index.ts b/src/frontend/src/controllers/API/index.ts
index 28a41d578..6970ac8bf 100644
--- a/src/frontend/src/controllers/API/index.ts
+++ b/src/frontend/src/controllers/API/index.ts
@@ -1,13 +1,15 @@
-import {
- BuildStatusTypeAPI,
- PromptTypeAPI,
- errorsTypeAPI,
- InitTypeAPI,
-} from "./../../types/api/index";
-import { APIObjectType, sendAllProps } from "../../types/api/index";
import axios, { AxiosResponse } from "axios";
-import { FlowStyleType, FlowType } from "../../types/flow";
import { ReactFlowJsonObject } from "reactflow";
+import { APIObjectType, sendAllProps } from "../../types/api/index";
+import { FlowStyleType, FlowType } from "../../types/flow";
+import {
+ APIClassType,
+ BuildStatusTypeAPI,
+ InitTypeAPI,
+ PromptTypeAPI,
+ UploadFileTypeAPI,
+ errorsTypeAPI,
+} from "./../../types/api/index";
/**
* Fetches all objects from the API endpoint.
@@ -22,7 +24,9 @@ const GITHUB_API_URL = "https://api.github.com";
export async function getRepoStars(owner, repo) {
try {
- const response = await axios.get(`${GITHUB_API_URL}/repos/${owner}/${repo}`);
+ const response = await axios.get(
+ `${GITHUB_API_URL}/repos/${owner}/${repo}`
+ );
return response.data.stargazers_count;
} catch (error) {
console.error("Error fetching repository data:", error);
@@ -46,23 +50,23 @@ export async function postValidateCode(
return await axios.post("/api/v1/validate/code", { code });
}
-export async function postValidateNode(
- nodeId: string,
- data: any
-): Promise> {
- return await axios.post(`/api/v1/validate/node/${nodeId}`, { data });
-}
-
/**
* Checks the prompt for the code block by sending it to an API endpoint.
- *
+ * @param {string} name - The name of the field to check.
* @param {string} template - The template string of the prompt to check.
+ * @param {APIClassType} frontend_node - The frontend node to check.
* @returns {Promise>} A promise that resolves to an AxiosResponse containing the validation results.
*/
-export async function checkPrompt(
- template: string
+export async function postValidatePrompt(
+ name: string,
+ template: string,
+ frontend_node: APIClassType
): Promise> {
- return await axios.post("/api/v1/validate/prompt", { template });
+ return await axios.post("/api/v1/validate/prompt", {
+ name: name,
+ template: template,
+ frontend_node: frontend_node,
+ });
}
/**
@@ -72,7 +76,7 @@ export async function checkPrompt(
*/
export async function getExamples(): Promise {
const url =
- "https://api.github.com/repos/logspace-ai/langflow_examples/contents/examples";
+ "https://api.github.com/repos/logspace-ai/langflow_examples/contents/examples?ref=fix_examples";
const response = await axios.get(url);
const jsonFiles = response.data.filter((file: any) => {
@@ -315,5 +319,23 @@ export async function getBuildStatus(
export async function postBuildInit(
flow: FlowType
): Promise> {
- return await axios.post(`/api/v1/build/init`, flow);
+ return await axios.post(`/api/v1/build/init/${flow.id}`, flow);
+}
+
+// fetch(`/upload/${id}`, {
+// method: "POST",
+// body: formData,
+// });
+/**
+ * Uploads a file to the server.
+ * @param {File} file - The file to upload.
+ * @param {string} id - The ID of the flow to upload the file to.
+ */
+export async function uploadFile(
+ file: File,
+ id: string
+): Promise> {
+ const formData = new FormData();
+ formData.append("file", file);
+ return await axios.post(`/api/v1/upload/${id}`, formData);
}
diff --git a/src/frontend/src/flow_constants.tsx b/src/frontend/src/flow_constants.tsx
new file mode 100644
index 000000000..1e2280ab9
--- /dev/null
+++ b/src/frontend/src/flow_constants.tsx
@@ -0,0 +1,364 @@
+/**
+ * Default description for the flow
+ * @constant
+ */
+export const DESCRIPTIONS: string[] = [
+ "Chain the Words, Master Language!",
+ "Language Architect at Work!",
+ "Empowering Language Engineering.",
+ "Craft Language Connections Here.",
+ "Create, Connect, Converse.",
+ "Smart Chains, Smarter Conversations.",
+ "Bridging Prompts for Brilliance.",
+ "Language Models, Unleashed.",
+ "Your Hub for Text Generation.",
+ "Promptly Ingenious!",
+ "Building Linguistic Labyrinths.",
+ "Create, Chain, Communicate.",
+ "Connect the Dots, Craft Language.",
+ "Interactive Language Weaving.",
+ "Generate, Innovate, Communicate.",
+ "Conversation Catalyst Engine.",
+ "Language Chainlink Master.",
+ "Design Dialogues with Langflow.",
+ "Nurture NLP Nodes Here.",
+ "Conversational Cartography Unlocked.",
+ "Design, Develop, Dialogize.",
+ "Unleashing Linguistic Creativity.",
+ "Graph Your Way to Great Conversations.",
+ "The Power of Language at Your Fingertips.",
+ "Sculpting Language with Precision.",
+ "Where Language Meets Logic.",
+ "Building Intelligent Interactions.",
+ "Your Passport to Linguistic Landscapes.",
+ "Create, Curate, Communicate with Langflow.",
+ "Flow into the Future of Language.",
+ "Mapping Meaningful Conversations.",
+ "Unravel the Art of Articulation.",
+ "Language Engineering Excellence.",
+ "Navigate the Networks of Conversation.",
+ "Crafting Conversations, One Node at a Time.",
+ "The Pinnacle of Prompt Generation.",
+ "Language Models, Mapped and Mastered.",
+ "Powerful Prompts, Perfectly Positioned.",
+ "Innovation in Interaction with Langflow.",
+ "Your Toolkit for Text Generation.",
+ "Unfolding Linguistic Possibilities.",
+ "Building Powerful Solutions with Language Models.",
+ "Uncover Business Opportunities with NLP.",
+ "Harness the Power of Conversational AI.",
+ "Transform Your Business with Smart Dialogues.",
+ "Craft Meaningful Interactions, Generate Value.",
+ "Unleashing Business Potential through Language Engineering.",
+ "Empowering Enterprises with Intelligent Interactions.",
+ "Driving Innovation in Business Communication.",
+ "Catalyzing Business Growth through Conversational AI.",
+ "Text Generation Meets Business Transformation.",
+ "Navigate the Linguistic Landscape, Discover Opportunities.",
+ "Create Powerful Connections, Boost Business Value.",
+ "Empowering Communication, Enabling Opportunities.",
+ "Advanced NLP for Groundbreaking Business Solutions.",
+ "Innovation in Interaction, Revolution in Revenue.",
+ "Maximize Impact with Intelligent Conversations.",
+ "Beyond Text Generation - Unleashing Business Opportunities.",
+ "Unlock the Power of AI in Your Business Conversations.",
+ "Crafting Dialogues that Drive Business Success.",
+ "Engineered for Excellence, Built for Business.",
+];
+
+/**
+ * Adjectives for the name of the flow
+ * @constant
+ *
+ */
+export const ADJECTIVES: string[] = [
+ "admiring",
+ "adoring",
+ "agitated",
+ "amazing",
+ "angry",
+ "awesome",
+ "backstabbing",
+ "berserk",
+ "big",
+ "boring",
+ "clever",
+ "cocky",
+ "compassionate",
+ "condescending",
+ "cranky",
+ "desperate",
+ "determined",
+ "distracted",
+ "dreamy",
+ "drunk",
+ "ecstatic",
+ "elated",
+ "elegant",
+ "evil",
+ "fervent",
+ "focused",
+ "furious",
+ "gigantic",
+ "gloomy",
+ "goofy",
+ "grave",
+ "happy",
+ "high",
+ "hopeful",
+ "hungry",
+ "insane",
+ "jolly",
+ "jovial",
+ "kickass",
+ "lonely",
+ "loving",
+ "mad",
+ "modest",
+ "naughty",
+ "nauseous",
+ "nostalgic",
+ "pedantic",
+ "pensive",
+ "prickly",
+ "reverent",
+ "romantic",
+ "sad",
+ "serene",
+ "sharp",
+ "sick",
+ "silly",
+ "sleepy",
+ "small",
+ "stoic",
+ "stupefied",
+ "suspicious",
+ "tender",
+ "thirsty",
+ "tiny",
+ "trusting",
+ "bubbly",
+ "charming",
+ "cheerful",
+ "comical",
+ "dazzling",
+ "delighted",
+ "dynamic",
+ "effervescent",
+ "enthusiastic",
+ "exuberant",
+ "fluffy",
+ "friendly",
+ "funky",
+ "giddy",
+ "giggly",
+ "gleeful",
+ "goofy",
+ "graceful",
+ "grinning",
+ "hilarious",
+ "inquisitive",
+ "joyous",
+ "jubilant",
+ "lively",
+ "mirthful",
+ "mischievous",
+ "optimistic",
+ "peppy",
+ "perky",
+ "playful",
+ "quirky",
+ "radiant",
+ "sassy",
+ "silly",
+ "spirited",
+ "sprightly",
+ "twinkly",
+ "upbeat",
+ "vibrant",
+ "witty",
+ "zany",
+ "zealous",
+];
+/**
+ * Nouns for the name of the flow
+ * @constant
+ *
+ */
+export const NOUNS: string[] = [
+ "albattani",
+ "allen",
+ "almeida",
+ "archimedes",
+ "ardinghelli",
+ "aryabhata",
+ "austin",
+ "babbage",
+ "banach",
+ "bardeen",
+ "bartik",
+ "bassi",
+ "bell",
+ "bhabha",
+ "bhaskara",
+ "blackwell",
+ "bohr",
+ "booth",
+ "borg",
+ "bose",
+ "boyd",
+ "brahmagupta",
+ "brattain",
+ "brown",
+ "carson",
+ "chandrasekhar",
+ "colden",
+ "cori",
+ "cray",
+ "curie",
+ "darwin",
+ "davinci",
+ "dijkstra",
+ "dubinsky",
+ "easley",
+ "einstein",
+ "elion",
+ "engelbart",
+ "euclid",
+ "euler",
+ "fermat",
+ "fermi",
+ "feynman",
+ "franklin",
+ "galileo",
+ "gates",
+ "goldberg",
+ "goldstine",
+ "goldwasser",
+ "golick",
+ "goodall",
+ "hamilton",
+ "hawking",
+ "heisenberg",
+ "heyrovsky",
+ "hodgkin",
+ "hoover",
+ "hopper",
+ "hugle",
+ "hypatia",
+ "jang",
+ "jennings",
+ "jepsen",
+ "joliot",
+ "jones",
+ "kalam",
+ "kare",
+ "keller",
+ "khorana",
+ "kilby",
+ "kirch",
+ "knuth",
+ "kowalevski",
+ "lalande",
+ "lamarr",
+ "leakey",
+ "leavitt",
+ "lichterman",
+ "liskov",
+ "lovelace",
+ "lumiere",
+ "mahavira",
+ "mayer",
+ "mccarthy",
+ "mcclintock",
+ "mclean",
+ "mcnulty",
+ "meitner",
+ "meninsky",
+ "mestorf",
+ "minsky",
+ "mirzakhani",
+ "morse",
+ "murdock",
+ "newton",
+ "nobel",
+ "noether",
+ "northcutt",
+ "noyce",
+ "panini",
+ "pare",
+ "pasteur",
+ "payne",
+ "perlman",
+ "pike",
+ "poincare",
+ "poitras",
+ "ptolemy",
+ "raman",
+ "ramanujan",
+ "ride",
+ "ritchie",
+ "roentgen",
+ "rosalind",
+ "saha",
+ "sammet",
+ "shaw",
+ "shirley",
+ "shockley",
+ "sinoussi",
+ "snyder",
+ "spence",
+ "stallman",
+ "stonebraker",
+ "swanson",
+ "swartz",
+ "swirles",
+ "tesla",
+ "thompson",
+ "torvalds",
+ "turing",
+ "varahamihira",
+ "visvesvaraya",
+ "volhard",
+ "wescoff",
+ "williams",
+ "wilson",
+ "wing",
+ "wozniak",
+ "wright",
+ "yalow",
+ "yonath",
+ "coulomb",
+ "degrasse",
+ "dewey",
+ "edison",
+ "eratosthenes",
+ "faraday",
+ "galton",
+ "gauss",
+ "herschel",
+ "hubble",
+ "joule",
+ "kaku",
+ "kepler",
+ "khayyam",
+ "lavoisier",
+ "maxwell",
+ "mendel",
+ "mendeleev",
+ "ohm",
+ "pascal",
+ "planck",
+ "riemann",
+ "schrodinger",
+ "sagan",
+ "tesla",
+ "tyson",
+ "volta",
+ "watt",
+ "weber",
+ "wien",
+ "zoBell",
+ "zuse",
+ "carroll",
+];
diff --git a/src/frontend/src/icons/Google/google.svg b/src/frontend/src/icons/Google/google.svg
index b518c5270..c599462cb 100644
--- a/src/frontend/src/icons/Google/google.svg
+++ b/src/frontend/src/icons/Google/google.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/src/frontend/src/icons/HuggingFace/index.tsx b/src/frontend/src/icons/HuggingFace/index.tsx
index 44fc68609..36599e0f6 100644
--- a/src/frontend/src/icons/HuggingFace/index.tsx
+++ b/src/frontend/src/icons/HuggingFace/index.tsx
@@ -1,7 +1,7 @@
import React, { forwardRef } from "react";
import { ReactComponent as HugginFaceSVG } from "./hf-logo.svg";
-export const HugginFaceIcon = forwardRef<
+export const HuggingFaceIcon = forwardRef<
SVGSVGElement,
React.PropsWithChildren<{}>
>((props, ref) => {
diff --git a/src/frontend/src/icons/Midjorney/index.tsx b/src/frontend/src/icons/Midjorney/index.tsx
index fd09aa700..fc2daacb8 100644
--- a/src/frontend/src/icons/Midjorney/index.tsx
+++ b/src/frontend/src/icons/Midjorney/index.tsx
@@ -1,9 +1,9 @@
import React, { forwardRef } from "react";
-import { ReactComponent as MidjorneySVG } from "./Midjourney_Emblem.svg";
+import { ReactComponent as MidjourneySVG } from "./Midjourney_Emblem.svg";
-export const MidjorneyIcon = forwardRef<
+export const MidjourneyIcon = forwardRef<
SVGSVGElement,
React.PropsWithChildren<{}>
>((props, ref) => {
- return ;
+ return ;
});
diff --git a/src/frontend/src/icons/MongoDB/index.tsx b/src/frontend/src/icons/MongoDB/index.tsx
new file mode 100644
index 000000000..aa27955c1
--- /dev/null
+++ b/src/frontend/src/icons/MongoDB/index.tsx
@@ -0,0 +1,9 @@
+import React, { forwardRef } from "react";
+import { ReactComponent as MongoDBSVG } from "./mongodb-icon.svg";
+
+export const MongoDBIcon = forwardRef<
+ SVGSVGElement,
+ React.PropsWithChildren<{}>
+>((props, ref) => {
+ return ;
+});
diff --git a/src/frontend/src/icons/MongoDB/mongodb-icon.svg b/src/frontend/src/icons/MongoDB/mongodb-icon.svg
new file mode 100644
index 000000000..54403d528
--- /dev/null
+++ b/src/frontend/src/icons/MongoDB/mongodb-icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/frontend/src/icons/Pinecone/index.tsx b/src/frontend/src/icons/Pinecone/index.tsx
new file mode 100644
index 000000000..3e0b9123e
--- /dev/null
+++ b/src/frontend/src/icons/Pinecone/index.tsx
@@ -0,0 +1,9 @@
+import React, { forwardRef } from "react";
+import { ReactComponent as PineconeSVG } from "./pinecone_logo.svg";
+
+export const PineconeIcon = forwardRef<
+ SVGSVGElement,
+ React.PropsWithChildren<{}>
+>((props, ref) => {
+ return ;
+});
diff --git a/src/frontend/src/icons/Pinecone/pinecone_logo.svg b/src/frontend/src/icons/Pinecone/pinecone_logo.svg
new file mode 100644
index 000000000..e9884a424
--- /dev/null
+++ b/src/frontend/src/icons/Pinecone/pinecone_logo.svg
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/frontend/src/icons/VertexAI/index.tsx b/src/frontend/src/icons/VertexAI/index.tsx
new file mode 100644
index 000000000..a5115da7e
--- /dev/null
+++ b/src/frontend/src/icons/VertexAI/index.tsx
@@ -0,0 +1,9 @@
+import React, { forwardRef } from "react";
+import { ReactComponent as VertexAISVG } from "./vertex_ai.svg";
+
+export const VertexAIIcon = forwardRef<
+ SVGSVGElement,
+ React.PropsWithChildren<{}>
+>((props, ref) => {
+ return ;
+});
diff --git a/src/frontend/src/icons/VertexAI/vertex_ai.svg b/src/frontend/src/icons/VertexAI/vertex_ai.svg
new file mode 100644
index 000000000..9da1d8d8b
--- /dev/null
+++ b/src/frontend/src/icons/VertexAI/vertex_ai.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/frontend/src/icons/supabase/index.tsx b/src/frontend/src/icons/supabase/index.tsx
new file mode 100644
index 000000000..f9e699ace
--- /dev/null
+++ b/src/frontend/src/icons/supabase/index.tsx
@@ -0,0 +1,9 @@
+import React, { forwardRef } from "react";
+import { ReactComponent as SupabaseSvg } from "./supabase-icon.svg";
+
+export const SupabaseIcon = forwardRef<
+ SVGSVGElement,
+ React.PropsWithChildren<{}>
+>((props, ref) => {
+ return ;
+});
diff --git a/src/frontend/src/icons/supabase/supabase-icon.svg b/src/frontend/src/icons/supabase/supabase-icon.svg
new file mode 100644
index 000000000..ac43e170a
--- /dev/null
+++ b/src/frontend/src/icons/supabase/supabase-icon.svg
@@ -0,0 +1,99 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/frontend/src/index.css b/src/frontend/src/index.css
index a0a908ec8..c4950ec4e 100644
--- a/src/frontend/src/index.css
+++ b/src/frontend/src/index.css
@@ -2,75 +2,12 @@
@tailwind components;
@tailwind utilities;
+
+/* TODO: Confirm that all colors here are found in tailwind config */
+
@layer base {
+
:root {
- --background: 0 0% 100%; /* hsl(0 0% 100%) */
- --foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
-
- --muted: 210 40% 96.1%; /* hsl(210 40% 96%) */
- --muted-foreground: 215.4 16.3% 46.9%; /* hsl(215 16% 46%) */
-
- --popover: 0 0% 100%; /* hsl(0 0% 100%) */
- --popover-foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
-
- --card: 0 0% 100%; /* hsl(0 0% 100%) */
- --card-foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
-
- --border: 214.3 31.8% 91.4%; /* hsl(214 32% 91%) */
- --input: 214.3 31.8% 91.4%; /* hsl(214 32% 91%) */
-
- --primary: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
- --primary-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
-
- --secondary: 210 40% 96.1%; /* hsl(210 40% 96%) */
- --secondary-foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
-
- --accent: 210 40% 96.1%; /* hsl(210 40% 96%) */
- --accent-foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
-
- --destructive: 0 100% 50%; /* hsl(0 100% 50%) */
- --destructive-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
-
- --ring: 215 20.2% 65.1%; /* hsl(215 20% 65%) */
-
- --radius: 0.5rem;
- }
-
- .dark {
- --background: 224 71% 4%; /* hsl(224 71% 4%) */
- --foreground: 213 31% 91%; /* hsl(213 31% 91%) */
-
- --muted: 223 47% 11%; /* hsl(223 47% 11%) */
- --muted-foreground: 215.4 16.3% 56.9%; /* hsl(215 16% 56%) */
-
- --popover: 224 71% 4%; /* hsl(224 71% 4%) */
- --popover-foreground: 215 20.2% 65.1%; /* hsl(215 20% 65%) */
-
- --card: 224 71% 4%; /* hsl(224 71% 4%) */
- --card-foreground: 213 31% 91%; /* hsl(213 31% 91%) */
-
- --border: 216 34% 17%; /* hsl(216 34% 17%) */
- --input: 216 34% 17%; /* hsl(216 34% 17%) */
-
- --primary: 210 40% 98%; /* hsl(210 40% 98%) */
- --primary-foreground: 222.2 47.4% 1.2%; /* hsl(222 47% 1%) */
-
- --secondary: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
- --secondary-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
-
- --accent: 216 34% 17%; /* hsl(216 34% 17%) */
- --accent-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
-
- --destructive: 0 63% 31%; /* hsl(0 63% 31%) */
- --destructive-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
-
- --ring: 216 34% 17%; /* hsl(216 34% 17%) */
-
- --radius: 0.5rem;
- }
-}
-
-:root {
--background: 0 0% 100%; /* hsl(0 0% 100%) */
--foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
--muted: 210 40% 98%; /* hsl(210 40% 98%) */
@@ -89,51 +26,97 @@
--accent-foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
--destructive: 0 100% 50%; /* hsl(0 100% 50%) */
--destructive-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
- --ring: 215 20.2% 65.1%; /* hsl(215 20% 65%) */
--radius: 0.5rem;
+ --ring: 215 20.2% 65.1%; /* hsl(215 20% 65%) */
+ --round-btn-shadow: #00000063;
+
+ --error-background: #fef2f2;
+ --error-foreground: #991b1b;
+
+ --success-background: #f0fdf4;
+ --success-foreground: #14532d;
+
+ --info-background: #f0f4fd;
+ --info-foreground: #141653;
+
+ --high-indigo: #4338ca;
+ --medium-indigo: #6366f1;
+
+ --chat-bot-icon: #afe6ef;
+ --chat-user-icon: #aface9;
+
+ /* Colors that are shared in dark and light mode */
+ --blur-shared: #151923de;
+ --build-trigger: #dc735b;
+ --chat-trigger: #5c8be1;
+ --chat-trigger-disabled: #b4c3da;
+ --status-red: #ef4444;
+ --status-yellow: #eab308;
+ --status-green: #4ade80;
+ --status-blue:#2563eb;
+ --connection: #555;
+
}
.dark {
- -background: 224 71% 4%;
- /* hsl(224 71% 4%) */
- -foreground: 213 31% 91%;
- /* hsl(213 31% 91%) */
- -muted: 223 47% 11%;
- /* hsl(223 47% 11%) */
- -muted-foreground: 215.4 16.3% 56.9%;
- /* hsl(215 16% 56%) */
- -popover: 224 71% 4%;
- /* hsl(224 71% 4%) */
- -popover-foreground: 215 20.2% 65.1%;
- /* hsl(215 20% 65%) */
- -card: 224 71% 4%;
- /* hsl(224 71% 4%) */
- -card-foreground: 213 31% 91%;
- /* hsl(213 31% 91%) */
- -border: 216 34% 17%;
- /* hsl(216 34% 17%) */
- -input: 216 34% 17%;
- /* hsl(216 34% 17%) */
- -primary: 210 40% 98%;
- /* hsl(210 40% 98%) */
- -primary-foreground: 222.2 47.4% 1.2%;
- /* hsl(222 47% 1%) */
- -secondary: 222.2 47.4% 11.2%;
- /* hsl(222 47% 11%) */
- -secondary-foreground: 210 40% 98%;
- /* hsl(210 40% 98%) */
- -accent: 216 34% 17%;
- /* hsl(216 34% 17%) */
- -accent-foreground: 210 40% 98%;
- /* hsl(210 40% 98%) */
- -destructive: 0 63% 31%;
- /* hsl(0 63% 31%) */
- -destructive-foreground: 210 40% 98%;
- /* hsl(210 40% 98%) */
- -ring: 216 34% 17%;
- /* hsl(216 34% 17%) */
- -radius: 0.5rem;
-}
+ --background: 224 35% 7.5%; /* hsl(224 40% 10%) */
+ --foreground: 213 31% 80%; /* hsl(213 31% 91%) */
+
+ --muted: 223 27% 11%; /* hsl(223 27% 11%) */
+ --muted-foreground: 215.4 16.3% 56.9%; /* hsl(215 16% 56%) */
+
+ --popover: 224 71% 4%; /* hsl(224 71% 4%) */
+ --popover-foreground: 215 20.2% 65.1%; /* hsl(215 20% 65%) */
+
+ --card: 224 25% 15.5%; /* hsl(224 71% 4%) */
+ --card-foreground: 213 31% 80%; /* hsl(213 31% 91%) */
+
+ --border: 216 24% 17%; /* hsl(216 34% 17%) */
+ --input: 216 24% 17%; /* hsl(216 34% 17%) */
+
+ --primary: 210 20% 80%; /* hsl(210 20% 80%) */
+ --primary-foreground: 222.2 27.4% 1.2%; /* hsl(222 47% 1%) */
+
+ --secondary: 222.2 37.4% 7.2%; /* hsl(222 47% 11%) */
+ --secondary-foreground: 210 40% 80%; /* hsl(210 40% 80%) */
+
+ --accent: 216 24% 20%; /* hsl(216 34% 17%) */
+ --accent-foreground: 210 30% 98%; /* hsl(210 40% 98%) */
+
+ --destructive: 0 63% 31%; /* hsl(0 63% 31%) */
+ --destructive-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
+
+ --ring: 216 24% 30%; /* hsl(216 24% 30%) */
+
+ --radius: 0.5rem;
+
+ --round-btn-shadow: #00000063;
+
+ --success-background: #022c22;
+ --success-foreground: #ecfdf5;
+
+ --error-foreground: #fef2f2;
+ --error-background: #450a0a;
+
+ --info-foreground: #eff6ff;
+ --info-background: #172554;
+
+
+ --high-indigo: #4338ca;
+ --medium-indigo: #6366f1;
+
+ /* Colors that are shared in dark and light mode */
+ --blur-shared: #151923d2;
+ --build-trigger: #dc735b;
+ --chat-trigger: #5c8be1;
+ --chat-trigger-disabled: #2d3b54;
+ --status-red: #ef4444;
+ --status-yellow: #eab308;
+ --status-green: #4ade80;
+ --status-blue: #2563eb;
+ --connection: #555;
+
+}}
@layer base {
* {
@@ -165,3 +148,944 @@ The cursor: default; property value restores the browser's default cursor style
.react-flow__pane {
cursor: default;
}
+
+
+@layer components {
+ .round-buttons-position {
+ @apply fixed right-4
+ }
+ .side-bar-arrangement {
+ @apply flex h-full w-52 flex-col overflow-hidden border-r scrollbar-hide
+ }
+ .side-bar-search-div-placement {
+ @apply relative mx-auto mb-2 mt-2 flex items-center
+ }
+ .side-bar-components-icon {
+ @apply h-6 w-4 text-ring
+ }
+ .side-bar-components-text {
+ @apply w-full truncate pr-1 text-xs text-foreground
+ }
+ .side-bar-components-div-form {
+ @apply flex w-full items-center justify-between rounded-md rounded-l-none border border-l-0 border-dashed border-ring bg-white px-3 py-1 text-sm
+ }
+ .side-bar-components-border {
+ @apply cursor-grab rounded-l-md border-l-8
+ }
+ .side-bar-components-gap {
+ @apply flex flex-col gap-2 p-2
+ }
+ .side-bar-components-div-arrangement {
+ @apply w-full overflow-auto scrollbar-hide
+ }
+ .search-icon {
+ @apply absolute inset-y-0 right-0 flex items-center py-1.5 pr-5
+ }
+ .extra-side-bar-save-disable {
+ @apply text-muted-foreground
+ }
+ .extra-side-bar-save-disable:hover {
+ @apply hover:text-accent-foreground
+ }
+ .side-bar-button-size {
+ @apply h-5 w-5
+ }
+ .side-bar-button-size:hover {
+ @apply hover:text-accent-foreground
+ }
+ .side-bar-buttons-arrangement {
+ @apply mb-2 mt-2 flex w-full items-center justify-between gap-2 px-2
+ }
+ .extra-side-bar-buttons {
+ @apply relative inline-flex w-full items-center justify-center rounded-md bg-background px-2 py-2 text-foreground shadow-sm ring-1 ring-inset ring-input transition-all duration-500 ease-in-out
+ }
+ .extra-side-bar-buttons:hover {
+ @apply hover:bg-muted
+ }
+ .button-div-style {
+ @apply gap-2 flex
+ }
+ .input-primary:focus{
+ @apply focus:placeholder-transparent focus:ring-ring focus:border-ring
+ }
+ .input-primary {
+ @apply bg-background block border-ring form-input px-3 placeholder:text-muted-foreground rounded-md shadow-sm sm:text-sm truncate w-full;
+ }
+
+ .input-edit-node{
+ @apply input-primary placeholder:text-center pt-0.5 pb-0.5 text-center
+ }
+ .input-search{
+ @apply input-primary pr-7 mx-2
+ }
+ .input-disable{
+ @apply bg-border placeholder:text-ring border-transparent
+ }
+ .input-dialog{
+ @apply text-ring cursor-pointer bg-transparent
+ }
+ .message-button {
+ @apply message-button-position flex h-12 w-12 items-center justify-center rounded-full bg-border px-3 py-1 shadow-md transition-all
+ }
+
+ .round-button-form {
+ @apply flex h-12 w-12 cursor-pointer justify-center rounded-full bg-border px-3 py-1 shadow-md
+ }
+ .round-button-div {
+ @apply flex items-center gap-3
+ }
+ .build-trigger-loading-icon {
+ @apply stroke-build-trigger
+ }
+ .build-trigger-icon {
+ @apply w-6 fill-build-trigger stroke-1 stroke-build-trigger
+ }
+ .message-button-position {
+ @apply fixed bottom-4 right-4
+ }
+ .message-button-icon {
+ @apply fill-chat-trigger stroke-chat-trigger stroke-1
+ }
+ .disabled-message-button-icon {
+ @apply fill-chat-trigger-disabled stroke-chat-trigger-disabled stroke-1
+ }
+ .components-disclosure-arrangement {
+ @apply -mt-px flex w-full select-none items-center justify-between border-y border-y-input bg-muted px-3 py-2
+ }
+ .components-disclosure-title {
+ @apply flex items-center text-sm text-primary
+ }
+ .components-disclosure-div {
+ @apply flex gap-2
+ }
+ .flow-page-positioning {
+ @apply h-full w-full overflow-hidden
+ }
+ .logspace-page-icon {
+ @apply absolute bottom-2 left-7 flex h-6 cursor-pointer flex-col items-center justify-start overflow-hidden rounded-lg bg-foreground px-2 text-center font-sans text-xs tracking-wide text-secondary transition-all duration-500 ease-in-out
+ }
+
+ .logspace-page-icon:hover {
+ @apply hover:h-12
+ }
+
+ .flex-max-width {
+ @apply flex w-full
+ }
+
+ .main-page-panel {
+ @apply flex-max-width h-full flex-col overflow-auto bg-muted px-16
+ }
+
+ .main-page-nav-arrangement {
+ @apply flex-max-width justify-between px-6 py-12 pb-2
+ }
+
+ .main-page-nav-title {
+ @apply flex items-center justify-center gap-2 text-2xl font-semibold
+ }
+
+ .main-page-nav-button {
+ @apply mr-2 w-4
+ }
+
+ .main-page-description-text {
+ @apply flex w-[60%] px-6 pb-14 text-muted-foreground
+ }
+
+ .main-page-flows-display {
+ @apply grid w-full gap-4 p-4 md:grid-cols-2 lg:grid-cols-4
+ }
+
+ .community-page-arrangement {
+ @apply flex-max-width h-full flex-col overflow-auto bg-muted px-16
+ }
+
+ .community-page-nav-arrangement {
+ @apply flex-max-width justify-between px-6 py-12 pb-2
+ }
+
+ .community-page-nav-title {
+ @apply flex items-center justify-center gap-2 text-2xl font-semibold
+ }
+
+ .community-page-nav-button {
+ @apply flex gap-2
+ }
+
+ .community-page-description-text {
+ @apply flex w-[70%] px-6 pb-8 text-muted-foreground
+ }
+
+ .community-pages-flows-panel {
+ @apply grid w-full gap-4 p-4 md:grid-cols-2 lg:grid-cols-4
+ }
+ .generic-node-div {
+ @apply relative flex w-96 flex-col justify-center rounded-lg bg-background
+ }
+ .generic-node-div-title {
+ @apply flex w-full items-center justify-between gap-8 rounded-t-lg border-b bg-muted p-4
+ }
+ .generic-node-title-arrangement {
+ @apply flex-max-width items-center truncate
+ }
+ .generic-node-icon {
+ @apply h-10 w-10 rounded p-1
+ }
+ .generic-node-tooltip-div {
+ @apply ml-2 truncate
+ }
+ .generic-node-validation-div {
+ @apply max-h-96 overflow-auto
+ }
+
+ .generic-node-status-position {
+ @apply relative top-[3px] h-5 w-5
+ }
+
+ .generic-node-status-animation {
+ @apply hidden h-4 w-4 animate-spin rounded-full bg-ring opacity-0
+ }
+ .generic-node-status {
+ @apply h-4 w-4 rounded-full opacity-100
+ }
+ .green-status {
+ @apply generic-node-status bg-status-green
+ }
+ .red-status {
+ @apply generic-node-status bg-status-red
+ }
+ .yellow-status {
+ @apply generic-node-status bg-status-yellow
+ }
+ .status-build-animation {
+ @apply hidden h-4 w-4 animate-spin rounded-full bg-ring opacity-0
+ }
+ .status-div {
+ @apply absolute w-4 duration-200 ease-in-out
+ }
+ .status-div:hover {
+ @apply hover:text-accent-foreground hover:transition-all
+ }
+ .generic-node-desc {
+ @apply h-full w-full py-5 text-foreground
+ }
+ .generic-node-desc-text {
+ @apply w-full px-5 pb-3 text-sm text-muted-foreground
+ }
+
+ .alert-icon {
+ @apply h-5 w-5
+ }
+ .alert-font-size {
+ @apply text-sm font-medium
+ }
+
+ .error-build-message {
+ @apply mt-6 w-96 cursor-pointer rounded-md bg-error-background p-4 shadow-xl
+ }
+ .error-build-message-circle {
+ @apply text-status-red alert-icon
+ }
+ .error-build-text {
+ @apply text-error-foreground
+ }
+ .error-build-foreground {
+ @apply error-build-text alert-font-size
+ }
+ .error-build-message-div {
+ @apply mt-2 text-sm error-build-text
+ }
+ .error-build-message-list {
+ @apply list-disc space-y-1 pl-5
+ }
+
+ .success-alert {
+ @apply mt-6 w-96 rounded-md bg-success-background p-4 shadow-xl
+ }
+ .success-alert-icon {
+ @apply alert-icon text-status-green
+ }
+ .success-alert-message {
+ @apply alert-font-size text-success-foreground
+ }
+
+ .card-component-title-display {
+ @apply round-button-div flex-max-width
+ }
+ .card-component-image {
+ @apply flex h-7 w-7 items-center justify-center rounded-full text-2xl
+ }
+ .card-component-title-size {
+ @apply inline-block w-full flex-1 break-words truncate-doubleline
+ }
+ .card-component-delete-button {
+ @apply flex self-start
+ }
+ .card-component-delete-icon {
+ @apply h-4 w-4 text-primary opacity-0 transition-all group-hover:opacity-100
+ }
+ .card-component-desc {
+ @apply pb-2 pt-2
+ }
+ .card-component-desc-text {
+ @apply truncate-doubleline
+ }
+ .card-component-footer-arrangement {
+ @apply flex-max-width items-end justify-between gap-2
+ }
+ .card-component-footer {
+ @apply flex flex-wrap gap-2
+ }
+
+ .unused-side-bar-aside {
+ @apply flex flex-shrink-0 flex-col overflow-hidden border-r transition-all duration-500
+ }
+ .unused-side-bar-arrangement {
+ @apply flex h-full w-52 flex-col items-start overflow-y-auto border bg-background scrollbar-hide
+ }
+ .unused-side-bar-division {
+ @apply flex-max-width flex-grow flex-col
+ }
+ .unused-side-bar-nav {
+ @apply flex-1 space-y-1
+ }
+ .unused-side-bar-link {
+ @apply flex-max-width items-center rounded-md py-2 pl-2 text-sm font-medium
+ }
+ .unused-side-bar-link-colors-true {
+ @apply bg-muted text-foreground
+ }
+ .unused-side-bar-link-colors-false {
+ @apply bg-background text-muted-foreground hover:bg-muted hover:text-foreground
+ }
+ .unused-side-bar-icon {
+ @apply mr-3 flex-shrink-0 h-6 w-6
+ }
+ .unused-side-bar-icon-false {
+ @apply text-ring group-hover:text-accent-foreground
+ }
+ .unused-side-bar-disclosure {
+ @apply unused-side-bar-link pr-1 text-left
+ }
+ .unused-side-bar-disclosure:focus {
+ @apply focus:outline-none focus:ring-1 focus:ring-ring
+ }
+ .unused-side-bar-disclosure-icon {
+ @apply unused-side-bar-icon text-ring group-hover:text-accent-foreground
+ }
+ .unused-side-bar-svg-true {
+ @apply text-ring rotate-90
+ }
+ .unused-side-bar-svg {
+ @apply ml-3 h-5 w-5 flex-shrink-0 duration-150 ease-in-out group-hover:text-accent-foreground
+ }
+ .unused-side-bar-disclosure-panel {
+ @apply flex w-full items-center rounded-md py-2 pl-11 pr-2 text-sm font-medium
+ }
+
+ .code-area-component {
+ @apply pointer-events-none w-full cursor-not-allowed
+ }
+ .code-area-input-positioning {
+ @apply flex-max-width items-center
+ }
+ .code-area-external-link {
+ @apply w-6 h-6 ml-3
+ }
+ .code-area-external-link:hover {
+ @apply hover:text-accent-foreground
+ }
+
+ .dropdown-component-outline {
+ @apply input-edit-node relative pr-8
+ }
+ .dropdown-component-false-outline {
+ @apply input-primary py-2 pl-3 pr-10 text-left
+ }
+ .dropdown-component-display {
+ @apply block w-full truncate bg-background
+ }
+ .dropdown-component-arrow {
+ @apply pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2
+ }
+ .dropdown-component-arrow-color {
+ @apply h-5 w-5 extra-side-bar-save-disable
+ }
+ .dropdown-component-options {
+ @apply z-10 mt-1 max-h-60 overflow-auto rounded-md bg-background py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm
+ }
+ .dropdown-component-true-options {
+ @apply dropdown-component-options w-[215px]
+ }
+ .dropdown-component-false-options {
+ @apply dropdown-component-options w-full
+ }
+ .dropdown-component-option {
+ @apply relative cursor-default select-none
+ }
+ .dropdown-component-false-option {
+ @apply dropdown-component-option py-0.5 pl-3 pr-12
+ }
+ .dropdown-component-true-option {
+ @apply dropdown-component-option py-2 pl-3 pr-9
+ }
+ .dropdown-component-choosal {
+ @apply absolute inset-y-0 right-0 flex items-center pr-4
+ }
+ .dropdown-component-check-icon {
+ @apply h-5 w-5 text-black
+ }
+
+ .edit-flow-arrangement {
+ @apply flex justify-between
+ }
+ .edit-flow-span {
+ @apply ml-10 animate-pulse text-status-red
+ }
+
+ .float-component-pointer {
+ @apply pointer-events-none cursor-not-allowed
+ }
+
+ .header-menu-bar {
+ @apply flex items-center gap-0.5 rounded-md px-1.5 py-1 text-sm font-medium
+ }
+ .header-menu-bar-display {
+ @apply flex max-w-[200px] items-center gap-2 cursor-pointer
+ }
+ .header-menu-flow-name {
+ @apply flex-1 truncate
+ }
+ .header-menu-options {
+ @apply mr-2 h-4 w-4
+ }
+
+ .header-arrangement {
+ @apply flex-max-width h-12 items-center justify-between border-b bg-muted
+ }
+ .header-start-display {
+ @apply flex w-96 items-center justify-start gap-2
+ }
+ .header-end-division {
+ @apply flex w-96 justify-end px-2
+ }
+ .header-end-display {
+ @apply ml-auto mr-2 flex items-center gap-5
+ }
+ .header-github-link-box {
+ @apply border border-input h-9 px-3 pr-0 rounded-md inline-flex shadow-sm items-center justify-center
+ }
+ .header-github-link {
+ @apply text-sm font-medium disabled:opacity-50 disabled:pointer-events-none ring-offset-background text-muted-foreground header-github-link-box
+ }
+ .header-github-link:focus-visible {
+ @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2
+ }
+ .header-github-link:hover {
+ @apply hover:bg-accent hover:text-accent-foreground
+ }
+ .header-github-display {
+ @apply -mr-px ml-2 flex h-9 items-center justify-center rounded-md rounded-l-none border bg-background px-2 text-sm
+ }
+ .header-notifications-box {
+ @apply fixed left-0 top-0 h-screen w-screen
+ }
+ .header-notifications {
+ @apply absolute right-[3px] h-1.5 w-1.5 rounded-full bg-destructive
+ }
+
+ .input-component-div {
+ @apply pointer-events-none relative cursor-not-allowed
+ }
+ .input-component-button {
+ @apply absolute inset-y-0 right-0 items-center text-muted-foreground
+ }
+ .input-component-true-button {
+ @apply input-component-button pr-2
+ }
+ .input-component-false-button {
+ @apply input-component-button px-4
+ }
+ .input-component-true-svg {
+ @apply absolute bottom-0.5 right-2 side-bar-button-size
+ }
+ .input-component-false-svg {
+ @apply absolute bottom-2 right-3 side-bar-button-size
+ }
+
+ .input-file-component {
+ @apply flex-max-width items-center
+ }
+
+ .toggle-component-switch {
+ @apply relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out
+ }
+ .toggle-component-switch:focus {
+ @apply focus:outline-none focus:ring-1 focus:ring-primary focus:ring-offset-1
+ }
+ .toggle-component-span {
+ @apply pointer-events-none relative inline-block h-5 w-5 transform rounded-full shadow ring-0 transition duration-200 ease-in-out
+ }
+ .toggle-component-second-span {
+ @apply absolute inset-0 flex h-full w-full items-center justify-center transition-opacity
+ }
+
+ .app-div {
+ @apply fixed bottom-5 left-5 flex flex-col-reverse
+ }
+
+ .chat-input-modal-txtarea {
+ @apply form-input block w-full rounded-md border-ring pr-10 custom-scroll sm:text-sm
+ }
+ .chat-input-modal-div {
+ @apply absolute bottom-0.5 right-3
+ }
+ .chat-input-modal-lock {
+ @apply side-bar-button-size animate-pulse text-ring
+ }
+ .chat-input-modal-send {
+ @apply side-bar-button-size text-ring hover:text-muted-foreground
+ }
+
+ .code-block-modal {
+ @apply flex items-center justify-between px-4 py-1.5
+ }
+ .code-block-modal-span {
+ @apply text-xs lowercase text-background
+ }
+ .code-block-modal-button {
+ @apply flex items-center gap-1.5 rounded bg-none p-1 text-xs text-background
+ }
+
+ .chat-message-modal {
+ @apply flex-max-width py-2 pl-2
+ }
+ .chat-message-modal-div {
+ @apply my-3 flex h-8 w-8 items-center justify-center overflow-hidden rounded-full
+ }
+ .chat-message-modal-img {
+ @apply absolute scale-150 transition-opacity duration-500
+ }
+ .chat-message-modal-display {
+ @apply flex-max-width items-center text-start
+ }
+ .chat-message-modal-text {
+ @apply relative inline-block w-full text-start text-sm font-normal text-muted-foreground
+ }
+ .chat-message-modal-icon-div {
+ @apply absolute -left-2 -top-1 cursor-pointer
+ }
+ .chat-message-modal-thought {
+ @apply ml-3 inline-block h-full w-[95%] rounded-md border border-ring bg-muted px-2 pb-3 pt-3 text-start text-muted-foreground chat-message-modal-thought-cursor
+ }
+ .chat-message-modal-thought-cursor {
+ @apply cursor-pointer scrollbar-hide overflow-scroll
+ }
+ .chat-message-modal-markdown {
+ @apply w-full px-4 pb-3 pr-8 pt-3
+ }
+ .chat-message-modal-markdown-span {
+ @apply mt-1 animate-pulse cursor-default
+ }
+ .chat-message-modal-alert {
+ @apply inline-block px-3 text-start text-muted-foreground
+ }
+
+ .file-card-modal-image-div {
+ @apply absolute right-0 top-0 rounded-bl-lg bg-muted px-1 text-sm font-bold text-foreground
+ }
+ .file-card-modal-image-button {
+ @apply px-2 py-1 text-ring
+ }
+ .file-card-modal-button {
+ @apply flex w-1/2 items-center justify-between rounded border border-ring bg-muted px-2 py-2 text-foreground shadow hover:drop-shadow-lg
+ }
+ .file-card-modal-div {
+ @apply mr-2 flex-max-width items-center gap-2 text-current
+ }
+ .file-card-modal-footer {
+ @apply flex flex-col items-start
+ }
+ .file-card-modal-name {
+ @apply truncate text-sm text-current
+ }
+ .file-card-modal-type {
+ @apply truncate text-xs text-ring
+ }
+
+ .send-message-modal-transition {
+ @apply fixed inset-0 bg-black bg-opacity-80 backdrop-blur-sm transition-opacity
+ }
+ .chat-modal-box {
+ @apply fixed inset-0 z-10 overflow-y-auto
+ }
+ .chat-modal-box-div {
+ @apply flex h-full items-end justify-center p-4 text-center sm:items-center sm:p-0
+ }
+ .chat-modal-dialog-panel {
+ @apply relative flex h-[95%] w-[690px] transform flex-col justify-between overflow-hidden rounded-lg bg-background text-left shadow-xl drop-shadow-2xl transition-all
+ }
+ .chat-modal-dialog-panel-div {
+ @apply relative w-full p-4
+ }
+ .chat-modal-dialog-trash-panel {
+ @apply absolute right-10 top-2 z-30 text-muted-foreground hover:text-status-red
+ }
+ .chat-modal-dialog-x-panel {
+ @apply absolute right-2 top-1.5 z-30 text-muted-foreground hover:text-status-red
+ }
+ .chat-modal-dialog-history {
+ @apply flex-max-width h-full flex-col items-center overflow-scroll border-t bg-background scrollbar-hide
+ }
+ .chat-modal-dialog-span-box {
+ @apply flex-max-width h-full flex-col items-center justify-center text-center align-middle
+ }
+ .chat-modal-dialog-desc {
+ @apply w-2/4 rounded-md border border-input bg-muted px-6 py-8
+ }
+ .chat-modal-input-div {
+ @apply flex-max-width flex-col items-center justify-between border-t bg-background p-3
+ }
+ .chat-modal-input {
+ @apply relative mt-1 w-full rounded-md shadow-sm
+ }
+ .code-area-modal-editor-div {
+ @apply mt-2 flex-max-width h-full
+ }
+ .code-area-modal-editor-box {
+ @apply h-[300px] w-full rounded-lg border-[1px] border-ring custom-scroll
+ }
+
+ .edit-node-modal-variable {
+ @apply h-5 w-5 stroke-2 pe-1 text-muted-foreground
+ }
+ .edit-node-modal-span {
+ @apply text-sm font-semibold text-primary
+ }
+ .edit-node-modal-arrangement {
+ @apply flex-max-width h-fit max-h-[400px]
+ }
+ .edit-node-modal-box {
+ @apply w-full rounded-lg border-[1px] border-input bg-background
+ }
+ .edit-node-modal-table {
+ @apply flex h-fit flex-col gap-5
+ }
+ .edit-node-modal-table-header {
+ @apply h-10 border-input text-xs font-medium text-ring
+ }
+ .edit-node-modal-table-cell {
+ @apply p-0 text-center text-sm text-foreground truncate sm:px-3
+ }
+ .edit-node-modal-second-cell {
+ @apply w-[300px] p-0 text-center text-xs text-foreground
+ }
+
+ .generic-modal-txtarea-div {
+ @apply mt-2 flex-max-width h-full
+ }
+
+ .button-box-modal-div {
+ @apply flex transform flex-col items-center justify-center rounded-lg border border-ring text-center shadow hover:scale-105 hover:shadow-lg
+ }
+
+ .dialog-header-modal-div {
+ @apply absolute left-0 top-2 z-50 hidden pl-4 pt-4 sm:block
+ }
+ .dialog-header-modal-button {
+ @apply rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2
+ }
+
+ .dialog-modal-examples-div {
+ @apply h-full w-full overflow-y-auto scrollbar-hide
+ }
+ .dialog-modal-example-true {
+ @apply mx-auto flex flex-row flex-wrap items-start justify-center overflow-auto
+ }
+ .dialog-modal-example-false {
+ @apply flex flex-row items-center justify-center
+ }
+ .dialog-modal-button-box-div {
+ @apply flex-max-width h-full items-center justify-evenly
+ }
+ .document-icon {
+ @apply h-10 w-10 flex-shrink-0
+ }
+ .loading-component-div {
+ @apply flex items-center justify-center align-middle
+ }
+ .dialog-modal-footer {
+ @apply mt-2 flex-max-width items-center justify-center
+ }
+ .dialog-modal-footer-link {
+ @apply flex items-center justify-center text-muted-foreground
+ }
+
+ .node-modal-div {
+ @apply fixed inset-0 bg-ring bg-opacity-75 transition-opacity
+ }
+ .node-modal-dialog-arrangement {
+ @apply fixed inset-0 z-10 overflow-y-auto
+ }
+ .node-modal-dialog-div {
+ @apply flex h-full items-end justify-center p-4 text-center sm:items-center sm:p-0
+ }
+ .node-modal-dialog-panel {
+ @apply relative flex h-[600px] w-[700px] transform flex-col justify-between overflow-hidden rounded-lg bg-background text-left shadow-xl transition-all sm:my-8
+ }
+ .node-modal-dialog-panel-div {
+ @apply absolute right-0 top-0 z-50 hidden pr-4 pt-4 sm:block
+ }
+ .node-modal-dialog-button {
+ @apply rounded-md text-ring hover:text-accent-foreground
+ }
+ .node-modal-dialog-icon-div {
+ @apply flex-max-width h-full flex-col items-center justify-center
+ }
+ .node-modal-icon-arrangement {
+ @apply z-10 flex-max-width justify-center pb-4 shadow-sm
+ }
+ .node-modal-icon {
+ @apply mt-4 h-10 w-10 rounded p-1
+ }
+ .node-modal-title-div {
+ @apply mt-4 text-center sm:ml-4 sm:text-left
+ }
+ .node-modal-title {
+ @apply text-lg font-medium leading-10 text-foreground
+ }
+ .node-modal-template-div {
+ @apply flex-max-width h-full flex-row items-center justify-center gap-4 bg-input p-4
+ }
+ .node-modal-template {
+ @apply w-full rounded-lg bg-background px-4 shadow sm:p-4
+ }
+ .node-modal-template-column {
+ @apply flex h-full flex-col gap-5
+ }
+ .node-modal-button-box {
+ @apply flex-max-width flex-row-reverse bg-input px-4 pb-3
+ }
+ .node-modal-button {
+ @apply inline-flex w-full justify-center rounded-md border border-transparent bg-status-red px-4 py-2 text-base font-medium text-background shadow-sm hover:bg-ring sm:ml-3 sm:w-auto sm:text-sm
+ }
+ .node-modal-button:focus {
+ @apply focus:outline-none focus:ring-1 focus:ring-ring focus:ring-offset-1
+ }
+
+ .prompt-modal-icon-box {
+ @apply mx-auto mt-4 flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-almost-light-blue sm:mx-0 sm:h-10 sm:w-10
+ }
+ .prompt-modal-icon {
+ @apply h-6 w-6 text-almost-medium-blue
+ }
+ .prompt-modal-txtarea-arrangement {
+ @apply flex-max-width h-full flex-row items-center justify-center gap-4 overflow-auto bg-accent p-4
+ }
+ .prompt-modal-txtarea-box {
+ @apply h-full w-full overflow-hidden rounded-lg bg-background px-4 py-5 shadow sm:p-6
+ }
+ .prompt-modal-txtarea {
+ @apply form-input h-full w-full rounded-lg border-ring
+ }
+
+ .txtarea-modal-arrangement {
+ @apply flex h-full w-full flex-row items-center justify-center gap-4 bg-input p-4
+ }
+ .txtarea-modal-box {
+ @apply w-full overflow-hidden rounded-lg bg-background px-4 py-5 shadow sm:p-6
+ }
+ .txtarea-modal-input {
+ @apply form-input h-full w-full
+ }
+
+ .api-modal-tabs {
+ @apply w-full h-full overflow-hidden text-center bg-muted rounded-md border
+ }
+ .api-modal-tablist-div {
+ @apply flex items-center justify-between px-2
+ }
+ .api-modal-tabs-content {
+ @apply overflow-hidden w-full h-full px-4 pb-4 -mt-1
+ }
+ .api-modal-accordion-display {
+ @apply flex w-full h-full mt-2
+ }
+ .api-modal-table-arrangement {
+ @apply flex flex-col gap-5 h-fit
+ }
+
+ .icons-parameters-comp{
+ @apply ml-3 h-6 w-6
+ }
+
+ .form-modal-lock-true {
+ @apply bg-input text-primary
+ }
+ .form-modal-no-input {
+ @apply bg-input text-center text-primary dark:bg-gray-700 dark:text-gray-300
+ }
+ .form-modal-lock-false {
+ @apply bg-white text-primary
+ }
+ .code-highlight{
+ @apply block px-3 py-2 w-full h-full text-sm outline-0 border-0 break-all overflow-y-hidden
+ }
+ .form-modal-lockchat {
+ @apply form-input focus:ring-ring focus:border-ring block w-full rounded-md border-border p-4 pr-16 custom-scroll sm:text-sm
+ }
+ .form-modal-send-icon-position {
+ @apply absolute bottom-2 right-4
+ }
+ .form-modal-send-button {
+ @apply rounded-md p-2 px-1 transition-all duration-300
+ }
+ .form-modal-lock-icon {
+ @apply ml-1 mr-1 h-5 w-5 animate-pulse
+ }
+ .form-modal-send-icon {
+ @apply mr-2 h-5 w-5 rotate-[44deg]
+ }
+ .form-modal-play-icon {
+ @apply h-5 w-5 mx-1
+ }
+ .form-modal-chat-position {
+ @apply flex-max-width px-2 py-6 pl-4 pr-9
+ }
+ .form-modal-chatbot-icon {
+ @apply mb-3 ml-3 mr-6 mt-1
+ }
+ .form-modal-chat-image {
+ @apply flex flex-col items-center gap-1
+ }
+ .form-modal-chat-img-box {
+ @apply relative flex h-8 w-8 items-center justify-center overflow-hidden rounded-md p-5 text-2xl
+ }
+ .form-modal-chat-bot-icon {
+ @apply form-modal-chat-img-box bg-chat-bot-icon
+ }
+ .form-modal-chat-user-icon {
+ @apply form-modal-chat-img-box bg-chat-user-icon
+ }
+ .form-modal-chat-icon-img {
+ @apply absolute scale-[60%]
+ }
+ .form-modal-chat-text-position {
+ @apply flex w-full flex-1 text-start
+ }
+ .form-modal-chat-text {
+ @apply relative flex w-full flex-col text-start text-sm font-normal text-muted-foreground
+ }
+ .form-modal-chat-icon-div {
+ @apply absolute -left-6 -top-3 cursor-pointer
+ }
+ .form-modal-chat-icon {
+ @apply h-4 w-4 animate-bounce
+ }
+ .form-modal-chat-thought-border {
+ @apply rounded-md border border-ring/60
+ }
+ .form-modal-chat-thought-size {
+ @apply inline-block h-full w-[95%]
+ }
+ .form-modal-chat-thought {
+ @apply cursor-pointer overflow-scroll bg-background text-start text-primary scrollbar-hide form-modal-chat-thought-border form-modal-chat-thought-size py-2 px-2
+ }
+ .form-modal-markdown-span {
+ @apply mt-1 animate-pulse cursor-default
+ }
+ .form-modal-initial-prompt-btn {
+ @apply mb-2 flex items-center gap-2 rounded-md border border-border bg-background shadow-sm px-4 py-2 text-sm font-semibold
+ }
+ .form-modal-iv-box {
+ @apply mt-2 flex-max-width h-[80vh]
+ }
+ .form-modal-iv-size {
+ @apply mr-6 flex h-full w-2/6 flex-col justify-start overflow-auto scrollbar-hide
+ }
+ .file-component-arrangement {
+ @apply flex items-center py-2
+ }
+ .file-component-variable {
+ @apply -ml-px mr-1 h-4 w-4 text-primary
+ }
+ .file-component-variables-span {
+ @apply font-semibold text-primary
+ }
+ .file-component-variables-title {
+ @apply flex items-center justify-between pt-2
+ }
+ .file-component-variables-div {
+ @apply mr-2.5 flex items-center
+ }
+ .file-component-variables-title-txt {
+ @apply text-sm font-medium text-primary
+ }
+ .file-component-accordion-div {
+ @apply flex items-start gap-3
+ }
+ .file-component-badge-div {
+ @apply flex-max-width items-center justify-between
+ }
+ .file-component-tab-column {
+ @apply flex flex-col gap-2 p-1
+ }
+ .tab-accordion-badge-div {
+ @apply flex flex-1 items-center justify-between py-4 text-sm font-normal text-muted-foreground transition-all
+ }
+ .eraser-column-arrangement {
+ @apply flex-max-width flex-1 flex-col
+ }
+ .eraser-size {
+ @apply relative flex h-full w-full flex-col rounded-md border bg-muted
+ }
+ .eraser-position {
+ @apply absolute right-3 top-3 z-50
+ }
+ .chat-message-div {
+ @apply flex-max-width h-full flex-col items-center overflow-scroll scrollbar-hide
+ }
+ .chat-alert-box {
+ @apply flex-max-width h-full flex-col items-center justify-center text-center align-middle
+ }
+ .langflow-chat-span {
+ @apply text-lg text-foreground
+ }
+ .langflow-chat-desc {
+ @apply w-2/4 rounded-md border border-border bg-muted px-6 py-8
+ }
+ .langflow-chat-desc-span {
+ @apply text-base text-muted-foreground
+ }
+ .langflow-chat-input-div {
+ @apply flex-max-width flex-col items-center justify-between px-8 pb-6
+ }
+ .langflow-chat-input {
+ @apply relative w-full rounded-md shadow-sm
+ }
+
+ .tooltip-fixed-width{
+ @apply max-w-[30vw] max-h-[20vh] overflow-auto
+ }
+
+ .ace-editor-arrangement {
+ @apply flex-max-width h-full flex-col transition-all
+ }
+ .ace-editor {
+ @apply h-full w-full rounded-lg border-[1px] border-border custom-scroll
+ }
+ .ace-editor-save-btn {
+ @apply flex-max-width h-fit justify-end
+ }
+
+ .export-modal-save-api {
+ @apply font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70
+ }
+
+ .chat-message-highlight {
+ @apply px-0.5 rounded-md bg-indigo-100 dark:bg-indigo-900
+ }
+
+
+}
diff --git a/src/frontend/src/index.tsx b/src/frontend/src/index.tsx
index 088db15a8..794fcbf32 100644
--- a/src/frontend/src/index.tsx
+++ b/src/frontend/src/index.tsx
@@ -1,9 +1,8 @@
-import React from "react";
import ReactDOM from "react-dom/client";
-import App from "./App";
-import reportWebVitals from "./reportWebVitals";
import { BrowserRouter } from "react-router-dom";
+import App from "./App";
import ContextWrapper from "./contexts";
+import reportWebVitals from "./reportWebVitals";
import "./index.css";
diff --git a/src/frontend/src/modals/ApiModal/index.tsx b/src/frontend/src/modals/ApiModal/index.tsx
index c3b821ac9..f3f84a9ac 100644
--- a/src/frontend/src/modals/ApiModal/index.tsx
+++ b/src/frontend/src/modals/ApiModal/index.tsx
@@ -1,14 +1,25 @@
-import { CodeBracketSquareIcon } from "@heroicons/react/24/outline";
-import { useContext, useState } from "react";
-import { PopUpContext } from "../../contexts/popUpContext";
+import "ace-builds/src-noconflict/ext-language_tools";
import "ace-builds/src-noconflict/mode-python";
import "ace-builds/src-noconflict/theme-github";
import "ace-builds/src-noconflict/theme-twilight";
-import "ace-builds/src-noconflict/ext-language_tools";
+import { useContext, useEffect, useRef, useState } from "react";
+import { PopUpContext } from "../../contexts/popUpContext";
// import "ace-builds/webpack-resolver";
-import { darkContext } from "../../contexts/darkContext";
+import { Check, Clipboard, Code2 } from "lucide-react";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
+import AccordionComponent from "../../components/AccordionComponent";
+import ShadTooltip from "../../components/ShadTooltipComponent";
+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 ToggleShadComponent from "../../components/toggleShadComponent";
import {
Dialog,
DialogContent,
@@ -17,24 +28,42 @@ import {
DialogTitle,
DialogTrigger,
} from "../../components/ui/dialog";
-import { FlowType } from "../../types/flow/index";
-import { getCurlCode, getPythonApiCode, getPythonCode } from "../../constants";
-import { EXPORT_CODE_DIALOG } from "../../constants";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "../../components/ui/table";
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "../../components/ui/tabs";
-import { Check, Clipboard } from "lucide-react";
+import {
+ EXPORT_CODE_DIALOG,
+ getCurlCode,
+ getPythonApiCode,
+ getPythonCode,
+} from "../../constants";
+import { darkContext } from "../../contexts/darkContext";
+import { TabsContext } from "../../contexts/tabsContext";
+import { FlowType } from "../../types/flow/index";
+import { buildTweaks, classNames } from "../../utils";
export default function ApiModal({ flow }: { flow: FlowType }) {
const [open, setOpen] = useState(true);
const { dark } = useContext(darkContext);
- const { closePopUp } = useContext(PopUpContext);
+ const { closePopUp, closeEdit, setCloseEdit } = useContext(PopUpContext);
const [activeTab, setActiveTab] = useState("0");
const [isCopied, setIsCopied] = useState(false);
-
+ const [enabled, setEnabled] = useState(null);
+ const [openAccordion, setOpenAccordion] = useState([]);
+ const tweak = useRef([]);
+ const tweaksList = useRef([]);
+ const { setTweak, getTweak, tabsState } = useContext(TabsContext);
const copyToClipboard = () => {
if (!navigator.clipboard || !navigator.clipboard.writeText) {
return;
@@ -48,18 +77,10 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
}, 2000);
});
};
- function setModalOpen(x: boolean) {
- setOpen(x);
- if (x === false) {
- closePopUp();
- }
- }
-
- const pythonApiCode = getPythonApiCode(flow);
-
- const curl_code = getCurlCode(flow);
- const pythonCode = getPythonCode(flow);
-
+ const pythonApiCode = getPythonApiCode(flow, tweak.current, tabsState);
+ const curl_code = getCurlCode(flow, tweak.current, tabsState);
+ const pythonCode = getPythonCode(flow, tweak.current, tabsState);
+ const tweaksCode = buildTweaks(flow);
const tabs = [
{
name: "cURL",
@@ -81,15 +102,177 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
code: pythonCode,
},
];
+
+ useEffect(() => {
+ if (closeEdit !== "") {
+ tweak.current = getTweak;
+ if (tweak.current.length > 0) {
+ setActiveTab("3");
+ openAccordions();
+ } else {
+ startTweaks();
+ }
+ } else {
+ if (tweak?.current) {
+ startTweaks();
+ }
+ }
+ }, [closeEdit]);
+
+ useEffect(() => {
+ filterNodes();
+ }, []);
+
+ if (Object.keys(tweaksCode).length > 0) {
+ tabs.push({
+ name: "Tweaks",
+ mode: "python",
+ image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
+ code: pythonCode,
+ });
+ }
+
+ function setModalOpen(x: boolean) {
+ setOpen(x);
+ if (x === false) {
+ setCloseEdit("");
+ setTweak([]);
+ closePopUp();
+ }
+ }
+
+ function startTweaks() {
+ const t = buildTweaks(flow);
+ tweak?.current?.push(t);
+ }
+
+ function filterNodes() {
+ let arrNodesWithValues = [];
+
+ flow["data"]["nodes"].forEach((t) => {
+ Object.keys(t["data"]["node"]["template"])
+ .filter(
+ (n) =>
+ n.charAt(0) !== "_" &&
+ t.data.node.template[n].show &&
+ (t.data.node.template[n].type === "str" ||
+ t.data.node.template[n].type === "bool" ||
+ t.data.node.template[n].type === "float" ||
+ t.data.node.template[n].type === "code" ||
+ t.data.node.template[n].type === "prompt" ||
+ t.data.node.template[n].type === "file" ||
+ t.data.node.template[n].type === "int")
+ )
+ .map((n, i) => {
+ arrNodesWithValues.push(t["id"]);
+ });
+ });
+
+ tweaksList.current = arrNodesWithValues.filter((value, index, self) => {
+ return self.indexOf(value) === index;
+ });
+ }
+
+ function buildTweakObject(tw, changes, template) {
+ if (template.type === "float") {
+ changes = parseFloat(changes);
+ }
+ if (template.type === "int") {
+ changes = parseInt(changes);
+ }
+ if (template.list === true && Array.isArray(changes)) {
+ changes = changes?.filter((x) => x !== "");
+ }
+
+ const existingTweak = tweak.current.find((element) =>
+ element.hasOwnProperty(tw)
+ );
+
+ if (existingTweak) {
+ existingTweak[tw][template["name"]] = changes;
+
+ if (existingTweak[tw][template["name"]] == template.value) {
+ tweak.current.forEach((element) => {
+ if (element[tw] && Object.keys(element[tw])?.length === 0) {
+ tweak.current = tweak.current.filter((obj) => {
+ const prop = obj[Object.keys(obj)[0]].prop;
+ return prop !== undefined && prop !== null && prop !== "";
+ });
+ }
+ });
+ }
+ } else {
+ const newTweak = {
+ [tw]: {
+ [template["name"]]: changes,
+ },
+ };
+ tweak.current.push(newTweak);
+ }
+
+ const pythonApiCode = getPythonApiCode(flow, tweak.current);
+ const curl_code = getCurlCode(flow, tweak.current);
+ const pythonCode = getPythonCode(flow, tweak.current);
+
+ tabs[0].code = curl_code;
+ tabs[1].code = pythonApiCode;
+ tabs[2].code = pythonCode;
+
+ setTweak(tweak.current);
+ }
+
+ function buildContent(value) {
+ const htmlContent = (
+
+ {value != null && value != "" ? value : "None"}
+
+ );
+ return htmlContent;
+ }
+
+ function getValue(value, node, template) {
+ let returnValue = value ?? "";
+
+ if (getTweak.length > 0) {
+ for (const obj of getTweak) {
+ Object.keys(obj).forEach((key) => {
+ const value = obj[key];
+ if (key == node["id"]) {
+ Object.keys(value).forEach((key) => {
+ if (key == template["name"]) {
+ returnValue = value[key];
+ }
+ });
+ }
+ });
+ }
+ } else {
+ return value ?? "";
+ }
+ return returnValue;
+ }
+
+ function openAccordions() {
+ let accordionsToOpen = [];
+ tweak.current.forEach((el) => {
+ Object.keys(el).forEach((key) => {
+ if (Object.keys(el[key]).length > 0) {
+ accordionsToOpen.push(key);
+ setOpenAccordion(accordionsToOpen);
+ }
+ });
+ });
+ }
+
return (
-
+
Code
-
@@ -97,39 +280,478 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
setActiveTab(value)}
+ value={activeTab}
+ className="api-modal-tabs"
+ onValueChange={(value) => {
+ setActiveTab(value);
+ if (value === "3") {
+ openAccordions();
+ }
+ }}
>
-
+
{tabs.map((tab, index) => (
- {tab.name}
+
+ {tab.name}
+
))}
-
-
- {isCopied ? : }
- {isCopied ? "Copied!" : "Copy code"}
-
-
+ {Number(activeTab) < 3 && (
+
+
+ {isCopied ? : }
+ {isCopied ? "Copied!" : "Copy code"}
+
+
+ )}
{tabs.map((tab, index) => (
-
- {tab.code}
-
+ {index < 3 ? (
+
+ {tab.code}
+
+ ) : index === 3 ? (
+ <>
+
+
+ {flow["data"]["nodes"].map((t: any, index) => (
+
+ {tweaksList.current.includes(t["data"]["id"]) && (
+
+
+
+
+
+
+ PARAM
+
+
+ VALUE
+
+
+
+
+ {Object.keys(t["data"]["node"]["template"])
+ .filter(
+ (n) =>
+ n.charAt(0) !== "_" &&
+ t.data.node.template[n].show &&
+ (t.data.node.template[n].type ===
+ "str" ||
+ t.data.node.template[n].type ===
+ "bool" ||
+ t.data.node.template[n].type ===
+ "float" ||
+ t.data.node.template[n].type ===
+ "code" ||
+ t.data.node.template[n].type ===
+ "prompt" ||
+ t.data.node.template[n].type ===
+ "file" ||
+ t.data.node.template[n].type ===
+ "int")
+ )
+ .map((n, i) => {
+ //console.log(t.data.node.template[n]);
+
+ return (
+
+
+ {n}
+
+
+
+ {t.data.node.template[n]
+ .type === "str" &&
+ !t.data.node.template[n]
+ .options ? (
+
+ {t.data.node.template[n]
+ .list ? (
+
{}}
+ onAddInput={(k) => {
+ buildTweakObject(
+ t["data"]["id"],
+ k,
+ t.data.node
+ .template[n]
+ );
+ }}
+ />
+ ) : t.data.node.template[n]
+ .multiline ? (
+
+
+ {
+ buildTweakObject(
+ t["data"]["id"],
+ k,
+ t.data.node
+ .template[n]
+ );
+ }}
+ />
+
+
+ ) : (
+ {
+ buildTweakObject(
+ t["data"]["id"],
+ k,
+ t.data.node
+ .template[n]
+ );
+ }}
+ />
+ )}
+
+ ) : t.data.node.template[n]
+ .type === "bool" ? (
+
+ {" "}
+ {
+ t.data.node.template[
+ n
+ ].value = e;
+ setEnabled(e);
+ buildTweakObject(
+ t["data"]["id"],
+ e,
+ t.data.node.template[
+ n
+ ]
+ );
+ }}
+ size="small"
+ disabled={false}
+ />
+
+ ) : t.data.node.template[n]
+ .type === "file" ? (
+
+
+ {}}
+ fileTypes={
+ t.data.node.template[
+ n
+ ].fileTypes
+ }
+ suffixes={
+ t.data.node.template[
+ n
+ ].suffixes
+ }
+ onFileChange={(
+ k: any
+ ) => {}}
+ >
+
+
+ ) : t.data.node.template[n]
+ .type === "float" ? (
+
+ {
+ buildTweakObject(
+ t["data"]["id"],
+ k,
+ t.data.node.template[
+ n
+ ]
+ );
+ }}
+ />
+
+ ) : t.data.node.template[n]
+ .type === "str" &&
+ t.data.node.template[n]
+ .options ? (
+
+ {
+ buildTweakObject(
+ t["data"]["id"],
+ k,
+ t.data.node.template[
+ n
+ ]
+ );
+ }}
+ value={getValue(
+ t.data.node.template[n]
+ .value,
+ t.data,
+ t.data.node.template[n]
+ )}
+ >
+
+ ) : t.data.node.template[n]
+ .type === "int" ? (
+
+ {
+ buildTweakObject(
+ t["data"]["id"],
+ k,
+ t.data.node.template[
+ n
+ ]
+ );
+ }}
+ />
+
+ ) : t.data.node.template[n]
+ .type === "prompt" ? (
+
+
+
{
+ buildTweakObject(
+ t["data"]["id"],
+ k,
+ t.data.node
+ .template[n]
+ );
+ }}
+ />
+
+
+ ) : t.data.node.template[n]
+ .type === "code" ? (
+
+
+ {
+ buildTweakObject(
+ t["data"]["id"],
+ k,
+ t.data.node
+ .template[n]
+ );
+ }}
+ />
+
+
+ ) : t.data.node.template[n]
+ .type === "Any" ? (
+ "-"
+ ) : (
+
+ )}
+
+
+
+ );
+ })}
+
+
+
+
+ )}
+
+ {tweaksList.current.length === 0 && (
+ <>
+
+ No tweaks are available for this flow.
+
+ >
+ )}
+
+ ))}
+
+ {/*
+
+
+
+
+
+ TWEAK
+
+
+ VALUE
+
+
+
+
+ {invoices.map((invoice) => (
+
+
+ {invoice.paymentStatus}
+
+
+ {invoice.paymentMethod}
+
+
+ ))}
+
+
+
*/}
+
+
+ >
+ ) : null}
))}
diff --git a/src/frontend/src/modals/EditNodeModal/index.tsx b/src/frontend/src/modals/EditNodeModal/index.tsx
index 1a270f46e..84d85014d 100644
--- a/src/frontend/src/modals/EditNodeModal/index.tsx
+++ b/src/frontend/src/modals/EditNodeModal/index.tsx
@@ -1,27 +1,17 @@
-import { useContext, useEffect, useRef, useState } from "react";
-import { PopUpContext } from "../../contexts/popUpContext";
-import { NodeDataType } from "../../types/flow";
-import { classNames, limitScrollFieldsModal } from "../../utils";
-import { typesContext } from "../../contexts/typesContext";
-import {
- Table,
- TableBody,
- TableCell,
- TableHead,
- TableHeader,
- TableRow,
-} from "../../components/ui/table";
-import ToggleShadComponent from "../../components/toggleShadComponent";
-import { VariableIcon } from "@heroicons/react/24/outline";
-import InputListComponent from "../../components/inputListComponent";
-import TextAreaComponent from "../../components/textAreaComponent";
-import InputComponent from "../../components/inputComponent";
-import FloatComponent from "../../components/floatComponent";
-import Dropdown from "../../components/dropdownComponent";
-import IntComponent from "../../components/intComponent";
-import InputFileComponent from "../../components/inputFileComponent";
-import PromptAreaComponent from "../../components/promptComponent";
+import { Variable } from "lucide-react";
+import { useContext, useRef, 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 ToggleShadComponent from "../../components/toggleShadComponent";
+import { Badge } from "../../components/ui/badge";
+import { Button } from "../../components/ui/button";
import {
Dialog,
DialogContent,
@@ -31,8 +21,19 @@ import {
DialogTitle,
DialogTrigger,
} from "../../components/ui/dialog";
-import { Button } from "../../components/ui/button";
-import { Badge } from "../../components/ui/badge";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "../../components/ui/table";
+import { PopUpContext } from "../../contexts/popUpContext";
+import { TabsContext } from "../../contexts/tabsContext";
+import { typesContext } from "../../contexts/typesContext";
+import { NodeDataType } from "../../types/flow";
+import { classNames, limitScrollFieldsModal } from "../../utils";
export default function EditNodeModal({ data }: { data: NodeDataType }) {
const [open, setOpen] = useState(true);
@@ -54,7 +55,12 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
const { closePopUp } = useContext(PopUpContext);
const { types } = useContext(typesContext);
const ref = useRef();
- const [enabled, setEnabled] = useState(null);
+ const { setTabsState, tabId } = useContext(TabsContext);
+ const { reactFlowInstance } = useContext(typesContext);
+
+ let disabled =
+ reactFlowInstance?.getEdges().some((e) => e.targetHandle === data.id) ??
+ false;
if (nodeLength == 0) {
closePopUp();
}
@@ -66,59 +72,69 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
}
}
- useEffect(() => {}, [closePopUp, data.node.template]);
-
- function changeAdvanced(node): void {
- Object.keys(data.node.template).filter((n, i) => {
+ function changeAdvanced(node) {
+ Object.keys(data.node.template).map((n, i) => {
if (n === node.name) {
data.node.template[n].advanced = !data.node.template[n].advanced;
}
- return true;
+ return n;
});
setNodeValue(!nodeValue);
}
+ const handleOnNewValue = (newValue: any, name) => {
+ data.node.template[name].value = newValue;
+ // Set state to pending
+ setTabsState((prev) => {
+ return {
+ ...prev,
+ [tabId]: {
+ ...prev[tabId],
+ isPending: true,
+ },
+ };
+ });
+ };
+
return (
-
-
+
+
{data.type}
ID: {data.id}
-
- {data.node?.description}
-
-
-
-
-
- Parameters
-
+
+
+ {data.node?.description}
+
+
+ Parameters
+
-
+
limitScrollFieldsModal
? "overflow-scroll overflow-x-hidden custom-scroll"
: "overflow-hidden"
)}
>
{nodeLength > 0 && (
-
+
-
-
+
+
PARAM
-
+
VALUE
- SHOW
+ SHOW
@@ -136,20 +152,20 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
data.node.template[t].type === "int")
)
.map((n, i) => (
-
-
+
+
{data.node.template[n].name
? data.node.template[n].name
: data.node.template[n].display_name}
-
+
{data.node.template[n].type === "str" &&
!data.node.template[n].options ? (
{data.node.template[n].list ? (
{
- data.node.template[n].value = t;
+ handleOnNewValue(t, n);
}}
/>
) : data.node.template[n].multiline ? (
{
- data.node.template[n].value = t;
+ handleOnNewValue(t, n);
}}
/>
) : (
{
- data.node.template[n].value = t;
+ handleOnNewValue(t, n);
}}
/>
)}
@@ -187,19 +203,18 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
{" "}
{
- data.node.template[n].value = e;
- setEnabled(e);
+ setEnabled={(t) => {
+ handleOnNewValue(t, n);
}}
size="small"
- disabled={false}
/>
) : data.node.template[n].type === "float" ? (
{
@@ -214,9 +229,7 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
numberOfOptions={nodeLength}
editNode={true}
options={data.node.template[n].options}
- onSelect={(newValue) =>
- (data.node.template[n].value = newValue)
- }
+ onSelect={(t) => handleOnNewValue(t, n)}
value={
data.node.template[n].value ??
"Choose an option"
@@ -226,11 +239,11 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
) : data.node.template[n].type === "int" ? (
{
- data.node.template[n].value = t;
+ handleOnNewValue(t, n);
}}
/>
@@ -238,37 +251,42 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
{
- data.node.template[n].value = t;
+ handleOnNewValue(t, n);
}}
fileTypes={data.node.template[n].fileTypes}
suffixes={data.node.template[n].suffixes}
onFileChange={(t: string) => {
- data.node.template[n].content = t;
+ handleOnNewValue(t, n);
}}
>
) : data.node.template[n].type === "prompt" ? (
{
+ data.node = nodeClass;
+ }}
value={data.node.template[n].value ?? ""}
onChange={(t: string) => {
- data.node.template[n].value = t;
+ handleOnNewValue(t, n);
}}
/>
) : data.node.template[n].type === "code" ? (
{
- data.node.template[n].value = t;
+ handleOnNewValue(t, n);
}}
/>
@@ -285,7 +303,7 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
setEnabled={(e) =>
changeAdvanced(data.node.template[n])
}
- disabled={false}
+ disabled={disabled}
size="small"
/>
diff --git a/src/frontend/src/modals/NodeModal/components/ModalField/index.tsx b/src/frontend/src/modals/NodeModal/components/ModalField/index.tsx
index f5f0fb668..ad82d0c0d 100644
--- a/src/frontend/src/modals/NodeModal/components/ModalField/index.tsx
+++ b/src/frontend/src/modals/NodeModal/components/ModalField/index.tsx
@@ -1,15 +1,14 @@
-import { useContext, useState } from "react";
-import { TabsContext } from "../../../../contexts/tabsContext";
-import InputListComponent from "../../../../components/inputListComponent";
-import Dropdown from "../../../../components/dropdownComponent";
-import TextAreaComponent from "../../../../components/textAreaComponent";
-import InputComponent from "../../../../components/inputComponent";
-import ToggleComponent from "../../../../components/toggleComponent";
-import FloatComponent from "../../../../components/floatComponent";
-import IntComponent from "../../../../components/intComponent";
-import InputFileComponent from "../../../../components/inputFileComponent";
-import PromptAreaComponent from "../../../../components/promptComponent";
+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";
export default function ModalField({
@@ -36,7 +35,7 @@ export default function ModalField({
return (
@@ -52,8 +51,8 @@ export default function ModalField({
>
{display && (
- {title}
- {required ? " *" : ""}
+ {title}
+ {required ? " *" : ""}
)}
@@ -101,6 +100,7 @@ export default function ModalField({
data.node.template[name].value = t;
setEnabled(t);
}}
+ size="small"
/>
) : type === "float" ? (
@@ -149,6 +149,7 @@ export default function ModalField({
) : type === "prompt" ? (
{
diff --git a/src/frontend/src/modals/NodeModal/index.tsx b/src/frontend/src/modals/NodeModal/index.tsx
index 6ceba62bf..c61af4ff4 100644
--- a/src/frontend/src/modals/NodeModal/index.tsx
+++ b/src/frontend/src/modals/NodeModal/index.tsx
@@ -1,17 +1,16 @@
import { Dialog, Transition } from "@headlessui/react";
-import { XMarkIcon } from "@heroicons/react/24/outline";
+import { X } from "lucide-react";
import { Fragment, useContext, useRef, useState } from "react";
import { PopUpContext } from "../../contexts/popUpContext";
+import { typesContext } from "../../contexts/typesContext";
import { NodeDataType } from "../../types/flow";
import {
classNames,
limitScrollFieldsModal,
nodeColors,
- nodeIcons,
- toNormalCase,
+ nodeIconsLucide,
toTitleCase,
} from "../../utils";
-import { typesContext } from "../../contexts/typesContext";
import ModalField from "./components/ModalField";
export default function NodeModal({ data }: { data: NodeDataType }) {
@@ -27,7 +26,8 @@ export default function NodeModal({ data }: { data: NodeDataType }) {
}, 300);
}
}
- const Icon = nodeIcons[types[data.type]];
+ // any to avoid type conflict
+ const Icon: any = nodeIconsLucide[types[data.type]];
return (
-
+
-
-
+
+
-
-
+
+
{
setModalOpen(false);
}}
>
Close
-
+
-
-
+
+
-
-
-
+
+
t.charAt(0) !== "_" &&
@@ -105,7 +103,7 @@ export default function NodeModal({ data }: { data: NodeDataType }) {
: "overflow-hidden"
)}
>
-
+
{Object.keys(data.node.template)
.filter(
(t) =>
@@ -143,10 +141,10 @@ export default function NodeModal({ data }: { data: NodeDataType }) {
-
+
{
setModalOpen(false);
}}
diff --git a/src/frontend/src/modals/baseModal/index.tsx b/src/frontend/src/modals/baseModal/index.tsx
new file mode 100644
index 000000000..4b135383c
--- /dev/null
+++ b/src/frontend/src/modals/baseModal/index.tsx
@@ -0,0 +1,69 @@
+import { ReactNode, useContext } from "react";
+
+import React from "react";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "../../components/ui/dialog";
+import { PopUpContext } from "../../contexts/popUpContext";
+
+type ContentProps = { children: ReactNode };
+type HeaderProps = { children: ReactNode; description: string };
+
+const Content: React.FC = ({ children }) => {
+ return {children}
;
+};
+
+const Header: React.FC<{ children: ReactNode; description: string }> = ({
+ children,
+ description,
+}) => {
+ return (
+
+ {children}
+ {description}
+
+ );
+};
+interface BaseModalProps {
+ children: [React.ReactElement, React.ReactElement];
+ open: boolean;
+ setOpen: (open: boolean) => void;
+}
+function BaseModal({ open, setOpen, children }: BaseModalProps) {
+ const { closePopUp, setCloseEdit } = useContext(PopUpContext);
+
+ function setModalOpen(x: boolean) {
+ setOpen(x);
+ if (x === false) {
+ setTimeout(() => {
+ setCloseEdit("editcode");
+ closePopUp();
+ }, 300);
+ }
+ }
+ const headerChild = React.Children.toArray(children).find(
+ (child) => (child as React.ReactElement).type === Header
+ );
+ const ContentChild = React.Children.toArray(children).find(
+ (child) => (child as React.ReactElement).type === Content
+ );
+ //UPDATE COLORS AND STYLE CLASSSES
+ return (
+
+
+
+ {headerChild}
+ {ContentChild}
+
+
+ );
+}
+
+BaseModal.Content = Content;
+BaseModal.Header = Header;
+export default BaseModal;
diff --git a/src/frontend/src/modals/chatModal/chatMessage/index.tsx b/src/frontend/src/modals/chatModal/chatMessage/index.tsx
deleted file mode 100644
index 318edc376..000000000
--- a/src/frontend/src/modals/chatModal/chatMessage/index.tsx
+++ /dev/null
@@ -1,167 +0,0 @@
-import { ChatBubbleOvalLeftEllipsisIcon } from "@heroicons/react/24/outline";
-import { useEffect, useRef, useState } from "react";
-import { ChatMessageType } from "../../../types/chat";
-import { classNames } from "../../../utils";
-import AiIcon from "../../../assets/Gooey Ring-5s-271px.svg";
-import AiIconStill from "../../../assets/froze-flow.png";
-import { UserIcon } from "@heroicons/react/24/solid";
-import FileCard from "../fileComponent";
-import ReactMarkdown from "react-markdown";
-import rehypeMathjax from "rehype-mathjax";
-import remarkGfm from "remark-gfm";
-import remarkMath from "remark-math";
-import { CodeBlock } from "./codeBlock";
-import Convert from "ansi-to-html";
-
-export default function ChatMessage({
- chat,
- lockChat,
- lastMessage,
-}: {
- chat: ChatMessageType;
- lockChat: boolean;
- lastMessage: boolean;
-}) {
- const convert = new Convert({ newline: true });
- const [message, setMessage] = useState("");
- const imgRef = useRef(null);
- useEffect(() => {
- setMessage(chat.message);
- }, [chat.message]);
- const [hidden, setHidden] = useState(true);
- return (
-
-
- {!chat.isSend && (
-
-
-
-
- )}
- {chat.isSend && (
-
- )}
-
- {!chat.isSend ? (
-
-
- {hidden && chat.thought && chat.thought !== "" && (
-
setHidden((prev) => !prev)}
- className="absolute -top-1 -left-2 cursor-pointer"
- >
-
-
- )}
- {chat.thought && chat.thought !== "" && !hidden && (
-
setHidden((prev) => !prev)}
- className=" text-start inline-block rounded-md text-gray-600 dark:text-gray-200 h-full border border-gray-300 dark:border-gray-500
- bg-muted dark:bg-gray-800 w-[95%] pb-3 pt-3 px-2 ml-3 cursor-pointer scrollbar-hide overflow-scroll"
- dangerouslySetInnerHTML={{
- __html: convert.toHtml(chat.thought),
- }}
- >
- )}
- {chat.thought && chat.thought !== "" && !hidden &&
}
-
-
-
-
- â
-
- );
- }
-
- children[0] = (children[0] as string).replace(
- "`â`",
- "â"
- );
- }
-
- const match = /language-(\w+)/.exec(className || "");
-
- return !inline ? (
-
- ) : (
-
- {children}
-
- );
- },
- }}
- >
- {message}
-
-
- {chat.files && (
-
- {chat.files.map((file, index) => {
- return (
-
-
-
- );
- })}
-
- )}
-
-
-
-
- ) : (
-
- )}
-
- );
-}
diff --git a/src/frontend/src/modals/chatModal/index.tsx b/src/frontend/src/modals/chatModal/index.tsx
deleted file mode 100644
index bf87e4100..000000000
--- a/src/frontend/src/modals/chatModal/index.tsx
+++ /dev/null
@@ -1,417 +0,0 @@
-import { Dialog, Transition } from "@headlessui/react";
-import { ChatBubbleOvalLeftEllipsisIcon } from "@heroicons/react/24/outline";
-import { Fragment, useContext, useEffect, useRef, useState } from "react";
-import { FlowType } from "../../types/flow";
-import { alertContext } from "../../contexts/alertContext";
-import { validateNodes } from "../../utils";
-import { typesContext } from "../../contexts/typesContext";
-import ChatMessage from "./chatMessage";
-import { Eraser } from "lucide-react";
-import { X } from "lucide-react";
-import { sendAllProps } from "../../types/api";
-import { ChatMessageType } from "../../types/chat";
-import ChatInput from "./chatInput";
-
-import _ from "lodash";
-
-export default function ChatModal({
- flow,
- open,
- setOpen,
-}: {
- open: boolean;
- setOpen: Function;
- flow: FlowType;
-}) {
- const [chatValue, setChatValue] = useState("");
- const [chatHistory, setChatHistory] = useState([]);
- const { reactFlowInstance } = useContext(typesContext);
- const { setErrorData, setNoticeData } = useContext(alertContext);
- const ws = useRef(null);
- const [lockChat, setLockChat] = useState(false);
- const isOpen = useRef(open);
- const messagesRef = useRef(null);
- const id = useRef(flow.id);
-
- useEffect(() => {
- if (messagesRef.current) {
- messagesRef.current.scrollTop = messagesRef.current.scrollHeight;
- }
- }, [chatHistory]);
-
- useEffect(() => {
- isOpen.current = open;
- }, [open]);
- useEffect(() => {
- id.current = flow.id;
- }, [flow.id]);
-
- var isStream = false;
-
- const addChatHistory = (
- message: string,
- isSend: boolean,
- thought?: string,
- files?: Array
- ) => {
- setChatHistory((old) => {
- let newChat = _.cloneDeep(old);
- if (files) {
- newChat.push({ message, isSend, files, thought });
- } else if (thought) {
- newChat.push({ message, isSend, thought });
- } else {
- newChat.push({ message, isSend });
- }
- return newChat;
- });
- };
-
- //add proper type signature for function
-
- function updateLastMessage({
- str,
- thought,
- end = false,
- files,
- }: {
- str?: string;
- thought?: string;
- // end param default is false
- end?: boolean;
- files?: Array;
- }) {
- setChatHistory((old) => {
- let newChat = [...old];
- if (str) {
- if (end) {
- newChat[newChat.length - 1].message = str;
- } else {
- newChat[newChat.length - 1].message =
- newChat[newChat.length - 1].message + str;
- }
- }
- if (thought) {
- newChat[newChat.length - 1].thought = thought;
- }
- if (files) {
- newChat[newChat.length - 1].files = files;
- }
- return newChat;
- });
- }
-
- function handleOnClose(event: CloseEvent) {
- if (isOpen.current) {
- setErrorData({ title: event.reason });
- setTimeout(() => {
- connectWS();
- setLockChat(false);
- }, 1000);
- }
- }
-
- function getWebSocketUrl(chatId, isDevelopment = false) {
- const isSecureProtocol = window.location.protocol === "https:";
- const webSocketProtocol = isSecureProtocol ? "wss" : "ws";
- const host = isDevelopment ? "localhost:7860" : window.location.host;
- const chatEndpoint = `/api/v1/chat/${chatId}`;
-
- return `${
- isDevelopment ? "ws" : webSocketProtocol
- }://${host}${chatEndpoint}`;
- }
-
- function handleWsMessage(data: any) {
- if (Array.isArray(data)) {
- //set chat history
- setChatHistory((_) => {
- let newChatHistory: ChatMessageType[] = [];
- data.forEach(
- (chatItem: {
- intermediate_steps?: "string";
- is_bot: boolean;
- message: string;
- type: string;
- files?: Array;
- }) => {
- if (chatItem.message) {
- newChatHistory.push(
- chatItem.files
- ? {
- isSend: !chatItem.is_bot,
- message: chatItem.message,
- thought: chatItem.intermediate_steps,
- files: chatItem.files,
- }
- : {
- isSend: !chatItem.is_bot,
- message: chatItem.message,
- thought: chatItem.intermediate_steps,
- }
- );
- }
- }
- );
- return newChatHistory;
- });
- }
- if (data.type === "start") {
- addChatHistory("", false);
- isStream = true;
- }
- if (data.type === "end") {
- if (data.message) {
- updateLastMessage({ str: data.message, end: true });
- }
- if (data.intermediate_steps) {
- updateLastMessage({
- str: data.message,
- thought: data.intermediate_steps,
- end: true,
- });
- }
- if (data.files) {
- updateLastMessage({
- end: true,
- files: data.files,
- });
- }
-
- setLockChat(false);
- isStream = false;
- }
- if (data.type === "stream" && isStream) {
- updateLastMessage({ str: data.message });
- }
- }
-
- function connectWS() {
- try {
- const urlWs = getWebSocketUrl(
- id.current,
- process.env.NODE_ENV === "development"
- );
- const newWs = new WebSocket(urlWs);
- newWs.onopen = () => {
- console.log("WebSocket connection established!");
- };
- newWs.onmessage = (event) => {
- const data = JSON.parse(event.data);
- console.log("Received data:", data);
- handleWsMessage(data);
- //get chat history
- };
- newWs.onclose = (event) => {
- handleOnClose(event);
- };
- newWs.onerror = (ev) => {
- console.log(ev, "error");
- if (flow.id === "") {
- connectWS();
- } else {
- setErrorData({
- title: "There was an error on web connection, please: ",
- list: [
- "Refresh the page",
- "Use a new flow tab",
- "Check if the backend is up",
- ],
- });
- }
- };
- ws.current = newWs;
- } catch (error) {
- if (flow.id === "") {
- connectWS();
- }
- console.log(error);
- }
- }
-
- useEffect(() => {
- connectWS();
- return () => {
- console.log("unmount");
- console.log(ws);
- if (ws.current) {
- ws.current.close();
- }
- };
- }, []);
-
- useEffect(() => {
- if (
- ws.current &&
- (ws.current.readyState === ws.current.CLOSED ||
- ws.current.readyState === ws.current.CLOSING)
- ) {
- connectWS();
- setLockChat(false);
- }
- }, [lockChat]);
-
- async function sendAll(data: sendAllProps) {
- try {
- if (ws) {
- ws.current.send(JSON.stringify(data));
- }
- } catch (error) {
- setErrorData({
- title: "There was an error sending the message",
- list: [error.message],
- });
- setChatValue(data.message);
- connectWS();
- }
- }
-
- useEffect(() => {
- if (ref.current) ref.current.scrollIntoView({ behavior: "smooth" });
- }, [chatHistory]);
-
- const ref = useRef(null);
-
- useEffect(() => {
- if (open && ref.current) {
- ref.current.focus();
- }
- }, [open]);
-
- function sendMessage() {
- if (chatValue !== "") {
- let nodeValidationErrors = validateNodes(reactFlowInstance);
- if (nodeValidationErrors.length === 0) {
- setLockChat(true);
- let message = chatValue;
- setChatValue("");
- addChatHistory(message, true);
- sendAll({
- ...reactFlowInstance.toObject(),
- message,
- chatHistory,
- name: flow.name,
- description: flow.description,
- });
- } else {
- setErrorData({
- title: "Oops! Looks like you missed some required information:",
- list: nodeValidationErrors,
- });
- }
- } else {
- setErrorData({
- title: "Error sending message",
- list: ["The message cannot be empty."],
- });
- }
- }
- function clearChat() {
- setChatHistory([]);
- ws.current.send(JSON.stringify({ clear_history: true }));
- if (lockChat) setLockChat(false);
- }
-
- function setModalOpen(x: boolean) {
- setOpen(x);
- }
- return (
-
-
-
-
-
-
-
-
-
-
-
- clearChat()}
- className="absolute top-2 right-10 hover:text-red-500 text-gray-600 dark:text-gray-300 dark:hover:text-red-500 z-30"
- >
-
-
- setModalOpen(false)}
- className="absolute top-1.5 right-2 hover:text-red-500 text-gray-600 dark:text-gray-300 dark:hover:text-red-500 z-30"
- >
-
-
-
-
- {chatHistory.length > 0 ? (
- chatHistory.map((c, i) => (
-
- ))
- ) : (
-
-
- đ{" "}
-
- LangFlow Chat
-
-
-
-
-
- Start a conversation and click the agentâs thoughts{" "}
-
-
- {" "}
- to inspect the chaining process.
-
-
-
- )}
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/src/frontend/src/modals/codeAreaModal/index.tsx b/src/frontend/src/modals/codeAreaModal/index.tsx
index 9f8815f4b..cc0495977 100644
--- a/src/frontend/src/modals/codeAreaModal/index.tsx
+++ b/src/frontend/src/modals/codeAreaModal/index.tsx
@@ -1,130 +1,121 @@
-import { XMarkIcon, CommandLineIcon } from "@heroicons/react/24/outline";
-import { Fragment, useContext, useRef, useState } from "react";
-import { PopUpContext } from "../../contexts/popUpContext";
-import AceEditor from "react-ace";
+import { DialogTitle } from "@radix-ui/react-dialog";
+import "ace-builds/src-noconflict/ace";
+import "ace-builds/src-noconflict/ext-language_tools";
import "ace-builds/src-noconflict/mode-python";
import "ace-builds/src-noconflict/theme-github";
import "ace-builds/src-noconflict/theme-twilight";
-import "ace-builds/src-noconflict/ext-language_tools";
-// import "ace-builds/webpack-resolver";
-import { darkContext } from "../../contexts/darkContext";
-import { postValidateCode } from "../../controllers/API";
-import { alertContext } from "../../contexts/alertContext";
-import { TabsContext } from "../../contexts/tabsContext";
-import {
- Dialog,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
- DialogTrigger,
-} from "../../components/ui/dialog";
+import { TerminalSquare } from "lucide-react";
+import { useContext, useState } from "react";
+import AceEditor from "react-ace";
import { Button } from "../../components/ui/button";
import { CODE_PROMPT_DIALOG_SUBTITLE } from "../../constants";
+import { alertContext } from "../../contexts/alertContext";
+import { darkContext } from "../../contexts/darkContext";
+import { PopUpContext } from "../../contexts/popUpContext";
+import { postValidateCode } from "../../controllers/API";
+import { APIClassType } from "../../types/api";
+import BaseModal from "../baseModal";
export default function CodeAreaModal({
value,
setValue,
+ nodeClass,
+ setNodeClass,
}: {
setValue: (value: string) => void;
value: string;
+ nodeClass: APIClassType;
+ setNodeClass: (Class: APIClassType) => void;
}) {
- const [open, setOpen] = useState(true);
const [code, setCode] = useState(value);
const { dark } = useContext(darkContext);
+ const { closePopUp, setCloseEdit } = useContext(PopUpContext);
const { setErrorData, setSuccessData } = useContext(alertContext);
- const { closePopUp } = useContext(PopUpContext);
- const ref = useRef();
+
function setModalOpen(x: boolean) {
- setOpen(x);
if (x === false) {
- setTimeout(() => {
- closePopUp();
- }, 300);
+ setCloseEdit("codearea");
+ closePopUp();
}
}
+
+ function handleClick() {
+ postValidateCode(code)
+ .then((apiReturn) => {
+ if (apiReturn.data) {
+ let importsErrors = apiReturn.data.imports.errors;
+ let funcErrors = apiReturn.data.function.errors;
+ if (funcErrors.length === 0 && importsErrors.length === 0) {
+ setSuccessData({
+ title: "Code is ready to run",
+ });
+ setValue(code);
+ setModalOpen(false);
+ } else {
+ if (funcErrors.length !== 0) {
+ setErrorData({
+ title: "There is an error in your function",
+ list: funcErrors,
+ });
+ }
+ if (importsErrors.length !== 0) {
+ setErrorData({
+ title: "There is an error in your imports",
+ list: importsErrors,
+ });
+ }
+ }
+ } else {
+ setErrorData({
+ title: "Something went wrong, please try again",
+ });
+ }
+ })
+ .catch((_) => {
+ setErrorData({
+ title: "There is something wrong with this code, please review it",
+ });
+ });
+ }
+
return (
-
-
-
-
-
- Edit Code
-
-
- {CODE_PROMPT_DIALOG_SUBTITLE}
-
-
-
-
{
- setCode(value);
- }}
- className="w-full rounded-lg h-[300px] custom-scroll border-[1px] border-gray-300 dark:border-gray-600"
+
+
+
+ Edit Code
+
+
+
+
+
+
+
{
+ setCode(value);
+ }}
+ className="h-full w-full rounded-lg border-[1px] border-border custom-scroll"
+ />
+
+
+
+ Check & Save
+
+
-
-
- {
- postValidateCode(code)
- .then((apiReturn) => {
- if (apiReturn.data) {
- let importsErrors = apiReturn.data.imports.errors;
- let funcErrors = apiReturn.data.function.errors;
- if (funcErrors.length === 0 && importsErrors.length === 0) {
- setSuccessData({
- title: "Code is ready to run",
- });
- setModalOpen(false);
- setValue(code);
- } else {
- if (funcErrors.length !== 0) {
- setErrorData({
- title: "There is an error in your function",
- list: funcErrors,
- });
- }
- if (importsErrors.length !== 0) {
- setErrorData({
- title: "There is an error in your imports",
- list: importsErrors,
- });
- }
- }
- } else {
- setErrorData({
- title: "Something went wrong, please try again",
- });
- }
- })
- .catch((_) =>
- setErrorData({
- title:
- "There is something wrong with this code, please review it",
- })
- );
- }}
- type="submit"
- >
- Check & Save
-
-
-
-
+
+
);
}
diff --git a/src/frontend/src/modals/exportModal/index.tsx b/src/frontend/src/modals/exportModal/index.tsx
index 30567b4f1..fb6867811 100644
--- a/src/frontend/src/modals/exportModal/index.tsx
+++ b/src/frontend/src/modals/exportModal/index.tsx
@@ -1,9 +1,8 @@
-import { ArrowDownTrayIcon } from "@heroicons/react/24/outline";
+import { Download } from "lucide-react";
import { useContext, useRef, useState } from "react";
-import { alertContext } from "../../contexts/alertContext";
-import { PopUpContext } from "../../contexts/popUpContext";
-import { TabsContext } from "../../contexts/tabsContext";
-import { removeApiKeys } from "../../utils";
+import EditFlowSettings from "../../components/EditFlowSettingsComponent";
+import { Button } from "../../components/ui/button";
+import { Checkbox } from "../../components/ui/checkbox";
import {
Dialog,
DialogContent,
@@ -13,18 +12,19 @@ import {
DialogTitle,
DialogTrigger,
} from "../../components/ui/dialog";
-import { Button } from "../../components/ui/button";
-import { Checkbox } from "../../components/ui/checkbox";
import { EXPORT_DIALOG_SUBTITLE } from "../../constants";
-import { Download } from "lucide-react";
-import EditFlowSettings from "../../components/EditFlowSettingsComponent";
+import { alertContext } from "../../contexts/alertContext";
+import { PopUpContext } from "../../contexts/popUpContext";
+import { TabsContext } from "../../contexts/tabsContext";
+import { removeApiKeys } from "../../utils";
export default function ExportModal() {
const [open, setOpen] = useState(true);
const { closePopUp } = useContext(PopUpContext);
const ref = useRef();
const { setErrorData } = useContext(alertContext);
- const { flows, tabId, updateFlow, downloadFlow } = useContext(TabsContext);
+ const { flows, tabId, updateFlow, downloadFlow, saveFlow } =
+ useContext(TabsContext);
const [isMaxLength, setIsMaxLength] = useState(false);
function setModalOpen(x: boolean) {
setOpen(x);
@@ -42,12 +42,13 @@ export default function ExportModal() {
return (
-
+
Export
@@ -70,10 +71,7 @@ export default function ExportModal() {
setChecked(event);
}}
/>
-
+
Save with my API keys
@@ -81,9 +79,18 @@ export default function ExportModal() {
{
- if (checked) downloadFlow(flows.find((f) => f.id === tabId));
+ if (checked)
+ downloadFlow(
+ flows.find((f) => f.id === tabId),
+ name,
+ description
+ );
else
- downloadFlow(removeApiKeys(flows.find((f) => f.id === tabId)));
+ downloadFlow(
+ removeApiKeys(flows.find((f) => f.id === tabId)),
+ name,
+ description
+ );
closePopUp();
}}
diff --git a/src/frontend/src/modals/flowSettingsModal/index.tsx b/src/frontend/src/modals/flowSettingsModal/index.tsx
index 3136dc1fe..44f82e331 100644
--- a/src/frontend/src/modals/flowSettingsModal/index.tsx
+++ b/src/frontend/src/modals/flowSettingsModal/index.tsx
@@ -1,7 +1,7 @@
+import { Settings2 } from "lucide-react";
import { useContext, useRef, useState } from "react";
-import { alertContext } from "../../contexts/alertContext";
-import { PopUpContext } from "../../contexts/popUpContext";
-import { TabsContext } from "../../contexts/tabsContext";
+import EditFlowSettings from "../../components/EditFlowSettingsComponent";
+import { Button } from "../../components/ui/button";
import {
Dialog,
DialogContent,
@@ -11,11 +11,10 @@ import {
DialogTitle,
DialogTrigger,
} from "../../components/ui/dialog";
-import { Button } from "../../components/ui/button";
import { SETTINGS_DIALOG_SUBTITLE } from "../../constants";
-import EditFlowSettings from "../../components/EditFlowSettingsComponent";
-import { Settings2 } from "lucide-react";
-import { updateFlowInDatabase } from "../../controllers/API";
+import { alertContext } from "../../contexts/alertContext";
+import { PopUpContext } from "../../contexts/popUpContext";
+import { TabsContext } from "../../contexts/tabsContext";
export default function FlowSettingsModal() {
const [open, setOpen] = useState(true);
@@ -48,11 +47,11 @@ export default function FlowSettingsModal() {
return (
-
+
Settings
-
+
{SETTINGS_DIALOG_SUBTITLE}
diff --git a/src/frontend/src/modals/chatModal/chatInput/index.tsx b/src/frontend/src/modals/formModal/chatInput/index.tsx
similarity index 53%
rename from src/frontend/src/modals/chatModal/chatInput/index.tsx
rename to src/frontend/src/modals/formModal/chatInput/index.tsx
index 5f02979ca..ec62c0505 100644
--- a/src/frontend/src/modals/chatModal/chatInput/index.tsx
+++ b/src/frontend/src/modals/formModal/chatInput/index.tsx
@@ -1,23 +1,20 @@
-import { LockClosedIcon, PaperAirplaneIcon } from "@heroicons/react/24/outline";
+import { Lock, LucideSend, Sparkles } from "lucide-react";
+import { useEffect } from "react";
import { classNames } from "../../../utils";
-import { useContext, useEffect, useRef, useState } from "react";
-import { TabsContext } from "../../../contexts/tabsContext";
-import { INPUT_STYLE } from "../../../constants";
+
export default function ChatInput({
lockChat,
chatValue,
sendMessage,
setChatValue,
inputRef,
+ noInput,
}) {
useEffect(() => {
if (!lockChat && inputRef.current) {
inputRef.current.focus();
}
- },[
- lockChat,inputRef
- ])
-
+ }, [lockChat, inputRef]);
useEffect(() => {
if (inputRef.current) {
@@ -36,7 +33,7 @@ export default function ChatInput({
}}
rows={1}
ref={inputRef}
- disabled={lockChat}
+ disabled={lockChat || noInput}
style={{
resize: "none",
bottom: `${inputRef?.current?.scrollHeight}px`,
@@ -53,25 +50,38 @@ export default function ChatInput({
}}
className={classNames(
lockChat
- ? " bg-input text-black dark:bg-gray-700 dark:text-gray-300"
- : " bg-white-200 text-black dark:bg-gray-900 dark:text-gray-300",
- "form-input block w-full custom-scroll rounded-md border-gray-300 dark:border-gray-600 pr-10 sm:text-sm" +
- INPUT_STYLE
+ ? " form-modal-lock-true bg-input"
+ : noInput
+ ? "form-modal-no-input bg-input"
+ : " form-modal-lock-false bg-background",
+
+ "form-modal-lockchat"
)}
- placeholder={"Send a message..."}
+ placeholder={
+ noInput
+ ? "No chat input variables found. Click to run your flow."
+ : "Send a message..."
+ }
/>
-
-
sendMessage()}>
+
+
sendMessage()}
+ >
{lockChat ? (
-
+
+ ) : noInput ? (
+
) : (
-
+
)}
diff --git a/src/frontend/src/modals/chatModal/chatMessage/codeBlock/index.tsx b/src/frontend/src/modals/formModal/chatMessage/codeBlock/index.tsx
similarity index 76%
rename from src/frontend/src/modals/chatModal/chatMessage/codeBlock/index.tsx
rename to src/frontend/src/modals/formModal/chatMessage/codeBlock/index.tsx
index 851f5118a..884215ebb 100644
--- a/src/frontend/src/modals/chatModal/chatMessage/codeBlock/index.tsx
+++ b/src/frontend/src/modals/formModal/chatMessage/codeBlock/index.tsx
@@ -1,5 +1,5 @@
import { IconCheck, IconClipboard, IconDownload } from "@tabler/icons-react";
-import { FC, memo, useState } from "react";
+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 "../../../../utils";
@@ -9,7 +9,7 @@ interface Props {
value: string;
}
-export const CodeBlock: FC = memo(({ language, value }) => {
+export function CodeBlock({ language, value }) {
const [isCopied, setIsCopied] = useState(false);
const copyToClipboard = () => {
@@ -48,28 +48,22 @@ export const CodeBlock: FC = memo(({ language, value }) => {
};
return (
-
-
{language}
+
+
{language}
-
+
{isCopied ? : }
{isCopied ? "Copied!" : "Copy code"}
-
+
= memo(({ language, value }) => {
);
-});
+}
CodeBlock.displayName = "CodeBlock";
diff --git a/src/frontend/src/modals/formModal/chatMessage/index.tsx b/src/frontend/src/modals/formModal/chatMessage/index.tsx
new file mode 100644
index 000000000..8cbf0877c
--- /dev/null
+++ b/src/frontend/src/modals/formModal/chatMessage/index.tsx
@@ -0,0 +1,209 @@
+import Convert from "ansi-to-html";
+import { ChevronDown } from "lucide-react";
+import { useMemo, useState } from "react";
+import ReactMarkdown from "react-markdown";
+import rehypeMathjax from "rehype-mathjax";
+import remarkGfm from "remark-gfm";
+import remarkMath from "remark-math";
+import MaleTechnology from "../../../assets/male-technologist.png";
+import Robot from "../../../assets/robot.png";
+import SanitizedHTMLWrapper from "../../../components/SanitizedHTMLWrapper";
+import { THOUGHTS_ICON } from "../../../constants";
+import { ChatMessageType } from "../../../types/chat";
+import { classNames } from "../../../utils";
+import FileCard from "../fileComponent";
+import { CodeBlock } from "./codeBlock";
+export default function ChatMessage({
+ chat,
+ lockChat,
+ lastMessage,
+}: {
+ chat: ChatMessageType;
+ lockChat: boolean;
+ lastMessage: boolean;
+}) {
+ const convert = new Convert({ newline: true });
+ const [hidden, setHidden] = useState(true);
+ const template = chat.template;
+ const [promptOpen, setPromptOpen] = useState(false);
+ return (
+
+
+ {!chat.isSend ? (
+
+
+
+
+
+ ) : (
+
+
+
+
+
+ )}
+
+ {!chat.isSend ? (
+
+
+ {hidden && chat.thought && chat.thought !== "" && (
+
setHidden((prev) => !prev)}
+ className="form-modal-chat-icon-div"
+ >
+
+
+ )}
+ {chat.thought && chat.thought !== "" && !hidden && (
+
setHidden((prev) => !prev)}
+ />
+ )}
+ {chat.thought && chat.thought !== "" && !hidden && }
+
+
+
+ {useMemo(
+ () => (
+ {
+ if (children.length) {
+ if (children[0] === "â") {
+ return (
+
+ â
+
+ );
+ }
+
+ children[0] = (children[0] as string).replace(
+ "`â`",
+ "â"
+ );
+ }
+
+ const match = /language-(\w+)/.exec(
+ className || ""
+ );
+
+ return !inline ? (
+
+ ) : (
+
+ {children}
+
+ );
+ },
+ }}
+ >
+ {chat.message.toString()}
+
+ ),
+ [chat.message, chat.message.toString()]
+ )}
+
+ {chat.files && (
+
+ {chat.files.map((file, index) => {
+ return (
+
+
+
+ );
+ })}
+
+ )}
+
+
+
+
+ ) : (
+
+ {template ? (
+ <>
+
{
+ setPromptOpen((old) => !old);
+ }}
+ >
+ Display Prompt
+
+
+
+ {promptOpen
+ ? template?.split("\n")?.map((line, index) => {
+ const regex = /{([^}]+)}/g;
+ let match;
+ let parts = [];
+ let lastIndex = 0;
+ while ((match = regex.exec(line)) !== null) {
+ // Push text up to the match
+ if (match.index !== lastIndex) {
+ parts.push(line.substring(lastIndex, match.index));
+ }
+ // Push div with matched text
+ if (chat.message[match[1]]) {
+ parts.push(
+
+ {chat.message[match[1]]}
+
+ );
+ }
+
+ // Update last index
+ lastIndex = regex.lastIndex;
+ }
+ // Push text after the last match
+ if (lastIndex !== line.length) {
+ parts.push(line.substring(lastIndex));
+ }
+ return {parts}
;
+ })
+ : chat.message[chat.chatKey]}
+
+ >
+ ) : (
+
{chat.message[chat.chatKey]}
+ )}
+
+ )}
+
+ );
+}
diff --git a/src/frontend/src/modals/chatModal/fileComponent/index.tsx b/src/frontend/src/modals/formModal/fileComponent/index.tsx
similarity index 55%
rename from src/frontend/src/modals/chatModal/fileComponent/index.tsx
rename to src/frontend/src/modals/formModal/fileComponent/index.tsx
index 2b5d97ae9..6ede4e136 100644
--- a/src/frontend/src/modals/chatModal/fileComponent/index.tsx
+++ b/src/frontend/src/modals/formModal/fileComponent/index.tsx
@@ -1,5 +1,5 @@
-import { CloudArrowDownIcon, DocumentIcon } from "@heroicons/react/24/outline";
import * as base64js from "base64-js";
+import { DownloadCloud, File } from "lucide-react";
import { useState } from "react";
export default function FileCard({ fileName, content, fileType }) {
@@ -26,24 +26,22 @@ export default function FileCard({ fileName, content, fileType }) {
if (fileType === "image") {
return (
{isHovered && (
-
+
-
+
)}
@@ -52,27 +50,24 @@ export default function FileCard({ fileName, content, fileType }) {
}
return (
-
-
+
+
{" "}
{fileType === "image" ? (
) : (
-
+
)}
-
+
{" "}
-
{fileName}
-
{fileType}
+
{fileName}
+
{fileType}
-
+
);
diff --git a/src/frontend/src/modals/formModal/index.tsx b/src/frontend/src/modals/formModal/index.tsx
new file mode 100644
index 000000000..45c101f3f
--- /dev/null
+++ b/src/frontend/src/modals/formModal/index.tsx
@@ -0,0 +1,577 @@
+import { Eraser, TerminalSquare, Variable } from "lucide-react";
+import { useContext, useEffect, useRef, useState } from "react";
+import { alertContext } from "../../contexts/alertContext";
+import { typesContext } from "../../contexts/typesContext";
+import { sendAllProps } from "../../types/api";
+import { ChatMessageType } from "../../types/chat";
+import { FlowType } from "../../types/flow";
+import { classNames, validateNodes } from "../../utils";
+import ChatInput from "./chatInput";
+import ChatMessage from "./chatMessage";
+
+import _ from "lodash";
+import ToggleShadComponent from "../../components/toggleShadComponent";
+import {
+ Accordion,
+ AccordionContent,
+ AccordionItem,
+ AccordionTrigger,
+} from "../../components/ui/accordion";
+import { Badge } from "../../components/ui/badge";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "../../components/ui/dialog";
+import { Textarea } from "../../components/ui/textarea";
+import { CHAT_FORM_DIALOG_SUBTITLE, THOUGHTS_ICON } from "../../constants";
+import { TabsContext } from "../../contexts/tabsContext";
+
+export default function FormModal({
+ flow,
+ open,
+ setOpen,
+}: {
+ open: boolean;
+ setOpen: Function;
+ flow: FlowType;
+}) {
+ const { tabsState, setTabsState } = useContext(TabsContext);
+ const [chatValue, setChatValue] = useState(() => {
+ try {
+ const { formKeysData } = tabsState[flow.id];
+ if (!formKeysData) {
+ throw new Error("formKeysData is undefined");
+ }
+ const inputKeys = formKeysData.input_keys;
+ const handleKeys = formKeysData.handle_keys;
+
+ const keyToUse = Object.keys(inputKeys).find(
+ (k) => !handleKeys.some((j) => j === k) && inputKeys[k] === ""
+ );
+
+ return inputKeys[keyToUse];
+ } catch (error) {
+ console.error(error);
+ // return a sensible default or `undefined` if no default is possible
+ return undefined;
+ }
+ });
+
+ const [chatHistory, setChatHistory] = useState
([]);
+ const { reactFlowInstance } = useContext(typesContext);
+ const { setErrorData } = useContext(alertContext);
+ const ws = useRef(null);
+ const [lockChat, setLockChat] = useState(false);
+ const isOpen = useRef(open);
+ const messagesRef = useRef(null);
+ const id = useRef(flow.id);
+ const tabsStateFlowId = tabsState[flow.id];
+ const tabsStateFlowIdFormKeysData = tabsStateFlowId.formKeysData;
+ const [chatKey, setChatKey] = useState(
+ Object.keys(tabsState[flow.id].formKeysData.input_keys).find(
+ (k) =>
+ !tabsState[flow.id].formKeysData.handle_keys.some((j) => j === k) &&
+ tabsState[flow.id].formKeysData.input_keys[k] === ""
+ )
+ );
+
+ useEffect(() => {
+ if (messagesRef.current) {
+ messagesRef.current.scrollTop = messagesRef.current.scrollHeight;
+ }
+ }, [chatHistory]);
+
+ useEffect(() => {
+ isOpen.current = open;
+ }, [open]);
+ useEffect(() => {
+ id.current = flow.id;
+ }, [flow.id, tabsStateFlowId, tabsStateFlowIdFormKeysData]);
+
+ var isStream = false;
+
+ const addChatHistory = (
+ message: string | Object,
+ isSend: boolean,
+ chatKey: string,
+ template?: string,
+ thought?: string,
+ files?: Array
+ ) => {
+ setChatHistory((old) => {
+ let newChat = _.cloneDeep(old);
+ if (files) {
+ newChat.push({ message, isSend, files, thought, chatKey });
+ } else if (thought) {
+ newChat.push({ message, isSend, thought, chatKey });
+ } else if (template) {
+ newChat.push({ message, isSend, chatKey, template });
+ } else {
+ newChat.push({ message, isSend, chatKey });
+ }
+ return newChat;
+ });
+ };
+
+ //add proper type signature for function
+
+ function updateLastMessage({
+ str,
+ thought,
+ end = false,
+ files,
+ }: {
+ str?: string;
+ thought?: string;
+ // end param default is false
+ end?: boolean;
+ files?: Array;
+ }) {
+ setChatHistory((old) => {
+ let newChat = [...old];
+ if (str) {
+ if (end) {
+ newChat[newChat.length - 1].message = str;
+ } else {
+ newChat[newChat.length - 1].message =
+ newChat[newChat.length - 1].message + str;
+ }
+ }
+ if (thought) {
+ newChat[newChat.length - 1].thought = thought;
+ }
+ if (files) {
+ newChat[newChat.length - 1].files = files;
+ }
+ return newChat;
+ });
+ }
+
+ function handleOnClose(event: CloseEvent) {
+ if (isOpen.current) {
+ setErrorData({ title: event.reason });
+ setTimeout(() => {
+ connectWS();
+ setLockChat(false);
+ }, 1000);
+ }
+ }
+
+ function getWebSocketUrl(chatId, isDevelopment = false) {
+ const isSecureProtocol = window.location.protocol === "https:";
+ const webSocketProtocol = isSecureProtocol ? "wss" : "ws";
+ const host = isDevelopment ? "localhost:7860" : window.location.host;
+ const chatEndpoint = `/api/v1/chat/${chatId}`;
+
+ return `${
+ isDevelopment ? "ws" : webSocketProtocol
+ }://${host}${chatEndpoint}`;
+ }
+
+ function handleWsMessage(data: any) {
+ if (Array.isArray(data)) {
+ //set chat history
+ setChatHistory((_) => {
+ let newChatHistory: ChatMessageType[] = [];
+ data.forEach(
+ (chatItem: {
+ intermediate_steps?: string;
+ is_bot: boolean;
+ message: string;
+ template: string;
+ type: string;
+ chatKey: string;
+ files?: Array;
+ }) => {
+ if (chatItem.message) {
+ newChatHistory.push(
+ chatItem.files
+ ? {
+ isSend: !chatItem.is_bot,
+ message: chatItem.message,
+ template: chatItem.template,
+ thought: chatItem.intermediate_steps,
+ files: chatItem.files,
+ chatKey: chatItem.chatKey,
+ }
+ : {
+ isSend: !chatItem.is_bot,
+ message: chatItem.message,
+ template: chatItem.template,
+ thought: chatItem.intermediate_steps,
+ chatKey: chatItem.chatKey,
+ }
+ );
+ }
+ }
+ );
+ return newChatHistory;
+ });
+ }
+ if (data.type === "start") {
+ addChatHistory("", false, chatKey);
+ isStream = true;
+ }
+ if (data.type === "end") {
+ if (data.message) {
+ updateLastMessage({ str: data.message, end: true });
+ }
+ if (data.intermediate_steps) {
+ updateLastMessage({
+ str: data.message,
+ thought: data.intermediate_steps,
+ end: true,
+ });
+ }
+ if (data.files) {
+ updateLastMessage({
+ end: true,
+ files: data.files,
+ });
+ }
+
+ setLockChat(false);
+ isStream = false;
+ }
+ if (data.type === "stream" && isStream) {
+ updateLastMessage({ str: data.message });
+ }
+ }
+
+ function connectWS() {
+ try {
+ const urlWs = getWebSocketUrl(
+ id.current,
+ process.env.NODE_ENV === "development"
+ );
+ const newWs = new WebSocket(urlWs);
+ newWs.onopen = () => {
+ console.log("WebSocket connection established!");
+ };
+ newWs.onmessage = (event) => {
+ const data = JSON.parse(event.data);
+ console.log("Received data:", data);
+ handleWsMessage(data);
+ //get chat history
+ };
+ newWs.onclose = (event) => {
+ handleOnClose(event);
+ };
+ newWs.onerror = (ev) => {
+ console.log(ev, "error");
+ if (flow.id === "") {
+ connectWS();
+ } else {
+ setErrorData({
+ title: "There was an error on web connection, please: ",
+ list: [
+ "Refresh the page",
+ "Use a new flow tab",
+ "Check if the backend is up",
+ ],
+ });
+ }
+ };
+ ws.current = newWs;
+ } catch (error) {
+ if (flow.id === "") {
+ connectWS();
+ }
+ console.log(error);
+ }
+ }
+
+ useEffect(() => {
+ connectWS();
+ return () => {
+ console.log("unmount");
+ console.log(ws);
+ if (ws.current) {
+ ws.current.close();
+ }
+ };
+ // do not add connectWS on dependencies array
+ }, []);
+
+ useEffect(() => {
+ if (
+ ws.current &&
+ (ws.current.readyState === ws.current.CLOSED ||
+ ws.current.readyState === ws.current.CLOSING)
+ ) {
+ connectWS();
+ setLockChat(false);
+ }
+ // do not add connectWS on dependencies array
+ }, [lockChat]);
+
+ async function sendAll(data: sendAllProps) {
+ try {
+ if (ws) {
+ ws.current.send(JSON.stringify(data));
+ }
+ } catch (error) {
+ setErrorData({
+ title: "There was an error sending the message",
+ list: [error.message],
+ });
+ setChatValue(data.inputs);
+ connectWS();
+ }
+ }
+
+ useEffect(() => {
+ if (ref.current) ref.current.scrollIntoView({ behavior: "smooth" });
+ }, [chatHistory]);
+
+ const ref = useRef(null);
+
+ useEffect(() => {
+ if (open && ref.current) {
+ ref.current.focus();
+ }
+ }, [open]);
+
+ function sendMessage() {
+ let nodeValidationErrors = validateNodes(reactFlowInstance);
+ if (nodeValidationErrors.length === 0) {
+ setLockChat(true);
+ let inputs = tabsState[id.current].formKeysData.input_keys;
+ setChatValue("");
+ const message = inputs;
+ addChatHistory(
+ message,
+ true,
+ chatKey,
+ tabsState[flow.id].formKeysData.template
+ );
+ sendAll({
+ ...reactFlowInstance.toObject(),
+ inputs: inputs,
+ chatHistory,
+ name: flow.name,
+ description: flow.description,
+ });
+ setTabsState((old) => {
+ if (!chatKey) return old;
+ let newTabsState = _.cloneDeep(old);
+ newTabsState[id.current].formKeysData.input_keys[chatKey] = "";
+ return newTabsState;
+ });
+ } else {
+ setErrorData({
+ title: "Oops! Looks like you missed some required information:",
+ list: nodeValidationErrors,
+ });
+ }
+ }
+ function clearChat() {
+ setChatHistory([]);
+ ws.current.send(JSON.stringify({ clear_history: true }));
+ if (lockChat) setLockChat(false);
+ }
+
+ function setModalOpen(x: boolean) {
+ setOpen(x);
+ }
+
+ function handleOnCheckedChange(checked: boolean, i: string) {
+ if (checked === true) {
+ setChatKey(i);
+ setChatValue(tabsState[flow.id].formKeysData.input_keys[i]);
+ } else {
+ setChatKey(null);
+ setChatValue("");
+ }
+ }
+ return (
+
+
+ {tabsState[flow.id].formKeysData && (
+
+
+
+ Chat
+
+
+ {CHAT_FORM_DIALOG_SUBTITLE}
+
+
+
+
+
+
+
+ Input Variables
+
+
+
+
+ Name
+
+
+
+ Chat Input
+
+
+
+
+ {Object.keys(tabsState[id.current].formKeysData.input_keys).map(
+ (i, k) => (
+
+
+
+
+
+ {i}
+
+
+
{
+ event.stopPropagation();
+ }}
+ >
+
+ handleOnCheckedChange(value, i)
+ }
+ size="small"
+ disabled={tabsState[
+ id.current
+ ].formKeysData.handle_keys.some((t) => t === i)}
+ />
+
+
+
+
+
+ {tabsState[
+ id.current
+ ].formKeysData.handle_keys.some((t) => t === i) && (
+
+ Source: Component
+
+ )}
+
+
+
+
+
+ )
+ )}
+ {tabsState[id.current].formKeysData.memory_keys.map((i, k) => (
+
+
+
+
+ {i}
+
+
+ Used as memory key
+
+
+ ))}
+
+
+
+
+
+ clearChat()}>
+
+
+
+
+ {chatHistory.length > 0 ? (
+ chatHistory.map((c, i) => (
+
+ ))
+ ) : (
+
+
+ đ{" "}
+
+ LangFlow Chat
+
+
+
+
+
+ Start a conversation and click the agent's thoughts{" "}
+
+
+ {" "}
+ to inspect the chaining process.
+
+
+
+ )}
+
+
+
+
+ {
+ setChatValue(value);
+ setTabsState((old) => {
+ let newTabsState = _.cloneDeep(old);
+ newTabsState[id.current].formKeysData.input_keys[
+ chatKey
+ ] = value;
+ return newTabsState;
+ });
+ }}
+ inputRef={ref}
+ />
+
+
+
+
+
+
+ )}
+
+ );
+}
diff --git a/src/frontend/src/modals/genericModal/index.tsx b/src/frontend/src/modals/genericModal/index.tsx
index d241e7c04..b2cf02fdb 100644
--- a/src/frontend/src/modals/genericModal/index.tsx
+++ b/src/frontend/src/modals/genericModal/index.tsx
@@ -1,143 +1,291 @@
-import { XMarkIcon, DocumentTextIcon } from "@heroicons/react/24/outline";
-import { Fragment, useContext, useRef, useState } from "react";
-import { PopUpContext } from "../../contexts/popUpContext";
-import { darkContext } from "../../contexts/darkContext";
-import { checkPrompt } from "../../controllers/API";
-import { alertContext } from "../../contexts/alertContext";
-import { TypeModal } from "../../utils";
-import {
- Dialog,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
- DialogTrigger,
-} from "../../components/ui/dialog";
+import { FileText, Variable } from "lucide-react";
+import { useContext, useEffect, useRef, useState } from "react";
+import SanitizedHTMLWrapper from "../../components/SanitizedHTMLWrapper";
+import ShadTooltip from "../../components/ShadTooltipComponent";
+import { Badge } from "../../components/ui/badge";
import { Button } from "../../components/ui/button";
+import { DialogTitle } from "../../components/ui/dialog";
import { Textarea } from "../../components/ui/textarea";
import { PROMPT_DIALOG_SUBTITLE, TEXT_DIALOG_SUBTITLE } from "../../constants";
+import { alertContext } from "../../contexts/alertContext";
+import { darkContext } from "../../contexts/darkContext";
+import { PopUpContext } from "../../contexts/popUpContext";
+import { postValidatePrompt } from "../../controllers/API";
+import { APIClassType } from "../../types/api";
+import {
+ INVALID_CHARACTERS,
+ TypeModal,
+ classNames,
+ getRandomKeyByssmm,
+ regexHighlight,
+ varHighlightHTML,
+} from "../../utils";
+import BaseModal from "../baseModal";
export default function GenericModal({
+ field_name = "",
value,
setValue,
buttonText,
modalTitle,
type,
+ nodeClass,
+ setNodeClass,
}: {
+ field_name?: string;
setValue: (value: string) => void;
value: string;
buttonText: string;
modalTitle: string;
type: number;
+ nodeClass?: APIClassType;
+ setNodeClass?: (Class: APIClassType) => void;
}) {
const [myButtonText] = useState(buttonText);
const [myModalTitle] = useState(modalTitle);
const [myModalType] = useState(type);
- const [open, setOpen] = useState(true);
- const [myValue, setMyValue] = useState(value);
+ const [inputValue, setInputValue] = useState(value);
+ const [isEdit, setIsEdit] = useState(true);
+ const [wordsHighlight, setWordsHighlight] = useState([]);
const { dark } = useContext(darkContext);
- const { setErrorData, setSuccessData } = useContext(alertContext);
- const { closePopUp } = useContext(PopUpContext);
+ const { setErrorData, setSuccessData, setNoticeData } =
+ useContext(alertContext);
+ const { closePopUp, setCloseEdit } = useContext(PopUpContext);
const ref = useRef();
function setModalOpen(x: boolean) {
- setOpen(x);
if (x === false) {
+ setCloseEdit("generic");
closePopUp();
}
}
+ function checkVariables(valueToCheck) {
+ const regex = /\{([^{}]+)\}/g;
+ const matches = [];
+ let match;
+ while ((match = regex.exec(valueToCheck))) {
+ matches.push(`{${match[1]}}`);
+ }
+
+ let invalid_chars = [];
+ let fixed_variables = [];
+ let input_variables = matches;
+ for (let variable of input_variables) {
+ let new_var = variable;
+ for (let char of INVALID_CHARACTERS) {
+ if (variable.includes(char)) {
+ invalid_chars.push(new_var);
+ }
+ }
+ fixed_variables.push(new_var);
+ if (new_var !== variable) {
+ const index = input_variables.indexOf(variable);
+ if (index !== -1) {
+ input_variables.splice(index, 1, new_var);
+ }
+ }
+ }
+
+ const filteredWordsHighlight = matches.filter(
+ (word) => !invalid_chars.includes(word)
+ );
+
+ setWordsHighlight(filteredWordsHighlight);
+ }
+
+ useEffect(() => {
+ if (type === TypeModal.PROMPT && inputValue && inputValue != "") {
+ checkVariables(inputValue);
+ }
+ }, [inputValue, type]);
+
+ const coloredContent = (inputValue || "")
+ .replace(//g, ">")
+ .replace(regexHighlight, varHighlightHTML({ name: "$1" }))
+ .replace(/\n/g, " ");
+
+ const TextAreaContentView = () => {
+ return (
+ {
+ setIsEdit(true);
+ }}
+ suppressWarning={true}
+ />
+ );
+ };
+
+ function validatePrompt(closeModal: boolean) {
+ postValidatePrompt(field_name, inputValue, nodeClass)
+ .then((apiReturn) => {
+ if (apiReturn.data) {
+ setNodeClass(apiReturn.data?.frontend_node);
+ let inputVariables = apiReturn.data.input_variables ?? [];
+ if (inputVariables && inputVariables.length === 0) {
+ setIsEdit(true);
+ setNoticeData({
+ title: "Your template does not have any variables.",
+ });
+ } else {
+ setIsEdit(false);
+ setSuccessData({
+ title: "Prompt is ready",
+ });
+ setModalOpen(closeModal);
+ setValue(inputValue);
+ }
+ } else {
+ setIsEdit(true);
+ setErrorData({
+ title: "Something went wrong, please try again",
+ });
+ }
+ })
+ .catch((error) => {
+ console.log(error);
+ setIsEdit(true);
+ return setErrorData({
+ title: "There is something wrong with this prompt, please review it",
+ list: [error?.response?.data?.detail],
+ });
+ });
+ }
+
return (
-
-
-
-
-
- {myModalTitle}
-
-
-
- {(() => {
- switch (myModalTitle) {
- case "Edit Text":
- return TEXT_DIALOG_SUBTITLE;
+
+ {
+ switch (myModalTitle) {
+ case "Edit Text":
+ return TEXT_DIALOG_SUBTITLE;
- case "Edit Prompt":
- return PROMPT_DIALOG_SUBTITLE;
+ case "Edit Prompt":
+ return PROMPT_DIALOG_SUBTITLE;
- default:
- return null;
- }
- })()}
-
-
-
-
-
-
-
- {
- switch (myModalType) {
- case 1:
- setModalOpen(false);
- break;
- case 2:
- checkPrompt(myValue)
- .then((apiReturn) => {
- if (apiReturn.data) {
- let inputVariables = apiReturn.data.input_variables;
- if (inputVariables.length === 0) {
- setErrorData({
- title:
- "The template you are attempting to use does not contain any variables for data entry.",
- });
- } else {
- setSuccessData({
- title: "Prompt is ready",
- });
- setModalOpen(false);
- setValue(myValue);
- }
- } else {
- setErrorData({
- title: "Something went wrong, please try again",
- });
- }
- })
- .catch((error) => {
- return setErrorData({
- title:
- "There is something wrong with this prompt, please review it",
- list: [error.response.data.detail],
- });
- });
- break;
-
- default:
- break;
- }
- }}
- type="submit"
+
+
+
+
+
- {myButtonText}
-
-
-
-
+ {type === TypeModal.PROMPT && isEdit ? (
+
+
+
+
+ {type === TypeModal.PROMPT && (
+
+
+
+
+
+ Prompt Variables:
+
+
+ {wordsHighlight.map((word, index) => (
+
+