Fix formatting issues in code

This commit is contained in:
anovazzi1 2024-01-18 16:48:13 -03:00
commit 65ec1ab3a5
21 changed files with 841 additions and 859 deletions

View file

@ -23,7 +23,6 @@ import useFlowsManagerStore from "./stores/flowsManagerStore";
import { useTypesStore } from "./stores/typesStore";
export default function App() {
const errorData = useAlertStore((state) => state.errorData);
const errorOpen = useAlertStore((state) => state.errorOpen);
const setErrorOpen = useAlertStore((state) => state.setErrorOpen);

View file

@ -131,25 +131,24 @@ export default function ParameterComponent({
if (data.node!.template[name].value !== code) {
takeSnapshot();
}
setNode(data.id, (oldNode) => {
let newNode = cloneDeep(oldNode);
newNode.data = {
...newNode.data,
node: newNodeClass,
description: newNodeClass.description ?? data.node!.description,
display_name: newNodeClass.display_name ?? data.node!.display_name,
};
newNode.data.node.template[name].value = code;
return newNode;
});
updateNodeInternals(data.id);
renderTooltips();
};
@ -273,9 +272,11 @@ export default function ParameterComponent({
<Handle
type={left ? "target" : "source"}
position={left ? Position.Left : Position.Right}
key={proxy
? scapedJSONStringfy({ ...id, proxy })
: scapedJSONStringfy(id)}
key={
proxy
? scapedJSONStringfy({ ...id, proxy })
: scapedJSONStringfy(id)
}
id={
proxy
? scapedJSONStringfy({ ...id, proxy })
@ -348,9 +349,11 @@ export default function ParameterComponent({
<Handle
type={left ? "target" : "source"}
position={left ? Position.Left : Position.Right}
key={proxy
? scapedJSONStringfy({ ...id, proxy })
: scapedJSONStringfy(id)}
key={
proxy
? scapedJSONStringfy({ ...id, proxy })
: scapedJSONStringfy(id)
}
id={
proxy
? scapedJSONStringfy({ ...id, proxy })

View file

@ -1,93 +1,92 @@
import { ReactNode, useContext, useState } from "react";
import NewChatView from "../newChatView";
import { extractTypeFromLongId, removeCountFromString } from "../../utils/utils";
import AccordionComponent from "../AccordionComponent";
import { Badge } from "../ui/badge";
import ShadTooltip from "../ShadTooltipComponent";
import IconComponent from "../genericIconComponent";
import { Textarea } from "../ui/textarea";
import { NodeDataType } from "../../types/flow";
import { ReactNode, useState } from "react";
import useFlowStore from "../../stores/flowStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
import useFlowIOStore from "../../stores/flowsIOStore";
import { NodeDataType } from "../../types/flow";
import { extractTypeFromLongId } from "../../utils/utils";
import AccordionComponent from "../AccordionComponent";
import IconComponent from "../genericIconComponent";
import NewChatView from "../newChatView";
import { Badge } from "../ui/badge";
import { Textarea } from "../ui/textarea";
export default function IOView(): JSX.Element {
const {inputIds, outputIds, updateNodeFlowData } =
useFlowIOStore();
const { reactFlowInstance } = useFlowStore();
const options = inputIds.concat(outputIds);
const [selectedView, setSelectedView] = useState<ReactNode>(handleSelectChange(options[0]));
// if (outputTypes.includes("ChatOutput")) {
// return <NewChatView />;
// }
function handleSelectChange(selected: string) {
const type = extractTypeFromLongId(selected);
return <NewChatView />
switch (type) {
case "ChatOutput":
return <NewChatView />;
break;
}
}
const { inputIds, outputIds, updateNodeFlowData } = useFlowIOStore();
const { reactFlowInstance } = useFlowStore();
const options = inputIds.concat(outputIds);
const [selectedView, setSelectedView] = useState<ReactNode>(
handleSelectChange(options[0])
);
// if (outputTypes.includes("ChatOutput")) {
// return <NewChatView />;
// }
function handleSelectChange(selected: string) {
const type = extractTypeFromLongId(selected);
return <NewChatView />;
switch (type) {
case "ChatOutput":
return <NewChatView />;
break;
}
}
return (
<div className="form-modal-iv-box">
<div className="mr-6 flex h-full w-2/6 flex-col justify-start overflow-auto scrollbar-hide">
<div className="file-component-arrangement">
<IconComponent
name="Variable"
className=" file-component-variable"
/>
<span className="file-component-variables-span text-md">
Inputs
</span>
</div>
{
inputIds.filter(input=>extractTypeFromLongId(input)!=="ChatInput").map((inputId,index) => {
const nodeData:NodeDataType = reactFlowInstance?.getNodes().find(node=>node.id===inputId)?.data;
return (
<div className="file-component-accordion-div" key={index}>
<AccordionComponent
trigger={
<div className="file-component-badge-div">
<Badge variant="gray" size="md">
{inputId}
</Badge>
<div
className="-mb-1"
onClick={(event) => {
event.stopPropagation();
}}
>
</div>
</div>
}
key={index}
keyValue={inputId}
>
<div className="file-component-tab-column">
<Textarea
value={reactFlowInstance?.getNodes().find(node=>node.id===inputId)?.data?.node?.template?.value.value}
className="custom-scroll"
onChange={(e) => {
e.target.value;
if(nodeData){
nodeData.node!.template["value"].value = e.target.value;
updateNodeFlowData(inputId,nodeData);
}
}}
placeholder="Enter text..."
></Textarea>
</div>
</AccordionComponent>
</div>
)
})
}
</div>
{selectedView}
</div>
);
return (
<div className="form-modal-iv-box">
<div className="mr-6 flex h-full w-2/6 flex-col justify-start overflow-auto scrollbar-hide">
<div className="file-component-arrangement">
<IconComponent name="Variable" className=" file-component-variable" />
<span className="file-component-variables-span text-md">Inputs</span>
</div>
{inputIds
.filter((input) => extractTypeFromLongId(input) !== "ChatInput")
.map((inputId, index) => {
const nodeData: NodeDataType = reactFlowInstance
?.getNodes()
.find((node) => node.id === inputId)?.data;
return (
<div className="file-component-accordion-div" key={index}>
<AccordionComponent
trigger={
<div className="file-component-badge-div">
<Badge variant="gray" size="md">
{inputId}
</Badge>
<div
className="-mb-1"
onClick={(event) => {
event.stopPropagation();
}}
></div>
</div>
}
key={index}
keyValue={inputId}
>
<div className="file-component-tab-column">
<Textarea
value={
reactFlowInstance
?.getNodes()
.find((node) => node.id === inputId)?.data?.node
?.template?.value.value
}
className="custom-scroll"
onChange={(e) => {
e.target.value;
if (nodeData) {
nodeData.node!.template["value"].value =
e.target.value;
updateNodeFlowData(inputId, nodeData);
}
}}
placeholder="Enter text..."
></Textarea>
</div>
</AccordionComponent>
</div>
);
})}
</div>
{selectedView}
</div>
);
}

View file

@ -5,123 +5,121 @@ import { chatInputType } from "../../../types/components";
import { classNames } from "../../../utils/utils";
export default function ChatInput({
lockChat,
chatValue,
sendMessage,
setChatValue,
inputRef,
noInput,
lockChat,
chatValue,
sendMessage,
setChatValue,
inputRef,
noInput,
}: chatInputType): JSX.Element {
const [repeate, setRepeate] = useState(1);
useEffect(() => {
if (!lockChat && inputRef.current) {
inputRef.current.focus();
}
}, [lockChat, inputRef]);
const [repeate, setRepeate] = useState(1);
useEffect(() => {
if (!lockChat && inputRef.current) {
inputRef.current.focus();
}
}, [lockChat, inputRef]);
function handleChange(value: number) {
console.log(value);
if (value > 0) {
setRepeate(value);
} else {
setRepeate(1);
}
}
function handleChange(value:number){
console.log(value)
if(value>0){
setRepeate(value);
}
else{
setRepeate(1);
}
}
useEffect(() => {
if (inputRef.current) {
inputRef.current.style.height = "inherit"; // Reset the height
inputRef.current.style.height = `${inputRef.current.scrollHeight}px`; // Set it to the scrollHeight
}
}, [chatValue]);
return (
<div className="relative">
<div className="flex flex-col">
<span className="text-xs">repeate</span>
<input
onChange={(e) => {
handleChange(parseInt(e.target.value));
}}
className="bg-background"
type="number"
min={0}
/>
</div>
<Textarea
onKeyDown={(event) => {
if (event.key === "Enter" && !lockChat && !event.shiftKey) {
sendMessage(repeate);
}
}}
rows={1}
ref={inputRef}
disabled={lockChat || noInput}
style={{
resize: "none",
bottom: `${inputRef?.current?.scrollHeight}px`,
maxHeight: "150px",
overflow: `${
inputRef.current && inputRef.current.scrollHeight > 150
? "auto"
: "hidden"
}`,
}}
value={lockChat ? "Thinking..." : chatValue}
onChange={(event): void => {
setChatValue(event.target.value);
}}
className={classNames(
lockChat
? " form-modal-lock-true bg-input"
: noInput
? "form-modal-no-input bg-input"
: " form-modal-lock-false bg-background",
useEffect(() => {
if (inputRef.current) {
inputRef.current.style.height = "inherit"; // Reset the height
inputRef.current.style.height = `${inputRef.current.scrollHeight}px`; // Set it to the scrollHeight
}
}, [chatValue]);
return (
<div className="relative">
<div className="flex flex-col">
<span className="text-xs">repeate</span>
<input onChange={(e)=>{
handleChange(parseInt(e.target.value));
}} className="bg-background" type="number" min={0}/>
</div>
<Textarea
onKeyDown={(event) => {
if (event.key === "Enter" && !lockChat && !event.shiftKey) {
sendMessage(repeate);
}
}}
rows={1}
ref={inputRef}
disabled={lockChat || noInput}
style={{
resize: "none",
bottom: `${inputRef?.current?.scrollHeight}px`,
maxHeight: "150px",
overflow: `${
inputRef.current && inputRef.current.scrollHeight > 150
? "auto"
: "hidden"
}`,
}}
value={lockChat ? "Thinking..." : chatValue}
onChange={(event): void => {
setChatValue(event.target.value);
}}
className={classNames(
lockChat
? " form-modal-lock-true bg-input"
: noInput
? "form-modal-no-input bg-input"
: " form-modal-lock-false bg-background",
"form-modal-lockchat"
)}
placeholder={
noInput
? "No chat input variables found. Click to run your flow."
: "Send a message..."
}
/>
<div className="form-modal-send-icon-position">
<button
className={classNames(
"form-modal-send-button",
noInput
? "bg-high-indigo text-background"
: chatValue === ""
? "text-primary"
: "bg-chat-send text-background"
)}
disabled={lockChat}
onClick={(): void => sendMessage()}
>
{lockChat ? (
<IconComponent
name="Lock"
className="form-modal-lock-icon"
aria-hidden="true"
/>
) : noInput ? (
<IconComponent
name="Sparkles"
className="form-modal-play-icon"
aria-hidden="true"
/>
) : (
<IconComponent
name="LucideSend"
className="form-modal-send-icon "
aria-hidden="true"
/>
)}
</button>
</div>
</div>
);
"form-modal-lockchat"
)}
placeholder={
noInput
? "No chat input variables found. Click to run your flow."
: "Send a message..."
}
/>
<div className="form-modal-send-icon-position">
<button
className={classNames(
"form-modal-send-button",
noInput
? "bg-high-indigo text-background"
: chatValue === ""
? "text-primary"
: "bg-chat-send text-background"
)}
disabled={lockChat}
onClick={(): void => sendMessage()}
>
{lockChat ? (
<IconComponent
name="Lock"
className="form-modal-lock-icon"
aria-hidden="true"
/>
) : noInput ? (
<IconComponent
name="Sparkles"
className="form-modal-play-icon"
aria-hidden="true"
/>
) : (
<IconComponent
name="LucideSend"
className="form-modal-send-icon "
aria-hidden="true"
/>
)}
</button>
</div>
</div>
);
}

View file

@ -6,74 +6,67 @@ import { programmingLanguages } from "../../../../constants/constants";
import { Props } from "../../../../types/components";
export function CodeBlock({ language, value }: Props): JSX.Element {
const [isCopied, setIsCopied] = useState<Boolean>(false);
const [isCopied, setIsCopied] = useState<Boolean>(false);
const copyToClipboard = (): void => {
if (!navigator.clipboard || !navigator.clipboard.writeText) {
return;
}
const copyToClipboard = (): void => {
if (!navigator.clipboard || !navigator.clipboard.writeText) {
return;
}
navigator.clipboard.writeText(value).then(() => {
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 2000);
});
};
const downloadAsFile = (): void => {
const fileExtension = programmingLanguages[language] || ".file";
const suggestedFileName = `${"generated-code"}${fileExtension}`;
const fileName = window.prompt("enter file name", suggestedFileName);
navigator.clipboard.writeText(value).then(() => {
setIsCopied(true);
if (!fileName) {
// user pressed cancel on prompt
return;
}
const blob = new Blob([value], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.download = fileName;
link.href = url;
link.style.display = "none";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
};
return (
<div className="codeblock font-sans text-[16px]">
<div className="code-block-modal">
<span className="code-block-modal-span">{language}</span>
setTimeout(() => {
setIsCopied(false);
}, 2000);
});
};
const downloadAsFile = (): void => {
const fileExtension = programmingLanguages[language] || ".file";
const suggestedFileName = `${"generated-code"}${fileExtension}`;
const fileName = window.prompt("enter file name", suggestedFileName);
<div className="flex items-center">
<button className="code-block-modal-button" onClick={copyToClipboard}>
{isCopied ? <IconCheck size={18} /> : <IconClipboard size={18} />}
{isCopied ? "Copied!" : "Copy code"}
</button>
<button className="code-block-modal-button" onClick={downloadAsFile}>
<IconDownload size={18} />
</button>
</div>
</div>
if (!fileName) {
// user pressed cancel on prompt
return;
}
const blob = new Blob([value], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.download = fileName;
link.href = url;
link.style.display = "none";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
};
return (
<div className="codeblock font-sans text-[16px]">
<div className="code-block-modal">
<span className="code-block-modal-span">{language}</span>
<div className="flex items-center">
<button className="code-block-modal-button" onClick={copyToClipboard}>
{isCopied ? <IconCheck size={18} /> : <IconClipboard size={18} />}
{isCopied ? "Copied!" : "Copy code"}
</button>
<button className="code-block-modal-button" onClick={downloadAsFile}>
<IconDownload size={18} />
</button>
</div>
</div>
<SyntaxHighlighter
className="overflow-auto"
language={language}
style={oneDark}
customStyle={{ margin: 0 }}
>
{value}
</SyntaxHighlighter>
</div>
);
<SyntaxHighlighter
className="overflow-auto"
language={language}
style={oneDark}
customStyle={{ margin: 0 }}
>
{value}
</SyntaxHighlighter>
</div>
);
}
CodeBlock.displayName = "CodeBlock";

View file

@ -13,222 +13,217 @@ import { chatMessagePropsType } from "../../../types/components";
import { classNames } from "../../../utils/utils";
import FileCard from "../fileComponent";
export default function ChatMessage({
chat,
lockChat,
lastMessage,
chat,
lockChat,
lastMessage,
}: chatMessagePropsType): JSX.Element {
const convert = new Convert({ newline: true });
const [hidden, setHidden] = useState(true);
const template = chat.template;
const [promptOpen, setPromptOpen] = useState(false);
return (
<div
className={classNames("form-modal-chat-position", chat.isSend ? "" : " ")}
>
<div className={classNames("form-modal-chatbot-icon ")}>
{!chat.isSend ? (
<div className="form-modal-chat-image">
<div className="form-modal-chat-bot-icon ">
<img
src={Robot}
className="form-modal-chat-icon-img"
alt="robot_image"
/>
</div>
<span className="text-xs truncate">{chat.sender_name}</span>
</div>
) : (
<div className="form-modal-chat-image">
<div className="form-modal-chat-user-icon ">
<img
src={MaleTechnology}
className="form-modal-chat-icon-img"
alt="male_technology"
/>
</div>
<span className="text-xs truncate">{chat.sender_name}</span>
</div>
)}
</div>
{!chat.isSend ? (
<div className="form-modal-chat-text-position">
<div className="form-modal-chat-text">
{hidden && chat.thought && chat.thought !== "" && (
<div
onClick={(): void => setHidden((prev) => !prev)}
className="form-modal-chat-icon-div"
>
<IconComponent
name="MessageSquare"
className="form-modal-chat-icon"
/>
</div>
)}
{chat.thought && chat.thought !== "" && !hidden && (
<SanitizedHTMLWrapper
className=" form-modal-chat-thought"
content={convert.toHtml(chat.thought)}
onClick={() => setHidden((prev) => !prev)}
/>
)}
{chat.thought && chat.thought !== "" && !hidden && <br></br>}
<div className="w-full">
<div className="w-full dark:text-white">
<div className="w-full">
{useMemo(
() =>
chat.message.toString() === "" && lockChat ? (
<IconComponent
name="MoreHorizontal"
className="h-8 w-8 animate-pulse"
/>
) : (
<ReactMarkdown
remarkPlugins={[remarkGfm, remarkMath]}
rehypePlugins={[rehypeMathjax]}
className="markdown prose min-w-full text-primary word-break-break-word
const convert = new Convert({ newline: true });
const [hidden, setHidden] = useState(true);
const template = chat.template;
const [promptOpen, setPromptOpen] = useState(false);
return (
<div
className={classNames("form-modal-chat-position", chat.isSend ? "" : " ")}
>
<div className={classNames("form-modal-chatbot-icon ")}>
{!chat.isSend ? (
<div className="form-modal-chat-image">
<div className="form-modal-chat-bot-icon ">
<img
src={Robot}
className="form-modal-chat-icon-img"
alt="robot_image"
/>
</div>
<span className="truncate text-xs">{chat.sender_name}</span>
</div>
) : (
<div className="form-modal-chat-image">
<div className="form-modal-chat-user-icon ">
<img
src={MaleTechnology}
className="form-modal-chat-icon-img"
alt="male_technology"
/>
</div>
<span className="truncate text-xs">{chat.sender_name}</span>
</div>
)}
</div>
{!chat.isSend ? (
<div className="form-modal-chat-text-position">
<div className="form-modal-chat-text">
{hidden && chat.thought && chat.thought !== "" && (
<div
onClick={(): void => setHidden((prev) => !prev)}
className="form-modal-chat-icon-div"
>
<IconComponent
name="MessageSquare"
className="form-modal-chat-icon"
/>
</div>
)}
{chat.thought && chat.thought !== "" && !hidden && (
<SanitizedHTMLWrapper
className=" form-modal-chat-thought"
content={convert.toHtml(chat.thought)}
onClick={() => setHidden((prev) => !prev)}
/>
)}
{chat.thought && chat.thought !== "" && !hidden && <br></br>}
<div className="w-full">
<div className="w-full dark:text-white">
<div className="w-full">
{useMemo(
() =>
chat.message.toString() === "" && lockChat ? (
<IconComponent
name="MoreHorizontal"
className="h-8 w-8 animate-pulse"
/>
) : (
<ReactMarkdown
remarkPlugins={[remarkGfm, remarkMath]}
rehypePlugins={[rehypeMathjax]}
className="markdown prose min-w-full text-primary word-break-break-word
dark:prose-invert"
components={{
pre({ node, ...props }) {
return <>{props.children}</>;
},
code: ({
node,
inline,
className,
children,
...props
}) => {
if (children.length) {
if (children[0] === "▍") {
return (
<span className="form-modal-markdown-span">
▍
</span>
);
}
components={{
pre({ node, ...props }) {
return <>{props.children}</>;
},
code: ({
node,
inline,
className,
children,
...props
}) => {
if (children.length) {
if (children[0] === "▍") {
return (
<span className="form-modal-markdown-span">
▍
</span>
);
}
children[0] = (children[0] as string).replace(
"`▍`",
"▍"
);
}
children[0] = (children[0] as string).replace(
"`▍`",
"▍"
);
}
const match = /language-(\w+)/.exec(
className || ""
);
return !inline ? (
<CodeTabsComponent
isMessage
tabs={[
{
name: (match && match[1]) || "",
mode: (match && match[1]) || "",
image:
"https://curl.se/logo/curl-symbol-transparent.png",
language: (match && match[1]) || "",
code: String(children).replace(/\n$/, ""),
},
]}
activeTab={"0"}
setActiveTab={() => {}}
/>
) : (
<code className={className} {...props}>
{children}
</code>
);
},
}}
>
{chat.message.toString()}
</ReactMarkdown>
),
[chat.message, chat.message.toString()]
)}
</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>
{template ? (
<>
<button
className="form-modal-initial-prompt-btn"
onClick={() => {
setPromptOpen((old) => !old);
}}
>
Display Prompt
<IconComponent
name="ChevronDown"
className={
"h-3 w-3 transition-all " + (promptOpen ? "rotate-180" : "")
}
/>
</button>
<span className="prose text-primary word-break-break-word dark:prose-invert">
{promptOpen
? template?.split("\n")?.map((line, index) => {
const regex = /{([^}]+)}/g;
let match;
let parts: Array<JSX.Element | string> = [];
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(
<span className="chat-message-highlight">
{chat.message[match[1]]}
</span>
);
}
const match = /language-(\w+)/.exec(
className || ""
);
return !inline ? (
<CodeTabsComponent
isMessage
tabs={[
{
name: (match && match[1]) || "",
mode: (match && match[1]) || "",
image:
"https://curl.se/logo/curl-symbol-transparent.png",
language: (match && match[1]) || "",
code: String(children).replace(/\n$/, ""),
},
]}
activeTab={"0"}
setActiveTab={() => {}}
/>
) : (
<code className={className} {...props}>
{children}
</code>
);
},
}}
>
{chat.message.toString()}
</ReactMarkdown>
),
[chat.message, chat.message.toString()]
)}
</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>
{template ? (
<>
<button
className="form-modal-initial-prompt-btn"
onClick={() => {
setPromptOpen((old) => !old);
}}
>
Display Prompt
<IconComponent
name="ChevronDown"
className={
"h-3 w-3 transition-all " + (promptOpen ? "rotate-180" : "")
}
/>
</button>
<span className="prose text-primary word-break-break-word dark:prose-invert">
{promptOpen
? template?.split("\n")?.map((line, index) => {
const regex = /{([^}]+)}/g;
let match;
let parts: Array<JSX.Element | string> = [];
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(
<span className="chat-message-highlight">
{chat.message[match[1]]}
</span>
);
}
// 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>;
})
: chat.message.toString()}
</span>
</>
) : (
<span>{chat.message.toString()}</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>;
})
: chat.message.toString()}
</span>
</>
) : (
<span>{chat.message.toString()}</span>
)}
</div>
)}
</div>
);
}

View file

@ -4,84 +4,82 @@ import IconComponent from "../../../components/genericIconComponent";
import { fileCardPropsType } from "../../../types/components";
export default function FileCard({
fileName,
content,
fileType,
fileName,
content,
fileType,
}: fileCardPropsType): JSX.Element {
const handleDownload = (): void => {
const byteArray = new Uint8Array(base64js.toByteArray(content));
const blob = new Blob([byteArray], { type: "application/octet-stream" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = fileName + ".png";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
};
const [isHovered, setIsHovered] = useState(false);
function handleMouseEnter(): void {
setIsHovered(true);
}
function handleMouseLeave(): void {
setIsHovered(false);
}
const handleDownload = (): void => {
const byteArray = new Uint8Array(base64js.toByteArray(content));
const blob = new Blob([byteArray], { type: "application/octet-stream" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = fileName + ".png";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
};
const [isHovered, setIsHovered] = useState(false);
function handleMouseEnter(): void {
setIsHovered(true);
}
function handleMouseLeave(): void {
setIsHovered(false);
}
if (fileType === "image") {
return (
<div
className="relative h-1/4 w-1/4"
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<img
src={`data:image/png;base64,${content}`}
alt="generated image"
className="h-full w-full rounded-lg"
/>
{isHovered && (
<div className={`file-card-modal-image-div `}>
<button
className="file-card-modal-image-button "
onClick={handleDownload}
>
<IconComponent
name="DownloadCloud"
className="h-5 w-5 text-current hover:scale-110"
/>
</button>
</div>
)}
</div>
);
}
if (fileType === "image") {
return (
<div
className="relative h-1/4 w-1/4"
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<img
src={`data:image/png;base64,${content}`}
alt="generated image"
className="h-full w-full rounded-lg"
/>
{isHovered && (
<div className={`file-card-modal-image-div `}>
<button
className="file-card-modal-image-button "
onClick={handleDownload}
>
<IconComponent
name="DownloadCloud"
className="h-5 w-5 text-current hover:scale-110"
/>
</button>
</div>
)}
</div>
);
}
return (
<button onClick={handleDownload} className="file-card-modal-button">
<div className="file-card-modal-div">
ooooooooooooooo{" "}
{fileType === "image" ? (
<img
src={`data:image/png;base64,${content}`}
alt=""
className="h-8 w-8"
/>
) : (
<IconComponent name="File" className="h-8 w-8" />
)}
<div className="file-card-modal-footer">
{" "}
<div className="file-card-modal-name">{fileName}</div>
<div className="file-card-modal-type">{fileType}</div>
</div>
<IconComponent
name="DownloadCloud"
className="ml-auto h-6 w-6 text-current"
/>
</div>
</button>
);
return (
<button onClick={handleDownload} className="file-card-modal-button">
<div className="file-card-modal-div">
ooooooooooooooo{" "}
{fileType === "image" ? (
<img
src={`data:image/png;base64,${content}`}
alt=""
className="h-8 w-8"
/>
) : (
<IconComponent name="File" className="h-8 w-8" />
)}
<div className="file-card-modal-footer">
{" "}
<div className="file-card-modal-name">{fileName}</div>
<div className="file-card-modal-type">{fileType}</div>
</div>
<IconComponent
name="DownloadCloud"
className="ml-auto h-6 w-6 text-current"
/>
</div>
</button>
);
}

View file

@ -1,193 +1,187 @@
import { useContext, useEffect, useRef, useState } from "react";
import { cloneDeep } from "lodash";
import { useEffect, useRef, useState } from "react";
import IconComponent from "../../components/genericIconComponent";
import useAlertStore from "../../stores/alertStore";
import useFlowStore from "../../stores/flowStore";
import useFlowIOStore from "../../stores/flowsIOStore";
import { sendAllProps } from "../../types/api";
import { ChatMessageType } from "../../types/chat";
import {
ChatMessageType,
ChatOutputType,
FlowPoolObjectType,
} from "../../types/chat";
import { NodeType } from "../../types/flow";
import { validateNodes } from "../../utils/reactflowUtils";
import { classNames } from "../../utils/utils";
import ChatInput from "./chatInput";
import ChatMessage from "./chatMessage";
import { cloneDeep } from "lodash";
import IconComponent from "../../components/genericIconComponent";
import {
ChatOutputType,
FlowPoolObjectType,
} from "../../types/chat";
import { validateNodes } from "../../utils/reactflowUtils";
import useAlertStore from "../../stores/alertStore";
import useFlowIOStore from "../../stores/flowsIOStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
import useFlowStore from "../../stores/flowStore";
export default function newChatView(): JSX.Element {
const [chatValue, setChatValue] = useState("");
const [chatHistory, setChatHistory] = useState<ChatMessageType[]>([]);
const { reactFlowInstance } = useFlowStore()
const {
flowPool,
outputIds,
inputIds, inputTypes,
updateNodeFlowData,
buildFlow,
CleanFlowPool
} = useFlowIOStore();
const { setErrorData } = useAlertStore();
const [lockChat, setLockChat] = useState(false);
const messagesRef = useRef<HTMLDivElement | null>(null);
const [chatValue, setChatValue] = useState("");
const [chatHistory, setChatHistory] = useState<ChatMessageType[]>([]);
const { reactFlowInstance } = useFlowStore();
const {
flowPool,
outputIds,
inputIds,
inputTypes,
updateNodeFlowData,
buildFlow,
CleanFlowPool,
} = useFlowIOStore();
const { setErrorData } = useAlertStore();
const [lockChat, setLockChat] = useState(false);
const messagesRef = useRef<HTMLDivElement | null>(null);
//build chat history
useEffect(() => {
const chatOutputResponses: FlowPoolObjectType[] = [];
outputIds.forEach((outputId) => {
if (outputId.includes("ChatOutput")) {
if (flowPool[outputId] && flowPool[outputId].length > 0) {
chatOutputResponses.push(...flowPool[outputId]);
}
}
});
const chatMessages: ChatMessageType[] = chatOutputResponses
.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp))
.filter((output) => !!output.data.artifacts.message)
.map((output) => {
const { sender, message, sender_name } = output.data
.artifacts as ChatOutputType;
console.log(output.data.artifacts);
const is_ai = sender === "Machine";
return { isSend: !is_ai, message, sender_name };
});
setChatHistory(chatMessages);
}, [flowPool, outputIds]);
useEffect(() => {
if (messagesRef.current) {
messagesRef.current.scrollTop = messagesRef.current.scrollHeight;
}
}, [chatHistory]);
//build chat history
useEffect(() => {
const chatOutputResponses: FlowPoolObjectType[] = [];
outputIds.forEach((outputId) => {
if (outputId.includes("ChatOutput")) {
if (flowPool[outputId] && flowPool[outputId].length > 0) {
chatOutputResponses.push(...flowPool[outputId]);
}
}
});
const chatMessages: ChatMessageType[] = chatOutputResponses
.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp)).filter((output) => !!output.data.artifacts.message)
.map((output) => {
const { sender, message, sender_name } = output.data.artifacts as ChatOutputType;
console.log(output.data.artifacts);
const is_ai = sender === "Machine";
return { isSend: !is_ai, message, sender_name };
});
setChatHistory(chatMessages);
}, [flowPool, outputIds]);
useEffect(() => {
if (messagesRef.current) {
messagesRef.current.scrollTop = messagesRef.current.scrollHeight;
}
}, [chatHistory]);
async function sendAll(data: sendAllProps): Promise<void> {}
useEffect(() => {
if (ref.current) ref.current.scrollIntoView({ behavior: "smooth" });
}, [chatHistory]);
const ref = useRef<HTMLDivElement | null>(null);
async function sendAll(data: sendAllProps): Promise<void> { }
useEffect(() => {
if (ref.current) ref.current.scrollIntoView({ behavior: "smooth" });
}, [chatHistory]);
useEffect(() => {
if (ref.current) {
ref.current.focus();
}
}, []);
async function sendMessage(count = 1): Promise<void> {
let nodeValidationErrors = validateNodes(
reactFlowInstance!.getNodes(),
reactFlowInstance!.getEdges()
);
if (nodeValidationErrors.length === 0) {
setLockChat(true);
setChatValue("");
const chatInputId = inputIds.find((inputId) =>
inputId.includes("ChatInput")
);
const chatInput: NodeType = reactFlowInstance?.getNode(
chatInputId!
) as NodeType;
if (chatInput) {
let newData = cloneDeep(chatInput.data);
newData.node!.template["message"].value = chatValue;
chatInput.data = { ...newData };
updateNodeFlowData(chatInputId!, newData);
}
for (let i = 0; i < count; i++) {
await buildFlow().catch((err) => {
console.error(err);
setLockChat(false);
});
}
setLockChat(false);
const ref = useRef<HTMLDivElement | null>(null);
//set chat message in the flow and run build
//@ts-ignore
} else {
setErrorData({
title: "Oops! Looks like you missed some required information:",
list: nodeValidationErrors,
});
}
}
function clearChat(): void {
setChatHistory([]);
CleanFlowPool();
//TODO tell backend to clear chat session
if (lockChat) setLockChat(false);
}
useEffect(() => {
if (ref.current) {
ref.current.focus();
}
}, []);
async function sendMessage(count = 1): Promise<void> {
let nodeValidationErrors = validateNodes(
reactFlowInstance!.getNodes(),
reactFlowInstance!.getEdges()
);
if (nodeValidationErrors.length === 0) {
setLockChat(true);
setChatValue("");
const chatInputId = inputIds.find((inputId) =>
inputId.includes("ChatInput")
);
const chatInput: NodeType = reactFlowInstance?.getNode(
chatInputId!
) as NodeType;
if (chatInput) {
let newData = cloneDeep(chatInput.data);
newData.node!.template["message"].value = chatValue;
chatInput.data = { ...newData };
updateNodeFlowData(chatInputId!, newData);
}
for (let i = 0; i < count; i++) {
await buildFlow().catch((err) => {
console.error(err);
setLockChat(false);
});
}
setLockChat(false);
//set chat message in the flow and run build
//@ts-ignore
} else {
setErrorData({
title: "Oops! Looks like you missed some required information:",
list: nodeValidationErrors,
});
}
}
function clearChat(): void {
setChatHistory([]);
CleanFlowPool();
//TODO tell backend to clear chat session
if (lockChat) setLockChat(false);
}
return (
<div className="eraser-column-arrangement">
<div className="eraser-size">
<div className="eraser-position">
<button disabled={lockChat} onClick={() => clearChat()}>
<IconComponent
name="Eraser"
className={classNames(
"h-5 w-5",
lockChat
? "animate-pulse text-primary"
: "text-primary hover:text-gray-600"
)}
aria-hidden="true"
/>
</button>
</div>
<div ref={messagesRef} className="chat-message-div">
{chatHistory?.length > 0 ? (
chatHistory.map((chat, index) => (
<ChatMessage
lockChat={lockChat}
chat={chat}
lastMessage={chatHistory.length - 1 === index ? true : false}
key={index}
/>
))
) : (
<div className="chat-alert-box">
<span>
👋 <span className="langflow-chat-span">Langflow Chat</span>
</span>
<br />
<div className="langflow-chat-desc">
<span className="langflow-chat-desc-span">
Start a conversation and click the agent's thoughts{" "}
<span>
<IconComponent
name="MessageSquare"
className="mx-1 inline h-5 w-5 animate-bounce "
/>
</span>{" "}
to inspect the chaining process.
</span>
</div>
</div>
)}
<div ref={ref}></div>
</div>
<div className="langflow-chat-input-div">
<div className="langflow-chat-input">
<ChatInput
chatValue={chatValue}
noInput={!inputTypes.includes("ChatInput")}
lockChat={lockChat}
sendMessage={(count) => sendMessage(count)}
setChatValue={(value) => {
setChatValue(value);
}}
inputRef={ref}
/>
</div>
</div>
</div>
</div>
);
return (
<div className="eraser-column-arrangement">
<div className="eraser-size">
<div className="eraser-position">
<button disabled={lockChat} onClick={() => clearChat()}>
<IconComponent
name="Eraser"
className={classNames(
"h-5 w-5",
lockChat
? "animate-pulse text-primary"
: "text-primary hover:text-gray-600"
)}
aria-hidden="true"
/>
</button>
</div>
<div ref={messagesRef} className="chat-message-div">
{chatHistory?.length > 0 ? (
chatHistory.map((chat, index) => (
<ChatMessage
lockChat={lockChat}
chat={chat}
lastMessage={chatHistory.length - 1 === index ? true : false}
key={index}
/>
))
) : (
<div className="chat-alert-box">
<span>
👋 <span className="langflow-chat-span">Langflow Chat</span>
</span>
<br />
<div className="langflow-chat-desc">
<span className="langflow-chat-desc-span">
Start a conversation and click the agent's thoughts{" "}
<span>
<IconComponent
name="MessageSquare"
className="mx-1 inline h-5 w-5 animate-bounce "
/>
</span>{" "}
to inspect the chaining process.
</span>
</div>
</div>
)}
<div ref={ref}></div>
</div>
<div className="langflow-chat-input-div">
<div className="langflow-chat-input">
<ChatInput
chatValue={chatValue}
noInput={!inputTypes.includes("ChatInput")}
lockChat={lockChat}
sendMessage={(count) => sendMessage(count)}
setChatValue={(value) => {
setChatValue(value);
}}
inputRef={ref}
/>
</div>
</div>
</div>
</div>
);
}

View file

@ -1,15 +1,15 @@
export default function TextInputComponent({
text,
emissor,
text,
emissor,
}: {
text: string;
emissor: string;
}) {
return (
<div>
<strong> {emissor}</strong>
<br></br>
<span>{text}</span>
</div>
);
}
text: string;
emissor: string;
}) {
return (
<div>
<strong> {emissor}</strong>
<br></br>
<span>{text}</span>
</div>
);
}

View file

@ -1,15 +1,15 @@
export default function TextOutputComponent({
text,
emissor,
}: {
text: string;
emissor: string;
}) {
return (
<div>
<strong>{emissor}</strong>
<br></br>
<div className="w-80 break-all">{text}</div>
</div>
);
text,
emissor,
}: {
text: string;
emissor: string;
}) {
return (
<div>
<strong>{emissor}</strong>
<br></br>
<div className="w-80 break-all">{text}</div>
</div>
);
}

View file

@ -678,5 +678,5 @@ export const LANGFLOW_SUPPORTED_TYPES = new Set([
export const priorityFields = new Set(["code", "template"]);
export const INPUT_TYPES = new Set(["ChatInput","TextInput"]);
export const INPUT_TYPES = new Set(["ChatInput", "TextInput"]);
export const OUTPUT_TYPES = new Set(["ChatOutput"]);

View file

@ -9,7 +9,6 @@ import ReactFlow, {
OnMove,
OnSelectionChangeParams,
SelectionDragHandler,
addEdge,
updateEdge,
} from "reactflow";
import GenericNode from "../../../../CustomNodes/GenericNode";
@ -19,13 +18,12 @@ import useFlowStore from "../../../../stores/flowStore";
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
import { useTypesStore } from "../../../../stores/typesStore";
import { APIClassType } from "../../../../types/api";
import { FlowType, NodeType, targetHandleType } from "../../../../types/flow";
import { FlowType, NodeType } from "../../../../types/flow";
import {
generateFlow,
generateNodeFromFlow,
getNodeId,
isValidConnection,
scapeJSONParse,
validateSelection,
} from "../../../../utils/reactflowUtils";
import { getRandomName, isWrappedWithClass } from "../../../../utils/utils";
@ -175,8 +173,8 @@ export default function Page({
useEffect(() => {
return () => {
cleanFlow();
}
}, [])
};
}, []);
const onConnectMod = useCallback(
(params: Connection) => {
@ -332,7 +330,7 @@ export default function Page({
<div className="h-full w-full">
<div className="h-full w-full" ref={reactFlowWrapper}>
{Object.keys(templates).length > 0 &&
Object.keys(types).length > 0 ? (
Object.keys(types).length > 0 ? (
<div id="react-flow-id" className="h-full w-full">
<ReactFlow
nodes={nodes}

View file

@ -4,7 +4,7 @@ import {
Select,
SelectContent,
SelectItem,
SelectTrigger
SelectTrigger,
} from "../../../../../components/ui/select-custom";
import { useDarkStore } from "../../../../../stores/darkStore";
import useFlowsManagerStore from "../../../../../stores/flowsManagerStore";

View file

@ -265,7 +265,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
},
getFilterEdge: [],
onConnect: (connection) => {
let newEdges: Edge[] = []
let newEdges: Edge[] = [];
get().setEdges((oldEdges) => {
newEdges = addEdge(
{
@ -287,8 +287,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
oldEdges
);
return newEdges;
})
});
useFlowsManagerStore
.getState()
.autoSaveCurrentFlow(

View file

@ -19,7 +19,6 @@ const useFlowIOStore = create<flowIOStoreType>((set, get) => ({
inputTypes: [],
inputIds: [],
outputIds: [],
setFlowPool: (flowPool) => {
set({ flowPool });
},

View file

@ -43,5 +43,4 @@ export const useTypesStore = create<TypesStoreType>((set, get) => ({
let newChange = typeof change === "function" ? change(get().data) : change;
set({ data: newChange });
},
}));

View file

@ -444,7 +444,7 @@ export type chatInputType = {
};
lockChat: boolean;
noInput: boolean;
sendMessage: (count?:number) => void;
sendMessage: (count?: number) => void;
setChatValue: (value: string) => void;
};

View file

@ -1,81 +1,85 @@
import { AxiosError } from "axios";
import { FlowType } from "../types/flow";
import { getVerticesOrder, postBuildVertex } from "../controllers/API";
import { FlowType } from "../types/flow";
type BuildVerticesParams = {
flow: FlowType; // Assuming FlowType is the type for your flow
nodeId?: string | null; // Assuming nodeId is of type string, and it's optional
onProgressUpdate?: (progress: number) => void; // Replace number with the actual type if it's not a number
onBuildUpdate?: (data: any) => void; // Replace any with the actual type of data
onBuildComplete?: (allNodesValid: boolean) => void;
onBuildError?: (title, list) => void;
};
export async function buildVertices({
flow,
nodeId = null,
onProgressUpdate,
onBuildUpdate,
onBuildComplete,
onBuildError,
}: BuildVerticesParams) {
try {
// Step 1: Get vertices order
console.log(JSON.parse(JSON.stringify(flow)));
let orderResponse = await getVerticesOrder(flow.id);
let verticesOrder = orderResponse.data.ids.flatMap((id) => id);
// Determine the range of vertices to build
let vertexIndex: number | null = null;
if (nodeId) {
vertexIndex = verticesOrder.indexOf(nodeId);
}
let buildRange =
vertexIndex !== null
? verticesOrder.slice(0, vertexIndex + 1)
: verticesOrder;
const buildResults: boolean[] = [];
for (let vertexId of buildRange) {
try {
const buildResponse = await postBuildVertex(flow, vertexId);
const buildData = buildResponse.data;
if (onProgressUpdate) {
const progress =
verticesOrder.indexOf(vertexId) / verticesOrder.length;
onProgressUpdate(progress);
}
if (onBuildUpdate) {
let data = {};
if (!buildData.valid) {
if (onBuildError) {
onBuildError("Error Building Component", [buildData.params]);
}
}
data[buildData.id] = buildData;
onBuildUpdate({ data, id: buildData.id });
}
buildResults.push(buildData.valid);
} catch (error : any) {
if (onBuildError) {
console.log(error)
onBuildError("Error Building Component", [(error as AxiosError<any>).response?.data?.detail??"Unknown Error"]);
}
flow: FlowType; // Assuming FlowType is the type for your flow
nodeId?: string | null; // Assuming nodeId is of type string, and it's optional
onProgressUpdate?: (progress: number) => void; // Replace number with the actual type if it's not a number
onBuildUpdate?: (data: any) => void; // Replace any with the actual type of data
onBuildComplete?: (allNodesValid: boolean) => void;
onBuildError?: (title, list) => void;
};
export async function buildVertices({
flow,
nodeId = null,
onProgressUpdate,
onBuildUpdate,
onBuildComplete,
onBuildError,
}: BuildVerticesParams) {
try {
// Step 1: Get vertices order
console.log(JSON.parse(JSON.stringify(flow)));
let orderResponse = await getVerticesOrder(flow.id);
let verticesOrder = orderResponse.data.ids.flatMap((id) => id);
// Determine the range of vertices to build
let vertexIndex: number | null = null;
if (nodeId) {
vertexIndex = verticesOrder.indexOf(nodeId);
}
let buildRange =
vertexIndex !== null
? verticesOrder.slice(0, vertexIndex + 1)
: verticesOrder;
const buildResults: boolean[] = [];
for (let vertexId of buildRange) {
try {
const buildResponse = await postBuildVertex(flow, vertexId);
const buildData = buildResponse.data;
if (onProgressUpdate) {
const progress =
verticesOrder.indexOf(vertexId) / verticesOrder.length;
onProgressUpdate(progress);
}
}
// Callback for when all vertices have been built
if (onBuildComplete) {
const allNodesValid = buildResults.every((result) => result);
onBuildComplete(allNodesValid);
}
} catch (error:any) {
// Callback for handling errors
if (onBuildError) {
if (onBuildUpdate) {
let data = {};
if (!buildData.valid) {
if (onBuildError) {
onBuildError("Error Building Component", [buildData.params]);
}
}
data[buildData.id] = buildData;
onBuildUpdate({ data, id: buildData.id });
}
buildResults.push(buildData.valid);
} catch (error: any) {
if (onBuildError) {
onBuildError("Error Building Component", [(error as AxiosError<any>).response?.data?.detail??"Unknown Error"]);
console.log(error);
onBuildError("Error Building Component", [
(error as AxiosError<any>).response?.data?.detail ??
"Unknown Error",
]);
}
}
}
}
// Callback for when all vertices have been built
if (onBuildComplete) {
const allNodesValid = buildResults.every((result) => result);
onBuildComplete(allNodesValid);
}
} catch (error: any) {
// Callback for handling errors
if (onBuildError) {
if (onBuildError) {
onBuildError("Error Building Component", [
(error as AxiosError<any>).response?.data?.detail ?? "Unknown Error",
]);
}
}
}
}

View file

@ -477,7 +477,7 @@ export function convertArrayToObj(arrayOfObjects) {
export function hasDuplicateKeys(array) {
const keys = {};
// Transforms an empty object into an object array without opening the 'editNode' modal to prevent the flow build from breaking.
if (!Array.isArray(array)) array = [{"": ""}];
if (!Array.isArray(array)) array = [{ "": "" }];
for (const obj of array) {
for (const key in obj) {
if (keys[key]) {

View file

@ -593,7 +593,7 @@ export function removeCountFromString(input: string): string {
}
export function extractTypeFromLongId(id: string): string {
let [newId,_] = id.split("-");
let [newId, _] = id.split("-");
return newId;
}