Merge branch 'form_io' into python_custom_node_component

This commit is contained in:
Gabriel Luiz Freitas Almeida 2023-07-07 01:36:18 -03:00
commit 320989870e
51 changed files with 1012 additions and 606 deletions

View file

@ -37,7 +37,7 @@
"base64-js": "^1.5.1",
"class-variance-authority": "^0.6.0",
"clsx": "^1.2.1",
"dompurify": "^3.0.3",
"dompurify": "^3.0.4",
"esbuild": "^0.17.18",
"lodash": "^4.17.21",
"lucide-react": "^0.233.0",
@ -5142,9 +5142,9 @@
}
},
"node_modules/dompurify": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.3.tgz",
"integrity": "sha512-axQ9zieHLnAnHh0sfAamKYiqXMJAVwu+LM/alQ7WDagoWessyWvMSFyW65CqF3owufNu8HBcE4cM2Vflu7YWcQ=="
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.4.tgz",
"integrity": "sha512-ae0mA+Qiqp6C29pqZX3fQgK+F91+F7wobM/v8DRzDqJdZJELXiFUx4PP4pK/mzUS0xkiSEx3Ncd9gr69jg3YsQ=="
},
"node_modules/electron-to-chromium": {
"version": "1.4.440",

View file

@ -32,7 +32,7 @@
"base64-js": "^1.5.1",
"class-variance-authority": "^0.6.0",
"clsx": "^1.2.1",
"dompurify": "^3.0.3",
"dompurify": "^3.0.4",
"esbuild": "^0.17.18",
"lodash": "^4.17.21",
"lucide-react": "^0.233.0",

View file

@ -102,7 +102,7 @@ export default function ParameterComponent({
refHtml.current = groupedObj.map((item, i) => {
const Icon: any = nodeIconsLucide[item.family];
return (
<span
key={getRandomKeyByssmm() + item.family + i}
@ -117,8 +117,7 @@ export default function ParameterComponent({
}}
>
<Icon
className="h-5 w-5"
className="h-5 w-5"
strokeWidth={1.5}
style={{
color: nodeColors[item.family] ?? nodeColors.unknown,
@ -129,7 +128,7 @@ export default function ParameterComponent({
{nodeNames[item.family] ?? ""}{" "}
<span className="text-xs">
{" "}
{item.type == "" ? '' : ' - '}
{item.type == "" ? "" : " - "}
{item.type.split(", ").length > 2
? item.type.split(", ").map((el, i) => (
<React.Fragment key={el + i}>

View file

@ -42,7 +42,7 @@ export default function ExtraSidebar() {
item.href.split("/")[2] === current[4]
? "text-ring"
: "text-ring group-hover:text-accent-foreground",
"mr-3 flex-shrink-0 h-6 w-6"
"mr-3 h-6 w-6 flex-shrink-0"
)}
/>
{item.name}
@ -71,8 +71,8 @@ export default function ExtraSidebar() {
<span className="flex-1">{item.name}</span>
<svg
className={classNames(
open ? "text-ring rotate-90" : "text-ring",
"ml-3 h-5 w-5 flex-shrink-0 transition-rotate duration-150 ease-in-out group-hover:text-accent-foreground"
open ? "rotate-90 text-ring" : "text-ring",
"transition-rotate ml-3 h-5 w-5 flex-shrink-0 duration-150 ease-in-out group-hover:text-accent-foreground"
)}
viewBox="0 0 20 20"
aria-hidden="true"

View file

@ -1,4 +1,4 @@
import { ShadTooltipProps } from "../../types/components";
import { RadialProgressType, ShadToolTipType } from "../../types/components";
import {
Tooltip,
TooltipContent,
@ -6,26 +6,22 @@ import {
TooltipTrigger,
} from "../ui/tooltip";
const ShadTooltip = ({
delayDuration = 500,
side,
export default function ShadTooltip({
content,
side,
asChild = true,
children,
style
}: ShadTooltipProps) => {
delayDuration,
}: ShadToolTipType) {
return (
<TooltipProvider>
<Tooltip delayDuration={delayDuration}>
<TooltipTrigger asChild>{children}</TooltipTrigger>
<TooltipTrigger asChild={asChild}>{children}</TooltipTrigger>
<TooltipContent
className={style}
side={side} avoidCollisions={false} sticky="always">
<TooltipContent side={side} avoidCollisions={false} sticky="always">
{content}
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
};
export default ShadTooltip;
}

View file

@ -11,7 +11,15 @@ import {
Card,
CardHeader,
} from "../ui/card";
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "../ui/dialog";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "../ui/dialog";
import { Button } from "@mui/material";
export const CardComponent = ({
@ -42,24 +50,26 @@ export const CardComponent = ({
</span>
{onDelete && (
<Dialog>
<DialogTrigger asChild>
<DialogTrigger asChild>
<button className="flex self-start">
<Trash2 className="w-4 h-4 text-primary opacity-0 group-hover:opacity-100 transition-all" />
</button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Are you sure absolutely sure?</DialogTitle>
<DialogDescription>
This action cannot be undone. Are you sure you want to permanently
delete this file from our servers?
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button type="submit" onClick={onDelete}>Confirm</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<Trash2 className="h-4 w-4 text-primary opacity-0 transition-all group-hover:opacity-100" />
</button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Are you sure absolutely sure?</DialogTitle>
<DialogDescription>
This action cannot be undone. Are you sure you want to
permanently delete this file from our servers?
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button type="submit" onClick={onDelete}>
Confirm
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)}
</CardTitle>
<CardDescription className="pb-2 pt-2">

View file

@ -8,7 +8,6 @@ import { useSSE } from "../../../contexts/SSEContext";
import { typesContext } from "../../../contexts/typesContext";
import { alertContext } from "../../../contexts/alertContext";
import { postBuildInit } from "../../../controllers/API";
import ShadTooltip from "../../ShadTooltipComponent";
import RadialProgressComponent from "../../RadialProgress";
import { TabsContext } from "../../../contexts/tabsContext";
@ -96,10 +95,9 @@ export default function BuildTrigger({
[flowId]: {
...old[flowId],
formKeysData: parsedData,
}
},
};
})
});
} else {
// Otherwise, process the data
const isValid = processStreamResult(parsedData);
@ -168,7 +166,7 @@ export default function BuildTrigger({
leaveFrom="translate-y-0"
leaveTo="translate-y-96"
>
<div className="fixed right-4 bottom-20">
<div className="fixed bottom-20 right-4">
<div
className={`${eventClick} align-center shadow-round-btn-shadow hover:shadow-round-btn-shadow flex h-12 w-12 cursor-pointer justify-center rounded-full bg-border px-3 py-1 shadow-md`}
onClick={() => {
@ -189,7 +187,10 @@ export default function BuildTrigger({
) : isBuilding ? (
<Loading strokeWidth={1.5} className="stroke-build-trigger" />
) : (
<Zap strokeWidth={1.5} className="sh-6 w-6 fill-build-trigger stroke-1 stroke-build-trigger" />
<Zap
strokeWidth={1.5}
className="sh-6 w-6 fill-build-trigger stroke-build-trigger stroke-1"
/>
)}
</div>
</button>

View file

@ -3,18 +3,30 @@ import { MessagesSquare } from "lucide-react";
import { alertContext } from "../../../contexts/alertContext";
import { useContext } from "react";
import ShadTooltip from "../../ShadTooltipComponent";
import {
CHAT_CANNOT_OPEN_DESCRIPTION,
CHAT_CANNOT_OPEN_TITLE,
FLOW_NOT_BUILT_DESCRIPTION,
FLOW_NOT_BUILT_TITLE,
} from "../../../constants";
export default function ChatTrigger({ open, setOpen, isBuilt }) {
export default function ChatTrigger({ open, setOpen, isBuilt, canOpen }) {
const { setErrorData } = useContext(alertContext);
function handleClick() {
if (isBuilt) {
setOpen(true);
if (canOpen) {
setOpen(true);
} else {
setErrorData({
title: CHAT_CANNOT_OPEN_TITLE,
list: [CHAT_CANNOT_OPEN_DESCRIPTION],
});
}
} else {
setErrorData({
title: "Flow not built",
list: ["Please build the flow before chatting"],
title: FLOW_NOT_BUILT_TITLE,
list: [FLOW_NOT_BUILT_DESCRIPTION],
});
}
}
@ -30,15 +42,26 @@ export default function ChatTrigger({ open, setOpen, isBuilt }) {
leaveFrom="translate-y-0"
leaveTo="translate-y-96"
>
<button onClick={handleClick} className={ "transition-all fixed bottom-4 right-4 flex justify-center items-center py-1 px-3 w-12 h-12 rounded-full shadow-md shadow-round-btn-shadow hover:shadow-round-btn-shadow bg-border "+ (!isBuilt ? "cursor-not-allowed" : "cursor-pointer")}>
<div className="flex gap-3">
<MessagesSquare
className={"h-6 w-6 transition-all " + (isBuilt ? "fill-chat-trigger stroke-chat-trigger stroke-1" : "fill-chat-trigger-disabled stroke-1 stroke-chat-trigger-disabled")}
style={{ color: "white" }}
strokeWidth={1.5}
/>
</div>
</button>
<button
onClick={handleClick}
className={
"shadow-round-btn-shadow hover:shadow-round-btn-shadow fixed bottom-4 right-4 flex h-12 w-12 items-center justify-center rounded-full bg-border px-3 py-1 shadow-md transition-all " +
(!isBuilt || !canOpen ? "cursor-not-allowed" : "cursor-pointer")
}
>
<div className="flex gap-3">
<MessagesSquare
className={
"h-6 w-6 transition-all " +
(isBuilt && canOpen
? "fill-chat-trigger stroke-chat-trigger stroke-1"
: "fill-chat-trigger-disabled stroke-chat-trigger-disabled stroke-1")
}
style={{ color: "white" }}
strokeWidth={1.5}
/>
</div>
</button>
</Transition>
);
}

View file

@ -13,6 +13,7 @@ import * as _ from "lodash";
export default function Chat({ flow }: ChatType) {
const [open, setOpen] = useState(false);
const [isBuilt, setIsBuilt] = useState(false);
const [canOpen, setCanOpen] = useState(false);
const { tabsState } = useContext(TabsContext);
useEffect(() => {
@ -58,6 +59,17 @@ export default function Chat({ flow }: ChatType) {
) {
setIsBuilt(false);
}
if (
tabsState &&
tabsState[flow.id] &&
tabsState[flow.id].formKeysData &&
tabsState[flow.id].formKeysData.input_keys &&
Object.keys(tabsState[flow.id].formKeysData.input_keys).length > 0
) {
setCanOpen(true);
} else {
setCanOpen(false);
}
prevNodesRef.current = currentNodes;
}, [tabsState]);
@ -71,10 +83,15 @@ export default function Chat({ flow }: ChatType) {
setIsBuilt={setIsBuilt}
isBuilt={isBuilt}
/>
{isBuilt && (
{isBuilt && canOpen && (
<FormModal key={flow.id} flow={flow} open={open} setOpen={setOpen} />
)}
<ChatTrigger open={open} setOpen={setOpen} isBuilt={isBuilt} />
<ChatTrigger
canOpen={canOpen}
open={open}
setOpen={setOpen}
isBuilt={isBuilt}
/>
</div>
</>
);

View file

@ -1,9 +1,7 @@
import { useContext, useEffect, useState } from "react";
import { PopUpContext } from "../../contexts/popUpContext";
import CodeAreaModal from "../../modals/codeAreaModal/v2";
import TextAreaModal from "../../modals/textAreaModal";
import { CodeAreaComponentType, TextAreaComponentType } from "../../types/components";
import { CodeAreaComponentType } from "../../types/components";
import { ExternalLink } from "lucide-react";
@ -54,7 +52,8 @@ export default function CodeAreaComponent({
className={
editNode
? "input-edit-node input-dialog "
: "input-primary input-dialog " + (disabled ? "input-disable" : "")
: "input-dialog input-primary " +
(disabled ? "input-disable" : "")
}
>
{myValue !== "" ? myValue : "Type something..."}
@ -63,9 +62,9 @@ export default function CodeAreaComponent({
onClick={() => {
openPopUp(
<CodeAreaModal
key={myValue}
dynamic={dynamic}
setNodeClass={setNodeClass}
key={myValue}
dynamic={dynamic}
setNodeClass={setNodeClass}
value={myValue}
nodeClass={nodeClass}
setValue={(t: string) => {
@ -77,7 +76,10 @@ export default function CodeAreaComponent({
}}
>
{!editNode && (
<ExternalLink strokeWidth={1.5} className="w-6 h-6 hover:text-accent-foreground ml-3" />
<ExternalLink
strokeWidth={1.5}
className="ml-3 h-6 w-6 hover:text-accent-foreground"
/>
)}
</button>
</div>

View file

@ -39,8 +39,8 @@ export default function Dropdown({
<Listbox.Button
className={
editNode
? "border-1 relative pr-8 input-edit-node"
: "py-2 pl-3 pr-10 text-left input-primary"
? "border-1 input-edit-node relative pr-8"
: "input-primary py-2 pl-3 pr-10 text-left"
}
>
<span className="block w-full truncate bg-background">

View file

@ -75,7 +75,7 @@ export default function Header() {
href="https://github.com/logspace-ai/langflow"
target="_blank"
rel="noreferrer"
className="inline-flex shadow-sm items-center justify-center text-sm font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background text-muted-foreground border border-input hover:bg-accent hover:text-accent-foreground h-9 px-3 pr-0 rounded-md"
className="inline-flex h-9 items-center justify-center rounded-md border border-input px-3 pr-0 text-sm font-medium text-muted-foreground shadow-sm ring-offset-background hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
>
<FaGithub className="mr-2 h-5 w-5" />
Star
@ -114,7 +114,7 @@ export default function Header() {
)}
</button>
<button
className="text-muted-foreground hover:text-accent-foreground relative"
className="relative text-muted-foreground hover:text-accent-foreground"
onClick={(event: React.MouseEvent<HTMLElement>) => {
setNotificationCenter(false);
const { top, left } = (

View file

@ -110,7 +110,10 @@ export default function InputFileComponent({
</span>
<button onClick={handleButtonClick}>
{!editNode && !loading && (
<FileSearch2 strokeWidth={1.5} className="w-6 h-6 hover:text-accent-foreground" />
<FileSearch2
strokeWidth={1.5}
className="h-6 w-6 hover:text-accent-foreground"
/>
)}
{!editNode && loading && (
<span className="loading loading-spinner loading-sm pointer-events-none h-8 pl-3"></span>

View file

@ -47,65 +47,66 @@ export default function PromptAreaComponent({
}, [reactFlowInstance, field_name]);
return (
<div
className={
disabled ? "pointer-events-none w-full cursor-not-allowed" : " w-full"
}
>
<div className="flex w-full items-center">
<span
onClick={() => {
openPopUp(
<GenericModal
type={TypeModal.PROMPT}
value={myValue}
buttonText="Check & Save"
modalTitle="Edit Prompt"
setValue={(t: string) => {
setMyValue(t);
onChange(t);
}}
nodeClass={nodeClass}
setNodeClass={setNodeClass}
<div className={disabled ? "cursor-not-allowed" : ""}>
<div className={disabled ? "pointer-events-none w-full " : " w-full"}>
<div className="flex w-full items-center">
<span
onClick={() => {
openPopUp(
<GenericModal
type={TypeModal.PROMPT}
value={myValue}
buttonText="Check & Save"
modalTitle="Edit Prompt"
setValue={(t: string) => {
setMyValue(t);
onChange(t);
}}
nodeClass={nodeClass}
setNodeClass={setNodeClass}
/>
);
}}
className={
editNode
? " input-edit-node " + " input-dialog "
: (disabled ? " input-disable " : "") +
" input-primary " +
" input-dialog "
}
>
{myValue !== "" ? myValue : "Type your prompt here"}
</span>
<button
onClick={() => {
openPopUp(
<GenericModal
field_name={field_name}
type={TypeModal.PROMPT}
value={myValue}
buttonText="Check & Save"
modalTitle="Edit Prompt"
setValue={(t: string) => {
setMyValue(t);
onChange(t);
}}
nodeClass={nodeClass}
setNodeClass={setNodeClass}
/>
);
}}
>
{!editNode && (
<ExternalLink
strokeWidth={1.5}
className={
"ml-3 h-6 w-6" +
(disabled ? " text-ring" : " hover:text-accent-foreground")
}
/>
);
}}
className={
editNode
? " input-edit-node " + " input-dialog "
: (disabled ? " input-disable " : "") +
" input-primary " +
" input-dialog "
}
>
{myValue !== "" ? myValue : "Type your prompt here"}
</span>
<button
onClick={() => {
openPopUp(
<GenericModal
field_name={field_name}
type={TypeModal.PROMPT}
value={myValue}
buttonText="Check & Save"
modalTitle="Edit Prompt"
setValue={(t: string) => {
setMyValue(t);
onChange(t);
}}
nodeClass={nodeClass}
setNodeClass={setNodeClass}
/>
);
}}
>
{!editNode && (
<ExternalLink
strokeWidth={1.5}
className="ml-3 h-6 w-6 hover:text-accent-foreground"
/>
)}
</button>
)}
</button>
</div>
</div>
</div>
);

View file

@ -27,12 +27,11 @@ export default function TextAreaComponent({
}, [closePopUp]);
return (
<div className={disabled ? "pointer-events-none cursor-not-allowed" : ""}>
<div className={disabled ? "cursor-not-allowed" : ""}>
<div
className={
editNode
? "w-full items-center"
: "w-full flex items-center"
(editNode ? "w-full items-center" : "flex w-full items-center") +
(disabled ? " pointer-events-none" : "")
}
>
<span
@ -52,12 +51,14 @@ export default function TextAreaComponent({
}}
className={
editNode
? "input-edit-node input-dialog "
: "input-primary input-dialog " + (disabled ? "input-disable" : "")
? "input-edit-node input-dialog "
: "input-dialog input-primary " +
(disabled ? "input-disable" : "")
}
>
{myValue !== "" ? myValue : "Type something..."}
</span>
<button
onClick={() => {
openPopUp(
@ -74,7 +75,15 @@ export default function TextAreaComponent({
);
}}
>
{!editNode && <ExternalLink strokeWidth={1.5} className="w-6 h-6 hover:text-accent-foreground ml-3" />}
{!editNode && (
<ExternalLink
strokeWidth={1.5}
className={
"ml-3 h-6 w-6" +
(disabled ? " text-ring" : " hover:text-accent-foreground")
}
/>
)}
</button>
</div>
</div>

View file

@ -27,7 +27,7 @@ const AccordionTrigger = React.forwardRef<
<AccordionPrimitive.Trigger
ref={ref}
className={cn(
"flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
"flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all [&[data-state=open]>svg]:rotate-180",
className
)}
{...props}

View file

@ -9,8 +9,7 @@ const badgeVariants = cva(
variant: {
default:
"bg-primary hover:bg-primary/80 border-transparent text-primary-foreground",
gray:
"bg-border hover:bg-border/80 text-secondary-foreground",
gray: "bg-border hover:bg-border/80 text-secondary-foreground",
secondary:
"bg-secondary hover:bg-secondary/80 border-transparent text-secondary-foreground",
destructive:
@ -21,7 +20,7 @@ const badgeVariants = cva(
sm: "h-4 text-xs",
md: "h-5 text-sm",
lg: "h-6 text-base",
}
},
},
defaultVariants: {
variant: "default",
@ -35,7 +34,10 @@ export interface BadgeProps
function Badge({ className, variant, size, ...props }: BadgeProps) {
return (
<div className={cn(badgeVariants({ variant, size }), className)} {...props} />
<div
className={cn(badgeVariants({ variant, size }), className)}
{...props}
/>
);
}

View file

@ -44,7 +44,7 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed gap-3 z-50 grid w-full rounded-b-lg border bg-background p-6 shadow-lg animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:max-w-lg sm:rounded-lg sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0",
"fixed z-50 grid w-full gap-3 rounded-b-lg border bg-background p-6 shadow-lg animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:max-w-lg sm:rounded-lg sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0",
className
)}
{...props}

View file

@ -19,7 +19,7 @@ const Switch = React.forwardRef<
<SwitchPrimitives.Thumb
aria-disabled={props.disabled}
className={cn(
"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 aria-disabled:bg-muted/70 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform aria-disabled:bg-muted/70 data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
)}
/>
</SwitchPrimitives.Root>

View file

@ -50,6 +50,15 @@ 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 FLOW_NOT_BUILT_DESCRIPTION =
"Please build the flow before chatting.";
/**
* The base text for subtitle of Text Dialog
* @constant
@ -180,7 +189,7 @@ export const EXPORT_CODE_DIALOG =
export const COLUMN_DIV_STYLE =
" w-full h-full flex overflow-auto flex-col bg-muted px-16 ";
export const NAV_DISPLAY_STYLE =
export const NAV_DISPLAY_STYLE =
" w-full flex justify-between py-12 pb-2 px-6 ";
/**
@ -210,7 +219,8 @@ 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 ";
export const BUTTON_DIV_STYLE =
" flex gap-2 focus:ring-1 focus:ring-offset-1 focus:ring-ring focus:outline-none ";
/**
* The base text for subtitle of code dialog
@ -513,3 +523,10 @@ export const NOUNS: string[] = [
*
*/
export const USER_PROJECTS_HEADER = "My Collection";
/**
* CSS for highlight HTML
* @constant
*
*/
export const HIGHLIGH_CSS =
"block pl-3 pr-14 py-2 w-full h-full text-sm outline-0 border-0 break-all";

View file

@ -285,7 +285,11 @@ export function TabsProvider({ children }: { children: ReactNode }) {
// create a link element and set its properties
const link = document.createElement("a");
link.href = jsonString;
link.download = `${flowName && flowName != "" ? flowName : 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();

View file

@ -104,11 +104,10 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
useEffect(() => {
if (closeEdit !== "") {
tweak.current = getTweak;
if(tweak.current.length > 0){
if (tweak.current.length > 0) {
setActiveTab("3");
openAccordions();
}
else{
} else {
startTweaks();
}
} else {
@ -250,25 +249,25 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
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);
}
});
tweak.current.forEach((el) => {
Object.keys(el).forEach((key) => {
if (Object.keys(el[key]).length > 0) {
accordionsToOpen.push(key);
setOpenAccordion(accordionsToOpen);
}
});
});
}
return (
<Dialog open={true} onOpenChange={setModalOpen}>
<DialogTrigger></DialogTrigger>
<DialogContent className="lg:max-w-[80vw] h-[80vh]">
<DialogContent className="h-[80vh] lg:max-w-[80vw]">
<DialogHeader>
<DialogTitle className="flex items-center">
<span className="pr-2">Code</span>
<Code2
className="h-6 w-6 text-gray-800 pl-1 dark:text-white"
className="h-6 w-6 pl-1 text-gray-800 dark:text-white"
aria-hidden="true"
/>
</DialogTitle>
@ -277,7 +276,7 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
<Tabs
value={activeTab}
className="w-full h-full overflow-hidden text-center bg-muted rounded-md border"
className="h-full w-full overflow-hidden rounded-md border bg-muted text-center"
onValueChange={(value) => {
setActiveTab(value);
if (value === "3") {
@ -296,7 +295,7 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
{Number(activeTab) < 3 && (
<div className="float-right">
<button
className="flex gap-1.5 items-center rounded bg-none p-1 text-xs text-gray-500 dark:text-gray-300"
className="flex items-center gap-1.5 rounded bg-none p-1 text-xs text-gray-500 dark:text-gray-300"
onClick={copyToClipboard}
>
{isCopied ? <Check size={18} /> : <Clipboard size={15} />}
@ -309,12 +308,12 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
{tabs.map((tab, index) => (
<TabsContent
value={index.toString()}
className="overflow-hidden w-full h-full px-4 pb-4 -mt-1"
className="-mt-1 h-full w-full overflow-hidden px-4 pb-4"
key={index} // Remember to add a unique key prop
>
{index < 3 ? (
<SyntaxHighlighter
className="w-full overflow-auto h-[60vh]"
className="h-[60vh] w-full overflow-auto"
language={tab.mode}
style={oneDark}
>
@ -322,10 +321,10 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
</SyntaxHighlighter>
) : index === 3 ? (
<>
<div className="flex w-full h-full mt-2">
<div className="mt-2 flex h-full w-full">
<div
className={classNames(
"w-full rounded-lg bg-muted h-[60vh]",
"h-[60vh] w-full rounded-lg bg-muted",
1 == 1
? "overflow-scroll overflow-x-hidden custom-scroll"
: "overflow-hidden"
@ -338,14 +337,14 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
trigger={t["data"]["id"]}
open={openAccordion}
>
<div className="flex flex-col gap-5 h-fit">
<div className="flex h-fit flex-col gap-5">
<Table className="table-fixed bg-muted outline-1">
<TableHeader className="border-input text-xs font-medium text-ring h-10">
<TableHeader className="h-10 border-input text-xs font-medium text-ring">
<TableRow className="dark:border-b-muted">
<TableHead className="h-7 text-center">
PARAM
</TableHead>
<TableHead className="p-0 h-7 text-center">
<TableHead className="h-7 p-0 text-center">
VALUE
</TableHead>
</TableRow>
@ -379,11 +378,11 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
key={i}
className="h-10 dark:border-b-muted"
>
<TableCell className="p-0 text-center text-gray-900 text-sm">
<TableCell className="p-0 text-center text-sm text-gray-900">
{n}
</TableCell>
<TableCell className="p-0 text-center text-gray-900 text-xs dark:text-gray-300">
<div className="w-[250px] m-auto">
<TableCell className="p-0 text-center text-xs text-gray-900 dark:text-gray-300">
<div className="m-auto w-[250px]">
{t.data.node.template[n]
.type === "str" &&
!t.data.node.template[n]

View file

@ -5,7 +5,7 @@ import "ace-builds/src-noconflict/mode-python";
import "ace-builds/src-noconflict/theme-github";
import "ace-builds/src-noconflict/theme-twilight";
import "ace-builds/src-noconflict/ext-language_tools";
import 'ace-builds/src-noconflict/ace';
import "ace-builds/src-noconflict/ace";
// import "ace-builds/webpack-resolver";
import { darkContext } from "../../contexts/darkContext";
import { postCustomComponent, postValidateCode } from "../../controllers/API";
@ -29,14 +29,13 @@ import {
TabsList,
TabsTrigger,
} from "../../components/ui/tabs";
import axios from "axios";
export default function CodeAreaModal({
value,
setValue,
nodeClass,
setNodeClass,
dynamic
dynamic,
}: {
setValue: (value: string) => void;
value: string;
@ -50,7 +49,9 @@ export default function CodeAreaModal({
const { dark } = useContext(darkContext);
const { setErrorData, setSuccessData } = useContext(alertContext);
const [activeTab, setActiveTab] = useState("0");
const [error, setError] = useState<{ detail: { error: string, traceback: string } }>(null)
const [error, setError] = useState<{
detail: { error: string; traceback: string };
}>(null);
const { closePopUp, setCloseEdit } = useContext(PopUpContext);
const ref = useRef();
function setModalOpen(x: boolean) {
@ -64,9 +65,9 @@ export default function CodeAreaModal({
}
console.log(dynamic);
useEffect(()=>{
useEffect(() => {
setValue(code);
},[code,setValue])
}, [code, setValue]);
function handleClick() {
setLoading(true);
@ -108,39 +109,40 @@ export default function CodeAreaModal({
title: "There is something wrong with this code, please review it",
});
});
}
else {
postCustomComponent(code, nodeClass).then((apiReturn) => {
const { data } = apiReturn;
if (data) {
setNodeClass(data);
setModalOpen(false);
}
}).catch((err) => {
setErrorData({
title: "There is something wrong with this code, please see the error on the errors tab",
} else {
postCustomComponent(code, nodeClass)
.then((apiReturn) => {
const { data } = apiReturn;
if (data) {
setNodeClass(data);
setModalOpen(false);
}
})
.catch((err) => {
setErrorData({
title:
"There is something wrong with this code, please see the error on the errors tab",
});
console.log(err.response.data);
setError(err.response.data);
});
console.log(err.response.data);
setError(err.response.data);
});
}
// axios.get("/api/v1/custom_component_error").catch((err) => {
// })
}
const tabs = [{ name: "code" }, { name: "errors" }]
const tabs = [{ name: "code" }, { name: "errors" }];
return (
<Dialog open={true} onOpenChange={setModalOpen}>
<DialogTrigger></DialogTrigger>
<DialogContent className="h-[500px] lg:max-w-[700px]">
<DialogContent className="min-w-[80vw]">
<DialogHeader>
<DialogTitle className="flex items-center">
<span className="pr-2">Edit Code</span>
<TerminalSquare
strokeWidth={1.5}
className="h-6 w-6 text-primary pl-1 "
strokeWidth={1.5}
className="h-6 w-6 pl-1 text-primary "
aria-hidden="true"
/>
</DialogTitle>
@ -148,44 +150,65 @@ export default function CodeAreaModal({
</DialogHeader>
<Tabs
defaultValue={"0"}
className="w-full h-full overflow-hidden text-center bg-muted rounded-md border"
className="h-full w-full overflow-hidden rounded-md border bg-muted text-center"
onValueChange={(value) => setActiveTab(value)}
>
<div className="flex flex-col items-start h-72 px-2">
<div className="flex h-72 flex-col items-start px-2">
<TabsList>
{tabs.map((tab, index) => (
<TabsTrigger disabled={index === 1 && error?.detail.error === undefined} key={index} value={index.toString()}>
<span className={error?.detail.error !== undefined && index===1 ? "text-destructive" : ""}>{tab.name}</span></TabsTrigger>
<TabsTrigger
disabled={index === 1 && error?.detail.error === undefined}
key={index}
value={index.toString()}
>
<span
className={
error?.detail.error !== undefined && index === 1
? "text-destructive"
: ""
}
>
{tab.name}
</span>
</TabsTrigger>
))}
</TabsList>
{tabs.map((tab, index) => (
<TabsContent
value={index.toString()}
className="overflow-hidden w-full h-full px-4 pb-4 mt-1"
className="mt-1 h-full w-full overflow-hidden px-4 pb-4"
>
{tab.name === "code" ? <div className="h-full w-full">
<AceEditor
value={code}
mode="python"
highlightActiveLine={true}
showPrintMargin={false}
fontSize={14}
showGutter
enableLiveAutocompletion
theme={dark ? "twilight" : "github"}
name="CodeEditor"
onChange={(value) => {
setCode(value);
}}
className="w-full rounded-lg h-full custom-scroll border-[1px] border-gray-300 dark:border-gray-600"
/>
</div> : <div className="w-full h-full bg-red-200 p-2 flex flex-col overflow-scroll custom-scroll text-left">
<h1 className="text-red-600 text-lg">{error?.detail?.error}</h1>
<span className="border border-red-300 w-full"></span>
<div className="text-red-500 text-sm">{error?.detail?.traceback}</div>
</div>}
</TabsContent>))
}
{tab.name === "code" ? (
<div className="h-full w-full">
<AceEditor
value={code}
mode="python"
highlightActiveLine={true}
showPrintMargin={false}
fontSize={14}
showGutter
enableLiveAutocompletion
theme={dark ? "twilight" : "github"}
name="CodeEditor"
onChange={(value) => {
setCode(value);
}}
className="h-full w-full rounded-lg border-[1px] border-gray-300 custom-scroll dark:border-gray-600"
/>
</div>
) : (
<div className="flex h-full w-full flex-col overflow-scroll bg-red-200 p-2 text-left custom-scroll">
<h1 className="text-lg text-red-600">
{error?.detail?.error}
</h1>
<span className="w-full border border-red-300"></span>
<div className="text-sm text-red-500">
{error?.detail?.traceback}
</div>
</div>
)}
</TabsContent>
))}
</div>
</Tabs>
<DialogFooter>

View file

@ -47,8 +47,8 @@ export default function ExportModal() {
<DialogTitle className="flex items-center">
<span className="pr-2">Export</span>
<Download
strokeWidth={1.5}
className="h-6 w-6 text-foreground pl-1"
strokeWidth={1.5}
className="h-6 w-6 pl-1 text-foreground"
aria-hidden="true"
/>
</DialogTitle>

View file

@ -52,20 +52,27 @@ export default function ChatInput({
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",
"p-4 form-input block w-full custom-scroll rounded-md border-gray-300 dark:border-gray-600 pr-16 sm:text-sm"
"form-input block w-full rounded-md border-gray-300 p-4 pr-16 custom-scroll dark:border-gray-600 sm:text-sm"
)}
placeholder={"Send a message..."}
/>
<div className="absolute bottom-2 right-4">
<button className={classNames("p-2 px-1 transition-all duration-300 rounded-md",chatValue == "" ? "text-primary" : " bg-indigo-600 text-background")} disabled={lockChat} onClick={() => sendMessage()}>
<button
className={classNames(
"rounded-md p-2 px-1 transition-all duration-300",
chatValue == "" ? "text-primary" : " bg-indigo-600 text-background"
)}
disabled={lockChat}
onClick={() => sendMessage()}
>
{lockChat ? (
<Lock
className="h-5 w-5 ml-1 mr-1 animate-pulse"
className="ml-1 mr-1 h-5 w-5 animate-pulse"
aria-hidden="true"
/>
) : (
<LucideSend
className="h-5 w-5 mr-2 rotate-[44deg] "
className="mr-2 h-5 w-5 rotate-[44deg] "
aria-hidden="true"
/>
)}

View file

@ -11,6 +11,13 @@ import remarkMath from "remark-math";
import { CodeBlock } from "./codeBlock";
import Convert from "ansi-to-html";
import { User2, MessageSquare } from "lucide-react";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "../../../components/ui/accordion";
import { Badge } from "../../../components/ui/badge";
export default function ChatMessage({
chat,
@ -22,145 +29,173 @@ export default function ChatMessage({
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);
const [template, setTemplate] = useState(chat.template);
return (
<div
className={classNames(
"w-full py-2 px-2 pl-4 pr-9 flex",
chat.isSend
? " bg-border"
: " "
)}
>
<div
className={classNames(
"rounded-full overflow-hidden w-8 h-8 flex items-center my-3 mx-3 justify-center"
)}
>
{!chat.isSend && (
<div className="relative w-8 h-8">
<img
className={
"absolute transition-opacity duration-500 scale-150 " +
(lockChat ? "opacity-100" : "opacity-0")
}
src={lastMessage ? AiIcon : AiIconStill}
/>
<img
className={
"absolute transition-opacity duration-500 scale-150 " +
(lockChat ? "opacity-0" : "opacity-100")
}
src={AiIconStill}
/>
</div>
)}
{chat.isSend && (
<User2 className="w-6 h-6 text-gray-800 dark:text-gray-200" />
)}
</div>
<div
className={classNames(
"flex w-full px-2 py-6 pl-4 pr-9",
chat.isSend ? " bg-border" : " "
)}
>
<div className={classNames("mb-3 ml-3 mr-6 mt-1 ")}>
{!chat.isSend ? (
<div className="w-full text-start flex items-center">
<div className="w-full relative text-start inline-block text-primary text-sm font-normal">
{hidden && chat.thought && chat.thought !== "" && (
<div
onClick={() => setHidden((prev) => !prev)}
className="absolute -top-1 -left-2 cursor-pointer"
>
<MessageSquare className="w-5 h-5 animate-bounce dark:text-white" />
</div>
)}
{chat.thought && chat.thought !== "" && !hidden && (
<div
onClick={() => setHidden((prev) => !prev)}
className=" text-start inline-block rounded-md text-primary 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),
}}
></div>
)}
{chat.thought && chat.thought !== "" && !hidden && <br></br>}
<div className="w-full pb-3 pt-3">
<div className="dark:text-white w-full">
<div className="w-full">
<ReactMarkdown
remarkPlugins={[remarkGfm, remarkMath]}
rehypePlugins={[rehypeMathjax]}
className="markdown prose dark:prose-invert text-primary max-w-full"
components={{
code({ node, inline, className, children, ...props }) {
if (children.length) {
if (children[0] == "▍") {
return (
<span className="animate-pulse cursor-default mt-1">
▍
</span>
);
}
children[0] = (children[0] as string).replace(
"`▍`",
"▍"
);
}
const match = /language-(\w+)/.exec(className || "");
return !inline ? (
<CodeBlock
key={Math.random()}
language={(match && match[1]) || ""}
value={String(children).replace(/\n$/, "")}
{...props}
/>
) : (
<code className={className} {...props}>
{children}
</code>
);
},
}}
>
{message}
</ReactMarkdown>
</div>
{chat.files && (
<div className="my-2 w-full">
{chat.files.map((file, index) => {
return (
<div key={index} className="my-2 w-full">
<FileCard
fileName={"Generated File"}
fileType={file.data_type}
content={file.data}
/>
</div>
);
})}
</div>
)}
</div>
</div>
</div>
<div className="flex h-8 w-8 items-center justify-center overflow-hidden rounded-md bg-[#afe6ef] p-5 text-2xl ">
🤖
</div>
) : (
<div className="w-full flex items-center">
<div className="text-start inline-block">
<span
className="text-primary"
dangerouslySetInnerHTML={{
__html: message.replace(/\n/g, "<br>"),
}}
></span>
</div>
<div className="flex h-8 w-8 items-center justify-center overflow-hidden rounded-md bg-[#aface9] p-5 text-2xl ">
👨‍💻
</div>
)}
</div>
{!chat.isSend ? (
<div className="flex w-full items-center text-start">
<div className="relative inline-block w-full text-start text-sm font-normal text-primary">
{hidden && chat.thought && chat.thought !== "" && (
<div
onClick={() => setHidden((prev) => !prev)}
className="absolute -left-2 -top-1 cursor-pointer"
>
<MessageSquare className="h-5 w-5 animate-bounce dark:text-white" />
</div>
)}
{chat.thought && chat.thought !== "" && !hidden && (
<div
onClick={() => setHidden((prev) => !prev)}
className=" ml-3 inline-block h-full w-[95%] cursor-pointer overflow-scroll rounded-md border
border-gray-300 bg-muted px-2 text-start text-primary scrollbar-hide dark:border-gray-500 dark:bg-gray-800"
dangerouslySetInnerHTML={{
__html: convert.toHtml(chat.thought),
}}
></div>
)}
{chat.thought && chat.thought !== "" && !hidden && <br></br>}
<div className="w-full">
<div className="w-full dark:text-white">
<div className="w-full">
<ReactMarkdown
remarkPlugins={[remarkGfm, remarkMath]}
rehypePlugins={[rehypeMathjax]}
className="markdown prose max-w-full text-primary dark:prose-invert"
components={{
code({ node, inline, className, children, ...props }) {
if (children.length) {
if (children[0] == "▍") {
return (
<span className="mt-1 animate-pulse cursor-default">
▍
</span>
);
}
children[0] = (children[0] as string).replace(
"`▍`",
"▍"
);
}
const match = /language-(\w+)/.exec(className || "");
return !inline ? (
<CodeBlock
key={Math.random()}
language={(match && match[1]) || ""}
value={String(children).replace(/\n$/, "")}
{...props}
/>
) : (
<code className={className} {...props}>
{children}
</code>
);
},
}}
>
{chat.message.toString()}
</ReactMarkdown>
</div>
{chat.files && (
<div className="my-2 w-full">
{chat.files.map((file, index) => {
return (
<div key={index} className="my-2 w-full">
<FileCard
fileName={"Generated File"}
fileType={file.data_type}
content={file.data}
/>
</div>
);
})}
</div>
)}
</div>
</div>
</div>
</div>
) : (
<div className="flex w-full items-center">
<div className="inline-block text-start">
<span className=" break-all text-primary">
<Accordion type="single" collapsible className="mb-4">
<AccordionItem
className=" rounded-md border border-ring/60 bg-muted px-4"
value="prompt"
>
<AccordionTrigger className="flex gap-4 text-base font-semibold">
Initial Prompt
</AccordionTrigger>
<AccordionContent className="max-h-96 overflow-auto break-all p-2 text-base">
{
// Make all the variables that are inside curly braces bold
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
parts.push(
<div>
<div className="my-1 inline-block rounded-md bg-indigo-100 px-2">
<span key={match.index}>
<span className=" text-xs font-semibold text-high-indigo">
{match[1]}
</span>
{chat.message[match[1]] ? (
<span>
&nbsp;&nbsp;{chat.message[match[1]]}
</span>
) : (
""
)}
</span>
</div>
</div>
);
// Update last index
lastIndex = regex.lastIndex;
}
// Push text after the last match
if (lastIndex !== line.length) {
parts.push(line.substring(lastIndex));
}
return <p>{parts}</p>;
})
}
</AccordionContent>
</AccordionItem>
</Accordion>
{chat.message[chat.chatKey]}
</span>
</div>
</div>
)}
</div>
);
}

View file

@ -91,19 +91,23 @@ export default function FormModal({
var isStream = false;
const addChatHistory = (
message: string,
message: string | Object,
isSend: boolean,
chatKey: string,
template?: string,
thought?: string,
files?: Array<any>
) => {
setChatHistory((old) => {
let newChat = _.cloneDeep(old);
if (files) {
newChat.push({ message, isSend, files, thought });
newChat.push({ message, isSend, files, thought, chatKey });
} else if (thought) {
newChat.push({ message, isSend, thought });
newChat.push({ message, isSend, thought, chatKey });
} else if (template) {
newChat.push({ message, isSend, chatKey, template });
} else {
newChat.push({ message, isSend });
newChat.push({ message, isSend, chatKey });
}
return newChat;
});
@ -174,7 +178,9 @@ export default function FormModal({
intermediate_steps?: string;
is_bot: boolean;
message: string;
template: string;
type: string;
chatKey: string;
files?: Array<any>;
}) => {
if (chatItem.message) {
@ -182,14 +188,18 @@ export default function FormModal({
chatItem.files
? {
isSend: !chatItem.is_bot,
message: formatMessage(chatItem.message),
message: chatItem.message,
template: chatItem.template,
thought: chatItem.intermediate_steps,
files: chatItem.files,
chatKey: chatItem.chatKey,
}
: {
isSend: !chatItem.is_bot,
message: formatMessage(chatItem.message),
message: chatItem.message,
template: chatItem.template,
thought: chatItem.intermediate_steps,
chatKey: chatItem.chatKey,
}
);
}
@ -199,7 +209,7 @@ export default function FormModal({
});
}
if (data.type === "start") {
addChatHistory("", false);
addChatHistory("", false, chatKey);
isStream = true;
}
if (data.type === "end") {
@ -319,23 +329,6 @@ export default function FormModal({
ref.current.focus();
}
}, [open]);
function formatMessage(inputs: any): string {
if (inputs) {
if (typeof inputs == "string") return inputs;
// inputs is a object with the keys and values being input_keys and keysValue
// so the formated message is a string with the keys and values separated by ": "
let message = "";
for (const [key, value] of Object.entries(inputs)) {
// make key bold
// dangerouslySetInnerHTML={{
// __html: message.replace(/\n/g, "<br>"),
// }}
message += `<b>${key}</b>: ${value}\n`;
}
return message;
}
return "";
}
function sendMessage() {
if (chatValue !== "") {
@ -344,8 +337,13 @@ export default function FormModal({
setLockChat(true);
let inputs = tabsState[id.current].formKeysData.input_keys;
setChatValue("");
const message = formatMessage(inputs);
addChatHistory(message, true);
const message = inputs;
addChatHistory(
message,
true,
chatKey,
tabsState[flow.id].formKeysData.template
);
sendAll({
...reactFlowInstance.toObject(),
inputs: inputs,
@ -404,7 +402,7 @@ export default function FormModal({
</DialogHeader>
<div className="mt-2 flex h-[80vh] w-full ">
<div className="mr-6 flex h-full w-2/5 flex-col justify-start overflow-auto scrollbar-hide">
<div className="mr-6 flex h-full w-2/6 flex-col justify-start overflow-auto scrollbar-hide">
<div className="flex items-center py-2">
<Variable className=" -ml-px mr-1 h-4 w-4 text-primary"></Variable>
<span className="text-md font-semibold text-primary">
@ -496,7 +494,7 @@ export default function FormModal({
))}
</Accordion>
</div>
<div className="w-full">
<div className="flex w-full flex-1 flex-col">
<div className="relative flex h-full w-full flex-col rounded-md border bg-muted">
<div className="absolute right-3 top-3 z-50">
<button disabled={lockChat} onClick={() => clearChat()}>

View file

@ -1,4 +1,4 @@
import { useContext, useRef, useState } from "react";
import { useContext, useRef, useState, useEffect } from "react";
import { PopUpContext } from "../../contexts/popUpContext";
import { darkContext } from "../../contexts/darkContext";
import { postValidatePrompt } from "../../controllers/API";
@ -14,9 +14,24 @@ import {
} from "../../components/ui/dialog";
import { Button } from "../../components/ui/button";
import { Textarea } from "../../components/ui/textarea";
import { PROMPT_DIALOG_SUBTITLE, TEXT_DIALOG_SUBTITLE } from "../../constants";
import {
HIGHLIGH_CSS,
PROMPT_DIALOG_SUBTITLE,
TEXT_DIALOG_SUBTITLE,
} from "../../constants";
import { FileText } from "lucide-react";
import { APIClassType } from "../../types/api";
import {
INVALID_CHARACTERS,
TypeModal,
classNames,
getRandomKeyByssmm,
regexHighlight,
varHighlightHTML,
} from "../../utils";
import { Badge } from "../../components/ui/badge";
import ShadTooltip from "../../components/ShadTooltipComponent";
import DOMPurify from "dompurify";
export default function GenericModal({
field_name = "",
@ -40,24 +55,122 @@ export default function GenericModal({
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 [wordsHighlightInvalid, setWordsHighlightInvalid] = useState([]);
const [wordsHighlight, setWordsHighlight] = useState([]);
const { dark } = useContext(darkContext);
const { setErrorData, setSuccessData } = useContext(alertContext);
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)
);
setWordsHighlightInvalid(invalid_chars);
setWordsHighlight(filteredWordsHighlight);
}
useEffect(() => {
if (type == TypeModal.PROMPT && inputValue && inputValue != "") {
checkVariables(inputValue);
}
}, []);
const coloredContent = (inputValue || "")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(regexHighlight, varHighlightHTML({ name: "$1" }))
.replace(/\n/g, "<br />");
const TextAreaContentView = () => {
return (
<div
className={HIGHLIGH_CSS}
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(coloredContent) }}
suppressContentEditableWarning={true}
onClick={() => {
setIsEdit(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.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) => {
setIsEdit(true);
return setErrorData({
title: "There is something wrong with this prompt, please review it",
list: [error.response.data.detail],
});
});
}
return (
<Dialog open={true} onOpenChange={setModalOpen}>
<DialogTrigger></DialogTrigger>
<DialogContent className="lg:max-w-[700px]">
<DialogContent className="min-w-[80vw]">
<DialogHeader>
<DialogTitle className="flex items-center">
<span className="pr-2">{myModalTitle}</span>
@ -83,17 +196,76 @@ export default function GenericModal({
</DialogDescription>
</DialogHeader>
<div className="mt-2 flex h-full w-full">
<Textarea
ref={ref}
className=" form-primary h-[300px] w-full "
value={myValue}
onChange={(e) => {
setMyValue(e.target.value);
setValue(e.target.value);
}}
placeholder="Type message here."
/>
{type == TypeModal.PROMPT &&
inputValue &&
inputValue != "" &&
wordsHighlight.length > 0 && (
<>
<div>
<span className="">Variables: </span>
{wordsHighlight.map((word, index) => (
<ShadTooltip
key={getRandomKeyByssmm() + index}
content={word.replace(/[{}]/g, "")}
asChild={false}
delayDuration={1500}
>
<Badge
key={index}
size="lg"
className="m-1 max-w-[40vw] cursor-default truncate p-2.5 text-sm"
>
<div className="relative bottom-[1px]">
<span>
{word.replace(/[{}]/g, "").length > 59
? word.replace(/[{}]/g, "").slice(0, 56) + "..."
: word.replace(/[{}]/g, "")}
</span>
</div>
</Badge>
</ShadTooltip>
))}
</div>
</>
)}
<div
className={classNames(
!isEdit ? "rounded-lg border" : "",
"flex h-[60vh] w-full"
)}
>
{type == TypeModal.PROMPT && isEdit ? (
<Textarea
ref={ref}
className="form-input h-full w-full rounded-lg border-gray-300 focus-visible:ring-1 dark:border-gray-700 dark:bg-gray-900 dark:text-white"
value={inputValue}
onBlur={() => {
blur();
setIsEdit(false);
}}
autoFocus
onChange={(e) => {
setInputValue(e.target.value);
checkVariables(e.target.value);
}}
placeholder="Type message here."
/>
) : type == TypeModal.PROMPT && !isEdit ? (
<TextAreaContentView />
) : type != TypeModal.PROMPT ? (
<Textarea
ref={ref}
className="form-input h-full w-full rounded-lg border-gray-300 focus-visible:ring-1 dark:border-gray-700 dark:bg-gray-900 dark:text-white"
value={inputValue}
onChange={(e) => {
setInputValue(e.target.value);
}}
placeholder="Type message here."
/>
) : (
<></>
)}
</div>
<DialogFooter>
@ -105,39 +277,7 @@ export default function GenericModal({
setModalOpen(false);
break;
case 2:
console.log("postValidatePrompt");
postValidatePrompt(field_name, myValue, nodeClass)
.then((apiReturn) => {
if (apiReturn.data) {
setNodeClass(apiReturn.data.frontend_node);
setModalOpen(false);
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],
});
});
validatePrompt(false);
break;
default:

View file

@ -160,7 +160,10 @@ export default function ImportModal() {
bgColor="bg-medium-emerald "
description={example.description ?? "Prebuilt Examples"}
icon={
<DocumentDuplicateIcon strokeWidth={1.5} className="h-6 w-6 flex-shrink-0" />
<DocumentDuplicateIcon
strokeWidth={1.5}
className="h-6 w-6 flex-shrink-0"
/>
}
onClick={() => {
addFlow(example, false);

View file

@ -1,124 +0,0 @@
import { Dialog, Transition } from "@headlessui/react";
import {
XMarkIcon,
ClipboardDocumentListIcon,
} from "@heroicons/react/24/outline";
import { Fragment, useContext, useRef, useState } from "react";
import { PopUpContext } from "../../contexts/popUpContext";
export default function TextAreaModal({
value,
setValue,
}: {
setValue: (value: string) => void;
value: string | string[];
}) {
const [open, setOpen] = useState(true);
const [myValue, setMyValue] = useState(value);
const { closePopUp, setCloseEdit } = useContext(PopUpContext);
const ref = useRef();
function setModalOpen(x: boolean) {
setOpen(x);
if (x === false) {
setTimeout(() => {
setCloseEdit("textarea");
closePopUp();
}, 300);
}
}
return (
<Transition.Root show={open} appear={true} as={Fragment}>
<Dialog
as="div"
className="relative z-10"
onClose={setModalOpen}
initialFocus={ref}
>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-ring bg-opacity-75 transition-opacity" />
</Transition.Child>
<div className="fixed inset-0 z-10 overflow-y-auto">
<div className="flex h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="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">
<div className=" absolute right-0 top-0 z-50 hidden pr-4 pt-4 sm:block">
<button
type="button"
className="rounded-md text-ring hover:text-accent-foreground"
onClick={() => {
setModalOpen(false);
}}
>
<span className="sr-only">Close</span>
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
</button>
</div>
<div className="flex h-full w-full flex-col items-center justify-center">
<div className="z-10 flex w-full justify-center pb-4 shadow-sm">
<div className="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">
<ClipboardDocumentListIcon
className="h-6 w-6 text-almost-medium-blue"
aria-hidden="true"
/>
</div>
<div className="mt-4 text-center sm:ml-4 sm:text-left">
<Dialog.Title
as="h3"
className="text-lg font-medium leading-10 text-foreground"
>
Edit text
</Dialog.Title>
</div>
</div>
<div className="flex h-full w-full flex-row items-center justify-center gap-4 bg-input p-4">
<div className="flex h-full w-full">
<div className="w-full overflow-hidden rounded-lg bg-background px-4 py-5 shadow sm:p-6">
<textarea
ref={ref}
className="form-input h-full w-full form-primary"
value={myValue}
onChange={(e) => {
setMyValue(e.target.value);
setValue(e.target.value);
}}
/>
</div>
</div>
</div>
<div className="flex w-full flex-row-reverse bg-input px-4 pb-3">
<button
type="button"
className="inline-flex w-full justify-center rounded-md border border-transparent px-4 py-2 text-base font-medium text-background shadow-sm hover:bg-ring focus:outline-none focus:ring-1 focus:ring-ring focus:ring-offset-1 sm:ml-3 sm:w-auto sm:text-sm"
onClick={() => {
setModalOpen(false);
}}
>
Finish editing
</button>
</div>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition.Root>
);
}

View file

@ -135,7 +135,7 @@ export default function ExtraSidebar() {
</div>
</div>
<div className="w-full overflow-auto scrollbar-hide pb-10">
<div className="w-full overflow-auto pb-10 scrollbar-hide">
{Object.keys(dataFilter)
.sort()
.map((d: keyof APIObjectType, i) =>

View file

@ -37,7 +37,7 @@ export type sendAllProps = {
viewport: Viewport;
inputs: any;
chatHistory: { message: string; isSend: boolean }[];
chatHistory: { message: string | object; isSend: boolean }[];
};
export type errorsTypeAPI = {
function: { errors: Array<string> };

View file

@ -3,8 +3,10 @@ import { FlowType } from "../flow";
export type ChatType = { flow: FlowType; reactFlowInstance: ReactFlowInstance };
export type ChatMessageType = {
message: string;
message: string | Object;
template?: string;
isSend: boolean;
thought?: string;
files?: Array<{ data: string; type: string; data_type: string }>;
chatKey: string;
};

View file

@ -142,3 +142,22 @@ export type ShadTooltipProps = {
children: ReactNode;
style?: string;
};
export type ShadToolTipType = {
content?: string;
side?: "top" | "right" | "bottom" | "left";
asChild?: boolean;
children?: ReactElement;
delayDuration?: number;
};
export type TextHighlightType = {
value?: string;
side?: "top" | "right" | "bottom" | "left";
asChild?: boolean;
children?: ReactElement;
delayDuration?: number;
};
export interface IVarHighlightType {
name: string;
}

View file

@ -39,6 +39,11 @@ export type TabsContextType = {
export type TabsState = {
[key: string]: {
isPending: boolean;
formKeysData: {input_keys?: Object, memory_keys?: Array<string>, handle_keys?: Array<string>};
formKeysData: {
template?: string;
input_keys?: Object;
memory_keys?: Array<string>;
handle_keys?: Array<string>;
};
};
};

View file

@ -51,6 +51,7 @@ import {
import { SupabaseIcon } from "./icons/supabase";
import { MongoDBIcon } from "./icons/MongoDB";
import { VertexAIIcon } from "./icons/VertexAI";
import { IVarHighlightType } from "./types/components";
export function classNames(...classes: Array<string>) {
return classes.filter(Boolean).join(" ");
@ -1016,3 +1017,30 @@ export function getRandomKeyByssmm(): string {
const milliseconds = String(now.getMilliseconds()).padStart(3, "0");
return seconds + milliseconds + Math.abs(Math.floor(Math.random() * 10001));
}
export const INVALID_CHARACTERS = [
" ",
",",
".",
":",
";",
"!",
"?",
"/",
"\\",
"(",
")",
"[",
"]",
];
export const regexHighlight = /\{([^}]+)\}/g;
export const varHighlightHTML = ({ name }: IVarHighlightType) => {
const html = `<div class="inline-flex items-center justify-center rounded-md font-medium text-primary bg-muted pb-1">
<span class='opacity-60 pl-1'>{</span>
<span>${name}</span>
<span class='opacity-60 pr-1'>}</span>
</div>`;
return html;
};