Fix formatting issues in code
This commit is contained in:
parent
5e5a64872c
commit
65ec1ab3a5
21 changed files with 841 additions and 859 deletions
|
|
@ -67,7 +67,9 @@ Human: {input}
|
||||||
class MidJourneyPromptChain(BaseCustomConversationChain):
|
class MidJourneyPromptChain(BaseCustomConversationChain):
|
||||||
"""MidJourneyPromptChain is a chain you can use to generate new MidJourney prompts."""
|
"""MidJourneyPromptChain is a chain you can use to generate new MidJourney prompts."""
|
||||||
|
|
||||||
template: Optional[str] = """I want you to act as a prompt generator for Midjourney's artificial intelligence program.
|
template: Optional[
|
||||||
|
str
|
||||||
|
] = """I want you to act as a prompt generator for Midjourney's artificial intelligence program.
|
||||||
Your job is to provide detailed and creative descriptions that will inspire unique and interesting images from the AI.
|
Your job is to provide detailed and creative descriptions that will inspire unique and interesting images from the AI.
|
||||||
Keep in mind that the AI is capable of understanding a wide range of language and can interpret abstract concepts, so feel free to be as imaginative and descriptive as possible.
|
Keep in mind that the AI is capable of understanding a wide range of language and can interpret abstract concepts, so feel free to be as imaginative and descriptive as possible.
|
||||||
For example, you could describe a scene from a futuristic city, or a surreal landscape filled with strange creatures.
|
For example, you could describe a scene from a futuristic city, or a surreal landscape filled with strange creatures.
|
||||||
|
|
@ -81,7 +83,9 @@ class MidJourneyPromptChain(BaseCustomConversationChain):
|
||||||
|
|
||||||
|
|
||||||
class TimeTravelGuideChain(BaseCustomConversationChain):
|
class TimeTravelGuideChain(BaseCustomConversationChain):
|
||||||
template: Optional[str] = """I want you to act as my time travel guide. You are helpful and creative. I will provide you with the historical period or future time I want to visit and you will suggest the best events, sights, or people to experience. Provide the suggestions and any necessary information.
|
template: Optional[
|
||||||
|
str
|
||||||
|
] = """I want you to act as my time travel guide. You are helpful and creative. I will provide you with the historical period or future time I want to visit and you will suggest the best events, sights, or people to experience. Provide the suggestions and any necessary information.
|
||||||
Current conversation:
|
Current conversation:
|
||||||
{history}
|
{history}
|
||||||
Human: {input}
|
Human: {input}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import useFlowsManagerStore from "./stores/flowsManagerStore";
|
||||||
import { useTypesStore } from "./stores/typesStore";
|
import { useTypesStore } from "./stores/typesStore";
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
|
|
||||||
const errorData = useAlertStore((state) => state.errorData);
|
const errorData = useAlertStore((state) => state.errorData);
|
||||||
const errorOpen = useAlertStore((state) => state.errorOpen);
|
const errorOpen = useAlertStore((state) => state.errorOpen);
|
||||||
const setErrorOpen = useAlertStore((state) => state.setErrorOpen);
|
const setErrorOpen = useAlertStore((state) => state.setErrorOpen);
|
||||||
|
|
|
||||||
|
|
@ -131,25 +131,24 @@ export default function ParameterComponent({
|
||||||
if (data.node!.template[name].value !== code) {
|
if (data.node!.template[name].value !== code) {
|
||||||
takeSnapshot();
|
takeSnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
setNode(data.id, (oldNode) => {
|
setNode(data.id, (oldNode) => {
|
||||||
let newNode = cloneDeep(oldNode);
|
let newNode = cloneDeep(oldNode);
|
||||||
|
|
||||||
newNode.data = {
|
newNode.data = {
|
||||||
...newNode.data,
|
...newNode.data,
|
||||||
node: newNodeClass,
|
node: newNodeClass,
|
||||||
description: newNodeClass.description ?? data.node!.description,
|
description: newNodeClass.description ?? data.node!.description,
|
||||||
display_name: newNodeClass.display_name ?? data.node!.display_name,
|
display_name: newNodeClass.display_name ?? data.node!.display_name,
|
||||||
};
|
};
|
||||||
|
|
||||||
newNode.data.node.template[name].value = code;
|
newNode.data.node.template[name].value = code;
|
||||||
|
|
||||||
return newNode;
|
return newNode;
|
||||||
});
|
});
|
||||||
|
|
||||||
updateNodeInternals(data.id);
|
updateNodeInternals(data.id);
|
||||||
|
|
||||||
renderTooltips();
|
renderTooltips();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -273,9 +272,11 @@ export default function ParameterComponent({
|
||||||
<Handle
|
<Handle
|
||||||
type={left ? "target" : "source"}
|
type={left ? "target" : "source"}
|
||||||
position={left ? Position.Left : Position.Right}
|
position={left ? Position.Left : Position.Right}
|
||||||
key={proxy
|
key={
|
||||||
? scapedJSONStringfy({ ...id, proxy })
|
proxy
|
||||||
: scapedJSONStringfy(id)}
|
? scapedJSONStringfy({ ...id, proxy })
|
||||||
|
: scapedJSONStringfy(id)
|
||||||
|
}
|
||||||
id={
|
id={
|
||||||
proxy
|
proxy
|
||||||
? scapedJSONStringfy({ ...id, proxy })
|
? scapedJSONStringfy({ ...id, proxy })
|
||||||
|
|
@ -348,9 +349,11 @@ export default function ParameterComponent({
|
||||||
<Handle
|
<Handle
|
||||||
type={left ? "target" : "source"}
|
type={left ? "target" : "source"}
|
||||||
position={left ? Position.Left : Position.Right}
|
position={left ? Position.Left : Position.Right}
|
||||||
key={proxy
|
key={
|
||||||
? scapedJSONStringfy({ ...id, proxy })
|
proxy
|
||||||
: scapedJSONStringfy(id)}
|
? scapedJSONStringfy({ ...id, proxy })
|
||||||
|
: scapedJSONStringfy(id)
|
||||||
|
}
|
||||||
id={
|
id={
|
||||||
proxy
|
proxy
|
||||||
? scapedJSONStringfy({ ...id, proxy })
|
? scapedJSONStringfy({ ...id, proxy })
|
||||||
|
|
|
||||||
|
|
@ -1,93 +1,92 @@
|
||||||
import { ReactNode, useContext, useState } from "react";
|
import { ReactNode, 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 useFlowStore from "../../stores/flowStore";
|
import useFlowStore from "../../stores/flowStore";
|
||||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
|
||||||
import useFlowIOStore from "../../stores/flowsIOStore";
|
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 {
|
export default function IOView(): JSX.Element {
|
||||||
const {inputIds, outputIds, updateNodeFlowData } =
|
const { inputIds, outputIds, updateNodeFlowData } = useFlowIOStore();
|
||||||
useFlowIOStore();
|
const { reactFlowInstance } = useFlowStore();
|
||||||
const { reactFlowInstance } = useFlowStore();
|
const options = inputIds.concat(outputIds);
|
||||||
const options = inputIds.concat(outputIds);
|
const [selectedView, setSelectedView] = useState<ReactNode>(
|
||||||
const [selectedView, setSelectedView] = useState<ReactNode>(handleSelectChange(options[0]));
|
handleSelectChange(options[0])
|
||||||
// if (outputTypes.includes("ChatOutput")) {
|
);
|
||||||
// return <NewChatView />;
|
// if (outputTypes.includes("ChatOutput")) {
|
||||||
// }
|
// return <NewChatView />;
|
||||||
function handleSelectChange(selected: string) {
|
// }
|
||||||
const type = extractTypeFromLongId(selected);
|
function handleSelectChange(selected: string) {
|
||||||
return <NewChatView />
|
const type = extractTypeFromLongId(selected);
|
||||||
switch (type) {
|
return <NewChatView />;
|
||||||
case "ChatOutput":
|
switch (type) {
|
||||||
return <NewChatView />;
|
case "ChatOutput":
|
||||||
break;
|
return <NewChatView />;
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
return (
|
<div className="form-modal-iv-box">
|
||||||
<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="mr-6 flex h-full w-2/6 flex-col justify-start overflow-auto scrollbar-hide">
|
<div className="file-component-arrangement">
|
||||||
<div className="file-component-arrangement">
|
<IconComponent name="Variable" className=" file-component-variable" />
|
||||||
<IconComponent
|
<span className="file-component-variables-span text-md">Inputs</span>
|
||||||
name="Variable"
|
</div>
|
||||||
className=" file-component-variable"
|
{inputIds
|
||||||
/>
|
.filter((input) => extractTypeFromLongId(input) !== "ChatInput")
|
||||||
<span className="file-component-variables-span text-md">
|
.map((inputId, index) => {
|
||||||
Inputs
|
const nodeData: NodeDataType = reactFlowInstance
|
||||||
</span>
|
?.getNodes()
|
||||||
</div>
|
.find((node) => node.id === inputId)?.data;
|
||||||
{
|
return (
|
||||||
inputIds.filter(input=>extractTypeFromLongId(input)!=="ChatInput").map((inputId,index) => {
|
<div className="file-component-accordion-div" key={index}>
|
||||||
const nodeData:NodeDataType = reactFlowInstance?.getNodes().find(node=>node.id===inputId)?.data;
|
<AccordionComponent
|
||||||
return (
|
trigger={
|
||||||
<div className="file-component-accordion-div" key={index}>
|
<div className="file-component-badge-div">
|
||||||
<AccordionComponent
|
<Badge variant="gray" size="md">
|
||||||
trigger={
|
{inputId}
|
||||||
<div className="file-component-badge-div">
|
</Badge>
|
||||||
<Badge variant="gray" size="md">
|
<div
|
||||||
{inputId}
|
className="-mb-1"
|
||||||
</Badge>
|
onClick={(event) => {
|
||||||
<div
|
event.stopPropagation();
|
||||||
className="-mb-1"
|
}}
|
||||||
onClick={(event) => {
|
></div>
|
||||||
event.stopPropagation();
|
</div>
|
||||||
}}
|
}
|
||||||
>
|
key={index}
|
||||||
</div>
|
keyValue={inputId}
|
||||||
</div>
|
>
|
||||||
}
|
<div className="file-component-tab-column">
|
||||||
key={index}
|
<Textarea
|
||||||
keyValue={inputId}
|
value={
|
||||||
>
|
reactFlowInstance
|
||||||
<div className="file-component-tab-column">
|
?.getNodes()
|
||||||
<Textarea
|
.find((node) => node.id === inputId)?.data?.node
|
||||||
value={reactFlowInstance?.getNodes().find(node=>node.id===inputId)?.data?.node?.template?.value.value}
|
?.template?.value.value
|
||||||
className="custom-scroll"
|
}
|
||||||
onChange={(e) => {
|
className="custom-scroll"
|
||||||
e.target.value;
|
onChange={(e) => {
|
||||||
if(nodeData){
|
e.target.value;
|
||||||
nodeData.node!.template["value"].value = e.target.value;
|
if (nodeData) {
|
||||||
updateNodeFlowData(inputId,nodeData);
|
nodeData.node!.template["value"].value =
|
||||||
}
|
e.target.value;
|
||||||
}}
|
updateNodeFlowData(inputId, nodeData);
|
||||||
placeholder="Enter text..."
|
}
|
||||||
></Textarea>
|
}}
|
||||||
</div>
|
placeholder="Enter text..."
|
||||||
</AccordionComponent>
|
></Textarea>
|
||||||
</div>
|
</div>
|
||||||
)
|
</AccordionComponent>
|
||||||
})
|
</div>
|
||||||
}
|
);
|
||||||
</div>
|
})}
|
||||||
{selectedView}
|
</div>
|
||||||
</div>
|
{selectedView}
|
||||||
);
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,123 +5,121 @@ import { chatInputType } from "../../../types/components";
|
||||||
import { classNames } from "../../../utils/utils";
|
import { classNames } from "../../../utils/utils";
|
||||||
|
|
||||||
export default function ChatInput({
|
export default function ChatInput({
|
||||||
lockChat,
|
lockChat,
|
||||||
chatValue,
|
chatValue,
|
||||||
sendMessage,
|
sendMessage,
|
||||||
setChatValue,
|
setChatValue,
|
||||||
inputRef,
|
inputRef,
|
||||||
noInput,
|
noInput,
|
||||||
}: chatInputType): JSX.Element {
|
}: chatInputType): JSX.Element {
|
||||||
const [repeate, setRepeate] = useState(1);
|
const [repeate, setRepeate] = useState(1);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!lockChat && inputRef.current) {
|
if (!lockChat && inputRef.current) {
|
||||||
inputRef.current.focus();
|
inputRef.current.focus();
|
||||||
}
|
}
|
||||||
}, [lockChat, inputRef]);
|
}, [lockChat, inputRef]);
|
||||||
|
|
||||||
|
function handleChange(value: number) {
|
||||||
|
console.log(value);
|
||||||
|
if (value > 0) {
|
||||||
|
setRepeate(value);
|
||||||
|
} else {
|
||||||
|
setRepeate(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleChange(value:number){
|
useEffect(() => {
|
||||||
console.log(value)
|
if (inputRef.current) {
|
||||||
if(value>0){
|
inputRef.current.style.height = "inherit"; // Reset the height
|
||||||
setRepeate(value);
|
inputRef.current.style.height = `${inputRef.current.scrollHeight}px`; // Set it to the scrollHeight
|
||||||
}
|
}
|
||||||
else{
|
}, [chatValue]);
|
||||||
setRepeate(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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(() => {
|
"form-modal-lockchat"
|
||||||
if (inputRef.current) {
|
)}
|
||||||
inputRef.current.style.height = "inherit"; // Reset the height
|
placeholder={
|
||||||
inputRef.current.style.height = `${inputRef.current.scrollHeight}px`; // Set it to the scrollHeight
|
noInput
|
||||||
}
|
? "No chat input variables found. Click to run your flow."
|
||||||
}, [chatValue]);
|
: "Send a message..."
|
||||||
|
}
|
||||||
|
/>
|
||||||
return (
|
<div className="form-modal-send-icon-position">
|
||||||
<div className="relative">
|
<button
|
||||||
<div className="flex flex-col">
|
className={classNames(
|
||||||
<span className="text-xs">repeate</span>
|
"form-modal-send-button",
|
||||||
<input onChange={(e)=>{
|
noInput
|
||||||
handleChange(parseInt(e.target.value));
|
? "bg-high-indigo text-background"
|
||||||
}} className="bg-background" type="number" min={0}/>
|
: chatValue === ""
|
||||||
|
? "text-primary"
|
||||||
|
: "bg-chat-send text-background"
|
||||||
</div>
|
)}
|
||||||
<Textarea
|
disabled={lockChat}
|
||||||
onKeyDown={(event) => {
|
onClick={(): void => sendMessage()}
|
||||||
if (event.key === "Enter" && !lockChat && !event.shiftKey) {
|
>
|
||||||
sendMessage(repeate);
|
{lockChat ? (
|
||||||
}
|
<IconComponent
|
||||||
}}
|
name="Lock"
|
||||||
rows={1}
|
className="form-modal-lock-icon"
|
||||||
ref={inputRef}
|
aria-hidden="true"
|
||||||
disabled={lockChat || noInput}
|
/>
|
||||||
style={{
|
) : noInput ? (
|
||||||
resize: "none",
|
<IconComponent
|
||||||
bottom: `${inputRef?.current?.scrollHeight}px`,
|
name="Sparkles"
|
||||||
maxHeight: "150px",
|
className="form-modal-play-icon"
|
||||||
overflow: `${
|
aria-hidden="true"
|
||||||
inputRef.current && inputRef.current.scrollHeight > 150
|
/>
|
||||||
? "auto"
|
) : (
|
||||||
: "hidden"
|
<IconComponent
|
||||||
}`,
|
name="LucideSend"
|
||||||
}}
|
className="form-modal-send-icon "
|
||||||
value={lockChat ? "Thinking..." : chatValue}
|
aria-hidden="true"
|
||||||
onChange={(event): void => {
|
/>
|
||||||
setChatValue(event.target.value);
|
)}
|
||||||
}}
|
</button>
|
||||||
className={classNames(
|
</div>
|
||||||
lockChat
|
</div>
|
||||||
? " 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>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,74 +6,67 @@ import { programmingLanguages } from "../../../../constants/constants";
|
||||||
import { Props } from "../../../../types/components";
|
import { Props } from "../../../../types/components";
|
||||||
|
|
||||||
export function CodeBlock({ language, value }: Props): JSX.Element {
|
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 => {
|
navigator.clipboard.writeText(value).then(() => {
|
||||||
if (!navigator.clipboard || !navigator.clipboard.writeText) {
|
setIsCopied(true);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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(() => {
|
if (!fileName) {
|
||||||
setIsCopied(true);
|
// 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(() => {
|
<div className="flex items-center">
|
||||||
setIsCopied(false);
|
<button className="code-block-modal-button" onClick={copyToClipboard}>
|
||||||
}, 2000);
|
{isCopied ? <IconCheck size={18} /> : <IconClipboard size={18} />}
|
||||||
});
|
{isCopied ? "Copied!" : "Copy code"}
|
||||||
};
|
</button>
|
||||||
const downloadAsFile = (): void => {
|
<button className="code-block-modal-button" onClick={downloadAsFile}>
|
||||||
const fileExtension = programmingLanguages[language] || ".file";
|
<IconDownload size={18} />
|
||||||
const suggestedFileName = `${"generated-code"}${fileExtension}`;
|
</button>
|
||||||
const fileName = window.prompt("enter file name", suggestedFileName);
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<SyntaxHighlighter
|
||||||
if (!fileName) {
|
className="overflow-auto"
|
||||||
// user pressed cancel on prompt
|
language={language}
|
||||||
return;
|
style={oneDark}
|
||||||
}
|
customStyle={{ margin: 0 }}
|
||||||
|
>
|
||||||
|
{value}
|
||||||
const blob = new Blob([value], { type: "text/plain" });
|
</SyntaxHighlighter>
|
||||||
const url = URL.createObjectURL(blob);
|
</div>
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
CodeBlock.displayName = "CodeBlock";
|
CodeBlock.displayName = "CodeBlock";
|
||||||
|
|
|
||||||
|
|
@ -13,222 +13,217 @@ import { chatMessagePropsType } from "../../../types/components";
|
||||||
import { classNames } from "../../../utils/utils";
|
import { classNames } from "../../../utils/utils";
|
||||||
import FileCard from "../fileComponent";
|
import FileCard from "../fileComponent";
|
||||||
|
|
||||||
|
|
||||||
export default function ChatMessage({
|
export default function ChatMessage({
|
||||||
chat,
|
chat,
|
||||||
lockChat,
|
lockChat,
|
||||||
lastMessage,
|
lastMessage,
|
||||||
}: chatMessagePropsType): JSX.Element {
|
}: chatMessagePropsType): JSX.Element {
|
||||||
const convert = new Convert({ newline: true });
|
const convert = new Convert({ newline: true });
|
||||||
const [hidden, setHidden] = useState(true);
|
const [hidden, setHidden] = useState(true);
|
||||||
const template = chat.template;
|
const template = chat.template;
|
||||||
const [promptOpen, setPromptOpen] = useState(false);
|
const [promptOpen, setPromptOpen] = useState(false);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames("form-modal-chat-position", chat.isSend ? "" : " ")}
|
className={classNames("form-modal-chat-position", chat.isSend ? "" : " ")}
|
||||||
>
|
>
|
||||||
<div className={classNames("form-modal-chatbot-icon ")}>
|
<div className={classNames("form-modal-chatbot-icon ")}>
|
||||||
{!chat.isSend ? (
|
{!chat.isSend ? (
|
||||||
<div className="form-modal-chat-image">
|
<div className="form-modal-chat-image">
|
||||||
<div className="form-modal-chat-bot-icon ">
|
<div className="form-modal-chat-bot-icon ">
|
||||||
<img
|
<img
|
||||||
src={Robot}
|
src={Robot}
|
||||||
className="form-modal-chat-icon-img"
|
className="form-modal-chat-icon-img"
|
||||||
alt="robot_image"
|
alt="robot_image"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs truncate">{chat.sender_name}</span>
|
<span className="truncate text-xs">{chat.sender_name}</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="form-modal-chat-image">
|
<div className="form-modal-chat-image">
|
||||||
<div className="form-modal-chat-user-icon ">
|
<div className="form-modal-chat-user-icon ">
|
||||||
<img
|
<img
|
||||||
src={MaleTechnology}
|
src={MaleTechnology}
|
||||||
className="form-modal-chat-icon-img"
|
className="form-modal-chat-icon-img"
|
||||||
alt="male_technology"
|
alt="male_technology"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs truncate">{chat.sender_name}</span>
|
<span className="truncate text-xs">{chat.sender_name}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!chat.isSend ? (
|
{!chat.isSend ? (
|
||||||
<div className="form-modal-chat-text-position">
|
<div className="form-modal-chat-text-position">
|
||||||
<div className="form-modal-chat-text">
|
<div className="form-modal-chat-text">
|
||||||
{hidden && chat.thought && chat.thought !== "" && (
|
{hidden && chat.thought && chat.thought !== "" && (
|
||||||
<div
|
<div
|
||||||
onClick={(): void => setHidden((prev) => !prev)}
|
onClick={(): void => setHidden((prev) => !prev)}
|
||||||
className="form-modal-chat-icon-div"
|
className="form-modal-chat-icon-div"
|
||||||
>
|
>
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="MessageSquare"
|
name="MessageSquare"
|
||||||
className="form-modal-chat-icon"
|
className="form-modal-chat-icon"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{chat.thought && chat.thought !== "" && !hidden && (
|
{chat.thought && chat.thought !== "" && !hidden && (
|
||||||
<SanitizedHTMLWrapper
|
<SanitizedHTMLWrapper
|
||||||
className=" form-modal-chat-thought"
|
className=" form-modal-chat-thought"
|
||||||
content={convert.toHtml(chat.thought)}
|
content={convert.toHtml(chat.thought)}
|
||||||
onClick={() => setHidden((prev) => !prev)}
|
onClick={() => setHidden((prev) => !prev)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{chat.thought && chat.thought !== "" && !hidden && <br></br>}
|
{chat.thought && chat.thought !== "" && !hidden && <br></br>}
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="w-full dark:text-white">
|
<div className="w-full dark:text-white">
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
{useMemo(
|
{useMemo(
|
||||||
() =>
|
() =>
|
||||||
chat.message.toString() === "" && lockChat ? (
|
chat.message.toString() === "" && lockChat ? (
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="MoreHorizontal"
|
name="MoreHorizontal"
|
||||||
className="h-8 w-8 animate-pulse"
|
className="h-8 w-8 animate-pulse"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ReactMarkdown
|
<ReactMarkdown
|
||||||
remarkPlugins={[remarkGfm, remarkMath]}
|
remarkPlugins={[remarkGfm, remarkMath]}
|
||||||
rehypePlugins={[rehypeMathjax]}
|
rehypePlugins={[rehypeMathjax]}
|
||||||
className="markdown prose min-w-full text-primary word-break-break-word
|
className="markdown prose min-w-full text-primary word-break-break-word
|
||||||
dark:prose-invert"
|
dark:prose-invert"
|
||||||
components={{
|
components={{
|
||||||
pre({ node, ...props }) {
|
pre({ node, ...props }) {
|
||||||
return <>{props.children}</>;
|
return <>{props.children}</>;
|
||||||
},
|
},
|
||||||
code: ({
|
code: ({
|
||||||
node,
|
node,
|
||||||
inline,
|
inline,
|
||||||
className,
|
className,
|
||||||
children,
|
children,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
if (children.length) {
|
if (children.length) {
|
||||||
if (children[0] === "▍") {
|
if (children[0] === "▍") {
|
||||||
return (
|
return (
|
||||||
<span className="form-modal-markdown-span">
|
<span className="form-modal-markdown-span">
|
||||||
▍
|
▍
|
||||||
</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(
|
// Update last index
|
||||||
className || ""
|
lastIndex = regex.lastIndex;
|
||||||
);
|
}
|
||||||
|
// Push text after the last match
|
||||||
|
if (lastIndex !== line.length) {
|
||||||
return !inline ? (
|
parts.push(line.substring(lastIndex));
|
||||||
<CodeTabsComponent
|
}
|
||||||
isMessage
|
return <p>{parts}</p>;
|
||||||
tabs={[
|
})
|
||||||
{
|
: chat.message.toString()}
|
||||||
name: (match && match[1]) || "",
|
</span>
|
||||||
mode: (match && match[1]) || "",
|
</>
|
||||||
image:
|
) : (
|
||||||
"https://curl.se/logo/curl-symbol-transparent.png",
|
<span>{chat.message.toString()}</span>
|
||||||
language: (match && match[1]) || "",
|
)}
|
||||||
code: String(children).replace(/\n$/, ""),
|
</div>
|
||||||
},
|
)}
|
||||||
]}
|
</div>
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,84 +4,82 @@ import IconComponent from "../../../components/genericIconComponent";
|
||||||
import { fileCardPropsType } from "../../../types/components";
|
import { fileCardPropsType } from "../../../types/components";
|
||||||
|
|
||||||
export default function FileCard({
|
export default function FileCard({
|
||||||
fileName,
|
fileName,
|
||||||
content,
|
content,
|
||||||
fileType,
|
fileType,
|
||||||
}: fileCardPropsType): JSX.Element {
|
}: fileCardPropsType): JSX.Element {
|
||||||
const handleDownload = (): void => {
|
const handleDownload = (): void => {
|
||||||
const byteArray = new Uint8Array(base64js.toByteArray(content));
|
const byteArray = new Uint8Array(base64js.toByteArray(content));
|
||||||
const blob = new Blob([byteArray], { type: "application/octet-stream" });
|
const blob = new Blob([byteArray], { type: "application/octet-stream" });
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
link.href = url;
|
link.href = url;
|
||||||
link.download = fileName + ".png";
|
link.download = fileName + ".png";
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link);
|
||||||
link.click();
|
link.click();
|
||||||
document.body.removeChild(link);
|
document.body.removeChild(link);
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
};
|
};
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
function handleMouseEnter(): void {
|
function handleMouseEnter(): void {
|
||||||
setIsHovered(true);
|
setIsHovered(true);
|
||||||
}
|
}
|
||||||
function handleMouseLeave(): void {
|
function handleMouseLeave(): void {
|
||||||
setIsHovered(false);
|
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 (
|
||||||
return (
|
<button onClick={handleDownload} className="file-card-modal-button">
|
||||||
<div
|
<div className="file-card-modal-div">
|
||||||
className="relative h-1/4 w-1/4"
|
ooooooooooooooo{" "}
|
||||||
onMouseEnter={handleMouseEnter}
|
{fileType === "image" ? (
|
||||||
onMouseLeave={handleMouseLeave}
|
<img
|
||||||
>
|
src={`data:image/png;base64,${content}`}
|
||||||
<img
|
alt=""
|
||||||
src={`data:image/png;base64,${content}`}
|
className="h-8 w-8"
|
||||||
alt="generated image"
|
/>
|
||||||
className="h-full w-full rounded-lg"
|
) : (
|
||||||
/>
|
<IconComponent name="File" className="h-8 w-8" />
|
||||||
{isHovered && (
|
)}
|
||||||
<div className={`file-card-modal-image-div `}>
|
<div className="file-card-modal-footer">
|
||||||
<button
|
{" "}
|
||||||
className="file-card-modal-image-button "
|
<div className="file-card-modal-name">{fileName}</div>
|
||||||
onClick={handleDownload}
|
<div className="file-card-modal-type">{fileType}</div>
|
||||||
>
|
</div>
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="DownloadCloud"
|
name="DownloadCloud"
|
||||||
className="h-5 w-5 text-current hover:scale-110"
|
className="ml-auto h-6 w-6 text-current"
|
||||||
/>
|
/>
|
||||||
</button>
|
</div>
|
||||||
</div>
|
</button>
|
||||||
)}
|
);
|
||||||
</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>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 { sendAllProps } from "../../types/api";
|
||||||
import { ChatMessageType } from "../../types/chat";
|
import {
|
||||||
|
ChatMessageType,
|
||||||
|
ChatOutputType,
|
||||||
|
FlowPoolObjectType,
|
||||||
|
} from "../../types/chat";
|
||||||
import { NodeType } from "../../types/flow";
|
import { NodeType } from "../../types/flow";
|
||||||
|
import { validateNodes } from "../../utils/reactflowUtils";
|
||||||
import { classNames } from "../../utils/utils";
|
import { classNames } from "../../utils/utils";
|
||||||
import ChatInput from "./chatInput";
|
import ChatInput from "./chatInput";
|
||||||
import ChatMessage from "./chatMessage";
|
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 {
|
export default function newChatView(): JSX.Element {
|
||||||
const [chatValue, setChatValue] = useState("");
|
const [chatValue, setChatValue] = useState("");
|
||||||
const [chatHistory, setChatHistory] = useState<ChatMessageType[]>([]);
|
const [chatHistory, setChatHistory] = useState<ChatMessageType[]>([]);
|
||||||
const { reactFlowInstance } = useFlowStore()
|
const { reactFlowInstance } = useFlowStore();
|
||||||
const {
|
const {
|
||||||
flowPool,
|
flowPool,
|
||||||
outputIds,
|
outputIds,
|
||||||
inputIds, inputTypes,
|
inputIds,
|
||||||
updateNodeFlowData,
|
inputTypes,
|
||||||
buildFlow,
|
updateNodeFlowData,
|
||||||
CleanFlowPool
|
buildFlow,
|
||||||
} = useFlowIOStore();
|
CleanFlowPool,
|
||||||
const { setErrorData } = useAlertStore();
|
} = useFlowIOStore();
|
||||||
const [lockChat, setLockChat] = useState(false);
|
const { setErrorData } = useAlertStore();
|
||||||
const messagesRef = useRef<HTMLDivElement | null>(null);
|
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
|
async function sendAll(data: sendAllProps): Promise<void> {}
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const chatOutputResponses: FlowPoolObjectType[] = [];
|
if (ref.current) ref.current.scrollIntoView({ behavior: "smooth" });
|
||||||
outputIds.forEach((outputId) => {
|
}, [chatHistory]);
|
||||||
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]);
|
|
||||||
|
|
||||||
|
const ref = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
async function sendAll(data: sendAllProps): Promise<void> { }
|
useEffect(() => {
|
||||||
useEffect(() => {
|
if (ref.current) {
|
||||||
if (ref.current) ref.current.scrollIntoView({ behavior: "smooth" });
|
ref.current.focus();
|
||||||
}, [chatHistory]);
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
useEffect(() => {
|
<div className="eraser-column-arrangement">
|
||||||
if (ref.current) {
|
<div className="eraser-size">
|
||||||
ref.current.focus();
|
<div className="eraser-position">
|
||||||
}
|
<button disabled={lockChat} onClick={() => clearChat()}>
|
||||||
}, []);
|
<IconComponent
|
||||||
|
name="Eraser"
|
||||||
|
className={classNames(
|
||||||
async function sendMessage(count = 1): Promise<void> {
|
"h-5 w-5",
|
||||||
let nodeValidationErrors = validateNodes(
|
lockChat
|
||||||
reactFlowInstance!.getNodes(),
|
? "animate-pulse text-primary"
|
||||||
reactFlowInstance!.getEdges()
|
: "text-primary hover:text-gray-600"
|
||||||
);
|
)}
|
||||||
if (nodeValidationErrors.length === 0) {
|
aria-hidden="true"
|
||||||
setLockChat(true);
|
/>
|
||||||
setChatValue("");
|
</button>
|
||||||
const chatInputId = inputIds.find((inputId) =>
|
</div>
|
||||||
inputId.includes("ChatInput")
|
<div ref={messagesRef} className="chat-message-div">
|
||||||
);
|
{chatHistory?.length > 0 ? (
|
||||||
const chatInput: NodeType = reactFlowInstance?.getNode(
|
chatHistory.map((chat, index) => (
|
||||||
chatInputId!
|
<ChatMessage
|
||||||
) as NodeType;
|
lockChat={lockChat}
|
||||||
if (chatInput) {
|
chat={chat}
|
||||||
let newData = cloneDeep(chatInput.data);
|
lastMessage={chatHistory.length - 1 === index ? true : false}
|
||||||
newData.node!.template["message"].value = chatValue;
|
key={index}
|
||||||
chatInput.data = { ...newData };
|
/>
|
||||||
updateNodeFlowData(chatInputId!, newData);
|
))
|
||||||
}
|
) : (
|
||||||
for (let i = 0; i < count; i++) {
|
<div className="chat-alert-box">
|
||||||
await buildFlow().catch((err) => {
|
<span>
|
||||||
console.error(err);
|
👋 <span className="langflow-chat-span">Langflow Chat</span>
|
||||||
setLockChat(false);
|
</span>
|
||||||
});
|
<br />
|
||||||
}
|
<div className="langflow-chat-desc">
|
||||||
setLockChat(false);
|
<span className="langflow-chat-desc-span">
|
||||||
|
Start a conversation and click the agent's thoughts{" "}
|
||||||
|
<span>
|
||||||
//set chat message in the flow and run build
|
<IconComponent
|
||||||
//@ts-ignore
|
name="MessageSquare"
|
||||||
} else {
|
className="mx-1 inline h-5 w-5 animate-bounce "
|
||||||
setErrorData({
|
/>
|
||||||
title: "Oops! Looks like you missed some required information:",
|
</span>{" "}
|
||||||
list: nodeValidationErrors,
|
to inspect the chaining process.
|
||||||
});
|
</span>
|
||||||
}
|
</div>
|
||||||
}
|
</div>
|
||||||
function clearChat(): void {
|
)}
|
||||||
setChatHistory([]);
|
<div ref={ref}></div>
|
||||||
CleanFlowPool();
|
</div>
|
||||||
//TODO tell backend to clear chat session
|
<div className="langflow-chat-input-div">
|
||||||
if (lockChat) setLockChat(false);
|
<div className="langflow-chat-input">
|
||||||
}
|
<ChatInput
|
||||||
|
chatValue={chatValue}
|
||||||
|
noInput={!inputTypes.includes("ChatInput")}
|
||||||
return (
|
lockChat={lockChat}
|
||||||
<div className="eraser-column-arrangement">
|
sendMessage={(count) => sendMessage(count)}
|
||||||
<div className="eraser-size">
|
setChatValue={(value) => {
|
||||||
<div className="eraser-position">
|
setChatValue(value);
|
||||||
<button disabled={lockChat} onClick={() => clearChat()}>
|
}}
|
||||||
<IconComponent
|
inputRef={ref}
|
||||||
name="Eraser"
|
/>
|
||||||
className={classNames(
|
</div>
|
||||||
"h-5 w-5",
|
</div>
|
||||||
lockChat
|
</div>
|
||||||
? "animate-pulse text-primary"
|
</div>
|
||||||
: "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>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
export default function TextInputComponent({
|
export default function TextInputComponent({
|
||||||
text,
|
text,
|
||||||
emissor,
|
emissor,
|
||||||
}: {
|
}: {
|
||||||
text: string;
|
text: string;
|
||||||
emissor: string;
|
emissor: string;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<strong> {emissor}</strong>
|
<strong> {emissor}</strong>
|
||||||
<br></br>
|
<br></br>
|
||||||
<span>{text}</span>
|
<span>{text}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
export default function TextOutputComponent({
|
export default function TextOutputComponent({
|
||||||
text,
|
text,
|
||||||
emissor,
|
emissor,
|
||||||
}: {
|
}: {
|
||||||
text: string;
|
text: string;
|
||||||
emissor: string;
|
emissor: string;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<strong>{emissor}</strong>
|
<strong>{emissor}</strong>
|
||||||
<br></br>
|
<br></br>
|
||||||
<div className="w-80 break-all">{text}</div>
|
<div className="w-80 break-all">{text}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -678,5 +678,5 @@ export const LANGFLOW_SUPPORTED_TYPES = new Set([
|
||||||
|
|
||||||
export const priorityFields = new Set(["code", "template"]);
|
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"]);
|
export const OUTPUT_TYPES = new Set(["ChatOutput"]);
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import ReactFlow, {
|
||||||
OnMove,
|
OnMove,
|
||||||
OnSelectionChangeParams,
|
OnSelectionChangeParams,
|
||||||
SelectionDragHandler,
|
SelectionDragHandler,
|
||||||
addEdge,
|
|
||||||
updateEdge,
|
updateEdge,
|
||||||
} from "reactflow";
|
} from "reactflow";
|
||||||
import GenericNode from "../../../../CustomNodes/GenericNode";
|
import GenericNode from "../../../../CustomNodes/GenericNode";
|
||||||
|
|
@ -19,13 +18,12 @@ import useFlowStore from "../../../../stores/flowStore";
|
||||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||||
import { useTypesStore } from "../../../../stores/typesStore";
|
import { useTypesStore } from "../../../../stores/typesStore";
|
||||||
import { APIClassType } from "../../../../types/api";
|
import { APIClassType } from "../../../../types/api";
|
||||||
import { FlowType, NodeType, targetHandleType } from "../../../../types/flow";
|
import { FlowType, NodeType } from "../../../../types/flow";
|
||||||
import {
|
import {
|
||||||
generateFlow,
|
generateFlow,
|
||||||
generateNodeFromFlow,
|
generateNodeFromFlow,
|
||||||
getNodeId,
|
getNodeId,
|
||||||
isValidConnection,
|
isValidConnection,
|
||||||
scapeJSONParse,
|
|
||||||
validateSelection,
|
validateSelection,
|
||||||
} from "../../../../utils/reactflowUtils";
|
} from "../../../../utils/reactflowUtils";
|
||||||
import { getRandomName, isWrappedWithClass } from "../../../../utils/utils";
|
import { getRandomName, isWrappedWithClass } from "../../../../utils/utils";
|
||||||
|
|
@ -175,8 +173,8 @@ export default function Page({
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
cleanFlow();
|
cleanFlow();
|
||||||
}
|
};
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
const onConnectMod = useCallback(
|
const onConnectMod = useCallback(
|
||||||
(params: Connection) => {
|
(params: Connection) => {
|
||||||
|
|
@ -332,7 +330,7 @@ export default function Page({
|
||||||
<div className="h-full w-full">
|
<div className="h-full w-full">
|
||||||
<div className="h-full w-full" ref={reactFlowWrapper}>
|
<div className="h-full w-full" ref={reactFlowWrapper}>
|
||||||
{Object.keys(templates).length > 0 &&
|
{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">
|
<div id="react-flow-id" className="h-full w-full">
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
SelectTrigger
|
SelectTrigger,
|
||||||
} from "../../../../../components/ui/select-custom";
|
} from "../../../../../components/ui/select-custom";
|
||||||
import { useDarkStore } from "../../../../../stores/darkStore";
|
import { useDarkStore } from "../../../../../stores/darkStore";
|
||||||
import useFlowsManagerStore from "../../../../../stores/flowsManagerStore";
|
import useFlowsManagerStore from "../../../../../stores/flowsManagerStore";
|
||||||
|
|
|
||||||
|
|
@ -265,7 +265,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
},
|
},
|
||||||
getFilterEdge: [],
|
getFilterEdge: [],
|
||||||
onConnect: (connection) => {
|
onConnect: (connection) => {
|
||||||
let newEdges: Edge[] = []
|
let newEdges: Edge[] = [];
|
||||||
get().setEdges((oldEdges) => {
|
get().setEdges((oldEdges) => {
|
||||||
newEdges = addEdge(
|
newEdges = addEdge(
|
||||||
{
|
{
|
||||||
|
|
@ -287,8 +287,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
oldEdges
|
oldEdges
|
||||||
);
|
);
|
||||||
return newEdges;
|
return newEdges;
|
||||||
|
});
|
||||||
})
|
|
||||||
useFlowsManagerStore
|
useFlowsManagerStore
|
||||||
.getState()
|
.getState()
|
||||||
.autoSaveCurrentFlow(
|
.autoSaveCurrentFlow(
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ const useFlowIOStore = create<flowIOStoreType>((set, get) => ({
|
||||||
inputTypes: [],
|
inputTypes: [],
|
||||||
inputIds: [],
|
inputIds: [],
|
||||||
outputIds: [],
|
outputIds: [],
|
||||||
|
|
||||||
setFlowPool: (flowPool) => {
|
setFlowPool: (flowPool) => {
|
||||||
set({ flowPool });
|
set({ flowPool });
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -43,5 +43,4 @@ export const useTypesStore = create<TypesStoreType>((set, get) => ({
|
||||||
let newChange = typeof change === "function" ? change(get().data) : change;
|
let newChange = typeof change === "function" ? change(get().data) : change;
|
||||||
set({ data: newChange });
|
set({ data: newChange });
|
||||||
},
|
},
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -444,7 +444,7 @@ export type chatInputType = {
|
||||||
};
|
};
|
||||||
lockChat: boolean;
|
lockChat: boolean;
|
||||||
noInput: boolean;
|
noInput: boolean;
|
||||||
sendMessage: (count?:number) => void;
|
sendMessage: (count?: number) => void;
|
||||||
setChatValue: (value: string) => void;
|
setChatValue: (value: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,81 +1,85 @@
|
||||||
import { AxiosError } from "axios";
|
import { AxiosError } from "axios";
|
||||||
import { FlowType } from "../types/flow";
|
|
||||||
import { getVerticesOrder, postBuildVertex } from "../controllers/API";
|
import { getVerticesOrder, postBuildVertex } from "../controllers/API";
|
||||||
|
import { FlowType } from "../types/flow";
|
||||||
|
|
||||||
type BuildVerticesParams = {
|
type BuildVerticesParams = {
|
||||||
flow: FlowType; // Assuming FlowType is the type for your flow
|
flow: FlowType; // Assuming FlowType is the type for your flow
|
||||||
nodeId?: string | null; // Assuming nodeId is of type string, and it's optional
|
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
|
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
|
onBuildUpdate?: (data: any) => void; // Replace any with the actual type of data
|
||||||
onBuildComplete?: (allNodesValid: boolean) => void;
|
onBuildComplete?: (allNodesValid: boolean) => void;
|
||||||
onBuildError?: (title, list) => void;
|
onBuildError?: (title, list) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function buildVertices({
|
export async function buildVertices({
|
||||||
flow,
|
flow,
|
||||||
nodeId = null,
|
nodeId = null,
|
||||||
onProgressUpdate,
|
onProgressUpdate,
|
||||||
onBuildUpdate,
|
onBuildUpdate,
|
||||||
onBuildComplete,
|
onBuildComplete,
|
||||||
onBuildError,
|
onBuildError,
|
||||||
}: BuildVerticesParams) {
|
}: BuildVerticesParams) {
|
||||||
try {
|
try {
|
||||||
// Step 1: Get vertices order
|
// Step 1: Get vertices order
|
||||||
console.log(JSON.parse(JSON.stringify(flow)));
|
console.log(JSON.parse(JSON.stringify(flow)));
|
||||||
let orderResponse = await getVerticesOrder(flow.id);
|
let orderResponse = await getVerticesOrder(flow.id);
|
||||||
let verticesOrder = orderResponse.data.ids.flatMap((id) => id);
|
let verticesOrder = orderResponse.data.ids.flatMap((id) => id);
|
||||||
// Determine the range of vertices to build
|
// Determine the range of vertices to build
|
||||||
let vertexIndex: number | null = null;
|
let vertexIndex: number | null = null;
|
||||||
if (nodeId) {
|
if (nodeId) {
|
||||||
vertexIndex = verticesOrder.indexOf(nodeId);
|
vertexIndex = verticesOrder.indexOf(nodeId);
|
||||||
}
|
}
|
||||||
let buildRange =
|
let buildRange =
|
||||||
vertexIndex !== null
|
vertexIndex !== null
|
||||||
? verticesOrder.slice(0, vertexIndex + 1)
|
? verticesOrder.slice(0, vertexIndex + 1)
|
||||||
: verticesOrder;
|
: verticesOrder;
|
||||||
|
|
||||||
const buildResults: boolean[] = [];
|
const buildResults: boolean[] = [];
|
||||||
for (let vertexId of buildRange) {
|
for (let vertexId of buildRange) {
|
||||||
try {
|
try {
|
||||||
const buildResponse = await postBuildVertex(flow, vertexId);
|
const buildResponse = await postBuildVertex(flow, vertexId);
|
||||||
const buildData = buildResponse.data;
|
const buildData = buildResponse.data;
|
||||||
if (onProgressUpdate) {
|
if (onProgressUpdate) {
|
||||||
const progress =
|
const progress =
|
||||||
verticesOrder.indexOf(vertexId) / verticesOrder.length;
|
verticesOrder.indexOf(vertexId) / verticesOrder.length;
|
||||||
onProgressUpdate(progress);
|
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"]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
if (onBuildUpdate) {
|
||||||
|
let data = {};
|
||||||
// Callback for when all vertices have been built
|
if (!buildData.valid) {
|
||||||
if (onBuildComplete) {
|
if (onBuildError) {
|
||||||
const allNodesValid = buildResults.every((result) => result);
|
onBuildError("Error Building Component", [buildData.params]);
|
||||||
onBuildComplete(allNodesValid);
|
}
|
||||||
}
|
}
|
||||||
} catch (error:any) {
|
data[buildData.id] = buildData;
|
||||||
// Callback for handling errors
|
|
||||||
if (onBuildError) {
|
onBuildUpdate({ data, id: buildData.id });
|
||||||
|
}
|
||||||
|
buildResults.push(buildData.valid);
|
||||||
|
} catch (error: any) {
|
||||||
if (onBuildError) {
|
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",
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -477,7 +477,7 @@ export function convertArrayToObj(arrayOfObjects) {
|
||||||
export function hasDuplicateKeys(array) {
|
export function hasDuplicateKeys(array) {
|
||||||
const keys = {};
|
const keys = {};
|
||||||
// Transforms an empty object into an object array without opening the 'editNode' modal to prevent the flow build from breaking.
|
// 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 obj of array) {
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
if (keys[key]) {
|
if (keys[key]) {
|
||||||
|
|
|
||||||
|
|
@ -593,7 +593,7 @@ export function removeCountFromString(input: string): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function extractTypeFromLongId(id: string): string {
|
export function extractTypeFromLongId(id: string): string {
|
||||||
let [newId,_] = id.split("-");
|
let [newId, _] = id.split("-");
|
||||||
return newId;
|
return newId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue