merge fix

This commit is contained in:
Cristhian Zanforlin Lousa 2023-07-25 10:41:57 -03:00
commit f58500c89b
29 changed files with 440 additions and 252 deletions

49
.github/workflows/pre-release.yml vendored Normal file
View file

@ -0,0 +1,49 @@
name: pre-release
on:
pull_request:
types:
- closed
branches:
- dev
paths:
- "pyproject.toml"
env:
POETRY_VERSION: "1.5.1"
jobs:
if_release:
if: |
${{ github.event.pull_request.merged == true }}
&& ${{ contains(github.event.pull_request.labels.*.name, 'pre-release') }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install poetry
run: pipx install poetry==$POETRY_VERSION
- name: Set up Python 3.10
uses: actions/setup-python@v4
with:
python-version: "3.10"
cache: "poetry"
- name: Build project for distribution
run: make build
- name: Check Version
id: check-version
run: |
echo version=$(poetry version --short) >> $GITHUB_OUTPUT
- name: Create Release
uses: ncipollo/release-action@v1
with:
artifacts: "dist/*"
token: ${{ secrets.GITHUB_TOKEN }}
draft: false
generateReleaseNotes: true
tag: v${{ steps.check-version.outputs.version }}
commit: main
- name: Publish to PyPI
env:
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_API_TOKEN }}
run: |
poetry publish

View file

@ -10,7 +10,7 @@ on:
- "pyproject.toml" - "pyproject.toml"
env: env:
POETRY_VERSION: "1.4.0" POETRY_VERSION: "1.5.1"
jobs: jobs:
if_release: if_release:

View file

@ -29,6 +29,7 @@
"@tabler/icons-react": "^2.18.0", "@tabler/icons-react": "^2.18.0",
"@tailwindcss/forms": "^0.5.3", "@tailwindcss/forms": "^0.5.3",
"@tailwindcss/line-clamp": "^0.4.4", "@tailwindcss/line-clamp": "^0.4.4",
"@types/axios": "^0.14.0",
"accordion": "^3.0.2", "accordion": "^3.0.2",
"ace-builds": "^1.16.0", "ace-builds": "^1.16.0",
"add": "^2.0.6", "add": "^2.0.6",
@ -142,6 +143,9 @@
} }
}, },
"node_modules/@babel/compat-data": { "node_modules/@babel/compat-data": {
"version": "7.22.6",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.6.tgz",
"integrity": "sha512-29tfsWTq2Ftu7MXmimyC0C5FDZv5DYxOZkh3XD3+QW4V/BYuv/LyEsjj3c0hqedEaDt6DBfDvexMKU8YevdqFg==",
"version": "7.22.9", "version": "7.22.9",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -9533,8 +9537,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/word-wrap": { "node_modules/word-wrap": {
"version": "1.2.3", "version": "1.2.5",
"license": "MIT", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }

View file

@ -24,6 +24,7 @@
"@tabler/icons-react": "^2.18.0", "@tabler/icons-react": "^2.18.0",
"@tailwindcss/forms": "^0.5.3", "@tailwindcss/forms": "^0.5.3",
"@tailwindcss/line-clamp": "^0.4.4", "@tailwindcss/line-clamp": "^0.4.4",
"@types/axios": "^0.14.0",
"accordion": "^3.0.2", "accordion": "^3.0.2",
"ace-builds": "^1.16.0", "ace-builds": "^1.16.0",
"add": "^2.0.6", "add": "^2.0.6",

View file

@ -1,4 +1,5 @@
import { useContext, useState } from "react"; import { cloneDeep } from "lodash";
import { useContext, useEffect, useState } from "react";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism"; import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
import AccordionComponent from "../../components/AccordionComponent"; import AccordionComponent from "../../components/AccordionComponent";
@ -54,9 +55,16 @@ export default function CodeTabsComponent({
}; };
}) { }) {
const [isCopied, setIsCopied] = useState<Boolean>(false); const [isCopied, setIsCopied] = useState<Boolean>(false);
const [data, setData] = useState(flow ? flow["data"]["nodes"] : null);
const [openAccordion, setOpenAccordion] = useState([]); const [openAccordion, setOpenAccordion] = useState([]);
const { dark } = useContext(darkContext); const { dark } = useContext(darkContext);
useEffect(() => {
if (flow && flow["data"]["nodes"]) {
setData(flow["data"]["nodes"]);
}
}, [flow]);
const copyToClipboard = () => { const copyToClipboard = () => {
if (!navigator.clipboard || !navigator.clipboard.writeText) { if (!navigator.clipboard || !navigator.clipboard.writeText) {
return; return;
@ -185,7 +193,7 @@ export default function CodeTabsComponent({
: "overflow-hidden" : "overflow-hidden"
)} )}
> >
{flow["data"]["nodes"].map((t: any, index) => ( {data.map((t: any, index) => (
<div className="px-3" key={index}> <div className="px-3" key={index}>
{tweaks.tweaksList.current.includes(t["data"]["id"]) && ( {tweaks.tweaksList.current.includes(t["data"]["id"]) && (
<AccordionComponent <AccordionComponent
@ -255,6 +263,16 @@ export default function CodeTabsComponent({
].value ].value
} }
onChange={(k) => { onChange={(k) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
return newInputList;
});
tweaks.buildTweakObject( tweaks.buildTweakObject(
t["data"]["id"], t["data"]["id"],
k, k,
@ -274,16 +292,29 @@ export default function CodeTabsComponent({
<TextAreaComponent <TextAreaComponent
disabled={false} disabled={false}
editNode={true} editNode={true}
value={tweaks.getValue( value={
!t.data.node.template[
n
].value ||
t.data.node.template[ t.data.node.template[
n n
].value, ].value === ""
t.data, ? ""
t.data.node.template[ : t.data.node
n .template[n]
] .value
)} }
onChange={(k) => { onChange={(k) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
return newInputList;
});
tweaks.buildTweakObject( tweaks.buildTweakObject(
t["data"]["id"], t["data"]["id"],
k, k,
@ -302,13 +333,27 @@ export default function CodeTabsComponent({
t.data.node.template[n] t.data.node.template[n]
.password ?? false .password ?? false
} }
value={tweaks.getValue( value={
!t.data.node.template[n]
.value ||
t.data.node.template[n] t.data.node.template[n]
.value, .value === ""
t.data, ? ""
t.data.node.template[n] : t.data.node.template[
)} n
].value
}
onChange={(k) => { onChange={(k) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
return newInputList;
});
tweaks.buildTweakObject( tweaks.buildTweakObject(
t["data"]["id"], t["data"]["id"],
k, k,
@ -328,9 +373,16 @@ export default function CodeTabsComponent({
.value .value
} }
setEnabled={(e) => { setEnabled={(e) => {
t.data.node.template[ setData((old) => {
n let newInputList =
].value = e; cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = e;
return newInputList;
});
tweaks.buildTweakObject( tweaks.buildTweakObject(
t["data"]["id"], t["data"]["id"],
e, e,
@ -345,12 +397,13 @@ export default function CodeTabsComponent({
"file" ? ( "file" ? (
<ShadTooltip <ShadTooltip
content={tweaks.buildContent( content={tweaks.buildContent(
tweaks.getValue( !t.data.node.template[n]
.value ||
t.data.node.template[n] t.data.node.template[n]
.value, .value === ""
t.data, ? ""
t.data.node.template[n] : t.data.node.template[n]
) .value
)} )}
> >
<div className="mx-auto"> <div className="mx-auto">
@ -382,13 +435,26 @@ export default function CodeTabsComponent({
<FloatComponent <FloatComponent
disabled={false} disabled={false}
editNode={true} editNode={true}
value={tweaks.getValue( value={
!t.data.node.template[n]
.value ||
t.data.node.template[n] t.data.node.template[n]
.value, .value === ""
t.data, ? ""
t.data.node.template[n] : t.data.node.template[n]
)} .value
}
onChange={(k) => { onChange={(k) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
return newInputList;
});
tweaks.buildTweakObject( tweaks.buildTweakObject(
t["data"]["id"], t["data"]["id"],
k, k,
@ -410,18 +476,31 @@ export default function CodeTabsComponent({
.options .options
} }
onSelect={(k) => { onSelect={(k) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
return newInputList;
});
tweaks.buildTweakObject( tweaks.buildTweakObject(
t["data"]["id"], t["data"]["id"],
k, k,
t.data.node.template[n] t.data.node.template[n]
); );
}} }}
value={tweaks.getValue( value={
!t.data.node.template[n]
.value ||
t.data.node.template[n] t.data.node.template[n]
.value, .value === ""
t.data, ? ""
t.data.node.template[n] : t.data.node.template[n]
)} .value
}
></Dropdown> ></Dropdown>
</div> </div>
) : t.data.node.template[n].type === ) : t.data.node.template[n].type ===
@ -430,13 +509,26 @@ export default function CodeTabsComponent({
<IntComponent <IntComponent
disabled={false} disabled={false}
editNode={true} editNode={true}
value={tweaks.getValue( value={
!t.data.node.template[n]
.value ||
t.data.node.template[n] t.data.node.template[n]
.value, .value === ""
t.data, ? ""
t.data.node.template[n] : t.data.node.template[n]
)} .value
}
onChange={(k) => { onChange={(k) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
return newInputList;
});
tweaks.buildTweakObject( tweaks.buildTweakObject(
t["data"]["id"], t["data"]["id"],
k, k,
@ -449,25 +541,40 @@ export default function CodeTabsComponent({
"prompt" ? ( "prompt" ? (
<ShadTooltip <ShadTooltip
content={tweaks.buildContent( content={tweaks.buildContent(
tweaks.getValue( !t.data.node.template[n]
.value ||
t.data.node.template[n] t.data.node.template[n]
.value, .value === ""
t.data, ? ""
t.data.node.template[n] : t.data.node.template[n]
) .value
)} )}
> >
<div className="mx-auto"> <div className="mx-auto">
<PromptAreaComponent <PromptAreaComponent
editNode={true} editNode={true}
disabled={false} disabled={false}
value={tweaks.getValue( value={
!t.data.node.template[n]
.value ||
t.data.node.template[n] t.data.node.template[n]
.value, .value === ""
t.data, ? ""
t.data.node.template[n] : t.data.node.template[
)} n
].value
}
onChange={(k) => { onChange={(k) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
return newInputList;
});
tweaks.buildTweakObject( tweaks.buildTweakObject(
t["data"]["id"], t["data"]["id"],
k, k,
@ -493,13 +600,27 @@ export default function CodeTabsComponent({
<CodeAreaComponent <CodeAreaComponent
disabled={false} disabled={false}
editNode={true} editNode={true}
value={tweaks.getValue( value={
!t.data.node.template[n]
.value ||
t.data.node.template[n] t.data.node.template[n]
.value, .value === ""
t.data, ? ""
t.data.node.template[n] : t.data.node.template[
)} n
].value
}
onChange={(k) => { onChange={(k) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
return newInputList;
});
tweaks.buildTweakObject( tweaks.buildTweakObject(
t["data"]["id"], t["data"]["id"],
k, k,

View file

@ -1,4 +1,4 @@
import { useEffect, useState } from "react"; import { useEffect } from "react";
import { FloatComponentType } from "../../types/components"; import { FloatComponentType } from "../../types/components";
import { Input } from "../ui/input"; import { Input } from "../ui/input";
@ -12,12 +12,6 @@ export default function FloatComponent({
const min = 0; const min = 0;
const max = 1; const max = 1;
const [myValue, setMyValue] = useState(value);
useEffect(() => {
setMyValue(value);
}, [value]);
// Clear component state // Clear component state
useEffect(() => { useEffect(() => {
if (disabled) { if (disabled) {
@ -48,7 +42,6 @@ export default function FloatComponent({
} }
onChange={(e) => { onChange={(e) => {
onChange(e.target.value); onChange(e.target.value);
setMyValue(e.target.value);
}} }}
/> />
</div> </div>

View file

@ -11,11 +11,6 @@ export default function InputComponent({
editNode = false, editNode = false,
}: InputComponentType) { }: InputComponentType) {
const [pwdVisible, setPwdVisible] = useState(false); const [pwdVisible, setPwdVisible] = useState(false);
const [myValue, setMyValue] = useState(value);
useEffect(() => {
setMyValue(value);
}, [value]);
// Clear component state // Clear component state
useEffect(() => { useEffect(() => {
@ -38,7 +33,6 @@ export default function InputComponent({
placeholder={password && editNode ? "Key" : "Type something..."} placeholder={password && editNode ? "Key" : "Type something..."}
onChange={(e) => { onChange={(e) => {
onChange(e.target.value); onChange(e.target.value);
setMyValue(e.target.value);
}} }}
/> />
{password && ( {password && (

View file

@ -1,10 +1,9 @@
import { useContext, useEffect, useState } from "react"; import { useEffect } from "react";
import { InputListComponentType } from "../../types/components"; import { InputListComponentType } from "../../types/components";
import { Input } from "../ui/input"; import { Input } from "../ui/input";
import { classNames } from "../../utils/utils"; import { classNames } from "../../utils/utils";
import _ from "lodash"; import _ from "lodash";
import { PopUpContext } from "../../contexts/popUpContext";
import IconComponent from "../genericIconComponent"; import IconComponent from "../genericIconComponent";
export default function InputListComponent({ export default function InputListComponent({
@ -13,31 +12,21 @@ export default function InputListComponent({
disabled, disabled,
editNode = false, editNode = false,
}: InputListComponentType) { }: InputListComponentType) {
const [inputList, setInputList] = useState(value ?? [""]);
const { closeEdit } = useContext(PopUpContext);
useEffect(() => {
if (value) {
setInputList(value);
}
}, [closeEdit]);
useEffect(() => { useEffect(() => {
if (disabled) { if (disabled) {
setInputList([""]);
onChange([""]); onChange([""]);
} }
}, [disabled, onChange]); }, [disabled]);
return ( return (
<div <div
className={ className={
classNames( classNames(
inputList.length > 1 && editNode ? "my-1" : "", value.length > 1 && editNode ? "my-1" : "",
"flex flex-col gap-3" "flex flex-col gap-3"
) )
}> }>
{inputList.map((i, idx) => { {value.map((i, idx) => {
return ( return (
<div key={idx} className="flex w-full gap-3"> <div key={idx} className="flex w-full gap-3">
<Input <Input
@ -47,23 +36,17 @@ export default function InputListComponent({
className={editNode ? "input-edit-node" : ""} className={editNode ? "input-edit-node" : ""}
placeholder="Type something..." placeholder="Type something..."
onChange={(e) => { onChange={(e) => {
setInputList((old) => { let newInputList = _.cloneDeep(value);
let newInputList = _.cloneDeep(inputList); newInputList[idx] = e.target.value;
newInputList[idx] = e.target.value; onChange(newInputList);
return newInputList;
});
onChange(inputList);
}} }}
/> />
{idx === inputList.length - 1 ? ( {idx === value.length - 1 ? (
<button <button
onClick={() => { onClick={() => {
setInputList((old) => { let newInputList = _.cloneDeep(value);
let newInputList = _.cloneDeep(old); newInputList.push("");
newInputList.push(""); onChange(newInputList);
return newInputList;
});
onChange(inputList);
}} }}
> >
<IconComponent <IconComponent
@ -74,12 +57,9 @@ export default function InputListComponent({
) : ( ) : (
<button <button
onClick={() => { onClick={() => {
setInputList((old) => { let newInputList = _.cloneDeep(value);
let newInputList = _.cloneDeep(old); newInputList.splice(idx, 1);
newInputList.splice(idx, 1); onChange(newInputList);
return newInputList;
});
onChange(inputList);
}} }}
> >
<IconComponent <IconComponent

View file

@ -1,4 +1,4 @@
import { useEffect, useState } from "react"; import { useEffect } from "react";
import { FloatComponentType } from "../../types/components"; import { FloatComponentType } from "../../types/components";
import { Input } from "../ui/input"; import { Input } from "../ui/input";
@ -10,12 +10,6 @@ export default function IntComponent({
}: FloatComponentType) { }: FloatComponentType) {
const min = 0; const min = 0;
const [myValue, setMyValue] = useState(value);
useEffect(() => {
setMyValue(value);
}, [value]);
// Clear component state // Clear component state
useEffect(() => { useEffect(() => {
if (disabled) { if (disabled) {
@ -58,7 +52,6 @@ export default function IntComponent({
placeholder={editNode ? "Integer number" : "Type an integer number"} placeholder={editNode ? "Integer number" : "Type an integer number"}
onChange={(e) => { onChange={(e) => {
onChange(e.target.value); onChange(e.target.value);
setMyValue(e.target.value);
}} }}
/> />
</div> </div>

View file

@ -1,4 +1,3 @@
import { useEffect, useState } from "react";
import { ToggleComponentType } from "../../types/components"; import { ToggleComponentType } from "../../types/components";
import { Switch } from "../ui/switch"; import { Switch } from "../ui/switch";
@ -27,12 +26,6 @@ export default function ToggleShadComponent({
scaleY = 1; scaleY = 1;
} }
const [myValue, setMyValue] = useState(enabled);
useEffect(() => {
setMyValue(enabled);
}, [enabled]);
return ( return (
<div className={disabled ? "pointer-events-none cursor-not-allowed " : ""}> <div className={disabled ? "pointer-events-none cursor-not-allowed " : ""}>
<Switch <Switch
@ -41,10 +34,9 @@ export default function ToggleShadComponent({
}} }}
disabled={disabled} disabled={disabled}
className="" className=""
checked={myValue} checked={enabled}
onCheckedChange={(x: boolean) => { onCheckedChange={(x: boolean) => {
setEnabled(x); setEnabled(x);
setMyValue(x);
}} }}
></Switch> ></Switch>
</div> </div>

View file

@ -56,4 +56,4 @@ const AccordionContent = React.forwardRef<
)); ));
AccordionContent.displayName = AccordionPrimitive.Content.displayName; AccordionContent.displayName = AccordionPrimitive.Content.displayName;
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }; export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };

View file

@ -77,9 +77,9 @@ CardFooter.displayName = "CardFooter";
export { export {
Card, Card,
CardHeader,
CardFooter,
CardTitle,
CardDescription,
CardContent, CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
}; };

View file

@ -116,10 +116,10 @@ DialogDescription.displayName = DialogPrimitive.Description.displayName;
export { export {
Dialog, Dialog,
DialogTrigger,
DialogContent, DialogContent,
DialogHeader,
DialogFooter,
DialogTitle,
DialogDescription, DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
}; };

View file

@ -182,18 +182,18 @@ DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
export { export {
DropdownMenu, DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuCheckboxItem, DropdownMenuCheckboxItem,
DropdownMenuRadioItem, DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel, DropdownMenuLabel,
DropdownMenuPortal,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator, DropdownMenuSeparator,
DropdownMenuShortcut, DropdownMenuShortcut,
DropdownMenuGroup,
DropdownMenuPortal,
DropdownMenuSub, DropdownMenuSub,
DropdownMenuSubContent, DropdownMenuSubContent,
DropdownMenuSubTrigger, DropdownMenuSubTrigger,
DropdownMenuRadioGroup, DropdownMenuTrigger,
}; };

View file

@ -218,19 +218,19 @@ MenubarShortcut.displayname = "MenubarShortcut";
export { export {
Menubar, Menubar,
MenubarMenu,
MenubarTrigger,
MenubarContent,
MenubarItem,
MenubarSeparator,
MenubarLabel,
MenubarCheckboxItem, MenubarCheckboxItem,
MenubarContent,
MenubarGroup,
MenubarItem,
MenubarLabel,
MenubarMenu,
MenubarPortal,
MenubarRadioGroup, MenubarRadioGroup,
MenubarRadioItem, MenubarRadioItem,
MenubarPortal, MenubarSeparator,
MenubarShortcut,
MenubarSub,
MenubarSubContent, MenubarSubContent,
MenubarSubTrigger, MenubarSubTrigger,
MenubarGroup, MenubarTrigger,
MenubarSub,
MenubarShortcut,
}; };

View file

@ -103,11 +103,11 @@ TableCaption.displayName = "TableCaption";
export { export {
Table, Table,
TableHeader,
TableBody, TableBody,
TableCaption,
TableCell,
TableFooter, TableFooter,
TableHead, TableHead,
TableHeader,
TableRow, TableRow,
TableCell,
TableCaption,
}; };

View file

@ -51,4 +51,4 @@ const TabsContent = React.forwardRef<
)); ));
TabsContent.displayName = TabsPrimitive.Content.displayName; TabsContent.displayName = TabsPrimitive.Content.displayName;
export { Tabs, TabsList, TabsTrigger, TabsContent }; export { Tabs, TabsContent, TabsList, TabsTrigger };

View file

@ -28,4 +28,4 @@ const TooltipContent = React.forwardRef<
)); ));
TooltipContent.displayName = TooltipPrimitive.Content.displayName; TooltipContent.displayName = TooltipPrimitive.Content.displayName;
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }; export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };

View file

@ -71,18 +71,7 @@ export function TypesProvider({ children }: { children: ReactNode }) {
// Clear the interval if successful. // Clear the interval if successful.
clearInterval(intervalId); clearInterval(intervalId);
} catch (error) { } catch (error) {
retryCount++; console.error("An error has occurred while fetching types.");
// On error, double the delay for the next attempt up to a maximum.
delay = Math.min(30000, delay * 2);
// Log errors but don't do anything else - the function will try again on the next interval.
console.error(error);
// Clear the old interval and start a new one with the new delay.
if (retryCount <= maxRetryCount) {
clearInterval(intervalId);
intervalId = setInterval(getTypes, delay);
} else {
console.error("Max retry attempts reached. Stopping retries.");
}
} }
} }

View file

@ -0,0 +1,58 @@
import axios, { AxiosError, AxiosInstance } from "axios";
import { useContext, useEffect, useRef } from "react";
import { alertContext } from "../../contexts/alertContext";
// Create a new Axios instance
const api: AxiosInstance = axios.create({
baseURL: "",
});
function ApiInterceptor() {
const retryCounts = useRef([]);
const { setErrorData } = useContext(alertContext);
useEffect(() => {
const interceptor = api.interceptors.response.use(
(response) => response,
async (error: AxiosError) => {
let retryCount = 0;
while (retryCount < 4) {
await sleep(5000); // Sleep for 5 seconds
retryCount++;
try {
const response = await axios.request(error.config);
return response;
} catch (error) {
if (retryCount === 3) {
setErrorData({
title: "There was an error on web connection, please: ",
list: [
"Refresh the page",
"Use a new flow tab",
"Check if the backend is up",
"Endpoint: " + error.config.url,
],
});
return Promise.reject(error);
}
}
}
}
);
return () => {
// Clean up the interceptor when the component unmounts
api.interceptors.response.eject(interceptor);
};
}, [retryCounts]);
return null;
}
// Function to sleep for a given duration in milliseconds
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export { ApiInterceptor, api };

View file

@ -1,5 +1,6 @@
import axios, { AxiosResponse } from "axios"; import { AxiosResponse } from "axios";
import { ReactFlowJsonObject } from "reactflow"; import { ReactFlowJsonObject } from "reactflow";
import { api } from "../../controllers/API/api";
import { APIObjectType, sendAllProps } from "../../types/api/index"; import { APIObjectType, sendAllProps } from "../../types/api/index";
import { FlowStyleType, FlowType } from "../../types/flow"; import { FlowStyleType, FlowType } from "../../types/flow";
import { import {
@ -17,16 +18,14 @@ import {
* @returns {Promise<AxiosResponse<APIObjectType>>} A promise that resolves to an AxiosResponse containing all the objects. * @returns {Promise<AxiosResponse<APIObjectType>>} A promise that resolves to an AxiosResponse containing all the objects.
*/ */
export async function getAll(): Promise<AxiosResponse<APIObjectType>> { export async function getAll(): Promise<AxiosResponse<APIObjectType>> {
return await axios.get(`/api/v1/all`); return await api.get(`/api/v1/all`);
} }
const GITHUB_API_URL = "https://api.github.com"; const GITHUB_API_URL = "https://api.github.com";
export async function getRepoStars(owner, repo) { export async function getRepoStars(owner, repo) {
try { try {
const response = await axios.get( const response = await api.get(`${GITHUB_API_URL}/repos/${owner}/${repo}`);
`${GITHUB_API_URL}/repos/${owner}/${repo}`
);
return response.data.stargazers_count; return response.data.stargazers_count;
} catch (error) { } catch (error) {
console.error("Error fetching repository data:", error); console.error("Error fetching repository data:", error);
@ -41,13 +40,13 @@ export async function getRepoStars(owner, repo) {
* @returns {AxiosResponse<any>} The API response. * @returns {AxiosResponse<any>} The API response.
*/ */
export async function sendAll(data: sendAllProps) { export async function sendAll(data: sendAllProps) {
return await axios.post(`/api/v1/predict`, data); return await api.post(`/api/v1/predict`, data);
} }
export async function postValidateCode( export async function postValidateCode(
code: string code: string
): Promise<AxiosResponse<errorsTypeAPI>> { ): Promise<AxiosResponse<errorsTypeAPI>> {
return await axios.post("/api/v1/validate/code", { code }); return await api.post("/api/v1/validate/code", { code });
} }
/** /**
@ -62,7 +61,7 @@ export async function postValidatePrompt(
template: string, template: string,
frontend_node: APIClassType frontend_node: APIClassType
): Promise<AxiosResponse<PromptTypeAPI>> { ): Promise<AxiosResponse<PromptTypeAPI>> {
return await axios.post("/api/v1/validate/prompt", { return await api.post("/api/v1/validate/prompt", {
name: name, name: name,
template: template, template: template,
frontend_node: frontend_node, frontend_node: frontend_node,
@ -77,14 +76,14 @@ export async function postValidatePrompt(
export async function getExamples(): Promise<FlowType[]> { export async function getExamples(): Promise<FlowType[]> {
const url = const url =
"https://api.github.com/repos/logspace-ai/langflow_examples/contents/examples?ref=main"; "https://api.github.com/repos/logspace-ai/langflow_examples/contents/examples?ref=main";
const response = await axios.get(url); const response = await api.get(url);
const jsonFiles = response.data.filter((file: any) => { const jsonFiles = response.data.filter((file: any) => {
return file.name.endsWith(".json"); return file.name.endsWith(".json");
}); });
const contentsPromises = jsonFiles.map(async (file: any) => { const contentsPromises = jsonFiles.map(async (file: any) => {
const contentResponse = await axios.get(file.download_url); const contentResponse = await api.get(file.download_url);
return contentResponse.data; return contentResponse.data;
}); });
@ -106,11 +105,12 @@ export async function saveFlowToDatabase(newFlow: {
style?: FlowStyleType; style?: FlowStyleType;
}): Promise<FlowType> { }): Promise<FlowType> {
try { try {
const response = await axios.post("/api/v1/flows/", { const response = await api.post("/api/v1/flows/", {
name: newFlow.name, name: newFlow.name,
data: newFlow.data, data: newFlow.data,
description: newFlow.description, description: newFlow.description,
}); });
if (response.status !== 201) { if (response.status !== 201) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
} }
@ -131,7 +131,7 @@ export async function updateFlowInDatabase(
updatedFlow: FlowType updatedFlow: FlowType
): Promise<FlowType> { ): Promise<FlowType> {
try { try {
const response = await axios.patch(`/api/v1/flows/${updatedFlow.id}`, { const response = await api.patch(`/api/v1/flows/${updatedFlow.id}`, {
name: updatedFlow.name, name: updatedFlow.name,
data: updatedFlow.data, data: updatedFlow.data,
description: updatedFlow.description, description: updatedFlow.description,
@ -155,7 +155,7 @@ export async function updateFlowInDatabase(
*/ */
export async function readFlowsFromDatabase() { export async function readFlowsFromDatabase() {
try { try {
const response = await axios.get("/api/v1/flows/"); const response = await api.get("/api/v1/flows/");
if (response.status !== 200) { if (response.status !== 200) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
} }
@ -168,7 +168,7 @@ export async function readFlowsFromDatabase() {
export async function downloadFlowsFromDatabase() { export async function downloadFlowsFromDatabase() {
try { try {
const response = await axios.get("/api/v1/flows/download/"); const response = await api.get("/api/v1/flows/download/");
if (response.status !== 200) { if (response.status !== 200) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
} }
@ -181,7 +181,7 @@ export async function downloadFlowsFromDatabase() {
export async function uploadFlowsToDatabase(flows) { export async function uploadFlowsToDatabase(flows) {
try { try {
const response = await axios.post(`/api/v1/flows/upload/`, flows); const response = await api.post(`/api/v1/flows/upload/`, flows);
if (response.status !== 201) { if (response.status !== 201) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
@ -202,7 +202,7 @@ export async function uploadFlowsToDatabase(flows) {
*/ */
export async function deleteFlowFromDatabase(flowId: string) { export async function deleteFlowFromDatabase(flowId: string) {
try { try {
const response = await axios.delete(`/api/v1/flows/${flowId}`); const response = await api.delete(`/api/v1/flows/${flowId}`);
if (response.status !== 200) { if (response.status !== 200) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
} }
@ -222,7 +222,7 @@ export async function deleteFlowFromDatabase(flowId: string) {
*/ */
export async function getFlowFromDatabase(flowId: number) { export async function getFlowFromDatabase(flowId: number) {
try { try {
const response = await axios.get(`/api/v1/flows/${flowId}`); const response = await api.get(`/api/v1/flows/${flowId}`);
if (response.status !== 200) { if (response.status !== 200) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
} }
@ -241,7 +241,7 @@ export async function getFlowFromDatabase(flowId: number) {
*/ */
export async function getFlowStylesFromDatabase() { export async function getFlowStylesFromDatabase() {
try { try {
const response = await axios.get("/api/v1/flow_styles/"); const response = await api.get("/api/v1/flow_styles/");
if (response.status !== 200) { if (response.status !== 200) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
} }
@ -261,7 +261,7 @@ export async function getFlowStylesFromDatabase() {
*/ */
export async function saveFlowStyleToDatabase(flowStyle: FlowStyleType) { export async function saveFlowStyleToDatabase(flowStyle: FlowStyleType) {
try { try {
const response = await axios.post("/api/v1/flow_styles/", flowStyle, { const response = await api.post("/api/v1/flow_styles/", flowStyle, {
headers: { headers: {
accept: "application/json", accept: "application/json",
"Content-Type": "application/json", "Content-Type": "application/json",
@ -284,7 +284,7 @@ export async function saveFlowStyleToDatabase(flowStyle: FlowStyleType) {
* @returns {Promise<AxiosResponse<any>>} A promise that resolves to an AxiosResponse containing the version information. * @returns {Promise<AxiosResponse<any>>} A promise that resolves to an AxiosResponse containing the version information.
*/ */
export async function getVersion() { export async function getVersion() {
const respnose = await axios.get("/api/v1/version"); const respnose = await api.get("/api/v1/version");
return respnose.data; return respnose.data;
} }
@ -294,7 +294,7 @@ export async function getVersion() {
* @returns {Promise<AxiosResponse<any>>} A promise that resolves to an AxiosResponse containing the health status. * @returns {Promise<AxiosResponse<any>>} A promise that resolves to an AxiosResponse containing the health status.
*/ */
export async function getHealth() { export async function getHealth() {
return await axios.get("/health"); // Health is the only endpoint that doesn't require /api/v1 return await api.get("/health"); // Health is the only endpoint that doesn't require /api/v1
} }
/** /**
@ -306,7 +306,7 @@ export async function getHealth() {
export async function getBuildStatus( export async function getBuildStatus(
flowId: string flowId: string
): Promise<BuildStatusTypeAPI> { ): Promise<BuildStatusTypeAPI> {
return await axios.get(`/api/v1/build/${flowId}/status`); return await api.get(`/api/v1/build/${flowId}/status`);
} }
//docs for postbuildinit //docs for postbuildinit
@ -319,7 +319,7 @@ export async function getBuildStatus(
export async function postBuildInit( export async function postBuildInit(
flow: FlowType flow: FlowType
): Promise<AxiosResponse<InitTypeAPI>> { ): Promise<AxiosResponse<InitTypeAPI>> {
return await axios.post(`/api/v1/build/init/${flow.id}`, flow); return await api.post(`/api/v1/build/init/${flow.id}`, flow);
} }
// fetch(`/upload/${id}`, { // fetch(`/upload/${id}`, {
@ -337,5 +337,5 @@ export async function uploadFile(
): Promise<AxiosResponse<UploadFileTypeAPI>> { ): Promise<AxiosResponse<UploadFileTypeAPI>> {
const formData = new FormData(); const formData = new FormData();
formData.append("file", file); formData.append("file", file);
return await axios.post(`/api/v1/upload/${id}`, formData); return await api.post(`/api/v1/upload/${id}`, formData);
} }

View file

@ -38,9 +38,10 @@
--info-background: #f0f4fd; --info-background: #f0f4fd;
--info-foreground: #141653; --info-foreground: #141653;
--high-indigo: #4338ca; --high-indigo: #4338ca;
--medium-indigo: #6366f1; --medium-indigo: #6366f1;
--low-indigo: #e0e7ff;
--chat-bot-icon: #afe6ef; --chat-bot-icon: #afe6ef;
--chat-user-icon: #aface9; --chat-user-icon: #aface9;
@ -52,6 +53,7 @@
--chat-trigger-disabled: #b4c3da; --chat-trigger-disabled: #b4c3da;
--status-red: #ef4444; --status-red: #ef4444;
--status-yellow: #eab308; --status-yellow: #eab308;
--chat-send: #059669;
--status-green: #4ade80; --status-green: #4ade80;
--status-blue:#2563eb; --status-blue:#2563eb;
--connection: #555; --connection: #555;
@ -104,6 +106,7 @@
--high-indigo: #4338ca; --high-indigo: #4338ca;
--medium-indigo: #6366f1; --medium-indigo: #6366f1;
--low-indigo: #e0e7ff;
/* Colors that are shared in dark and light mode */ /* Colors that are shared in dark and light mode */
--blur-shared: #151923d2; --blur-shared: #151923d2;
@ -112,6 +115,7 @@
--chat-trigger-disabled: #2d3b54; --chat-trigger-disabled: #2d3b54;
--status-red: #ef4444; --status-red: #ef4444;
--status-yellow: #eab308; --status-yellow: #eab308;
--chat-send: #059669;
--status-green: #4ade80; --status-green: #4ade80;
--status-blue: #2563eb; --status-blue: #2563eb;
--connection: #555; --connection: #555;

View file

@ -4,6 +4,7 @@ import App from "./App";
import ContextWrapper from "./contexts"; import ContextWrapper from "./contexts";
import reportWebVitals from "./reportWebVitals"; import reportWebVitals from "./reportWebVitals";
import { ApiInterceptor } from "./controllers/API/api";
import "./index.css"; import "./index.css";
const root = ReactDOM.createRoot( const root = ReactDOM.createRoot(
@ -13,6 +14,7 @@ root.render(
<ContextWrapper> <ContextWrapper>
<BrowserRouter> <BrowserRouter>
<App /> <App />
<ApiInterceptor />
</BrowserRouter> </BrowserRouter>
</ContextWrapper> </ContextWrapper>
); );

View file

@ -22,11 +22,10 @@ import {
TableRow, TableRow,
} from "../../components/ui/table"; } from "../../components/ui/table";
import { limitScrollFieldsModal } from "../../constants/constants"; import { limitScrollFieldsModal } from "../../constants/constants";
import { PopUpContext } from "../../contexts/popUpContext";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { typesContext } from "../../contexts/typesContext"; import { typesContext } from "../../contexts/typesContext";
import { NodeDataType } from "../../types/flow"; import { NodeDataType } from "../../types/flow";
import { classNames, getRandomKeyByssmm } from "../../utils/utils"; import { classNames } from "../../utils/utils";
import BaseModal from "../baseModal"; import BaseModal from "../baseModal";
const EditNodeModal = forwardRef( const EditNodeModal = forwardRef(
@ -48,7 +47,6 @@ const EditNodeModal = forwardRef(
const [myData, setMyData] = useState(data); const [myData, setMyData] = useState(data);
const { setTabsState, tabId } = useContext(TabsContext); const { setTabsState, tabId } = useContext(TabsContext);
const { reactFlowInstance } = useContext(typesContext); const { reactFlowInstance } = useContext(typesContext);
const { setCloseEdit } = useContext(PopUpContext);
let disabled = let disabled =
reactFlowInstance?.getEdges().some((e) => e.targetHandle === data.id) ?? reactFlowInstance?.getEdges().some((e) => e.targetHandle === data.id) ??
@ -72,7 +70,6 @@ const EditNodeModal = forwardRef(
useEffect(() => { useEffect(() => {
setMyData(data); // reset data to what it is on node when opening modal setMyData(data); // reset data to what it is on node when opening modal
setCloseEdit(getRandomKeyByssmm().toString());
}, [modalOpen]); }, [modalOpen]);
return ( return (

View file

@ -69,10 +69,10 @@ export default function ChatInput({
className={classNames( className={classNames(
"form-modal-send-button", "form-modal-send-button",
noInput noInput
? "bg-indigo-600 text-background" ? "bg-high-indigo text-background"
: chatValue === "" : chatValue === ""
? "text-primary" ? "text-primary"
: "bg-emerald-600 text-background" : "bg-chat-send text-background"
)} )}
disabled={lockChat} disabled={lockChat}
onClick={() => sendMessage()} onClick={() => sendMessage()}

View file

@ -78,69 +78,75 @@ export default function ChatMessage({
<div className="w-full dark:text-white"> <div className="w-full dark:text-white">
<div className="w-full"> <div className="w-full">
{useMemo( {useMemo(
() => ( () =>
<ReactMarkdown chat.message.toString() === "" && lockChat ? (
remarkPlugins={[remarkGfm, remarkMath]} <IconComponent
rehypePlugins={[rehypeMathjax]} name="MoreHorizontal"
className="markdown prose inline-block break-words text-primary dark:prose-invert className="h-8 w-8 animate-pulse"
/>
) : (
<ReactMarkdown
remarkPlugins={[remarkGfm, remarkMath]}
rehypePlugins={[rehypeMathjax]}
className="markdown prose inline-block break-words text-primary dark:prose-invert
sm:w-[30vw] sm:max-w-[30vw] lg:w-[40vw] lg:max-w-[40vw]" sm:w-[30vw] sm:max-w-[30vw] lg:w-[40vw] lg:max-w-[40vw]"
components={{ components={{
pre({ node, ...props }) { pre({ node, ...props }) {
return <>{props.children}</>; return <>{props.children}</>;
}, },
code: ({ code: ({
node, node,
inline, inline,
className, className,
children, children,
...props ...props
}) => { }) => {
if (children.length) { if (children.length) {
if (children[0] === "▍") { if (children[0] === "▍") {
return ( return (
<span className="form-modal-markdown-span"> <span className="form-modal-markdown-span">
▍ ▍
</span> </span>
);
}
children[0] = (children[0] as string).replace(
"`▍`",
"▍"
); );
} }
children[0] = (children[0] as string).replace( const match = /language-(\w+)/.exec(
"`▍`", className || ""
"▍"
); );
}
const match = /language-(\w+)/.exec( return !inline ? (
className || "" <CodeTabsComponent
); isMessage
tabs={[
return !inline ? ( {
<CodeTabsComponent name: (match && match[1]) || "",
isMessage mode: (match && match[1]) || "",
tabs={[ image:
{ "https://curl.se/logo/curl-symbol-transparent.png",
name: (match && match[1]) || "", language: (match && match[1]) || "",
mode: (match && match[1]) || "", code: String(children).replace(/\n$/, ""),
image: },
"https://curl.se/logo/curl-symbol-transparent.png", ]}
language: (match && match[1]) || "", activeTab={"0"}
code: String(children).replace(/\n$/, ""), setActiveTab={() => {}}
}, />
]} ) : (
activeTab={"0"} <code className={className} {...props}>
setActiveTab={() => {}} {children}
/> </code>
) : ( );
<code className={className} {...props}> },
{children} }}
</code> >
); {chat.message.toString()}
}, </ReactMarkdown>
}} ),
>
{chat.message.toString()}
</ReactMarkdown>
),
[chat.message, chat.message.toString()] [chat.message, chat.message.toString()]
)} )}
</div> </div>

View file

@ -395,7 +395,7 @@ export default function FormModal({
<span className="pr-2">Chat</span> <span className="pr-2">Chat</span>
<IconComponent <IconComponent
name="prompts" name="prompts"
className="h-6 w-6 pl-1 text-gray-800 dark:text-white" className="h-6 w-6 pl-1 text-foreground"
aria-hidden="true" aria-hidden="true"
/> />
</DialogTitle> </DialogTitle>

View file

@ -41,6 +41,7 @@ import {
MessageSquare, MessageSquare,
MessagesSquare, MessagesSquare,
MoonIcon, MoonIcon,
MoreHorizontal,
Paperclip, Paperclip,
Plus, Plus,
Redo, Redo,
@ -268,6 +269,7 @@ export const nodeIconsLucide = {
Copy, Copy,
Upload, Upload,
MessageSquare, MessageSquare,
MoreHorizontal,
}; };
export function getConnectedNodes(edge: Edge, nodes: Array<Node>): Array<Node> { export function getConnectedNodes(edge: Edge, nodes: Array<Node>): Array<Node> {
const sourceId = edge.source; const sourceId = edge.source;

View file

@ -29,6 +29,8 @@ module.exports = {
}, },
extend: { extend: {
colors: { colors: {
"low-indigo": "var(--low-indigo)",
"chat-send": "var(--chat-send)",
connection: "var(--connection)", connection: "var(--connection)",
"almost-dark-gray": "var(--almost-dark-gray)", "almost-dark-gray": "var(--almost-dark-gray)",
"almost-light-blue": "var(--almost-light-blue)", "almost-light-blue": "var(--almost-light-blue)",