merge fix zustand
This commit is contained in:
commit
6a8ca8db47
160 changed files with 9418 additions and 6208 deletions
29
src/frontend/.eslintrc.json
Normal file
29
src/frontend/.eslintrc.json
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"extends": ["eslint:recommended", "plugin:node/recommended"],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018
|
||||
},
|
||||
"rules": {
|
||||
"no-console": "warn",
|
||||
"no-self-assign": "warn",
|
||||
"no-self-compare": "warn",
|
||||
"complexity": ["error", { "max": 15 }],
|
||||
"indent": ["error", 2, { "SwitchCase": 1 }],
|
||||
"no-dupe-keys": "error",
|
||||
"no-invalid-regexp": "error",
|
||||
"no-undef": "error",
|
||||
"no-return-assign": "error",
|
||||
"no-redeclare": "error",
|
||||
"no-empty": "error",
|
||||
"no-await-in-loop": "error",
|
||||
"node/exports-style": ["error", "module.exports"],
|
||||
"node/file-extension-in-import": ["error", "always"],
|
||||
"node/prefer-global/buffer": ["error", "always"],
|
||||
"node/prefer-global/console": ["error", "always"],
|
||||
"node/prefer-global/process": ["error", "always"],
|
||||
"node/prefer-global/url-search-params": ["error", "always"],
|
||||
"node/prefer-global/url": ["error", "always"],
|
||||
"node/prefer-promises/dns": "error",
|
||||
"node/prefer-promises/fs": "error"
|
||||
}
|
||||
}
|
||||
4133
src/frontend/package-lock.json
generated
4133
src/frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -3,12 +3,8 @@
|
|||
"version": "0.1.2",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@headlessui/react": "^1.7.17",
|
||||
"@heroicons/react": "^2.0.18",
|
||||
"@mui/material": "^5.14.7",
|
||||
"@preact/signals-react": "^2.0.0",
|
||||
"@million/lint": "^0.0.73",
|
||||
"@radix-ui/react-accordion": "^1.1.2",
|
||||
"@radix-ui/react-checkbox": "^1.0.4",
|
||||
"@radix-ui/react-dialog": "^1.0.4",
|
||||
|
|
@ -19,7 +15,7 @@
|
|||
"@radix-ui/react-menubar": "^1.0.3",
|
||||
"@radix-ui/react-popover": "^1.0.6",
|
||||
"@radix-ui/react-progress": "^1.0.3",
|
||||
"@radix-ui/react-select": "^1.2.2",
|
||||
"@radix-ui/react-select": "^2.0.0",
|
||||
"@radix-ui/react-separator": "^1.0.3",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@radix-ui/react-switch": "^1.0.3",
|
||||
|
|
@ -29,9 +25,7 @@
|
|||
"@tailwindcss/forms": "^0.5.6",
|
||||
"@tailwindcss/line-clamp": "^0.4.4",
|
||||
"@types/axios": "^0.14.0",
|
||||
"accordion": "^3.0.2",
|
||||
"ace-builds": "^1.24.1",
|
||||
"add": "^2.0.6",
|
||||
"ansi-to-html": "^0.7.2",
|
||||
"axios": "^1.5.0",
|
||||
"base64-js": "^1.5.1",
|
||||
|
|
@ -43,6 +37,7 @@
|
|||
"framer-motion": "^11.0.6",
|
||||
"lodash": "^4.17.21",
|
||||
"lucide-react": "^0.331.0",
|
||||
"million": "^3.0.6",
|
||||
"moment": "^2.29.4",
|
||||
"playwright": "^1.42.0",
|
||||
"react": "^18.2.0",
|
||||
|
|
@ -55,8 +50,6 @@
|
|||
"react-markdown": "^8.0.7",
|
||||
"react-router-dom": "^6.15.0",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-tabs": "^6.0.2",
|
||||
"react-tooltip": "^5.21.1",
|
||||
"react18-json-view": "^0.2.3",
|
||||
"reactflow": "^11.9.2",
|
||||
"rehype-mathjax": "^4.0.3",
|
||||
|
|
@ -64,8 +57,6 @@
|
|||
"remark-math": "^5.1.1",
|
||||
"shadcn-ui": "^0.2.3",
|
||||
"short-unique-id": "^4.4.4",
|
||||
"switch": "^0.0.0",
|
||||
"table": "^6.8.1",
|
||||
"tailwind-merge": "^1.14.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"uuid": "^9.0.0",
|
||||
|
|
@ -117,6 +108,8 @@
|
|||
"@vitejs/plugin-react-swc": "^3.3.2",
|
||||
"autoprefixer": "^10.4.15",
|
||||
"daisyui": "^4.0.4",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"postcss": "^8.4.29",
|
||||
"prettier": "^2.8.8",
|
||||
"prettier-plugin-organize-imports": "^3.2.3",
|
||||
|
|
|
|||
|
|
@ -27,11 +27,15 @@ import useAlertStore from "../../../../stores/alertStore";
|
|||
import useFlowStore from "../../../../stores/flowStore";
|
||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||
import { useTypesStore } from "../../../../stores/typesStore";
|
||||
import { APIClassType, ResponseErrorTypeAPI } from "../../../../types/api";
|
||||
import {
|
||||
APIClassType,
|
||||
ResponseErrorDetailAPI,
|
||||
ResponseErrorTypeAPI,
|
||||
} from "../../../../types/api";
|
||||
import { ParameterComponentType } from "../../../../types/components";
|
||||
import {
|
||||
debouncedHandleUpdateValues,
|
||||
handleUpdateValues,
|
||||
throttledHandleUpdateValues,
|
||||
} from "../../../../utils/parameterUtils";
|
||||
import {
|
||||
convertObjToArray,
|
||||
|
|
@ -104,10 +108,11 @@ export default function ParameterComponent({
|
|||
});
|
||||
}
|
||||
} catch (error) {
|
||||
let responseError = error as ResponseErrorTypeAPI;
|
||||
let responseError = error as ResponseErrorDetailAPI;
|
||||
|
||||
setErrorData({
|
||||
title: "Error while updating the Component",
|
||||
list: [responseError.response.data.detail.error ?? "Unknown error"],
|
||||
list: [responseError.response.data.detail ?? "Unknown error"],
|
||||
});
|
||||
}
|
||||
setIsLoading(false);
|
||||
|
|
@ -136,10 +141,11 @@ export default function ParameterComponent({
|
|||
});
|
||||
}
|
||||
} catch (error) {
|
||||
let responseError = error as ResponseErrorTypeAPI;
|
||||
let responseError = error as ResponseErrorDetailAPI;
|
||||
|
||||
setErrorData({
|
||||
title: "Error while updating the Component",
|
||||
list: [responseError.response.data.detail.error ?? "Unknown error"],
|
||||
list: [responseError.response.data.detail ?? "Unknown error"],
|
||||
});
|
||||
}
|
||||
setIsLoading(false);
|
||||
|
|
@ -164,7 +170,7 @@ export default function ParameterComponent({
|
|||
if (shouldUpdate) {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
newTemplate = await throttledHandleUpdateValues(name, data);
|
||||
newTemplate = await debouncedHandleUpdateValues(name, data);
|
||||
} catch (error) {
|
||||
let responseError = error as ResponseErrorTypeAPI;
|
||||
setErrorData({
|
||||
|
|
@ -377,7 +383,7 @@ export default function ParameterComponent({
|
|||
!showNode ? "mt-0" : ""
|
||||
)}
|
||||
style={{
|
||||
borderColor: color,
|
||||
borderColor: color ?? nodeColors.unknown,
|
||||
}}
|
||||
onClick={() => {
|
||||
setFilterEdge(groupedEdge.current);
|
||||
|
|
@ -406,7 +412,7 @@ export default function ParameterComponent({
|
|||
}
|
||||
>
|
||||
{!left && data.node?.frozen && (
|
||||
<div>
|
||||
<div className="pr-1">
|
||||
<IconComponent className="h-5 w-5 text-ice" name={"Snowflake"} />
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -421,7 +427,7 @@ export default function ParameterComponent({
|
|||
{title}
|
||||
</span>
|
||||
)}
|
||||
<span className={(info === "" ? "" : "ml-1 ") + " text-status-red pl-1"}>
|
||||
<span className={(required ? "ml-2 " : "") + "text-status-red"}>
|
||||
{required ? "*" : ""}
|
||||
</span>
|
||||
<div className="">
|
||||
|
|
@ -445,7 +451,7 @@ export default function ParameterComponent({
|
|||
<div className="flex">
|
||||
<ShadTooltip
|
||||
styleClasses={"tooltip-fixed-width custom-scroll nowheel"}
|
||||
delayDuration={0}
|
||||
delayDuration={1000}
|
||||
content={refHtml.current}
|
||||
side={left ? "left" : "right"}
|
||||
>
|
||||
|
|
@ -473,7 +479,7 @@ export default function ParameterComponent({
|
|||
"h-3 w-3 rounded-full border-2 bg-background"
|
||||
)}
|
||||
style={{
|
||||
borderColor: color,
|
||||
borderColor: color ?? nodeColors.unknown,
|
||||
}}
|
||||
onClick={() => {
|
||||
setFilterEdge(groupedEdge.current);
|
||||
|
|
@ -679,6 +685,7 @@ export default function ParameterComponent({
|
|||
) : left === true && type === "int" ? (
|
||||
<div className="mt-2 w-full">
|
||||
<IntComponent
|
||||
rangeSpec={data.node?.template[name].rangeSpec}
|
||||
disabled={disabled}
|
||||
value={data.node?.template[name].value ?? ""}
|
||||
onChange={handleOnNewValue}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { cloneDeep } from "lodash";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { NodeToolbar, useUpdateNodeInternals } from "reactflow";
|
||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
|
|
@ -86,7 +86,11 @@ export default function GenericNode({
|
|||
if (!thisNodeTemplate.code) return;
|
||||
const currentCode = thisNodeTemplate.code?.value;
|
||||
const thisNodesCode = data.node!.template?.code?.value;
|
||||
if (currentCode !== thisNodesCode) {
|
||||
const componentsToIgnore = ["Custom Component", "Prompt"];
|
||||
if (
|
||||
currentCode !== thisNodesCode &&
|
||||
!componentsToIgnore.includes(data.node!.display_name)
|
||||
) {
|
||||
setIsOutdated(true);
|
||||
} else {
|
||||
setIsOutdated(false);
|
||||
|
|
@ -293,7 +297,6 @@ export default function GenericNode({
|
|||
);
|
||||
}
|
||||
};
|
||||
|
||||
const getSpecificClassFromBuildStatus = (
|
||||
buildStatus: BuildStatus | undefined,
|
||||
validationStatus: validationStatusType | null
|
||||
|
|
@ -342,17 +345,16 @@ export default function GenericNode({
|
|||
const getNodeSizeClass = (showNode) =>
|
||||
showNode ? "w-96 rounded-lg" : "w-26 h-26 rounded-full";
|
||||
|
||||
return (
|
||||
<>
|
||||
const memoizedNodeToolbarComponent = useMemo(() => {
|
||||
return (
|
||||
<NodeToolbar>
|
||||
<NodeToolbarComponent
|
||||
position={{ x: xPos, y: yPos }}
|
||||
data={data}
|
||||
deleteNode={(id) => {
|
||||
takeSnapshot();
|
||||
deleteNode(id);
|
||||
}}
|
||||
setShowNode={(show: boolean) => {
|
||||
setShowNode={(show) => {
|
||||
setNode(data.id, (old) => ({
|
||||
...old,
|
||||
data: { ...old.data, showNode: show },
|
||||
|
|
@ -366,8 +368,25 @@ export default function GenericNode({
|
|||
updateNodeCode={updateNodeCode}
|
||||
isOutdated={isOutdated}
|
||||
selected={selected}
|
||||
></NodeToolbarComponent>
|
||||
/>
|
||||
</NodeToolbar>
|
||||
);
|
||||
}, [
|
||||
data,
|
||||
deleteNode,
|
||||
takeSnapshot,
|
||||
setNode,
|
||||
setShowNode,
|
||||
handles,
|
||||
showNode,
|
||||
updateNodeCode,
|
||||
isOutdated,
|
||||
selected,
|
||||
]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{memoizedNodeToolbarComponent}
|
||||
<div
|
||||
className={getNodeBorderClassName(
|
||||
selected,
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: none; display: block; shape-rendering: auto;" width="271px" height="271px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
|
||||
<defs>
|
||||
<filter id="ldio-978hsxudfzl-filter" x="-100%" y="-100%" width="300%" height="300%" color-interpolation-filters="sRGB">
|
||||
<feGaussianBlur in="SourceGraphic" stdDeviation="3.6"></feGaussianBlur>
|
||||
<feComponentTransfer result="cutoff">
|
||||
<feFuncA type="table" tableValues="0 0 0 0 0 0 1 1 1 1 1"></feFuncA>
|
||||
</feComponentTransfer>
|
||||
</filter>
|
||||
</defs>
|
||||
<g filter="url(#ldio-978hsxudfzl-filter)"><g transform="translate(50 50)">
|
||||
<g>
|
||||
<circle cx="8" cy="0" r="5" fill="#2edbb5">
|
||||
<animate attributeName="r" keyTimes="0;0.5;1" values="5.3999999999999995;12.6;5.3999999999999995" dur="5s" repeatCount="indefinite" begin="-0.2s"></animate>
|
||||
</circle>
|
||||
<animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="5s" repeatCount="indefinite" begin="0s"></animateTransform>
|
||||
</g>
|
||||
</g><g transform="translate(50 50)">
|
||||
<g>
|
||||
<circle cx="8" cy="0" r="5" fill="#1d99ff">
|
||||
<animate attributeName="r" keyTimes="0;0.5;1" values="5.3999999999999995;12.6;5.3999999999999995" dur="2.5s" repeatCount="indefinite" begin="-0.15000000000000002s"></animate>
|
||||
</circle>
|
||||
<animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="2.5s" repeatCount="indefinite" begin="-0.05s"></animateTransform>
|
||||
</g>
|
||||
</g><g transform="translate(50 50)">
|
||||
<g>
|
||||
<circle cx="8" cy="0" r="5" fill="#4f41ff">
|
||||
<animate attributeName="r" keyTimes="0;0.5;1" values="5.3999999999999995;12.6;5.3999999999999995" dur="1.6666666666666665s" repeatCount="indefinite" begin="-0.1s"></animate>
|
||||
</circle>
|
||||
<animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="1.6666666666666665s" repeatCount="indefinite" begin="-0.1s"></animateTransform>
|
||||
</g>
|
||||
</g><g transform="translate(50 50)">
|
||||
<g>
|
||||
<circle cx="8" cy="0" r="5" fill="#8400ff">
|
||||
<animate attributeName="r" keyTimes="0;0.5;1" values="5.3999999999999995;12.6;5.3999999999999995" dur="1.25s" repeatCount="indefinite" begin="-0.05s"></animate>
|
||||
</circle>
|
||||
<animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="1.25s" repeatCount="indefinite" begin="-0.15000000000000002s"></animateTransform>
|
||||
</g>
|
||||
</g></g>
|
||||
<!-- [ldio] generated by https://loading.io/ --></svg>
|
||||
|
Before Width: | Height: | Size: 2.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
|
|
@ -12,6 +12,7 @@ export default function AccordionComponent({
|
|||
children,
|
||||
open = [],
|
||||
keyValue,
|
||||
sideBar,
|
||||
}: AccordionComponentType): JSX.Element {
|
||||
const [value, setValue] = useState(
|
||||
open.length === 0 ? "" : getOpenAccordion()
|
||||
|
|
@ -45,7 +46,9 @@ export default function AccordionComponent({
|
|||
onClick={() => {
|
||||
handleClick();
|
||||
}}
|
||||
className="ml-3"
|
||||
className={
|
||||
sideBar ? "w-full bg-muted px-[0.75rem] py-[0.5rem]" : "ml-3"
|
||||
}
|
||||
>
|
||||
{trigger}
|
||||
</AccordionTrigger>
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
|||
onChange={handleDescriptionChange}
|
||||
value={description!}
|
||||
placeholder="Flow description"
|
||||
className="mt-2 max-h-[100px] font-normal"
|
||||
className="mt-2 max-h-[100px] resize-none font-normal"
|
||||
rows={3}
|
||||
onDoubleClickCapture={(event) => {
|
||||
handleFocus(event);
|
||||
|
|
|
|||
|
|
@ -1,69 +0,0 @@
|
|||
import { cloneDeep } from "lodash";
|
||||
import useFlowStore from "../../stores/flowStore";
|
||||
import { IOInputProps } from "../../types/components";
|
||||
import IOFileInput from "../IOInputs/FileInput";
|
||||
import { Textarea } from "../ui/textarea";
|
||||
|
||||
export default function IOInputField({
|
||||
inputType,
|
||||
inputId,
|
||||
left,
|
||||
}: IOInputProps): JSX.Element | undefined {
|
||||
const nodes = useFlowStore((state) => state.nodes);
|
||||
const setNode = useFlowStore((state) => state.setNode);
|
||||
const node = nodes.find((node) => node.id === inputId);
|
||||
function handleInputType() {
|
||||
if (!node) return <>"No node found!"</>;
|
||||
switch (inputType) {
|
||||
case "TextInput":
|
||||
return (
|
||||
<Textarea
|
||||
className={`w-full ${left ? "" : " h-full"}`}
|
||||
placeholder={"Enter text..."}
|
||||
value={node.data.node!.template["input_value"].value}
|
||||
onChange={(e) => {
|
||||
e.target.value;
|
||||
if (node) {
|
||||
let newNode = cloneDeep(node);
|
||||
newNode.data.node!.template["input_value"].value =
|
||||
e.target.value;
|
||||
setNode(node.id, newNode);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
case "FileLoader":
|
||||
return (
|
||||
<IOFileInput
|
||||
field={node.data.node!.template["file_path"]["value"]}
|
||||
updateValue={(e) => {
|
||||
if (node) {
|
||||
let newNode = cloneDeep(node);
|
||||
newNode.data.node!.template["file_path"].value = e;
|
||||
setNode(node.id, newNode);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
default:
|
||||
return (
|
||||
<Textarea
|
||||
className="w-full custom-scroll"
|
||||
placeholder={"Enter text..."}
|
||||
value={node.data.node!.template["input_value"]}
|
||||
onChange={(e) => {
|
||||
e.target.value;
|
||||
if (node) {
|
||||
let newNode = cloneDeep(node);
|
||||
newNode.data.node!.template["input_value"].value =
|
||||
e.target.value;
|
||||
setNode(node.id, newNode);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
return handleInputType();
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
import useFlowStore from "../../stores/flowStore";
|
||||
import { IOOutputProps } from "../../types/components";
|
||||
import { Textarea } from "../ui/textarea";
|
||||
|
||||
export default function IOOutputView({
|
||||
outputType,
|
||||
outputId,
|
||||
left,
|
||||
}: IOOutputProps): JSX.Element | undefined {
|
||||
const nodes = useFlowStore((state) => state.nodes);
|
||||
const setNode = useFlowStore((state) => state.setNode);
|
||||
const flowPool = useFlowStore((state) => state.flowPool);
|
||||
const node = nodes.find((node) => node.id === outputId);
|
||||
function handleOutputType() {
|
||||
if (!node) return <>"No node found!"</>;
|
||||
switch (outputType) {
|
||||
case "TextOutput":
|
||||
return (
|
||||
<Textarea
|
||||
className={`w-full custom-scroll ${left ? "" : " h-full"}`}
|
||||
placeholder={"Empty"}
|
||||
// update to real value on flowPool
|
||||
value={
|
||||
(flowPool[node.id] ?? [])[(flowPool[node.id]?.length ?? 1) - 1]
|
||||
?.params ?? ""
|
||||
}
|
||||
readOnly
|
||||
/>
|
||||
);
|
||||
|
||||
default:
|
||||
return (
|
||||
<Textarea
|
||||
className={`w-full custom-scroll ${left ? "" : " h-full"}`}
|
||||
placeholder={"Empty"}
|
||||
// update to real value on flowPool
|
||||
value={
|
||||
(flowPool[node.id] ?? [])[(flowPool[node.id]?.length ?? 1) - 1]
|
||||
?.params ?? ""
|
||||
}
|
||||
readOnly
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
return handleOutputType();
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
import Tooltip, { TooltipProps, tooltipClasses } from "@mui/material/Tooltip";
|
||||
import { styled } from "@mui/material/styles";
|
||||
|
||||
export const LightTooltip = styled(({ className, ...props }: TooltipProps) => (
|
||||
<Tooltip {...props} classes={{ popper: className }} />
|
||||
))(({ theme }) => ({
|
||||
[`& .${tooltipClasses.tooltip}`]: {
|
||||
backgroundColor: theme.palette.common.white,
|
||||
color: "rgba(0, 0, 0, 0.87)",
|
||||
boxShadow: theme.shadows[2],
|
||||
fontSize: 14,
|
||||
},
|
||||
[`& .${tooltipClasses.arrow}:before`]: {
|
||||
color: theme.palette.common.white,
|
||||
boxShadow: theme.shadows[1],
|
||||
},
|
||||
}));
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
export default function LoadingSpinner({}) {
|
||||
return <></>;
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
import { useNavigate } from "react-router-dom";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { cn } from "../../utils/utils";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
import { Card, CardContent } from "../ui/card";
|
||||
|
||||
export default function NewFlowCardComponent({}: {}) {
|
||||
const addFlow = useFlowsManagerStore((state) => state.addFlow);
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={cn(
|
||||
"group relative flex h-48 w-2/6 flex-col justify-between overflow-hidden transition-all hover:shadow-md"
|
||||
)}
|
||||
>
|
||||
<CardContent className="flex h-full w-full items-center justify-center align-middle">
|
||||
<button
|
||||
onClick={() => {
|
||||
addFlow(true).then((id) => {
|
||||
navigate("/flow/" + id);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<IconComponent
|
||||
className={cn("h-12 w-12 text-muted-foreground")}
|
||||
name="PlusCircle"
|
||||
/>
|
||||
</button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
import { RadialProgressType } from "../../types/components";
|
||||
|
||||
export default function RadialProgressComponent({
|
||||
value,
|
||||
color,
|
||||
}: RadialProgressType): JSX.Element {
|
||||
const style = {
|
||||
"--value": value! * 100,
|
||||
"--size": "1.5rem",
|
||||
"--thickness": "2px",
|
||||
} as React.CSSProperties;
|
||||
|
||||
return (
|
||||
<div className={"radial-progress " + color} style={style}>
|
||||
<strong className="text-[8px]">{Math.trunc(value! * 100)}%</strong>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
"use client";
|
||||
import type { FC } from "react";
|
||||
import React from "react";
|
||||
import { Tooltip as ReactTooltip } from "react-tooltip";
|
||||
import "react-tooltip/dist/react-tooltip.css";
|
||||
import { TooltipProps } from "../../types/components";
|
||||
import { classNames } from "../../utils/utils";
|
||||
|
||||
const TooltipReact: FC<TooltipProps> = ({
|
||||
selector,
|
||||
content,
|
||||
disabled,
|
||||
position = "top",
|
||||
children,
|
||||
htmlContent,
|
||||
className,
|
||||
clickable,
|
||||
delayShow,
|
||||
}: TooltipProps): JSX.Element => {
|
||||
return (
|
||||
<div className="tooltip-container">
|
||||
{React.cloneElement(children as React.ReactElement, {
|
||||
"data-tooltip-id": selector,
|
||||
})}
|
||||
<ReactTooltip
|
||||
id={selector}
|
||||
content={content}
|
||||
className={classNames(
|
||||
"z-[9999] !bg-white !text-xs !font-normal !text-foreground !opacity-100 !shadow-md",
|
||||
className!
|
||||
)}
|
||||
place={position}
|
||||
clickable={clickable}
|
||||
isOpen={disabled ? false : undefined}
|
||||
delayShow={delayShow}
|
||||
positionStrategy="absolute"
|
||||
float={true}
|
||||
>
|
||||
{htmlContent && htmlContent}
|
||||
</ReactTooltip>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TooltipReact;
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
import { TooltipComponentType } from "../../types/components";
|
||||
import { LightTooltip } from "../LightTooltipComponent";
|
||||
|
||||
export default function Tooltip({
|
||||
children,
|
||||
title,
|
||||
placement,
|
||||
}: TooltipComponentType): JSX.Element {
|
||||
return (
|
||||
<LightTooltip placement={placement} title={title} arrow>
|
||||
{children}
|
||||
</LightTooltip>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
import { Transition } from "@headlessui/react";
|
||||
import { useState } from "react";
|
||||
import Loading from "../../../components/ui/loading";
|
||||
import { FlowType } from "../../../types/flow";
|
||||
|
||||
import { MISSED_ERROR_ALERT } from "../../../constants/alerts_constants";
|
||||
import { BuildStatus } from "../../../constants/enums";
|
||||
import useAlertStore from "../../../stores/alertStore";
|
||||
import useFlowStore from "../../../stores/flowStore";
|
||||
import { validateNodes } from "../../../utils/reactflowUtils";
|
||||
import RadialProgressComponent from "../../RadialProgress";
|
||||
import IconComponent from "../../genericIconComponent";
|
||||
|
||||
export default function BuildTrigger({
|
||||
open,
|
||||
flow,
|
||||
}: {
|
||||
open: boolean;
|
||||
flow: FlowType;
|
||||
}): JSX.Element {
|
||||
const isBuilding = useFlowStore((state) => state.isBuilding);
|
||||
const setIsBuilding = useFlowStore((state) => state.setIsBuilding);
|
||||
const buildFlow = useFlowStore((state) => state.buildFlow);
|
||||
const nodes = useFlowStore((state) => state.nodes);
|
||||
const edges = useFlowStore((state) => state.edges);
|
||||
const updateBuildStatus = useFlowStore((state) => state.updateBuildStatus);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
|
||||
const eventClick = isBuilding ? "pointer-events-none" : "";
|
||||
const [progress, setProgress] = useState(0);
|
||||
|
||||
async function handleBuild(flow: FlowType): Promise<void> {
|
||||
try {
|
||||
if (isBuilding) {
|
||||
return;
|
||||
}
|
||||
const errorsObjs = validateNodes(nodes, edges);
|
||||
const errors = errorsObjs.flatMap((errorObj) => errorObj.errors);
|
||||
if (errors.length > 0) {
|
||||
setErrorData({
|
||||
title: MISSED_ERROR_ALERT,
|
||||
list: errors,
|
||||
});
|
||||
const ids = errorsObjs.map((errorObj) => errorObj.id);
|
||||
updateBuildStatus(ids, BuildStatus.ERROR);
|
||||
return;
|
||||
}
|
||||
const minimumLoadingTime = 200; // in milliseconds
|
||||
const startTime = Date.now();
|
||||
setIsBuilding(true);
|
||||
|
||||
await enforceMinimumLoadingTime(startTime, minimumLoadingTime);
|
||||
await buildFlow({});
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
} finally {
|
||||
setIsBuilding(false);
|
||||
}
|
||||
}
|
||||
|
||||
const hasIO = useFlowStore((state) => state.hasIO);
|
||||
|
||||
async function enforceMinimumLoadingTime(
|
||||
startTime: number,
|
||||
minimumLoadingTime: number
|
||||
) {
|
||||
const elapsedTime = Date.now() - startTime;
|
||||
const remainingTime = minimumLoadingTime - elapsedTime;
|
||||
|
||||
if (remainingTime > 0) {
|
||||
return new Promise((resolve) => setTimeout(resolve, remainingTime));
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Transition
|
||||
show={!open}
|
||||
appear={true}
|
||||
enter="transition ease-out duration-300"
|
||||
enterFrom="translate-y-96"
|
||||
enterTo="translate-y-0"
|
||||
leave="transition ease-in duration-300"
|
||||
leaveFrom="translate-y-0"
|
||||
leaveTo="translate-y-96"
|
||||
>
|
||||
<div
|
||||
className={hasIO ? "fixed bottom-20 right-4" : "fixed bottom-4 right-4"}
|
||||
>
|
||||
<div
|
||||
className={`${eventClick} round-button-form`}
|
||||
onClick={() => {
|
||||
handleBuild(flow);
|
||||
}}
|
||||
>
|
||||
<button>
|
||||
<div className="round-button-div">
|
||||
{isBuilding && progress < 1 ? (
|
||||
// Render your loading animation here when isBuilding is true
|
||||
<RadialProgressComponent
|
||||
// ! confirm below works
|
||||
color={"text-build-trigger"}
|
||||
value={progress}
|
||||
></RadialProgressComponent>
|
||||
) : isBuilding ? (
|
||||
<Loading
|
||||
strokeWidth={1.5}
|
||||
className="build-trigger-loading-icon"
|
||||
/>
|
||||
) : (
|
||||
<IconComponent
|
||||
name="Zap"
|
||||
className="sh-6 w-6 fill-build-trigger stroke-build-trigger stroke-1"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
import { Transition } from "@headlessui/react";
|
||||
|
||||
import {
|
||||
CHAT_CANNOT_OPEN_DESCRIPTION,
|
||||
CHAT_CANNOT_OPEN_TITLE,
|
||||
FLOW_NOT_BUILT_DESCRIPTION,
|
||||
FLOW_NOT_BUILT_TITLE,
|
||||
} from "../../../constants/constants";
|
||||
import useAlertStore from "../../../stores/alertStore";
|
||||
import { chatTriggerPropType } from "../../../types/components";
|
||||
import IconComponent from "../../genericIconComponent";
|
||||
|
||||
export default function ChatTrigger({
|
||||
open,
|
||||
setOpen,
|
||||
isBuilt,
|
||||
canOpen,
|
||||
}: chatTriggerPropType): JSX.Element {
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
|
||||
function handleClick(): void {
|
||||
if (isBuilt) {
|
||||
if (canOpen) {
|
||||
setOpen(true);
|
||||
} else {
|
||||
setErrorData({
|
||||
title: CHAT_CANNOT_OPEN_TITLE,
|
||||
list: [CHAT_CANNOT_OPEN_DESCRIPTION],
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setErrorData({
|
||||
title: FLOW_NOT_BUILT_TITLE,
|
||||
list: [FLOW_NOT_BUILT_DESCRIPTION],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Transition
|
||||
show={!open}
|
||||
appear={true}
|
||||
enter="transition ease-out duration-300"
|
||||
enterFrom="translate-y-96"
|
||||
enterTo="translate-y-0"
|
||||
leave="transition ease-in duration-300"
|
||||
leaveFrom="translate-y-0"
|
||||
leaveTo="translate-y-96"
|
||||
>
|
||||
<button
|
||||
onClick={handleClick}
|
||||
className={
|
||||
"shadow-round-btn-shadow hover:shadow-round-btn-shadow message-button " +
|
||||
(!isBuilt || !canOpen ? "cursor-not-allowed" : "cursor-pointer")
|
||||
}
|
||||
>
|
||||
<div className="flex gap-3">
|
||||
<IconComponent
|
||||
name="MessagesSquare"
|
||||
className={
|
||||
"h-6 w-6 transition-all " +
|
||||
(isBuilt && canOpen
|
||||
? "message-button-icon"
|
||||
: "disabled-message-button-icon")
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
</Transition>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,20 +1,17 @@
|
|||
import { Transition } from "@headlessui/react";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import ApiModal from "../../modals/ApiModal";
|
||||
import IOModal from "../../modals/IOModal";
|
||||
import ShareModal from "../../modals/shareModal";
|
||||
import useFlowStore from "../../stores/flowStore";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { useStoreStore } from "../../stores/storeStore";
|
||||
import { ChatType } from "../../types/chat";
|
||||
import { classNames } from "../../utils/utils";
|
||||
import IOView from "../IOview";
|
||||
import ForwardedIconComponent from "../genericIconComponent";
|
||||
import { Separator } from "../ui/separator";
|
||||
|
||||
export default function FlowToolbar({ flow }: ChatType): JSX.Element {
|
||||
export default function FlowToolbar(): JSX.Element {
|
||||
const [open, setOpen] = useState(false);
|
||||
const flowState = useFlowStore((state) => state.flowState);
|
||||
const nodes = useFlowStore((state) => state.nodes);
|
||||
const hasIO = useFlowStore((state) => state.hasIO);
|
||||
const hasStore = useStoreStore((state) => state.hasStore);
|
||||
const validApiKey = useStoreStore((state) => state.validApiKey);
|
||||
|
|
@ -92,15 +89,15 @@ export default function FlowToolbar({ flow }: ChatType): JSX.Element {
|
|||
<div className="flex">
|
||||
<div className="flex h-full w-full gap-1 rounded-sm text-medium-indigo transition-all">
|
||||
{hasIO ? (
|
||||
<IOView open={open} setOpen={setOpen} disable={!hasIO}>
|
||||
<div className="relative inline-flex w-full items-center justify-center gap-1 px-5 py-3 text-sm font-semibold text-medium-indigo transition-all transition-all duration-150 ease-in-out ease-in-out hover:bg-hover">
|
||||
<IOModal open={open} setOpen={setOpen} disable={!hasIO}>
|
||||
<div className="relative inline-flex w-full items-center justify-center gap-1 px-5 py-3 text-sm font-semibold text-medium-indigo transition-all transition-all duration-500 ease-in-out ease-in-out hover:bg-hover">
|
||||
<ForwardedIconComponent
|
||||
name="Zap"
|
||||
className={"message-button-icon h-5 w-5 transition-all"}
|
||||
/>
|
||||
Run
|
||||
</div>
|
||||
</IOView>
|
||||
</IOModal>
|
||||
) : (
|
||||
<div
|
||||
className={`relative inline-flex w-full cursor-not-allowed items-center justify-center gap-1 px-5 py-3 text-sm font-semibold text-muted-foreground transition-all duration-150 ease-in-out ease-in-out`}
|
||||
|
|
|
|||
|
|
@ -569,6 +569,11 @@ export default function CodeTabsComponent({
|
|||
<IntComponent
|
||||
disabled={false}
|
||||
editNode={true}
|
||||
rangeSpec={
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].rangeSpec
|
||||
}
|
||||
value={
|
||||
!node.data.node.template[
|
||||
templateField
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import dynamicIconImports from "lucide-react/dynamicIconImports";
|
|||
import { Suspense, forwardRef, lazy, memo } from "react";
|
||||
import { IconComponentProps } from "../../types/components";
|
||||
import { nodeIconsLucide } from "../../utils/styleUtils";
|
||||
import { cn } from "../../utils/utils";
|
||||
import Loading from "../ui/loading";
|
||||
|
||||
const ForwardedIconComponent = memo(
|
||||
forwardRef(
|
||||
|
|
@ -34,7 +36,9 @@ const ForwardedIconComponent = memo(
|
|||
return null; // Render nothing until the icon is loaded
|
||||
}
|
||||
const fallback = (
|
||||
<div style={{ background: "#ddd", width: 24, height: 24 }} />
|
||||
<div className={cn(className, "flex items-center justify-center")}>
|
||||
<Loading />
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<Suspense fallback={fallback}>
|
||||
|
|
|
|||
|
|
@ -193,29 +193,31 @@ export const MenuBar = ({
|
|||
setOpen={setOpenSettings}
|
||||
></FlowSettingsModal>
|
||||
</div>
|
||||
<ShadTooltip
|
||||
content={
|
||||
SAVED_HOVER +
|
||||
new Date(currentFlow.updated_at ?? "").toLocaleString("en-US", {
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
second: "numeric",
|
||||
})
|
||||
}
|
||||
side="bottom"
|
||||
styleClasses="cursor-default"
|
||||
>
|
||||
<div className="flex cursor-default items-center gap-1.5 text-sm text-muted-foreground">
|
||||
<IconComponent
|
||||
name={isBuilding || saveLoading ? "Loader2" : "CheckCircle2"}
|
||||
className={cn(
|
||||
"h-4 w-4",
|
||||
isBuilding || saveLoading ? "animate-spin" : "animate-wiggle"
|
||||
)}
|
||||
/>
|
||||
{printByBuildStatus()}
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
{(currentFlow.updated_at || saveLoading) && (
|
||||
<ShadTooltip
|
||||
content={
|
||||
SAVED_HOVER +
|
||||
new Date(currentFlow.updated_at ?? "").toLocaleString("en-US", {
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
second: "numeric",
|
||||
})
|
||||
}
|
||||
side="bottom"
|
||||
styleClasses="cursor-default"
|
||||
>
|
||||
<div className="flex cursor-default items-center gap-1.5 text-sm text-muted-foreground">
|
||||
<IconComponent
|
||||
name={isBuilding || saveLoading ? "Loader2" : "CheckCircle2"}
|
||||
className={cn(
|
||||
"h-4 w-4",
|
||||
isBuilding || saveLoading ? "animate-spin" : "animate-wiggle"
|
||||
)}
|
||||
/>
|
||||
{printByBuildStatus()}
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ export default function InputComponent({
|
|||
? " text-clip password "
|
||||
: "",
|
||||
editNode ? " input-edit-node " : "",
|
||||
password && setSelectedOption ? "pr-16" : "",
|
||||
password && setSelectedOption ? "pr-[62.9px]" : "",
|
||||
(!password && setSelectedOption) ||
|
||||
(password && !setSelectedOption)
|
||||
? "pr-8"
|
||||
|
|
|
|||
|
|
@ -9,11 +9,12 @@ import { Input } from "../ui/input";
|
|||
export default function IntComponent({
|
||||
value,
|
||||
onChange,
|
||||
rangeSpec,
|
||||
disabled,
|
||||
editNode = false,
|
||||
id = "",
|
||||
}: IntComponentType): JSX.Element {
|
||||
const min = 0;
|
||||
const min = -Infinity;
|
||||
|
||||
// Clear component state
|
||||
useEffect(() => {
|
||||
|
|
@ -31,8 +32,9 @@ export default function IntComponent({
|
|||
handleKeyDown(event, value, "");
|
||||
}}
|
||||
type="number"
|
||||
step="1"
|
||||
min={0}
|
||||
step={rangeSpec?.step ?? 1}
|
||||
min={rangeSpec?.min ?? min}
|
||||
max={rangeSpec?.max ?? undefined}
|
||||
onInput={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (Number(event.target.value) < min) {
|
||||
event.target.value = min.toString();
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
import { IconCheck, IconClipboard, IconDownload } from "@tabler/icons-react";
|
||||
import { useState } from "react";
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
||||
import { programmingLanguages } from "../../../../constants/constants";
|
||||
import { Props } from "../../../../types/components";
|
||||
|
||||
export function CodeBlock({ language, value }: Props): JSX.Element {
|
||||
const [isCopied, setIsCopied] = useState<Boolean>(false);
|
||||
|
||||
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);
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
CodeBlock.displayName = "CodeBlock";
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
import * as base64js from "base64-js";
|
||||
import { useState } from "react";
|
||||
import IconComponent from "../../../components/genericIconComponent";
|
||||
import { fileCardPropsType } from "../../../types/components";
|
||||
|
||||
export default function FileCard({
|
||||
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);
|
||||
}
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
import React, { ReactNode } from "react";
|
||||
|
||||
interface ElementStackProps {
|
||||
children: ReactNode[];
|
||||
}
|
||||
|
||||
const ElementStack: React.FC<ElementStackProps> = ({ children }) => {
|
||||
return (
|
||||
<div
|
||||
className={`grid grid-cols-1`}
|
||||
style={{ display: "grid", gridAutoFlow: "row" }}
|
||||
>
|
||||
{children.map((child, index) => (
|
||||
<div
|
||||
key={index}
|
||||
style={{
|
||||
gridColumn: 1,
|
||||
gridRow: 1,
|
||||
transform: `translateX(${index * 0.1}rem)`,
|
||||
zIndex: children.length - index,
|
||||
}}
|
||||
>
|
||||
{child}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ElementStack;
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
import { Switch } from "@headlessui/react";
|
||||
import { useEffect } from "react";
|
||||
import { ToggleComponentType } from "../../types/components";
|
||||
import { classNames } from "../../utils/utils";
|
||||
|
||||
export default function ToggleComponent({
|
||||
enabled,
|
||||
setEnabled,
|
||||
disabled,
|
||||
}: ToggleComponentType): JSX.Element {
|
||||
// set component state as disabled
|
||||
useEffect(() => {
|
||||
if (disabled) {
|
||||
setEnabled(false);
|
||||
}
|
||||
}, [disabled, setEnabled]);
|
||||
return (
|
||||
<div className={disabled ? "pointer-events-none cursor-not-allowed" : ""}>
|
||||
<Switch
|
||||
checked={enabled}
|
||||
onChange={(isEnabled: boolean) => {
|
||||
setEnabled(isEnabled);
|
||||
}}
|
||||
className={classNames(
|
||||
enabled ? "bg-primary" : "bg-input",
|
||||
"toggle-component-switch "
|
||||
)}
|
||||
>
|
||||
<span className="sr-only">Use setting</span>
|
||||
<span
|
||||
className={classNames(
|
||||
enabled ? "translate-x-5" : "translate-x-0",
|
||||
"toggle-component-span",
|
||||
disabled ? "bg-input " : "bg-background"
|
||||
)}
|
||||
>
|
||||
<span
|
||||
className={classNames(
|
||||
enabled
|
||||
? "opacity-0 duration-100 ease-out"
|
||||
: "opacity-100 duration-200 ease-in",
|
||||
"toggle-component-second-span"
|
||||
)}
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<span
|
||||
className={classNames(
|
||||
enabled
|
||||
? "opacity-100 duration-200 ease-in"
|
||||
: "opacity-0 duration-100 ease-out",
|
||||
"toggle-component-second-span"
|
||||
)}
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
</span>
|
||||
</Switch>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -32,7 +32,7 @@ const AccordionTrigger = React.forwardRef<
|
|||
)}
|
||||
>
|
||||
{children}
|
||||
<ChevronDownIcon className="h-4 w-4 text-muted-foreground transition-transform duration-200" />
|
||||
<ChevronDownIcon className="h-4 w-4 font-bold text-primary transition-transform duration-200" />
|
||||
</div>
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
|
|
|
|||
|
|
@ -43,17 +43,17 @@ export interface ButtonProps
|
|||
|
||||
function toTitleCase(text: string) {
|
||||
return text
|
||||
.split(' ')
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
||||
.join(' ');
|
||||
.split(" ")
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, asChild = false,children, ...props }, ref) => {
|
||||
({ className, variant, size, asChild = false, children, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : "button";
|
||||
let newChildren = children;
|
||||
if (typeof(children)==="string"){
|
||||
newChildren = toTitleCase(children)
|
||||
if (typeof children === "string") {
|
||||
newChildren = toTitleCase(children);
|
||||
}
|
||||
return (
|
||||
<Comp
|
||||
|
|
@ -61,7 +61,6 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|||
ref={ref}
|
||||
children={newChildren}
|
||||
{...props}
|
||||
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -753,4 +753,4 @@ export const NATIVE_CATEGORIES = [
|
|||
"agents",
|
||||
];
|
||||
|
||||
export const SAVE_DEBOUNCE_TIME = 500;
|
||||
export const SAVE_DEBOUNCE_TIME = 300;
|
||||
|
|
|
|||
|
|
@ -14,3 +14,8 @@ export enum BuildStatus {
|
|||
INACTIVE = "INACTIVE",
|
||||
ERROR = "ERROR",
|
||||
}
|
||||
|
||||
export enum InputOutput {
|
||||
INPUT = "input",
|
||||
OUTPUT = "output",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ export function AuthProvider({ children }): React.ReactElement {
|
|||
useFlowsManagerStore.setState({ isLoading: false });
|
||||
}
|
||||
});
|
||||
}, [setUserData, setLoading, autoLogin, setIsAdmin]);
|
||||
}, [autoLogin]);
|
||||
|
||||
function getUser() {
|
||||
getLoggedUser()
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M16 8.016A8.522 8.522 0 008.016 16h-.032A8.521 8.521 0 000 8.016v-.032A8.521 8.521 0 007.984 0h.032A8.522 8.522 0 0016 7.984v.032z" fill="url(#prefix__paint0_radial_980_20147)"/><defs><radialGradient id="prefix__paint0_radial_980_20147" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(16.1326 5.4553 -43.70045 129.2322 1.588 6.503)"><stop offset=".067" stop-color="#9168C0"/><stop offset=".343" stop-color="#5684D1"/><stop offset=".672" stop-color="#1BA1E3"/></radialGradient></defs></svg>
|
||||
|
After Width: | Height: | Size: 599 B |
10
src/frontend/src/icons/GoogleGenerativeAI/GoogleGemini.jsx
Normal file
10
src/frontend/src/icons/GoogleGenerativeAI/GoogleGemini.jsx
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
const SvgGoogleGenerativeAI = (props) => (<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" {...props}>
|
||||
<path d="M16 8.016A8.522 8.522 0 008.016 16h-.032A8.521 8.521 0 000 8.016v-.032A8.521 8.521 0 007.984 0h.032A8.522 8.522 0 0016 7.984v.032z" fill="url(#prefix__paint0_radial_980_20147)"/>
|
||||
<defs>
|
||||
<radialGradient id="prefix__paint0_radial_980_20147" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(16.1326 5.4553 -43.70045 129.2322 1.588 6.503)">
|
||||
<stop offset=".067" stop-color="#9168C0"/><stop offset=".343" stop-color="#5684D1"/><stop offset=".672" stop-color="#1BA1E3"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
export default SvgGoogleGenerativeAI;
|
||||
9
src/frontend/src/icons/GoogleGenerativeAI/index.tsx
Normal file
9
src/frontend/src/icons/GoogleGenerativeAI/index.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import React, { forwardRef } from "react";
|
||||
import SvgGoogleGenerativeAI from "./GoogleGemini";
|
||||
|
||||
export const GoogleGenerativeAIIcon = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return <SvgGoogleGenerativeAI ref={ref} {...props} />;
|
||||
});
|
||||
|
|
@ -8,14 +8,17 @@ import "./style/index.css";
|
|||
// @ts-ignore
|
||||
import "./style/applies.css";
|
||||
// @ts-ignore
|
||||
import { StrictMode } from "react";
|
||||
import "./style/classes.css";
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById("root") as HTMLElement
|
||||
);
|
||||
root.render(
|
||||
<ContextWrapper>
|
||||
<App />
|
||||
</ContextWrapper>
|
||||
<StrictMode>
|
||||
<ContextWrapper>
|
||||
<App />
|
||||
</ContextWrapper>
|
||||
</StrictMode>
|
||||
);
|
||||
reportWebVitals();
|
||||
|
|
|
|||
|
|
@ -60,10 +60,6 @@ const EditNodeModal = forwardRef(
|
|||
|
||||
const edges = useFlowStore((state) => state.edges);
|
||||
const setNode = useFlowStore((state) => state.setNode);
|
||||
const setNoticeData = useAlertStore((state) => state.setNoticeData);
|
||||
const globalVariablesEntries = useGlobalVariablesStore(
|
||||
(state) => state.globalVariablesEntries
|
||||
);
|
||||
|
||||
function changeAdvanced(n) {
|
||||
setMyData((old) => {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { Button } from "../../ui/button";
|
||||
import { Button } from "../../../../../../components/ui/button";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { BASE_URL_API } from "../../../constants/constants";
|
||||
import { uploadFile } from "../../../controllers/API";
|
||||
import useFlowsManagerStore from "../../../stores/flowsManagerStore";
|
||||
import { IOFileInputProps } from "../../../types/components";
|
||||
import IconComponent from "../../genericIconComponent";
|
||||
import IconComponent from "../../../../../../components/genericIconComponent";
|
||||
import { BASE_URL_API } from "../../../../../../constants/constants";
|
||||
import { uploadFile } from "../../../../../../controllers/API";
|
||||
import useFlowsManagerStore from "../../../../../../stores/flowsManagerStore";
|
||||
import { IOFileInputProps } from "../../../../../../types/components";
|
||||
|
||||
export default function IOFileInput({ field, updateValue }: IOFileInputProps) {
|
||||
//component to handle file upload from chatIO
|
||||
109
src/frontend/src/modals/IOModal/components/IOFieldView/index.tsx
Normal file
109
src/frontend/src/modals/IOModal/components/IOFieldView/index.tsx
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
import { cloneDeep } from "lodash";
|
||||
import { Textarea } from "../../../../components/ui/textarea";
|
||||
import { InputOutput } from "../../../../constants/enums";
|
||||
import useFlowStore from "../../../../stores/flowStore";
|
||||
import { IOFieldViewProps } from "../../../../types/components";
|
||||
import IOFileInput from "./components/FileInput";
|
||||
|
||||
export default function IOFieldView({
|
||||
type,
|
||||
fieldType,
|
||||
fieldId,
|
||||
left,
|
||||
}: IOFieldViewProps): JSX.Element | undefined {
|
||||
const nodes = useFlowStore((state) => state.nodes);
|
||||
const setNode = useFlowStore((state) => state.setNode);
|
||||
const flowPool = useFlowStore((state) => state.flowPool);
|
||||
const node = nodes.find((node) => node.id === fieldId);
|
||||
function handleOutputType() {
|
||||
if (!node) return <>"No node found!"</>;
|
||||
switch (type) {
|
||||
case InputOutput.INPUT:
|
||||
switch (fieldType) {
|
||||
case "TextInput":
|
||||
return (
|
||||
<Textarea
|
||||
className={`w-full custom-scroll ${left ? " min-h-32" : " h-full"}`}
|
||||
placeholder={"Enter text..."}
|
||||
value={node.data.node!.template["input_value"].value}
|
||||
onChange={(e) => {
|
||||
e.target.value;
|
||||
if (node) {
|
||||
let newNode = cloneDeep(node);
|
||||
newNode.data.node!.template["input_value"].value =
|
||||
e.target.value;
|
||||
setNode(node.id, newNode);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
case "FileLoader":
|
||||
return (
|
||||
<IOFileInput
|
||||
field={node.data.node!.template["file_path"]["value"]}
|
||||
updateValue={(e) => {
|
||||
if (node) {
|
||||
let newNode = cloneDeep(node);
|
||||
newNode.data.node!.template["file_path"].value = e;
|
||||
setNode(node.id, newNode);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
default:
|
||||
return (
|
||||
<Textarea
|
||||
className={`w-full custom-scroll ${left ? " min-h-32" : " h-full"}`}
|
||||
placeholder={"Enter text..."}
|
||||
value={node.data.node!.template["input_value"]}
|
||||
onChange={(e) => {
|
||||
e.target.value;
|
||||
if (node) {
|
||||
let newNode = cloneDeep(node);
|
||||
newNode.data.node!.template["input_value"].value =
|
||||
e.target.value;
|
||||
setNode(node.id, newNode);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case InputOutput.OUTPUT:
|
||||
switch (fieldType) {
|
||||
case "TextOutput":
|
||||
return (
|
||||
<Textarea
|
||||
className={`w-full custom-scroll ${left ? " min-h-32" : " h-full"}`}
|
||||
placeholder={"Empty"}
|
||||
// update to real value on flowPool
|
||||
value={
|
||||
(flowPool[node.id] ?? [])[
|
||||
(flowPool[node.id]?.length ?? 1) - 1
|
||||
]?.params ?? ""
|
||||
}
|
||||
readOnly
|
||||
/>
|
||||
);
|
||||
|
||||
default:
|
||||
return (
|
||||
<Textarea
|
||||
className={`w-full custom-scroll ${left ? " min-h-32" : " h-full"}`}
|
||||
placeholder={"Empty"}
|
||||
// update to real value on flowPool
|
||||
value={
|
||||
(flowPool[node.id] ?? [])[
|
||||
(flowPool[node.id]?.length ?? 1) - 1
|
||||
]?.params ?? ""
|
||||
}
|
||||
readOnly
|
||||
/>
|
||||
);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return handleOutputType();
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import IconComponent from "../../../components/genericIconComponent";
|
||||
import { Textarea } from "../../../components/ui/textarea";
|
||||
import IconComponent from "../../../../../components/genericIconComponent";
|
||||
import { Textarea } from "../../../../../components/ui/textarea";
|
||||
import {
|
||||
CHAT_INPUT_PLACEHOLDER,
|
||||
CHAT_INPUT_PLACEHOLDER_SEND,
|
||||
} from "../../../constants/constants";
|
||||
import useFlowsManagerStore from "../../../stores/flowsManagerStore";
|
||||
import { chatInputType } from "../../../types/components";
|
||||
import { classNames } from "../../../utils/utils";
|
||||
} from "../../../../../constants/constants";
|
||||
import useFlowsManagerStore from "../../../../../stores/flowsManagerStore";
|
||||
import { chatInputType } from "../../../../../types/components";
|
||||
import { classNames } from "../../../../../utils/utils";
|
||||
|
||||
export default function ChatInput({
|
||||
lockChat,
|
||||
|
|
@ -25,17 +25,8 @@ export default function ChatInput({
|
|||
}
|
||||
}, [lockChat, inputRef]);
|
||||
|
||||
/* function handleChange(value: number) {
|
||||
console.log(value);
|
||||
if (value > 0) {
|
||||
setRepeat(value);
|
||||
} else {
|
||||
setRepeat(1);
|
||||
}
|
||||
} */
|
||||
|
||||
useEffect(() => {
|
||||
if (inputRef.current) {
|
||||
if (inputRef.current && inputRef.current.scrollHeight !== 0) {
|
||||
inputRef.current.style.height = "inherit"; // Reset the height
|
||||
inputRef.current.style.height = `${inputRef.current.scrollHeight}px`; // Set it to the scrollHeight
|
||||
}
|
||||
|
|
@ -50,7 +41,8 @@ export default function ChatInput({
|
|||
event.key === "Enter" &&
|
||||
!lockChat &&
|
||||
!saveLoading &&
|
||||
!event.shiftKey
|
||||
!event.shiftKey &&
|
||||
!event.nativeEvent.isComposing
|
||||
) {
|
||||
sendMessage(repeat);
|
||||
}
|
||||
|
|
@ -108,7 +100,7 @@ export default function ChatInput({
|
|||
/>
|
||||
) : noInput ? (
|
||||
<IconComponent
|
||||
name="Play"
|
||||
name="Zap"
|
||||
className="form-modal-play-icon"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
|
@ -2,8 +2,8 @@ import { IconCheck, IconClipboard, IconDownload } from "@tabler/icons-react";
|
|||
import { useState } from "react";
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
||||
import { programmingLanguages } from "../../../../constants/constants";
|
||||
import { Props } from "../../../../types/components";
|
||||
import { programmingLanguages } from "../../../../../../constants/constants";
|
||||
import { Props } from "../../../../../../types/components";
|
||||
|
||||
export function CodeBlock({ language, value }: Props): JSX.Element {
|
||||
const [isCopied, setIsCopied] = useState<Boolean>(false);
|
||||
|
|
@ -4,15 +4,14 @@ import Markdown from "react-markdown";
|
|||
import rehypeMathjax from "rehype-mathjax";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkMath from "remark-math";
|
||||
import MaleTechnology from "../../../assets/male-technologist.png";
|
||||
import Robot from "../../../assets/robot.png";
|
||||
import SanitizedHTMLWrapper from "../../../components/SanitizedHTMLWrapper";
|
||||
import CodeTabsComponent from "../../../components/codeTabsComponent";
|
||||
import IconComponent from "../../../components/genericIconComponent";
|
||||
import useAlertStore from "../../../stores/alertStore";
|
||||
import useFlowStore from "../../../stores/flowStore";
|
||||
import { chatMessagePropsType } from "../../../types/components";
|
||||
import { classNames } from "../../../utils/utils";
|
||||
import MaleTechnology from "../../../../../assets/male-technologist.png";
|
||||
import Robot from "../../../../../assets/robot.png";
|
||||
import SanitizedHTMLWrapper from "../../../../../components/SanitizedHTMLWrapper";
|
||||
import CodeTabsComponent from "../../../../../components/codeTabsComponent";
|
||||
import IconComponent from "../../../../../components/genericIconComponent";
|
||||
import useFlowStore from "../../../../../stores/flowStore";
|
||||
import { chatMessagePropsType } from "../../../../../types/components";
|
||||
import { classNames, cn } from "../../../../../utils/utils";
|
||||
import FileCard from "../fileComponent";
|
||||
|
||||
export default function ChatMessage({
|
||||
|
|
@ -34,7 +33,6 @@ export default function ChatMessage({
|
|||
const [isStreaming, setIsStreaming] = useState(false);
|
||||
const eventSource = useRef<EventSource | undefined>(undefined);
|
||||
const updateFlowPool = useFlowStore((state) => state.updateFlowPool);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const chatMessageRef = useRef(chatMessage);
|
||||
|
||||
// Sync ref with state
|
||||
|
|
@ -59,17 +57,7 @@ export default function ChatMessage({
|
|||
setIsStreaming(false);
|
||||
eventSource.current?.close();
|
||||
setStreamUrl(undefined);
|
||||
// property data is not available in the event object
|
||||
// so check if the event object has a data property
|
||||
if (event.data) {
|
||||
let parsedData = JSON.parse(event.data);
|
||||
if (parsedData.error) {
|
||||
reject(new Error(parsedData.error));
|
||||
} else
|
||||
reject(new Error("An error occurred while streaming the output"));
|
||||
} else {
|
||||
reject(new Error("An error occurred while streaming the output"));
|
||||
}
|
||||
reject(new Error("Streaming failed"));
|
||||
};
|
||||
eventSource.current.addEventListener("close", (event) => {
|
||||
setStreamUrl(undefined); // Update state to reflect the stream is closed
|
||||
|
|
@ -91,10 +79,7 @@ export default function ChatMessage({
|
|||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
setErrorData({
|
||||
title: "Streaming Error",
|
||||
list: [error.message],
|
||||
});
|
||||
console.error(error);
|
||||
setLockChat(false);
|
||||
});
|
||||
}
|
||||
|
|
@ -123,30 +108,28 @@ export default function ChatMessage({
|
|||
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
|
||||
className={classNames(
|
||||
"mr-3 mt-1 flex max-w-16 flex-col items-center gap-1 overflow-hidden px-3 pb-3"
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<div
|
||||
className={cn(
|
||||
"relative flex h-8 w-8 items-center justify-center overflow-hidden rounded-md p-5 text-2xl",
|
||||
!chat.isSend ? "bg-chat-bot-icon" : "bg-chat-user-icon"
|
||||
)}
|
||||
>
|
||||
<img
|
||||
src={!chat.isSend ? Robot : MaleTechnology}
|
||||
className="absolute scale-[60%]"
|
||||
alt={!chat.isSend ? "robot_image" : "male_technology"}
|
||||
/>
|
||||
</div>
|
||||
<span className="max-w-16 truncate text-xs">
|
||||
{chat.sender_name}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{!chat.isSend ? (
|
||||
<div className="form-modal-chat-text-position min-w-96 flex-grow">
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import * as base64js from "base64-js";
|
||||
import { useState } from "react";
|
||||
import IconComponent from "../../../components/genericIconComponent";
|
||||
import { fileCardPropsType } from "../../../types/components";
|
||||
import IconComponent from "../../../../../components/genericIconComponent";
|
||||
import { fileCardPropsType } from "../../../../../types/components";
|
||||
|
||||
export default function FileCard({
|
||||
fileName,
|
||||
|
|
@ -1,31 +1,32 @@
|
|||
import { useEffect, useRef, useState } from "react";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import { NOCHATOUTPUT_NOTICE_ALERT } from "../../constants/alerts_constants";
|
||||
import IconComponent from "../../../../components/genericIconComponent";
|
||||
import { NOCHATOUTPUT_NOTICE_ALERT } from "../../../../constants/alerts_constants";
|
||||
import {
|
||||
CHAT_FIRST_INITIAL_TEXT,
|
||||
CHAT_SECOND_INITIAL_TEXT,
|
||||
} from "../../constants/constants";
|
||||
import { deleteFlowPool } from "../../controllers/API";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import useFlowStore from "../../stores/flowStore";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { sendAllProps } from "../../types/api";
|
||||
} from "../../../../constants/constants";
|
||||
import { deleteFlowPool } from "../../../../controllers/API";
|
||||
import useAlertStore from "../../../../stores/alertStore";
|
||||
import useFlowStore from "../../../../stores/flowStore";
|
||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||
import { sendAllProps } from "../../../../types/api";
|
||||
import {
|
||||
ChatMessageType,
|
||||
ChatOutputType,
|
||||
FlowPoolObjectType,
|
||||
} from "../../types/chat";
|
||||
import { classNames } from "../../utils/utils";
|
||||
} from "../../../../types/chat";
|
||||
import { chatViewProps } from "../../../../types/components";
|
||||
import { classNames } from "../../../../utils/utils";
|
||||
import ChatInput from "./chatInput";
|
||||
import ChatMessage from "./chatMessage";
|
||||
|
||||
export default function NewChatView({
|
||||
export default function ChatView({
|
||||
sendMessage,
|
||||
chatValue,
|
||||
setChatValue,
|
||||
lockChat,
|
||||
setLockChat,
|
||||
}): JSX.Element {
|
||||
}: chatViewProps): JSX.Element {
|
||||
const { flowPool, outputs, inputs, CleanFlowPool } = useFlowStore();
|
||||
const { setNoticeData } = useAlertStore();
|
||||
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
|
||||
|
|
@ -38,11 +39,11 @@ export default function NewChatView({
|
|||
const outputTypes = outputs.map((obj) => obj.type);
|
||||
const updateFlowPool = useFlowStore((state) => state.updateFlowPool);
|
||||
|
||||
useEffect(() => {
|
||||
if (!outputTypes.includes("ChatOutput")) {
|
||||
setNoticeData({ title: NOCHATOUTPUT_NOTICE_ALERT });
|
||||
}
|
||||
}, []);
|
||||
// useEffect(() => {
|
||||
// if (!outputTypes.includes("ChatOutput")) {
|
||||
// setNoticeData({ title: NOCHATOUTPUT_NOTICE_ALERT });
|
||||
// }
|
||||
// }, []);
|
||||
|
||||
//build chat history
|
||||
useEffect(() => {
|
||||
|
|
@ -1,36 +1,38 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import AccordionComponent from "../../components/AccordionComponent";
|
||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import { Badge } from "../../components/ui/badge";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import {
|
||||
Tabs,
|
||||
TabsContent,
|
||||
TabsList,
|
||||
TabsTrigger,
|
||||
} from "../../components/ui/tabs";
|
||||
import {
|
||||
CHAT_FORM_DIALOG_SUBTITLE,
|
||||
OUTPUTS_MODAL_TITLE,
|
||||
TEXT_INPUT_MODAL_TITLE,
|
||||
} from "../../constants/constants";
|
||||
import BaseModal from "../../modals/baseModal";
|
||||
import { InputOutput } from "../../constants/enums";
|
||||
import useFlowStore from "../../stores/flowStore";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { IOModalPropsType } from "../../types/components";
|
||||
import { NodeType } from "../../types/flow";
|
||||
import { updateVerticesOrder } from "../../utils/buildUtils";
|
||||
import { cn } from "../../utils/utils";
|
||||
import AccordionComponent from "../AccordionComponent";
|
||||
import IOInputField from "../IOInputField";
|
||||
import IOOutputView from "../IOOutputView";
|
||||
import ShadTooltip from "../ShadTooltipComponent";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
import NewChatView from "../newChatView";
|
||||
import { Badge } from "../ui/badge";
|
||||
import { Button } from "../ui/button";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
|
||||
import BaseModal from "../baseModal";
|
||||
import IOFieldView from "./components/IOFieldView";
|
||||
import ChatView from "./components/chatView";
|
||||
|
||||
export default function IOView({
|
||||
export default function IOModal({
|
||||
children,
|
||||
open,
|
||||
setOpen,
|
||||
disable,
|
||||
}: {
|
||||
children: JSX.Element;
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
disable?: boolean;
|
||||
}): JSX.Element {
|
||||
}: IOModalPropsType): JSX.Element {
|
||||
const allNodes = useFlowStore((state) => state.nodes);
|
||||
const inputs = useFlowStore((state) => state.inputs).filter(
|
||||
(input) => input.type !== "ChatInput"
|
||||
);
|
||||
|
|
@ -52,9 +54,24 @@ export default function IOView({
|
|||
const [selectedTab, setSelectedTab] = useState(
|
||||
inputs.length > 0 ? 1 : outputs.length > 0 ? 2 : 0
|
||||
);
|
||||
|
||||
function startView() {
|
||||
if (!chatInput && !chatOutput) {
|
||||
if (inputs.length > 0) {
|
||||
return inputs[0];
|
||||
}
|
||||
else {
|
||||
return outputs[0];
|
||||
}
|
||||
}
|
||||
else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
const [selectedViewField, setSelectedViewField] = useState<
|
||||
{ type: string; id: string } | undefined
|
||||
>(undefined);
|
||||
>(startView());
|
||||
|
||||
const buildFlow = useFlowStore((state) => state.buildFlow);
|
||||
const setIsBuilding = useFlowStore((state) => state.setIsBuilding);
|
||||
|
|
@ -99,19 +116,13 @@ export default function IOView({
|
|||
}
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedViewField(undefined);
|
||||
setSelectedViewField(startView());
|
||||
setSelectedTab(inputs.length > 0 ? 1 : outputs.length > 0 ? 2 : 0);
|
||||
}, [inputs.length, outputs.length]);
|
||||
}, [allNodes]);
|
||||
|
||||
return (
|
||||
<BaseModal
|
||||
size={
|
||||
haveChat || selectedViewField
|
||||
? selectedTab === 0
|
||||
? "large-thin"
|
||||
: "large"
|
||||
: "small"
|
||||
}
|
||||
size={selectedTab === 0 ? "large-thin" : "large"}
|
||||
open={open}
|
||||
setOpen={setOpen}
|
||||
disable={disable}
|
||||
|
|
@ -134,8 +145,7 @@ export default function IOView({
|
|||
{selectedTab !== 0 && (
|
||||
<div
|
||||
className={cn(
|
||||
"mr-6 flex h-full w-2/6 flex-shrink-0 flex-col justify-start transition-all duration-300",
|
||||
haveChat || selectedViewField ? "w-2/6" : "w-full"
|
||||
"mr-6 flex h-full w-2/6 flex-shrink-0 flex-col justify-start transition-all duration-300"
|
||||
)}
|
||||
>
|
||||
<Tabs
|
||||
|
|
@ -212,10 +222,11 @@ export default function IOView({
|
|||
<div className="file-component-tab-column">
|
||||
<div className="">
|
||||
{input && (
|
||||
<IOInputField
|
||||
<IOFieldView
|
||||
type={InputOutput.INPUT}
|
||||
left={true}
|
||||
inputType={input.type}
|
||||
inputId={input.id}
|
||||
fieldType={input.type}
|
||||
fieldId={input.id}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -230,7 +241,7 @@ export default function IOView({
|
|||
className="api-modal-tabs-content mt-4"
|
||||
>
|
||||
<div className="mx-2 mb-2 flex items-center gap-2 text-sm font-bold">
|
||||
<IconComponent className="h-4 w-4" name={"FileType2"} />
|
||||
<IconComponent className="h-4 w-4" name={"Type"} />
|
||||
{OUTPUTS_MODAL_TITLE}
|
||||
</div>
|
||||
{nodes
|
||||
|
|
@ -279,10 +290,11 @@ export default function IOView({
|
|||
<div className="file-component-tab-column">
|
||||
<div className="">
|
||||
{output && (
|
||||
<IOOutputView
|
||||
<IOFieldView
|
||||
type={InputOutput.OUTPUT}
|
||||
left={true}
|
||||
outputType={output.type}
|
||||
outputId={output.id}
|
||||
fieldType={output.type}
|
||||
fieldId={output.id}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -296,74 +308,81 @@ export default function IOView({
|
|||
</div>
|
||||
)}
|
||||
|
||||
{haveChat || selectedViewField ? (
|
||||
<div className="flex h-full min-w-96 flex-grow">
|
||||
{selectedViewField && (
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-full w-full flex-col items-start gap-4 pt-4",
|
||||
!selectedViewField ? "hidden" : ""
|
||||
)}
|
||||
>
|
||||
<div className="font-xl flex items-center justify-center gap-3 font-semibold">
|
||||
<button onClick={() => setSelectedViewField(undefined)}>
|
||||
<IconComponent
|
||||
name={"ArrowLeft"}
|
||||
className="h-6 w-6"
|
||||
></IconComponent>
|
||||
</button>
|
||||
{selectedViewField.type}
|
||||
</div>
|
||||
<div className="h-full w-full">
|
||||
{inputs.some(
|
||||
(input) => input.id === selectedViewField.id
|
||||
) ? (
|
||||
<IOInputField
|
||||
left={false}
|
||||
inputType={selectedViewField.type!}
|
||||
inputId={selectedViewField.id!}
|
||||
/>
|
||||
) : (
|
||||
<IOOutputView
|
||||
left={false}
|
||||
outputType={selectedViewField.type!}
|
||||
outputId={selectedViewField.id!}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex h-full min-w-96 flex-grow">
|
||||
{selectedViewField && (
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-full w-full",
|
||||
selectedViewField ? "hidden" : ""
|
||||
"flex h-full w-full flex-col items-start gap-4 pt-4",
|
||||
!selectedViewField ? "hidden" : ""
|
||||
)}
|
||||
>
|
||||
<NewChatView
|
||||
<div className="font-xl flex items-center justify-center gap-3 font-semibold">
|
||||
{(haveChat && <button onClick={() => setSelectedViewField(undefined)}>
|
||||
<IconComponent
|
||||
name={"ArrowLeft"}
|
||||
className="h-6 w-6"
|
||||
></IconComponent>
|
||||
</button>)}
|
||||
{
|
||||
nodes.find((node) => node.id === selectedViewField.id)
|
||||
?.data.node.display_name
|
||||
}
|
||||
</div>
|
||||
<div className="h-full w-full">
|
||||
{inputs.some(
|
||||
(input) => input.id === selectedViewField.id
|
||||
) ? (
|
||||
<IOFieldView
|
||||
type={InputOutput.INPUT}
|
||||
left={false}
|
||||
fieldType={selectedViewField.type!}
|
||||
fieldId={selectedViewField.id!}
|
||||
/>
|
||||
) : (
|
||||
<IOFieldView
|
||||
type={InputOutput.OUTPUT}
|
||||
left={false}
|
||||
fieldType={selectedViewField.type!}
|
||||
fieldId={selectedViewField.id!}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-full w-full",
|
||||
selectedViewField ? "hidden" : ""
|
||||
)}
|
||||
>
|
||||
{haveChat ? (
|
||||
<ChatView
|
||||
sendMessage={sendMessage}
|
||||
chatValue={chatValue}
|
||||
setChatValue={setChatValue}
|
||||
lockChat={lockChat}
|
||||
setLockChat={setLockChat}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<span className="flex h-full w-full items-center justify-center font-thin text-muted-foreground">
|
||||
Select an IO component to view
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="absolute bottom-8 right-8"></div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BaseModal.Content>
|
||||
<BaseModal.Footer>
|
||||
{!haveChat && (
|
||||
<div className="flex w-full justify-end pt-2">
|
||||
<div className="flex w-full justify-end pt-2">
|
||||
<Button
|
||||
variant={"outline"}
|
||||
className="flex gap-2 px-3"
|
||||
onClick={() => sendMessage(1)}
|
||||
>
|
||||
<IconComponent
|
||||
name={isBuilding ? "Loader2" : "Play"}
|
||||
name={isBuilding ? "Loader2" : "Zap"}
|
||||
className={cn(
|
||||
"h-4 w-4",
|
||||
isBuilding
|
||||
|
|
@ -1,6 +1,11 @@
|
|||
import { useNavigate } from "react-router-dom";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { Card, CardContent, CardDescription, CardTitle } from "../ui/card";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardTitle,
|
||||
} from "../../../../components/ui/card";
|
||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||
|
||||
export default function NewFlowCardComponent() {
|
||||
const addFlow = useFlowsManagerStore((state) => state.addFlow);
|
||||
|
|
@ -1,21 +1,28 @@
|
|||
import { useNavigate } from "react-router-dom";
|
||||
/// <reference types="vite-plugin-svgr/client" />
|
||||
//@ts-ignore
|
||||
import { ReactComponent as TransferFiles } from "../../assets/undraw_transfer_files_re_a2a9.svg";
|
||||
import { ReactComponent as TransferFiles } from "../../../../assets/undraw_transfer_files_re_a2a9.svg";
|
||||
//@ts-ignore
|
||||
import { ReactComponent as BasicPrompt } from "../../assets/undraw_design_components_9vy6.svg";
|
||||
import { ReactComponent as BasicPrompt } from "../../../../assets/undraw_design_components_9vy6.svg";
|
||||
//@ts-ignore
|
||||
import { ReactComponent as ChatWithHistory } from "../../assets/undraw_mobile_messages_re_yx8w.svg";
|
||||
import { ReactComponent as ChatWithHistory } from "../../../../assets/undraw_mobile_messages_re_yx8w.svg";
|
||||
//@ts-ignore
|
||||
import { ReactComponent as Assistant } from "../../assets/undraw_team_collaboration_re_ow29.svg";
|
||||
import { ReactComponent as Assistant } from "../../../../assets/undraw_team_collaboration_re_ow29.svg";
|
||||
//@ts-ignore
|
||||
import { ReactComponent as APIRequest } from "../../assets/undraw_real_time_analytics_re_yliv.svg";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { FlowType } from "../../types/flow";
|
||||
import { updateIds } from "../../utils/reactflowUtils";
|
||||
import { Card, CardContent, CardDescription, CardTitle } from "../ui/card";
|
||||
import { ReactComponent as APIRequest } from "../../../../assets/undraw_real_time_analytics_re_yliv.svg";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardTitle,
|
||||
} from "../../../../components/ui/card";
|
||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||
import { UndrawCardComponentProps } from "../../../../types/components";
|
||||
import { updateIds } from "../../../../utils/reactflowUtils";
|
||||
|
||||
export default function UndrawCardComponent({ flow }: { flow: FlowType }) {
|
||||
export default function UndrawCardComponent({
|
||||
flow,
|
||||
}: UndrawCardComponentProps): JSX.Element {
|
||||
const addFlow = useFlowsManagerStore((state) => state.addFlow);
|
||||
const navigate = useNavigate();
|
||||
|
||||
35
src/frontend/src/modals/NewFlowModal/index.tsx
Normal file
35
src/frontend/src/modals/NewFlowModal/index.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { newFlowModalPropsType } from "../../types/components";
|
||||
import BaseModal from "../baseModal";
|
||||
import NewFlowCardComponent from "./components/NewFlowCardComponent";
|
||||
import UndrawCardComponent from "./components/undrawCards";
|
||||
|
||||
export default function NewFlowModal({
|
||||
open,
|
||||
setOpen,
|
||||
}: newFlowModalPropsType): JSX.Element {
|
||||
const examples = useFlowsManagerStore((state) => state.examples);
|
||||
|
||||
return (
|
||||
<BaseModal size="three-cards" open={open} setOpen={setOpen}>
|
||||
<BaseModal.Header description={"Select a template below"}>
|
||||
<span className="pr-2" data-testid="modal-title">
|
||||
Get Started
|
||||
</span>
|
||||
{/* <IconComponent
|
||||
name="Group"
|
||||
className="h-6 w-6 stroke-2 text-primary "
|
||||
aria-hidden="true"
|
||||
/> */}
|
||||
</BaseModal.Header>
|
||||
<BaseModal.Content>
|
||||
<div className=" grid h-full w-full grid-cols-3 gap-3 overflow-auto p-4 custom-scroll">
|
||||
<NewFlowCardComponent />
|
||||
{examples.map((example, idx) => {
|
||||
return <UndrawCardComponent key={idx} flow={example} />;
|
||||
})}
|
||||
</div>
|
||||
</BaseModal.Content>
|
||||
</BaseModal>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
import { useEffect } from "react";
|
||||
import IconComponent from "../../../components/genericIconComponent";
|
||||
import { Textarea } from "../../../components/ui/textarea";
|
||||
import {
|
||||
CHAT_INPUT_PLACEHOLDER,
|
||||
CHAT_INPUT_PLACEHOLDER_SEND,
|
||||
} from "../../../constants/constants";
|
||||
import { chatInputType } from "../../../types/components";
|
||||
import { classNames } from "../../../utils/utils";
|
||||
|
||||
export default function ChatInput({
|
||||
lockChat,
|
||||
chatValue,
|
||||
sendMessage,
|
||||
setChatValue,
|
||||
inputRef,
|
||||
noInput,
|
||||
}: chatInputType): JSX.Element {
|
||||
useEffect(() => {
|
||||
if (!lockChat && inputRef.current) {
|
||||
inputRef.current.focus();
|
||||
}
|
||||
}, [lockChat, inputRef]);
|
||||
|
||||
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">
|
||||
<Textarea
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Enter" && !event.nativeEvent.isComposing && !lockChat && !event.shiftKey) {
|
||||
sendMessage();
|
||||
}
|
||||
}}
|
||||
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..."
|
||||
: typeof chatValue === "object" &&
|
||||
Object.keys(chatValue)?.length === 0
|
||||
? CHAT_INPUT_PLACEHOLDER
|
||||
: 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 ? CHAT_INPUT_PLACEHOLDER : CHAT_INPUT_PLACEHOLDER_SEND
|
||||
}
|
||||
/>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,221 +0,0 @@
|
|||
import Convert from "ansi-to-html";
|
||||
import { useState } from "react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import rehypeMathjax from "rehype-mathjax";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkMath from "remark-math";
|
||||
import MaleTechnology from "../../../assets/male-technologist.png";
|
||||
import Robot from "../../../assets/robot.png";
|
||||
import SanitizedHTMLWrapper from "../../../components/SanitizedHTMLWrapper";
|
||||
import CodeTabsComponent from "../../../components/codeTabsComponent";
|
||||
import IconComponent from "../../../components/genericIconComponent";
|
||||
import { chatMessagePropsType } from "../../../types/components";
|
||||
import { classNames } from "../../../utils/utils";
|
||||
import FileCard from "../fileComponent";
|
||||
|
||||
export default function ChatMessage({
|
||||
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>
|
||||
</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>
|
||||
</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">
|
||||
{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>
|
||||
);
|
||||
}
|
||||
|
||||
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>
|
||||
)}
|
||||
</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[chat.chatKey]}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<span>{chat.message[chat.chatKey]}</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,650 +0,0 @@
|
|||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
import { sendAllProps } from "../../types/api";
|
||||
import { ChatMessageType } from "../../types/chat";
|
||||
import { FlowType } from "../../types/flow";
|
||||
import { classNames } from "../../utils/utils";
|
||||
import ChatInput from "./chatInput";
|
||||
import ChatMessage from "./chatMessage";
|
||||
|
||||
import _, { cloneDeep } from "lodash";
|
||||
import AccordionComponent from "../../components/AccordionComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import ToggleShadComponent from "../../components/toggleShadComponent";
|
||||
import { Badge } from "../../components/ui/badge";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "../../components/ui/dialog";
|
||||
import { Textarea } from "../../components/ui/textarea";
|
||||
import {
|
||||
CHAT_ERROR_ALERT,
|
||||
INFO_MISSING_ALERT,
|
||||
MSG_ERROR_ALERT,
|
||||
} from "../../constants/alerts_constants";
|
||||
import {
|
||||
CHAT_FIRST_INITIAL_TEXT,
|
||||
CHAT_FORM_DIALOG_SUBTITLE,
|
||||
CHAT_SECOND_INITIAL_TEXT,
|
||||
LANGFLOW_CHAT_TITLE,
|
||||
} from "../../constants/constants";
|
||||
import { BuildStatus } from "../../constants/enums";
|
||||
import { AuthContext } from "../../contexts/authContext";
|
||||
import { getBuildStatus } from "../../controllers/API";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import useFlowStore from "../../stores/flowStore";
|
||||
import { FlowState } from "../../types/tabs";
|
||||
import { validateNodes } from "../../utils/reactflowUtils";
|
||||
|
||||
export default function FormModal({
|
||||
flow,
|
||||
open,
|
||||
setOpen,
|
||||
}: {
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
flow: FlowType;
|
||||
}): JSX.Element {
|
||||
const nodes = useFlowStore((state) => state.nodes);
|
||||
const edges = useFlowStore((state) => state.edges);
|
||||
const updateBuildStatus = useFlowStore((state) => state.updateBuildStatus);
|
||||
const flowState = useFlowStore((state) => state.flowState);
|
||||
const setFlowState = useFlowStore((state) => state.setFlowState);
|
||||
const [chatValue, setChatValue] = useState(() => {
|
||||
try {
|
||||
if (!flowState) {
|
||||
throw new Error("flowState is undefined");
|
||||
}
|
||||
const inputKeys = flowState.input_keys;
|
||||
const handleKeys = flowState.handle_keys;
|
||||
|
||||
const keyToUse = Object.keys(inputKeys!).find(
|
||||
(key) => !handleKeys?.some((j) => j === key) && inputKeys![key] === ""
|
||||
);
|
||||
|
||||
return inputKeys![keyToUse!];
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
// return a sensible default or `undefined` if no default is possible
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
|
||||
const [chatHistory, setChatHistory] = useState<ChatMessageType[]>([]);
|
||||
const template = useRef(flowState?.template ?? undefined);
|
||||
const { accessToken } = useContext(AuthContext);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const ws = useRef<WebSocket | null>(null);
|
||||
const [lockChat, setLockChat] = useState(false);
|
||||
const isOpen = useRef(open);
|
||||
const messagesRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const [chatKey, setChatKey] = useState(() => {
|
||||
if (flowState?.input_keys) {
|
||||
return Object.keys(flowState.input_keys!).find(
|
||||
(key) =>
|
||||
!flowState.handle_keys!.some((j) => j === key) &&
|
||||
flowState.input_keys![key] === ""
|
||||
);
|
||||
}
|
||||
// TODO: return a sensible default
|
||||
return "";
|
||||
});
|
||||
useEffect(() => {
|
||||
if (messagesRef.current) {
|
||||
messagesRef.current.scrollTop = messagesRef.current.scrollHeight;
|
||||
}
|
||||
}, [chatHistory]);
|
||||
|
||||
useEffect(() => {
|
||||
isOpen.current = open;
|
||||
}, [open]);
|
||||
|
||||
var isStream = false;
|
||||
|
||||
const addChatHistory = (
|
||||
message: string | Object,
|
||||
isSend: boolean,
|
||||
chatKey: string,
|
||||
template?: string,
|
||||
thought?: string,
|
||||
files?: Array<any>
|
||||
) => {
|
||||
setChatHistory((old) => {
|
||||
let newChat = _.cloneDeep(old);
|
||||
if (files) {
|
||||
newChat.push({ message, isSend, files, thought, chatKey });
|
||||
} else if (thought) {
|
||||
newChat.push({ message, isSend, thought, chatKey });
|
||||
} else if (template) {
|
||||
newChat.push({ message, isSend, chatKey, template });
|
||||
} else {
|
||||
newChat.push({ message, isSend, chatKey });
|
||||
}
|
||||
return newChat;
|
||||
});
|
||||
};
|
||||
//add proper type signature for function
|
||||
|
||||
function updateLastMessage({
|
||||
str,
|
||||
thought,
|
||||
prompt,
|
||||
end = false,
|
||||
files,
|
||||
}: {
|
||||
str?: string;
|
||||
thought?: string;
|
||||
prompt?: string;
|
||||
end?: boolean;
|
||||
files?: Array<any>;
|
||||
}) {
|
||||
setChatHistory((old) => {
|
||||
let newChat = [...old];
|
||||
if (str) {
|
||||
if (end) {
|
||||
newChat[newChat.length - 1].message = str;
|
||||
} else {
|
||||
newChat[newChat.length - 1].message =
|
||||
newChat[newChat.length - 1].message + str;
|
||||
}
|
||||
}
|
||||
|
||||
if (thought && newChat[newChat.length - 1]?.thought) {
|
||||
newChat[newChat.length - 1].thought = thought;
|
||||
}
|
||||
if (files && newChat[newChat.length - 1]?.files) {
|
||||
newChat[newChat.length - 1].files = files;
|
||||
}
|
||||
if (prompt && newChat[newChat.length - 2]?.template) {
|
||||
newChat[newChat.length - 2].template = prompt;
|
||||
}
|
||||
return newChat;
|
||||
});
|
||||
}
|
||||
|
||||
function handleOnClose(event: CloseEvent): void {
|
||||
if (isOpen.current) {
|
||||
//check if the user has been logged out, if so close the chat when the user is redirected to the login page
|
||||
if (window.location.href.includes("login")) {
|
||||
setOpen(false);
|
||||
ws.current?.close();
|
||||
return;
|
||||
}
|
||||
|
||||
getBuildStatus(flow.id)
|
||||
.then((response) => {
|
||||
if (response.data.built) {
|
||||
connectWS();
|
||||
} else {
|
||||
setErrorData({
|
||||
title: CHAT_ERROR_ALERT,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
setErrorData({
|
||||
title: error.data?.detail ? error.data.detail : error.message,
|
||||
});
|
||||
});
|
||||
setErrorData({ title: event.reason });
|
||||
setTimeout(() => {
|
||||
setLockChat(false);
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
//TODO improve check of user authentication
|
||||
function getWebSocketUrl(
|
||||
chatId: string,
|
||||
isDevelopment: boolean = false
|
||||
): string {
|
||||
const isSecureProtocol =
|
||||
window.location.protocol === "https:" || window.location.port === "443";
|
||||
const webSocketProtocol = isSecureProtocol ? "wss" : "ws";
|
||||
const host = isDevelopment ? "localhost:7860" : window.location.host;
|
||||
|
||||
const chatEndpoint = `/api/v1/chat/${chatId}`;
|
||||
|
||||
return `${
|
||||
isDevelopment ? "ws" : webSocketProtocol
|
||||
}://${host}${chatEndpoint}?token=${encodeURIComponent(accessToken!)}`;
|
||||
}
|
||||
|
||||
function handleWsMessage(data: any) {
|
||||
if (Array.isArray(data) && data.length > 0) {
|
||||
//set chat history
|
||||
setChatHistory((_) => {
|
||||
let newChatHistory: ChatMessageType[] = [];
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (data[i].type === "prompt" && data[i].prompt) {
|
||||
if (data[i - 1] && !data[i - 1].is_bot) {
|
||||
data[i - 1].prompt = data[i].prompt;
|
||||
template.current = data[i].prompt;
|
||||
}
|
||||
}
|
||||
}
|
||||
data = data.filter((item: any) => item.type !== "prompt");
|
||||
data.forEach(
|
||||
(chatItem: {
|
||||
intermediate_steps?: string;
|
||||
is_bot: boolean;
|
||||
message: string;
|
||||
prompt?: string;
|
||||
type: string;
|
||||
chatKey: string;
|
||||
files?: Array<any>;
|
||||
}) => {
|
||||
if (chatItem.message) {
|
||||
newChatHistory.push(
|
||||
chatItem.files
|
||||
? {
|
||||
isSend: !chatItem.is_bot,
|
||||
message: chatItem.message,
|
||||
template: chatItem.prompt,
|
||||
thought: chatItem.intermediate_steps,
|
||||
files: chatItem.files,
|
||||
chatKey: chatItem.chatKey,
|
||||
}
|
||||
: {
|
||||
isSend: !chatItem.is_bot,
|
||||
message: chatItem.message,
|
||||
template: chatItem.prompt,
|
||||
thought: chatItem.intermediate_steps,
|
||||
chatKey: chatItem.chatKey,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
return newChatHistory;
|
||||
});
|
||||
}
|
||||
if (data.type === "start") {
|
||||
addChatHistory("", false, chatKey!);
|
||||
isStream = true;
|
||||
}
|
||||
if (data.type === "end") {
|
||||
if (data.message) {
|
||||
updateLastMessage({
|
||||
str: data.message,
|
||||
end: true,
|
||||
prompt: template.current,
|
||||
});
|
||||
}
|
||||
if (data.intermediate_steps) {
|
||||
updateLastMessage({
|
||||
str: data.message,
|
||||
thought: data.intermediate_steps,
|
||||
end: true,
|
||||
});
|
||||
}
|
||||
if (data.files) {
|
||||
updateLastMessage({
|
||||
end: true,
|
||||
files: data.files,
|
||||
});
|
||||
}
|
||||
setLockChat(false);
|
||||
isStream = false;
|
||||
}
|
||||
if (data.type == "prompt" && data.prompt) {
|
||||
template.current = data.prompt;
|
||||
}
|
||||
if (data.type === "stream" && isStream) {
|
||||
updateLastMessage({ str: data.message });
|
||||
}
|
||||
}
|
||||
|
||||
function connectWS(): void {
|
||||
try {
|
||||
const urlWs = getWebSocketUrl(
|
||||
flow.id,
|
||||
process.env.NODE_ENV === "development"
|
||||
);
|
||||
const newWs = new WebSocket(urlWs);
|
||||
newWs.onopen = () => {
|
||||
console.log("WebSocket connection established!");
|
||||
};
|
||||
newWs.onmessage = (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
handleWsMessage(data);
|
||||
//get chat history
|
||||
};
|
||||
newWs.onclose = (event) => {
|
||||
handleOnClose(event);
|
||||
};
|
||||
newWs.onerror = (ev) => {
|
||||
console.log(ev);
|
||||
connectWS();
|
||||
};
|
||||
ws.current = newWs;
|
||||
} catch (error) {
|
||||
if (flow.id === "") {
|
||||
connectWS();
|
||||
}
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
connectWS();
|
||||
return () => {
|
||||
console.log(ws);
|
||||
if (ws.current) {
|
||||
ws.current.close();
|
||||
}
|
||||
};
|
||||
// do not add connectWS on dependencies array
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (ws.current) {
|
||||
console.log("closing ws");
|
||||
ws.current.close();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
ws.current &&
|
||||
(ws.current.readyState === ws.current.CLOSED ||
|
||||
ws.current.readyState === ws.current.CLOSING)
|
||||
) {
|
||||
connectWS();
|
||||
setLockChat(false);
|
||||
}
|
||||
// do not add connectWS on dependencies array
|
||||
}, [lockChat]);
|
||||
|
||||
async function sendAll(data: sendAllProps): Promise<void> {
|
||||
try {
|
||||
if (ws) {
|
||||
ws.current?.send(JSON.stringify(data));
|
||||
}
|
||||
} catch (error) {
|
||||
setErrorData({
|
||||
title: MSG_ERROR_ALERT,
|
||||
list: [(error as { message: string }).message],
|
||||
});
|
||||
setChatValue(data.inputs);
|
||||
connectWS();
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (ref.current) ref.current.scrollIntoView({ behavior: "smooth" });
|
||||
}, [chatHistory]);
|
||||
|
||||
const ref = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (open && ref.current) {
|
||||
ref.current.focus();
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
function sendMessage(): void {
|
||||
let nodeValidationErrors = validateNodes(nodes, edges);
|
||||
const errors = nodeValidationErrors.flatMap((error) => error.errors);
|
||||
if (errors.length === 0) {
|
||||
setLockChat(true);
|
||||
let inputs = flowState?.input_keys;
|
||||
setChatValue("");
|
||||
const message = inputs;
|
||||
addChatHistory(message!, true, chatKey!, template.current);
|
||||
sendAll({
|
||||
...flow.data!,
|
||||
inputs: inputs!,
|
||||
chatHistory,
|
||||
name: flow.name,
|
||||
description: flow.description,
|
||||
chatKey: chatKey!,
|
||||
});
|
||||
if (flowState && chatKey) {
|
||||
setFlowState((old: FlowState | undefined) => {
|
||||
let newFlowState = cloneDeep(old!);
|
||||
newFlowState.input_keys![chatKey] = "";
|
||||
return newFlowState;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setErrorData({
|
||||
title: INFO_MISSING_ALERT,
|
||||
list: errors,
|
||||
});
|
||||
const ids = nodeValidationErrors.map((error) => error.id);
|
||||
updateBuildStatus(ids, BuildStatus.ERROR);
|
||||
}
|
||||
}
|
||||
function clearChat(): void {
|
||||
setChatHistory([]);
|
||||
template.current = flowState?.template;
|
||||
ws.current?.send(JSON.stringify({ clear_history: true }));
|
||||
if (lockChat) setLockChat(false);
|
||||
}
|
||||
|
||||
function handleOnCheckedChange(checked: boolean, i: string) {
|
||||
if (checked === true) {
|
||||
setChatKey(i);
|
||||
setChatValue(flowState?.input_keys![i] ?? "");
|
||||
} else {
|
||||
setChatKey(null!);
|
||||
setChatValue("");
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger hidden></DialogTrigger>
|
||||
{flowState && flowState && (
|
||||
<DialogContent className="min-w-[80vw]">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center">
|
||||
<span className="pr-2">Chat</span>
|
||||
<IconComponent
|
||||
name="prompts"
|
||||
className="h-6 w-6 pl-1 text-foreground"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</DialogTitle>
|
||||
<DialogDescription>{CHAT_FORM_DIALOG_SUBTITLE}</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="flex-max-width mt-2 h-[80vh]">
|
||||
<div className="form-modal-iv-size">
|
||||
<div className="file-component-arrangement">
|
||||
<IconComponent
|
||||
name="Variable"
|
||||
className=" file-component-variable"
|
||||
/>
|
||||
<span className="file-component-variables-span text-md">
|
||||
Input Variables
|
||||
</span>
|
||||
</div>
|
||||
<div className="file-component-variables-title">
|
||||
<div className="file-component-variables-div">
|
||||
<span className="text-sm font-medium text-primary">Name</span>
|
||||
</div>
|
||||
<div className="file-component-variables-div">
|
||||
<span className="text-sm font-medium text-primary">
|
||||
Chat Input
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{flowState?.input_keys
|
||||
? Object.keys(flowState?.input_keys!).map((key, index) => (
|
||||
<div className="file-component-accordion-div" key={index}>
|
||||
<AccordionComponent
|
||||
trigger={
|
||||
<div className="file-component-badge-div">
|
||||
<Badge variant="gray" size="md">
|
||||
{key}
|
||||
</Badge>
|
||||
|
||||
<div
|
||||
className="-mb-1"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<ToggleShadComponent
|
||||
enabled={chatKey === key}
|
||||
setEnabled={(value) =>
|
||||
handleOnCheckedChange(value, key)
|
||||
}
|
||||
size="small"
|
||||
disabled={flowState.handle_keys!.some(
|
||||
(t) => t === key
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
key={index}
|
||||
keyValue={key}
|
||||
>
|
||||
<div className="file-component-tab-column">
|
||||
{flowState?.handle_keys!.some((t) => t === key) && (
|
||||
<div className="font-normal text-muted-foreground ">
|
||||
Source: Component
|
||||
</div>
|
||||
)}
|
||||
<Textarea
|
||||
className="custom-scroll"
|
||||
value={flowState?.input_keys![key]}
|
||||
onChange={(e) => {
|
||||
if (flowState) {
|
||||
setFlowState((old: FlowState | undefined) => {
|
||||
let newFlowState = cloneDeep(old!);
|
||||
newFlowState.input_keys![key] =
|
||||
e.target.value;
|
||||
return newFlowState;
|
||||
});
|
||||
}
|
||||
}}
|
||||
disabled={chatKey === key}
|
||||
placeholder="Enter text..."
|
||||
></Textarea>
|
||||
</div>
|
||||
</AccordionComponent>
|
||||
</div>
|
||||
))
|
||||
: null}
|
||||
{flowState?.memory_keys!.map((key, index) => (
|
||||
<div className="file-component-accordion-div" key={index}>
|
||||
<AccordionComponent
|
||||
trigger={
|
||||
<div className="file-component-badge-div">
|
||||
<Badge variant="gray" size="md">
|
||||
{key}
|
||||
</Badge>
|
||||
<div className="-mb-1">
|
||||
<ToggleShadComponent
|
||||
enabled={chatKey === key}
|
||||
setEnabled={() => {}}
|
||||
size="small"
|
||||
disabled={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
key={index}
|
||||
keyValue={key}
|
||||
>
|
||||
<div className="file-component-tab-column">
|
||||
<div className="font-normal text-muted-foreground ">
|
||||
Source: Memory
|
||||
</div>
|
||||
</div>
|
||||
</AccordionComponent>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<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}
|
||||
updateChat={() => {}}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<div className="chat-alert-box">
|
||||
<span>
|
||||
👋{" "}
|
||||
<span className="langflow-chat-span">
|
||||
{LANGFLOW_CHAT_TITLE}
|
||||
</span>
|
||||
</span>
|
||||
<br />
|
||||
<div className="langflow-chat-desc">
|
||||
<span className="langflow-chat-desc-span">
|
||||
{CHAT_FIRST_INITIAL_TEXT}{" "}
|
||||
<span>
|
||||
<IconComponent
|
||||
name="MessageSquare"
|
||||
className="mx-1 inline h-5 w-5 animate-bounce "
|
||||
/>
|
||||
</span>{" "}
|
||||
{CHAT_SECOND_INITIAL_TEXT}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div ref={ref}></div>
|
||||
</div>
|
||||
<div className="langflow-chat-input-div">
|
||||
<div className="langflow-chat-input">
|
||||
<ChatInput
|
||||
chatValue={chatValue}
|
||||
noInput={!chatKey}
|
||||
lockChat={lockChat}
|
||||
sendMessage={sendMessage}
|
||||
setChatValue={(value) => {
|
||||
setChatValue(value);
|
||||
if (flowState && chatKey) {
|
||||
setFlowState((old: FlowState | undefined) => {
|
||||
let newFlowState = cloneDeep(old!);
|
||||
newFlowState.input_keys![chatKey] = value;
|
||||
return newFlowState;
|
||||
});
|
||||
}
|
||||
}}
|
||||
inputRef={ref}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
)}
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
|
@ -141,6 +141,7 @@ export default function ShareModal({
|
|||
});
|
||||
});
|
||||
};
|
||||
console.log("ShareModal");
|
||||
|
||||
const handleUpdateComponent = () => {
|
||||
handleShareComponent(true);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import ReactFlow, {
|
|||
updateEdge,
|
||||
} from "reactflow";
|
||||
import GenericNode from "../../../../CustomNodes/GenericNode";
|
||||
import FlowToolbar from "../../../../components/chatComponent";
|
||||
import {
|
||||
INVALID_SELECTION_ERROR_ALERT,
|
||||
UPLOAD_ALERT_LIST,
|
||||
|
|
@ -38,7 +37,6 @@ import {
|
|||
import { getRandomName, isWrappedWithClass } from "../../../../utils/utils";
|
||||
import ConnectionLineComponent from "../ConnectionLineComponent";
|
||||
import SelectionMenu from "../SelectionMenuComponent";
|
||||
import ExtraSidebar from "../extraSidebarComponent";
|
||||
|
||||
const nodeTypes = {
|
||||
genericNode: GenericNode,
|
||||
|
|
@ -59,6 +57,9 @@ export default function Page({
|
|||
const templates = useTypesStore((state) => state.templates);
|
||||
const setFilterEdge = useFlowStore((state) => state.setFilterEdge);
|
||||
const reactFlowWrapper = useRef<HTMLDivElement>(null);
|
||||
const [showCanvas, setSHowCanvas] = useState(
|
||||
Object.keys(templates).length > 0 && Object.keys(types).length > 0
|
||||
);
|
||||
|
||||
const reactFlowInstance = useFlowStore((state) => state.reactFlowInstance);
|
||||
const setReactFlowInstance = useFlowStore(
|
||||
|
|
@ -271,6 +272,12 @@ export default function Page({
|
|||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setSHowCanvas(
|
||||
Object.keys(templates).length > 0 && Object.keys(types).length > 0
|
||||
);
|
||||
}, [templates, types]);
|
||||
|
||||
const onConnectMod = useCallback(
|
||||
(params: Connection) => {
|
||||
takeSnapshot();
|
||||
|
|
@ -432,71 +439,60 @@ export default function Page({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-full overflow-hidden">
|
||||
{!view && <ExtraSidebar />}
|
||||
{/* Main area */}
|
||||
<main className="flex flex-1">
|
||||
{/* Primary column */}
|
||||
<div className="h-full w-full">
|
||||
<div className="h-full w-full" ref={reactFlowWrapper}>
|
||||
{Object.keys(templates).length > 0 &&
|
||||
Object.keys(types).length > 0 ? (
|
||||
<div id="react-flow-id" className="h-full w-full">
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
onConnect={onConnectMod}
|
||||
disableKeyboardA11y={true}
|
||||
onInit={setReactFlowInstance}
|
||||
nodeTypes={nodeTypes}
|
||||
onEdgeUpdate={onEdgeUpdate}
|
||||
onEdgeUpdateStart={onEdgeUpdateStart}
|
||||
onEdgeUpdateEnd={onEdgeUpdateEnd}
|
||||
onNodeDragStart={onNodeDragStart}
|
||||
onNodeDragStop={onNodeDragStop}
|
||||
onSelectionDragStart={onSelectionDragStart}
|
||||
onSelectionEnd={onSelectionEnd}
|
||||
onSelectionStart={onSelectionStart}
|
||||
connectionLineComponent={ConnectionLineComponent}
|
||||
onDragOver={onDragOver}
|
||||
onMoveEnd={onMoveEnd}
|
||||
onDrop={onDrop}
|
||||
onSelectionChange={onSelectionChange}
|
||||
deleteKeyCode={[]}
|
||||
className="theme-attribution"
|
||||
minZoom={0.01}
|
||||
maxZoom={8}
|
||||
zoomOnScroll={!view}
|
||||
zoomOnPinch={!view}
|
||||
panOnDrag={!view}
|
||||
proOptions={{ hideAttribution: true }}
|
||||
onPaneClick={onPaneClick}
|
||||
>
|
||||
<Background className="" />
|
||||
{!view && (
|
||||
<Controls
|
||||
className="bg-muted fill-foreground stroke-foreground text-primary
|
||||
<div className="h-full w-full" ref={reactFlowWrapper}>
|
||||
{showCanvas ? (
|
||||
<div id="react-flow-id" className="h-full w-full">
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
onConnect={onConnectMod}
|
||||
disableKeyboardA11y={true}
|
||||
onInit={setReactFlowInstance}
|
||||
nodeTypes={nodeTypes}
|
||||
onEdgeUpdate={onEdgeUpdate}
|
||||
onEdgeUpdateStart={onEdgeUpdateStart}
|
||||
onEdgeUpdateEnd={onEdgeUpdateEnd}
|
||||
onNodeDragStart={onNodeDragStart}
|
||||
onNodeDragStop={onNodeDragStop}
|
||||
onSelectionDragStart={onSelectionDragStart}
|
||||
onSelectionEnd={onSelectionEnd}
|
||||
onSelectionStart={onSelectionStart}
|
||||
connectionLineComponent={ConnectionLineComponent}
|
||||
onDragOver={onDragOver}
|
||||
onMoveEnd={onMoveEnd}
|
||||
onDrop={onDrop}
|
||||
onSelectionChange={onSelectionChange}
|
||||
deleteKeyCode={[]}
|
||||
className="theme-attribution"
|
||||
minZoom={0.01}
|
||||
maxZoom={8}
|
||||
zoomOnScroll={!view}
|
||||
zoomOnPinch={!view}
|
||||
panOnDrag={!view}
|
||||
proOptions={{ hideAttribution: true }}
|
||||
onPaneClick={onPaneClick}
|
||||
>
|
||||
<Background className="" />
|
||||
{!view && (
|
||||
<Controls
|
||||
className="[&>button]:bg-muted fill-foreground stroke-foreground text-primary
|
||||
[&>button]:border-b-border hover:[&>button]:bg-border"
|
||||
></Controls>
|
||||
)}
|
||||
<SelectionMenu
|
||||
isVisible={selectionMenuVisible}
|
||||
nodes={lastSelection?.nodes}
|
||||
onClick={() => {
|
||||
handleGroupNode();
|
||||
}}
|
||||
/>
|
||||
</ReactFlow>
|
||||
{!view && <FlowToolbar flow={flow} />}
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
></Controls>
|
||||
)}
|
||||
</div>
|
||||
<SelectionMenu
|
||||
isVisible={selectionMenuVisible}
|
||||
nodes={lastSelection?.nodes}
|
||||
onClick={() => {
|
||||
handleGroupNode();
|
||||
}}
|
||||
/>
|
||||
</ReactFlow>
|
||||
</div>
|
||||
</main>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export default function ParentDisclosureComponent({
|
|||
data-testid={testId}
|
||||
>
|
||||
<div className="flex gap-4">
|
||||
<span className="parent-disclosure-title">{title}</span>
|
||||
<span className="parent-disclosure-title ">{title}</span>
|
||||
</div>
|
||||
<div className="components-disclosure-div">
|
||||
{buttons.map((btn, index) => (
|
||||
|
|
@ -28,9 +28,9 @@ export default function ParentDisclosureComponent({
|
|||
))}
|
||||
<div>
|
||||
<IconComponent
|
||||
name="ChevronRight"
|
||||
name="ChevronsUpDownIcon"
|
||||
className={`${
|
||||
open || openDisc ? "rotate-90 transform" : ""
|
||||
open || openDisc ? "" : ""
|
||||
} h-4 w-4 text-foreground`}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -234,6 +234,20 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
[]
|
||||
);
|
||||
|
||||
const getIcon = useMemo(() => {
|
||||
return (SBSectionName: string) => {
|
||||
if (nodeIconsLucide[SBSectionName]) {
|
||||
return (
|
||||
<IconComponent
|
||||
name={SBSectionName}
|
||||
strokeWidth={1.5}
|
||||
className="w-[22px] text-primary"
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="side-bar-arrangement">
|
||||
<div className="side-bar-search-div-placement">
|
||||
|
|
@ -271,6 +285,11 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
</div>
|
||||
<Separator />
|
||||
<div className="side-bar-components-div-arrangement">
|
||||
<div className="parent-disclosure-arrangement">
|
||||
<div className="flex items-center gap-4 align-middle">
|
||||
<span className="parent-disclosure-title">Core Components</span>
|
||||
</div>
|
||||
</div>
|
||||
{Object.keys(dataFilter)
|
||||
.sort(sortKeys)
|
||||
.filter((x) => PRIORITY_SIDEBAR_ORDER.includes(x))
|
||||
|
|
@ -310,7 +329,7 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
<SidebarDraggableComponent
|
||||
sectionName={SBSectionName as string}
|
||||
apiClass={dataFilter[SBSectionName][SBItemName]}
|
||||
key={index}
|
||||
key={index + SBItemName}
|
||||
onDragStart={(event) =>
|
||||
onDragStart(event, {
|
||||
//split type to remove type in nodes saved with same name removing it's
|
||||
|
|
@ -344,9 +363,7 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
)
|
||||
)}{" "}
|
||||
<ParentDisclosureComponent
|
||||
openDisc={
|
||||
getFilterEdge.length !== 0 || search.length !== 0 ? true : false
|
||||
}
|
||||
openDisc={false}
|
||||
key={"Extended"}
|
||||
button={{
|
||||
title: "Extended",
|
||||
|
|
@ -361,6 +378,7 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
Object.keys(dataFilter[SBSectionName]).length > 0 ? (
|
||||
<>
|
||||
<DisclosureComponent
|
||||
isChild={false}
|
||||
openDisc={
|
||||
getFilterEdge.length !== 0 || search.length !== 0
|
||||
? true
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ import ToolbarSelectItem from "./toolbarSelectItem";
|
|||
export default function NodeToolbarComponent({
|
||||
data,
|
||||
deleteNode,
|
||||
position,
|
||||
setShowNode,
|
||||
numberOfHandles,
|
||||
showNode,
|
||||
|
|
@ -65,12 +64,11 @@ export default function NodeToolbarComponent({
|
|||
const hasStore = useStoreStore((state) => state.hasStore);
|
||||
const hasApiKey = useStoreStore((state) => state.hasApiKey);
|
||||
const validApiKey = useStoreStore((state) => state.validApiKey);
|
||||
const [updateMinimize, setUpdateMinimize] = useState(showNode);
|
||||
|
||||
const isMinimal = numberOfHandles <= 1;
|
||||
const isGroup = data.node?.flow ? true : false;
|
||||
|
||||
const frozen = data.node?.frozen ?? false;
|
||||
// const frozen = data.node?.frozen ?? false;
|
||||
const paste = useFlowStore((state) => state.paste);
|
||||
const nodes = useFlowStore((state) => state.nodes);
|
||||
const edges = useFlowStore((state) => state.edges);
|
||||
|
|
@ -79,6 +77,7 @@ export default function NodeToolbarComponent({
|
|||
const setEdges = useFlowStore((state) => state.setEdges);
|
||||
const unselectAll = useFlowStore((state) => state.unselectAll);
|
||||
const saveComponent = useFlowsManagerStore((state) => state.saveComponent);
|
||||
const getNodePosition = useFlowStore((state) => state.getNodePosition);
|
||||
const flows = useFlowsManagerStore((state) => state.flows);
|
||||
const version = useDarkStore((state) => state.version);
|
||||
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
|
||||
|
|
@ -146,7 +145,7 @@ export default function NodeToolbarComponent({
|
|||
takeSnapshot();
|
||||
expandGroupNode(
|
||||
data.id,
|
||||
updateFlowPosition(position, data.node?.flow!),
|
||||
updateFlowPosition(getNodePosition(data.id), data.node?.flow!),
|
||||
data.node!.template,
|
||||
nodes,
|
||||
edges,
|
||||
|
|
@ -268,6 +267,15 @@ export default function NodeToolbarComponent({
|
|||
|
||||
useEffect(() => {
|
||||
function onKeyDown(event: KeyboardEvent) {
|
||||
if (
|
||||
selected &&
|
||||
(hasApiKey || hasStore) &&
|
||||
(event.ctrlKey || event.metaKey) &&
|
||||
event.key === "u"
|
||||
) {
|
||||
event.preventDefault();
|
||||
handleSelectChange("update");
|
||||
}
|
||||
if (
|
||||
selected &&
|
||||
isGroup &&
|
||||
|
|
@ -309,7 +317,7 @@ export default function NodeToolbarComponent({
|
|||
selected &&
|
||||
(event.ctrlKey || event.metaKey) &&
|
||||
event.shiftKey &&
|
||||
event.key === "C"
|
||||
event.key === "U"
|
||||
) {
|
||||
event.preventDefault();
|
||||
if (hasCode) return setOpenModal((state) => !state);
|
||||
|
|
@ -318,8 +326,8 @@ export default function NodeToolbarComponent({
|
|||
if (
|
||||
selected &&
|
||||
!isGroup &&
|
||||
(event.ctrlKey || event.metaKey) &&
|
||||
event.key === "e"
|
||||
(event.ctrlKey || event.metaKey) && event.shiftKey&&
|
||||
event.key === "A"
|
||||
) {
|
||||
event.preventDefault();
|
||||
setShowModalAdvanced((state) => !state);
|
||||
|
|
@ -399,7 +407,23 @@ export default function NodeToolbarComponent({
|
|||
</button>
|
||||
</ShadTooltip>
|
||||
|
||||
<ShadTooltip content="Freeze" side="top">
|
||||
<ShadTooltip content={"Duplicate"} side="top">
|
||||
<button
|
||||
data-testid="save-button-modal"
|
||||
className={classNames(
|
||||
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10",
|
||||
)}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
handleSelectChange("duplicate");
|
||||
}
|
||||
}
|
||||
>
|
||||
<IconComponent name="Copy" className="h-4 w-4" />
|
||||
</button>
|
||||
</ShadTooltip>
|
||||
|
||||
{/* <ShadTooltip content="Freeze" side="top">
|
||||
<button
|
||||
className={classNames(
|
||||
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10"
|
||||
|
|
@ -412,7 +436,7 @@ export default function NodeToolbarComponent({
|
|||
...old.data,
|
||||
node: {
|
||||
...old.data.node,
|
||||
frozen: old.data?.node?.frozen ? false : true,
|
||||
// frozen: old.data?.node?.frozen ? false : true,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
|
@ -427,7 +451,7 @@ export default function NodeToolbarComponent({
|
|||
)}
|
||||
/>
|
||||
</button>
|
||||
</ShadTooltip>
|
||||
</ShadTooltip> */}
|
||||
|
||||
<Select onValueChange={handleSelectChange} value="">
|
||||
<ShadTooltip content="More" side="top">
|
||||
|
|
@ -451,16 +475,16 @@ export default function NodeToolbarComponent({
|
|||
{nodeLength > 0 && (
|
||||
<SelectItem value={nodeLength === 0 ? "disabled" : "advanced"}>
|
||||
<ToolbarSelectItem
|
||||
keyboardKey="E"
|
||||
keyboardKey="A"
|
||||
isMac={navigator.userAgent.toUpperCase().includes("MAC")}
|
||||
shift={false}
|
||||
value={"Edit"}
|
||||
shift={true}
|
||||
value={"Advanced"}
|
||||
icon={"Settings2"}
|
||||
dataTestId="edit-button-modal"
|
||||
/>
|
||||
</SelectItem>
|
||||
)}
|
||||
<SelectItem value={"duplicate"}>
|
||||
{/* <SelectItem value={"duplicate"}>
|
||||
<ToolbarSelectItem
|
||||
keyboardKey="D"
|
||||
isMac={navigator.userAgent.toUpperCase().includes("MAC")}
|
||||
|
|
@ -469,7 +493,7 @@ export default function NodeToolbarComponent({
|
|||
icon={"Copy"}
|
||||
dataTestId="duplicate-button-modal"
|
||||
/>
|
||||
</SelectItem>
|
||||
</SelectItem> */}
|
||||
<SelectItem value={"copy"}>
|
||||
<ToolbarSelectItem
|
||||
keyboardKey="C"
|
||||
|
|
@ -505,7 +529,7 @@ export default function NodeToolbarComponent({
|
|||
value={"Share"}
|
||||
icon={"Share3"}
|
||||
styleObj={{
|
||||
iconClasses: "relative top-0.5 -m-1 mr-1 h-6 w-6",
|
||||
iconClasses: "relative top-0.5 -m-1 mr-[0.25rem] h-6 w-6",
|
||||
}}
|
||||
dataTestId="share-button-modal"
|
||||
/>
|
||||
|
|
@ -631,22 +655,24 @@ export default function NodeToolbarComponent({
|
|||
)}
|
||||
{hasCode && (
|
||||
<div className="hidden">
|
||||
<CodeAreaComponent
|
||||
open={openModal}
|
||||
setOpen={setOpenModal}
|
||||
readonly={
|
||||
data.node?.flow && data.node.template[name].dynamic
|
||||
? true
|
||||
: false
|
||||
}
|
||||
dynamic={data.node?.template[name].dynamic ?? false}
|
||||
setNodeClass={handleNodeClass}
|
||||
nodeClass={data.node}
|
||||
disabled={false}
|
||||
value={data.node?.template[name].value ?? ""}
|
||||
onChange={handleOnNewValue}
|
||||
id={"code-input-node-toolbar-" + name}
|
||||
/>
|
||||
{openModal && (
|
||||
<CodeAreaComponent
|
||||
open={openModal}
|
||||
setOpen={setOpenModal}
|
||||
readonly={
|
||||
data.node?.flow && data.node.template[name].dynamic
|
||||
? true
|
||||
: false
|
||||
}
|
||||
dynamic={data.node?.template[name].dynamic ?? false}
|
||||
setNodeClass={handleNodeClass}
|
||||
nodeClass={data.node}
|
||||
disabled={false}
|
||||
value={data.node?.template[name].value ?? ""}
|
||||
onChange={handleOnNewValue}
|
||||
id={"code-input-node-toolbar-" + name}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import { useEffect } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import FlowToolbar from "../../components/chatComponent";
|
||||
import Header from "../../components/headerComponent";
|
||||
import { useDarkStore } from "../../stores/darkStore";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import Page from "./components/PageComponent";
|
||||
import ExtraSidebar from "./components/extraSidebarComponent";
|
||||
|
||||
export default function FlowPage(): JSX.Element {
|
||||
export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
|
||||
const setCurrentFlowId = useFlowsManagerStore(
|
||||
(state) => state.setCurrentFlowId
|
||||
);
|
||||
|
|
@ -17,12 +19,22 @@ export default function FlowPage(): JSX.Element {
|
|||
useEffect(() => {
|
||||
setCurrentFlowId(id!);
|
||||
}, [id]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<div className="flow-page-positioning">
|
||||
{currentFlow && <Page flow={currentFlow} />}
|
||||
{currentFlow && (
|
||||
<div className="flex h-full overflow-hidden">
|
||||
{!view && <ExtraSidebar />}
|
||||
<main className="flex flex-1">
|
||||
{/* Primary column */}
|
||||
<div className="h-full w-full">
|
||||
<Page flow={currentFlow} />
|
||||
</div>
|
||||
{!view && <FlowToolbar />}
|
||||
</main>
|
||||
</div>
|
||||
)}
|
||||
<a
|
||||
target={"_blank"}
|
||||
href="https://logspace.ai/"
|
||||
|
|
|
|||
|
|
@ -2,23 +2,20 @@ import { Group, ToyBrick } from "lucide-react";
|
|||
import { useEffect, useState } from "react";
|
||||
import { Outlet, useLocation, useNavigate } from "react-router-dom";
|
||||
import DropdownButton from "../../components/DropdownButtonComponent";
|
||||
import NewFlowCardComponent from "../../components/NewFLowCard2";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import PageLayout from "../../components/pageLayout";
|
||||
import SidebarNav from "../../components/sidebarComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import UndrawCardComponent from "../../components/undrawCards";
|
||||
import { CONSOLE_ERROR_MSG } from "../../constants/alerts_constants";
|
||||
import {
|
||||
MY_COLLECTION_DESC,
|
||||
USER_PROJECTS_HEADER,
|
||||
} from "../../constants/constants";
|
||||
import BaseModal from "../../modals/baseModal";
|
||||
import NewFlowModal from "../../modals/NewFlowModal";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { downloadFlows } from "../../utils/reactflowUtils";
|
||||
export default function HomePage(): JSX.Element {
|
||||
const addFlow = useFlowsManagerStore((state) => state.addFlow);
|
||||
const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow);
|
||||
const setCurrentFlowId = useFlowsManagerStore(
|
||||
(state) => state.setCurrentFlowId
|
||||
|
|
@ -29,7 +26,6 @@ export default function HomePage(): JSX.Element {
|
|||
const location = useLocation();
|
||||
const pathname = location.pathname;
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const examples = useFlowsManagerStore((state) => state.examples);
|
||||
const is_component = pathname === "/components";
|
||||
const dropdownOptions = [
|
||||
{
|
||||
|
|
@ -119,26 +115,7 @@ export default function HomePage(): JSX.Element {
|
|||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
<BaseModal size="three-cards" open={openModal} setOpen={setOpenModal}>
|
||||
<BaseModal.Header description={"Select a template below"}>
|
||||
<span className="pr-2" data-testid="modal-title">
|
||||
Get Started
|
||||
</span>
|
||||
{/* <IconComponent
|
||||
name="Group"
|
||||
className="h-6 w-6 stroke-2 text-primary "
|
||||
aria-hidden="true"
|
||||
/> */}
|
||||
</BaseModal.Header>
|
||||
<BaseModal.Content>
|
||||
<div className=" grid h-full w-full grid-cols-3 gap-3 overflow-auto p-4 custom-scroll">
|
||||
<NewFlowCardComponent />
|
||||
{examples.map((example, idx) => {
|
||||
return <UndrawCardComponent key={idx} flow={example} />;
|
||||
})}
|
||||
</div>
|
||||
</BaseModal.Content>
|
||||
</BaseModal>
|
||||
<NewFlowModal open={openModal} setOpen={setOpenModal} />
|
||||
</PageLayout>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ export default function DeleteAccountPage() {
|
|||
// Implement your account deletion logic here
|
||||
// For example, make an API call to delete the account
|
||||
// Upon successful deletion, you can redirect the user to another page
|
||||
console.log("Account deleted!");
|
||||
// Implement the logic to redirect the user after account deletion.
|
||||
// For example, use react-router-dom's useHistory hook.
|
||||
};
|
||||
|
|
|
|||
|
|
@ -70,6 +70,10 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
}
|
||||
get().setFlowPool(newFlowPool);
|
||||
},
|
||||
getNodePosition: (nodeId: string) => {
|
||||
const node = get().nodes.find((node) => node.id === nodeId);
|
||||
return node?.position || { x: 0, y: 0 };
|
||||
},
|
||||
updateFlowPool: (
|
||||
nodeId: string,
|
||||
data: FlowPoolObjectType | ChatOutputType | chatInputType,
|
||||
|
|
@ -525,9 +529,9 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
onGetOrderSuccess: () => {
|
||||
setNoticeData({ title: "Running components" });
|
||||
},
|
||||
onBuildComplete: () => {
|
||||
onBuildComplete: (allNodesValid) => {
|
||||
const nodeId = startNodeId || stopNodeId;
|
||||
if (nodeId) {
|
||||
if (nodeId && allNodesValid) {
|
||||
setSuccessData({
|
||||
title: `${
|
||||
get().nodes.find((node) => node.id === nodeId)?.data.node
|
||||
|
|
|
|||
|
|
@ -96,19 +96,19 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
|||
});
|
||||
});
|
||||
},
|
||||
autoSaveCurrentFlow: debounce(
|
||||
(nodes: Node[], edges: Edge[], viewport: Viewport) => {
|
||||
set({ saveLoading: true });
|
||||
if (get().currentFlow) {
|
||||
get().saveFlow(
|
||||
{ ...get().currentFlow!, data: { nodes, edges, viewport } },
|
||||
true
|
||||
);
|
||||
}
|
||||
},
|
||||
SAVE_DEBOUNCE_TIME
|
||||
),
|
||||
autoSaveCurrentFlow: (nodes: Node[], edges: Edge[], viewport: Viewport) => {
|
||||
if (get().currentFlow) {
|
||||
get().saveFlow(
|
||||
{ ...get().currentFlow!, data: { nodes, edges, viewport } },
|
||||
true
|
||||
);
|
||||
}
|
||||
},
|
||||
saveFlow: (flow: FlowType, silent?: boolean) => {
|
||||
set({ saveLoading: true }); // set saveLoading true immediately
|
||||
return get().saveFlowDebounce(flow, silent); // call the debounced function directly
|
||||
},
|
||||
saveFlowDebounce: debounce((flow: FlowType, silent?: boolean) => {
|
||||
set({ saveLoading: true });
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
updateFlowInDatabase(flow)
|
||||
|
|
@ -142,7 +142,7 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
|||
reject(err);
|
||||
});
|
||||
});
|
||||
},
|
||||
}, SAVE_DEBOUNCE_TIME),
|
||||
uploadFlows: () => {
|
||||
return new Promise<void>((resolve) => {
|
||||
const input = document.createElement("input");
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
@apply fill-chat-trigger-disabled stroke-chat-trigger-disabled stroke-1;
|
||||
}
|
||||
.parent-disclosure-arrangement {
|
||||
@apply flex w-full select-none items-center justify-between border-y border-y-input bg-background px-3 py-2;
|
||||
@apply flex w-full select-none items-center justify-between bg-background px-3 py-1;
|
||||
}
|
||||
.components-disclosure-arrangement {
|
||||
@apply -mt-px flex w-full select-none items-center justify-between border-y border-y-input bg-muted px-3 py-2;
|
||||
|
|
@ -194,7 +194,7 @@
|
|||
@apply -mt-px flex w-full select-none items-center justify-between border-y border-y-input bg-muted px-3 py-2;
|
||||
}
|
||||
.parent-disclosure-title {
|
||||
@apply p-2 px-2 text-sm font-semibold;
|
||||
@apply p-2 px-2 text-sm font-medium;
|
||||
}
|
||||
.components-disclosure-title {
|
||||
@apply flex items-center text-sm text-primary;
|
||||
|
|
@ -917,7 +917,7 @@
|
|||
@apply mr-2 h-5 w-5 rotate-[44deg];
|
||||
}
|
||||
.form-modal-play-icon {
|
||||
@apply mx-1 h-5 w-5;
|
||||
@apply mx-1 h-5 w-5 fill-current;
|
||||
}
|
||||
.form-modal-chat-position {
|
||||
@apply flex-max-width px-2 py-6 pl-4 pr-9;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { ReactElement, ReactNode, SetStateAction } from "react";
|
||||
import { ReactFlowJsonObject, XYPosition } from "reactflow";
|
||||
import { ReactFlowJsonObject } from "reactflow";
|
||||
import { InputOutput } from "../../constants/enums";
|
||||
import { APIClassType, APITemplateType, TemplateVariableType } from "../api";
|
||||
import { ChatMessageType } from "../chat";
|
||||
import { FlowStyleType, FlowType, NodeDataType, NodeType } from "../flow/index";
|
||||
|
|
@ -171,6 +172,7 @@ export type RangeSpecType = {
|
|||
export type IntComponentType = {
|
||||
value: string;
|
||||
disabled?: boolean;
|
||||
rangeSpec: RangeSpecType;
|
||||
onChange: (value: string) => void;
|
||||
editNode?: boolean;
|
||||
id?: string;
|
||||
|
|
@ -219,6 +221,8 @@ export type AccordionComponentType = {
|
|||
open?: string[];
|
||||
trigger?: string | ReactElement;
|
||||
keyValue?: string;
|
||||
openDisc?: boolean;
|
||||
sideBar?: boolean;
|
||||
};
|
||||
export type Side = "top" | "right" | "bottom" | "left";
|
||||
|
||||
|
|
@ -500,7 +504,6 @@ export type fileCardPropsType = {
|
|||
export type nodeToolbarPropsType = {
|
||||
data: NodeDataType;
|
||||
deleteNode: (idx: string) => void;
|
||||
position: XYPosition;
|
||||
setShowNode: (boolean: any) => void;
|
||||
numberOfHandles: number;
|
||||
showNode: boolean;
|
||||
|
|
@ -565,12 +568,6 @@ export type chatMessagePropsType = {
|
|||
) => void;
|
||||
};
|
||||
|
||||
export type formModalPropsType = {
|
||||
open: boolean;
|
||||
setOpen: Function;
|
||||
flow: FlowType;
|
||||
};
|
||||
|
||||
export type genericModalPropsType = {
|
||||
field_name?: string;
|
||||
setValue: (value: string) => void;
|
||||
|
|
@ -585,6 +582,18 @@ export type genericModalPropsType = {
|
|||
readonly?: boolean;
|
||||
};
|
||||
|
||||
export type newFlowModalPropsType = {
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
};
|
||||
|
||||
export type IOModalPropsType = {
|
||||
children: JSX.Element;
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
disable?: boolean;
|
||||
};
|
||||
|
||||
export type buttonBoxPropsType = {
|
||||
onClick: () => void;
|
||||
title: string;
|
||||
|
|
@ -695,15 +704,21 @@ export type dropdownButtonPropsType = {
|
|||
dropdownOptions?: boolean;
|
||||
};
|
||||
|
||||
export type IOInputProps = {
|
||||
inputType: string;
|
||||
inputId: string;
|
||||
export type IOFieldViewProps = {
|
||||
type: InputOutput;
|
||||
fieldType: string;
|
||||
fieldId: string;
|
||||
left?: boolean;
|
||||
};
|
||||
export type IOOutputProps = {
|
||||
outputType: string;
|
||||
outputId: string;
|
||||
left?: boolean;
|
||||
|
||||
export type UndrawCardComponentProps = { flow: FlowType };
|
||||
|
||||
export type chatViewProps = {
|
||||
sendMessage: (count?: number) => void;
|
||||
chatValue: string;
|
||||
setChatValue: (value: string) => void;
|
||||
lockChat: boolean;
|
||||
setLockChat: (lock: boolean) => void;
|
||||
};
|
||||
|
||||
export type IOFileInputProps = {
|
||||
|
|
|
|||
|
|
@ -131,4 +131,5 @@ export type FlowStoreType = {
|
|||
data: FlowPoolObjectType | ChatOutputType | chatInputType,
|
||||
buildId?: string
|
||||
) => void;
|
||||
getNodePosition: (nodeId: string) => { x: number; y: number };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,7 +11,11 @@ export type FlowsManagerStoreType = {
|
|||
isLoading: boolean;
|
||||
setIsLoading: (isLoading: boolean) => void;
|
||||
refreshFlows: () => Promise<void>;
|
||||
saveFlow: (flow: FlowType, silent?: boolean) => Promise<void>;
|
||||
saveFlow: (flow: FlowType, silent?: boolean) => Promise<void> | undefined;
|
||||
saveFlowDebounce: (
|
||||
flow: FlowType,
|
||||
silent?: boolean
|
||||
) => Promise<void> | undefined;
|
||||
autoSaveCurrentFlow: (
|
||||
nodes: Node[],
|
||||
edges: Edge[],
|
||||
|
|
|
|||
|
|
@ -204,12 +204,11 @@ export async function buildVertices({
|
|||
if (stop) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (onBuildComplete) {
|
||||
const allNodesValid = buildResults.every((result) => result);
|
||||
onBuildComplete(allNodesValid);
|
||||
useFlowStore.getState().setIsBuilding(false);
|
||||
}
|
||||
}
|
||||
if (onBuildComplete) {
|
||||
const allNodesValid = buildResults.every((result) => result);
|
||||
onBuildComplete(allNodesValid);
|
||||
useFlowStore.getState().setIsBuilding(false);
|
||||
}
|
||||
}
|
||||
async function buildVertex({
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { throttle } from "lodash";
|
||||
import { debounce } from "lodash";
|
||||
import { postCustomComponentUpdate } from "../controllers/API";
|
||||
import { ResponseErrorTypeAPI } from "../types/api";
|
||||
import { NodeDataType } from "../types/flow";
|
||||
|
|
@ -38,4 +38,4 @@ export const handleUpdateValues = async (name: string, data: NodeDataType) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const throttledHandleUpdateValues = throttle(handleUpdateValues, 10);
|
||||
export const debouncedHandleUpdateValues = debounce(handleUpdateValues, 200);
|
||||
|
|
|
|||
|
|
@ -1170,7 +1170,7 @@ export function downloadNode(NodeFLow: FlowType) {
|
|||
type: "application/json",
|
||||
});
|
||||
element.href = URL.createObjectURL(file);
|
||||
element.download = `${NodeFLow?.name??"node"}.json`;
|
||||
element.download = `${NodeFLow?.name ?? "node"}.json`;
|
||||
element.click();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import {
|
|||
ChevronsRight,
|
||||
ChevronsUpDown,
|
||||
Circle,
|
||||
ChevronsUpDownIcon,
|
||||
CircleDot,
|
||||
Clipboard,
|
||||
Code,
|
||||
|
|
@ -116,6 +117,7 @@ import {
|
|||
TerminalIcon,
|
||||
TerminalSquare,
|
||||
TextCursorInput,
|
||||
TextSearch,
|
||||
ToyBrick,
|
||||
Trash2,
|
||||
Type,
|
||||
|
|
@ -150,6 +152,7 @@ import { EvernoteIcon } from "../icons/Evernote";
|
|||
import { FBIcon } from "../icons/FacebookMessenger";
|
||||
import { GitBookIcon } from "../icons/GitBook";
|
||||
import { GoogleIcon } from "../icons/Google";
|
||||
import { GoogleGenerativeAIIcon } from "../icons/GoogleGenerativeAI";
|
||||
import {
|
||||
GradientInfinity,
|
||||
GradientSave,
|
||||
|
|
@ -217,11 +220,11 @@ export const gradients = [
|
|||
];
|
||||
|
||||
export const nodeColors: { [char: string]: string } = {
|
||||
inputs: "#9AAE42",
|
||||
inputs: "#10B981",
|
||||
outputs: "#AA2411",
|
||||
data: "#6344BE",
|
||||
data: "#4367BF",
|
||||
prompts: "#4367BF",
|
||||
models: "#AA2411",
|
||||
models: "#6344BE",
|
||||
model_specs: "#6344BE",
|
||||
chains: "#FE7500",
|
||||
Document: "#7AAE42",
|
||||
|
|
@ -249,6 +252,9 @@ export const nodeColors: { [char: string]: string } = {
|
|||
retrievers: "#e6b25a",
|
||||
unknown: "#9CA3AF",
|
||||
custom_components: "#ab11ab",
|
||||
Records:"#31a3cc",
|
||||
Record:"#31a3cc",
|
||||
|
||||
};
|
||||
|
||||
export const nodeNames: { [char: string]: string } = {
|
||||
|
|
@ -319,6 +325,7 @@ export const nodeIconsLucide: iconsType = {
|
|||
BingSearchAPIWrapper: BingIcon,
|
||||
BingSearchRun: BingIcon,
|
||||
Cohere: CohereIcon,
|
||||
ChevronsUpDownIcon,
|
||||
CohereEmbeddings: CohereIcon,
|
||||
EverNoteLoader: EvernoteIcon,
|
||||
FacebookChatLoader: FBIcon,
|
||||
|
|
@ -327,6 +334,7 @@ export const nodeIconsLucide: iconsType = {
|
|||
GoogleSearchResults: GoogleIcon,
|
||||
GoogleSearchRun: GoogleIcon,
|
||||
Google: GoogleIcon,
|
||||
GoogleGenerativeAI: GoogleGenerativeAIIcon,
|
||||
HNLoader: HackerNewsIcon,
|
||||
HuggingFaceHub: HuggingFaceIcon,
|
||||
HuggingFace: HuggingFaceIcon,
|
||||
|
|
@ -371,7 +379,7 @@ export const nodeIconsLucide: iconsType = {
|
|||
saved_components: GradientSave,
|
||||
documentloaders: Paperclip,
|
||||
vectorstores: Layers,
|
||||
vectorsearch: Search,
|
||||
vectorsearch: TextSearch,
|
||||
toolkits: Package2,
|
||||
textsplitters: Scissors,
|
||||
wrappers: Gift,
|
||||
|
|
|
|||
|
|
@ -289,11 +289,10 @@ export function buildTweakObject(tweak: tweakType) {
|
|||
|
||||
/**
|
||||
* Function to get Chat Input Field
|
||||
* @param {FlowType} flow - The current flow.
|
||||
* @param {FlowsState} tabsState - The current tabs state.
|
||||
* @returns {string} - The chat input field
|
||||
*/
|
||||
export function getChatInputField(flow: FlowType, flowState?: FlowState) {
|
||||
export function getChatInputField(flowState?: FlowState) {
|
||||
let chat_input_field = "text";
|
||||
|
||||
if (flowState && flowState.input_keys) {
|
||||
|
|
@ -305,13 +304,14 @@ export function getChatInputField(flow: FlowType, flowState?: FlowState) {
|
|||
/**
|
||||
* Function to get the python code for the API
|
||||
* @param {string} flowId - The id of the flow
|
||||
* @param {boolean} isAuth - If the API is authenticated
|
||||
* @param {any[]} tweak - The tweaks
|
||||
* @returns {string} - The python code
|
||||
*/
|
||||
export function getPythonApiCode(
|
||||
flow: FlowType,
|
||||
isAuth: boolean,
|
||||
tweak?: any[],
|
||||
flowState?: FlowState
|
||||
tweak?: any[]
|
||||
): string {
|
||||
const flowId = flow.id;
|
||||
|
||||
|
|
@ -320,13 +320,10 @@ export function getPythonApiCode(
|
|||
// node.data.id
|
||||
// }
|
||||
const tweaks = buildTweaks(flow);
|
||||
const inputs = buildInputs();
|
||||
return `import requests
|
||||
from typing import Optional
|
||||
|
||||
BASE_API_URL = "${window.location.protocol}//${
|
||||
window.location.host
|
||||
}/api/v1/process"
|
||||
BASE_API_URL = "${window.location.protocol}//${window.location.host}/api/v1/run"
|
||||
FLOW_ID = "${flowId}"
|
||||
# You can tweak the flow by adding a tweaks dictionary
|
||||
# e.g {"OpenAI-XXXXX": {"model_name": "gpt-4"}}
|
||||
|
|
@ -336,9 +333,12 @@ TWEAKS = ${
|
|||
: JSON.stringify(tweaks, null, 2)
|
||||
}
|
||||
|
||||
def run_flow(inputs: dict, flow_id: str, tweaks: Optional[dict] = None${
|
||||
!isAuth ? `, api_key: Optional[str] = None` : ""
|
||||
}) -> dict:
|
||||
def run_flow(message: str,
|
||||
flow_id: str,
|
||||
output_type: str = "chat",
|
||||
input_type: str = "chat",
|
||||
tweaks: Optional[dict] = None,
|
||||
api_key: Optional[str] = None) -> dict:
|
||||
"""
|
||||
Run a flow with a given message and optional tweaks.
|
||||
|
||||
|
|
@ -349,7 +349,11 @@ def run_flow(inputs: dict, flow_id: str, tweaks: Optional[dict] = None${
|
|||
"""
|
||||
api_url = f"{BASE_API_URL}/{flow_id}"
|
||||
|
||||
payload = {"inputs": inputs}
|
||||
payload = {
|
||||
"input_value": message,
|
||||
"output_type": output_type,
|
||||
"input_type": input_type,
|
||||
}
|
||||
headers = None
|
||||
if tweaks:
|
||||
payload["tweaks"] = tweaks
|
||||
|
|
@ -359,9 +363,9 @@ def run_flow(inputs: dict, flow_id: str, tweaks: Optional[dict] = None${
|
|||
return response.json()
|
||||
|
||||
# Setup any tweaks you want to apply to the flow
|
||||
inputs = ${inputs}
|
||||
message = "message"
|
||||
${!isAuth ? `api_key = "<your api key>"` : ""}
|
||||
print(run_flow(inputs, flow_id=FLOW_ID, tweaks=TWEAKS${
|
||||
print(run_flow(message=message, flow_id=FLOW_ID, tweaks=TWEAKS${
|
||||
!isAuth ? `, api_key=api_key` : ""
|
||||
}))`;
|
||||
}
|
||||
|
|
@ -369,28 +373,27 @@ print(run_flow(inputs, flow_id=FLOW_ID, tweaks=TWEAKS${
|
|||
/**
|
||||
* Function to get the curl code for the API
|
||||
* @param {string} flowId - The id of the flow
|
||||
* @param {boolean} isAuth - If the API is authenticated
|
||||
* @returns {string} - The curl code
|
||||
*/
|
||||
export function getCurlCode(
|
||||
flow: FlowType,
|
||||
isAuth: boolean,
|
||||
tweak?: any[],
|
||||
flowState?: FlowState
|
||||
tweak?: any[]
|
||||
): string {
|
||||
const flowId = flow.id;
|
||||
const tweaks = buildTweaks(flow);
|
||||
const inputs = buildInputs();
|
||||
|
||||
const arrayOfOutputs = getOutputIds(flow);
|
||||
|
||||
return `curl -X POST \\
|
||||
${window.location.protocol}//${window.location.host}/api/v1/run/${flowId} \\
|
||||
${window.location.protocol}//${
|
||||
window.location.host
|
||||
}/api/v1/run/${flowId}?stream=false \\
|
||||
-H 'Content-Type: application/json'\\${
|
||||
!isAuth ? `\n -H 'x-api-key: <your api key>'\\` : ""
|
||||
}
|
||||
-d '{"inputs": [${inputs}],
|
||||
"outputs": [${arrayOfOutputs}],
|
||||
"stream": false,
|
||||
-d '{"input_value": "message",
|
||||
"output_type": "chat",
|
||||
"input_type": "chat",
|
||||
"tweaks": ${
|
||||
tweak && tweak.length > 0
|
||||
? buildTweakObject(tweak)
|
||||
|
|
@ -419,26 +422,23 @@ export function getOutputIds(flow) {
|
|||
/**
|
||||
* Function to get the python code for the API
|
||||
* @param {string} flow - The current flow
|
||||
* @param {any[]} tweak - The tweaks
|
||||
* @returns {string} - The python code
|
||||
*/
|
||||
export function getPythonCode(
|
||||
flow: FlowType,
|
||||
tweak?: any[],
|
||||
flowState?: FlowState
|
||||
): string {
|
||||
export function getPythonCode(flow: FlowType, tweak?: any[]): string {
|
||||
const flowName = flow.name;
|
||||
const tweaks = buildTweaks(flow);
|
||||
const inputs = buildInputs();
|
||||
return `from langflow.load import load_flow_from_json
|
||||
|
||||
return `from langflow.load import run_flow_from_json
|
||||
TWEAKS = ${
|
||||
tweak && tweak.length > 0
|
||||
? buildTweakObject(tweak)
|
||||
: JSON.stringify(tweaks, null, 2)
|
||||
}
|
||||
flow = load_flow_from_json("${flowName}.json", tweaks=TWEAKS)
|
||||
# Now you can use it like any chain
|
||||
inputs = ${inputs}
|
||||
flow(inputs)`;
|
||||
|
||||
result = run_flow_from_json(flow="${flowName}.json",
|
||||
input_value="message",
|
||||
tweaks=TWEAKS)`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -454,7 +454,7 @@ export function getWidgetCode(
|
|||
const flowId = flow.id;
|
||||
const flowName = flow.name;
|
||||
const inputs = buildInputs();
|
||||
let chat_input_field = getChatInputField(flow, flowState);
|
||||
let chat_input_field = getChatInputField(flowState);
|
||||
|
||||
return `<script src="https://cdn.jsdelivr.net/gh/logspace-ai/langflow-embedded-chat@main/dist/build/static/js/bundle.min.js"></script>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue