Merge branch 'dev' into interceptor-dev

This commit is contained in:
Lucas Oliveira 2023-07-25 10:07:34 -03:00
commit bfbe66a547
42 changed files with 3010 additions and 4222 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"
env:
POETRY_VERSION: "1.4.0"
POETRY_VERSION: "1.5.1"
jobs:
if_release:

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,4 @@
import { cloneDeep } from "lodash";
import React, { useContext, useEffect, useRef, useState } from "react";
import { Handle, Position, useUpdateNodeInternals } from "reactflow";
import ShadTooltip from "../../../../components/ShadTooltipComponent";
@ -13,14 +14,10 @@ import PromptAreaComponent from "../../../../components/promptComponent";
import TextAreaComponent from "../../../../components/textAreaComponent";
import ToggleShadComponent from "../../../../components/toggleShadComponent";
import { MAX_LENGTH_TO_SCROLL_TOOLTIP } from "../../../../constants/constants";
import { PopUpContext } from "../../../../contexts/popUpContext";
import { TabsContext } from "../../../../contexts/tabsContext";
import { typesContext } from "../../../../contexts/typesContext";
import { ParameterComponentType } from "../../../../types/components";
import {
cleanEdges,
isValidConnection,
} from "../../../../utils/reactflowUtils";
import { isValidConnection } from "../../../../utils/reactflowUtils";
import {
nodeColors,
nodeIconsLucide,
@ -36,6 +33,7 @@ export default function ParameterComponent({
left,
id,
data,
setData,
tooltipTitle,
title,
color,
@ -51,7 +49,6 @@ export default function ParameterComponent({
const infoHtml = useRef(null);
const updateNodeInternals = useUpdateNodeInternals();
const [position, setPosition] = useState(0);
const { closePopUp } = useContext(PopUpContext);
const { setTabsState, tabId, save } = useContext(TabsContext);
// Update component position
@ -66,15 +63,16 @@ export default function ParameterComponent({
updateNodeInternals(data.id);
}, [data.id, position, updateNodeInternals]);
useEffect(() => {}, [closePopUp, data.node.template]);
const { reactFlowInstance } = useContext(typesContext);
let disabled =
reactFlowInstance?.getEdges().some((e) => e.targetHandle === id) ?? false;
const [myData, setMyData] = useState(useContext(typesContext).data);
const { data: myData } = useContext(typesContext);
const handleOnNewValue = (newValue: any) => {
data.node.template[name].value = newValue;
let newData = cloneDeep(data);
newData.node.template[name].value = newValue;
setData(newData);
// Set state to pending
setTabsState((prev) => {
return {
@ -244,7 +242,6 @@ export default function ParameterComponent({
) : (
<InputComponent
disabled={disabled}
disableCopyPaste={true}
password={data.node.template[name].password ?? false}
value={data.node.template[name].value ?? ""}
onChange={handleOnNewValue}
@ -266,7 +263,6 @@ export default function ParameterComponent({
<div className="mt-2 w-full">
<FloatComponent
disabled={disabled}
disableCopyPaste={true}
value={data.node.template[name].value ?? ""}
onChange={handleOnNewValue}
/>
@ -311,7 +307,6 @@ export default function ParameterComponent({
<div className="mt-2 w-full">
<IntComponent
disabled={disabled}
disableCopyPaste={true}
value={data.node.template[name].value ?? ""}
onChange={handleOnNewValue}
/>
@ -322,15 +317,6 @@ export default function ParameterComponent({
field_name={name}
setNodeClass={(nodeClass) => {
data.node = nodeClass;
if (reactFlowInstance) {
cleanEdges({
flow: {
edges: reactFlowInstance.getEdges(),
nodes: reactFlowInstance.getNodes(),
},
updateEdge: (edge) => reactFlowInstance.setEdges(edge),
});
}
}}
nodeClass={data.node}
disabled={disabled}

View file

@ -1,31 +1,33 @@
import { cloneDeep } from "lodash";
import { useContext, useEffect, useRef, useState } from "react";
import { NodeToolbar } from "reactflow";
import { NodeToolbar, useUpdateNodeInternals } from "reactflow";
import ShadTooltip from "../../components/ShadTooltipComponent";
import Tooltip from "../../components/TooltipComponent";
import IconComponent from "../../components/genericIconComponent";
import { useSSE } from "../../contexts/SSEContext";
import { alertContext } from "../../contexts/alertContext";
import { PopUpContext } from "../../contexts/popUpContext";
import { TabsContext } from "../../contexts/tabsContext";
import { typesContext } from "../../contexts/typesContext";
import NodeModal from "../../modals/NodeModal";
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
import { NodeDataType } from "../../types/flow";
import { cleanEdges } from "../../utils/reactflowUtils";
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
import { classNames, toTitleCase } from "../../utils/utils";
import ParameterComponent from "./components/parameterComponent";
export default function GenericNode({
data,
data: olddata,
selected,
}: {
data: NodeDataType;
selected: boolean;
}) {
const [data, setData] = useState(olddata);
const { setErrorData } = useContext(alertContext);
const { updateFlow, flows, tabId } = useContext(TabsContext);
const updateNodeInternals = useUpdateNodeInternals();
const showError = useRef(true);
const { types, deleteNode } = useContext(typesContext);
const { closePopUp, openPopUp } = useContext(PopUpContext);
const { types, deleteNode, reactFlowInstance } = useContext(typesContext);
// any to avoid type conflict
const Icon: any =
nodeIconsLucide[data.type] || nodeIconsLucide[types[data.type]];
@ -33,7 +35,26 @@ export default function GenericNode({
const [validationStatus, setValidationStatus] = useState(null);
// State for outline color
const { sseData, isBuilding } = useSSE();
const refHtml = useRef(null);
useEffect(() => {
olddata.node = data.node;
let myFlow = flows.find((flow) => flow.id === tabId);
if (reactFlowInstance && myFlow) {
let flow = cloneDeep(myFlow);
flow.data = reactFlowInstance.toObject();
cleanEdges({
flow: {
edges: flow.data.edges,
nodes: flow.data.nodes,
},
updateEdge: (edge) => {
flow.data.edges = edge;
reactFlowInstance.setEdges(edge);
updateNodeInternals(data.id);
},
});
updateFlow(flow);
}
}, [data]);
// New useEffect to watch for changes in sseData and update validation status
useEffect(() => {
@ -58,13 +79,12 @@ export default function GenericNode({
deleteNode(data.id);
return;
}
useEffect(() => {}, [closePopUp, data.node.template]);
return (
<>
<NodeToolbar>
<NodeToolbarComponent
data={data}
openPopUp={openPopUp}
setData={setData}
deleteNode={deleteNode}
></NodeToolbarComponent>
</NodeToolbar>
@ -90,15 +110,6 @@ export default function GenericNode({
</ShadTooltip>
</div>
</div>
<div className="round-button-div">
<button
className="relative"
onClick={(event) => {
event.preventDefault();
openPopUp(<NodeModal data={data} />);
}}
></button>
</div>
<div className="round-button-div">
<div>
<Tooltip
@ -168,6 +179,7 @@ export default function GenericNode({
!data.node.template[t].advanced ? (
<ParameterComponent
data={data}
setData={setData}
color={
nodeColors[types[data.node.template[t].type]] ??
nodeColors[data.node.template[t].type] ??
@ -214,6 +226,7 @@ export default function GenericNode({
</div>
<ParameterComponent
data={data}
setData={setData}
color={nodeColors[types[data.type]] ?? nodeColors.unknown}
title={
data.node.output_types && data.node.output_types.length > 0

View file

@ -7,7 +7,7 @@ type InputProps = {
name: string | null;
description: string | null;
maxLength?: number;
flows: Array<{ id: string; name: string }>;
flows: Array<{ id: string; name: string; description: string }>;
tabId: string;
setName: (name: string) => void;
setDescription: (description: string) => void;
@ -37,7 +37,13 @@ export const EditFlowSettings: React.FC<InputProps> = ({
setName(value);
};
const [desc, setDesc] = useState(
flows.find((f) => f.id === tabId).description
);
const handleDescriptionChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
flows.find((f) => f.id === tabId).description = event.target.value;
setDesc(flows.find((f) => f.id === tabId).description);
setDescription(event.target.value);
};
@ -51,7 +57,7 @@ export const EditFlowSettings: React.FC<InputProps> = ({
)}
</div>
<Input
className="mt-2 font-normal"
className="nopan nodrag noundo nocopy mt-2 font-normal"
onChange={handleNameChange}
type="text"
name="name"
@ -62,12 +68,15 @@ export const EditFlowSettings: React.FC<InputProps> = ({
/>
</Label>
<Label>
<div className="edit-flow-arrangement mt-3">
<span className="font-medium ">Description (optional)</span>
</div>
<Textarea
name="description"
id="description"
onChange={handleDescriptionChange}
value={description ?? ""}
value={desc}
placeholder="Flow description"
className="mt-2 max-h-[100px] font-normal"
rows={3}

View file

@ -1,5 +1,4 @@
import { useContext, useEffect, useState } from "react";
import { PopUpContext } from "../../contexts/popUpContext";
import { useEffect, useState } from "react";
import CodeAreaModal from "../../modals/codeAreaModal";
import { TextAreaComponentType } from "../../types/components";
@ -16,7 +15,6 @@ export default function CodeAreaComponent({
const [myValue, setMyValue] = useState(
typeof value == "string" ? value : JSON.stringify(value)
);
const { openPopUp } = useContext(PopUpContext);
useEffect(() => {
if (disabled) {
setMyValue("");
@ -30,10 +28,6 @@ export default function CodeAreaComponent({
return (
<div className={disabled ? "pointer-events-none w-full " : " w-full"}>
<div className="flex w-full items-center">
<span
onClick={() => {
openPopUp(
<CodeAreaModal
value={myValue}
nodeClass={nodeClass}
@ -42,9 +36,9 @@ export default function CodeAreaComponent({
setMyValue(t);
onChange(t);
}}
/>
);
}}
>
<div className="flex w-full items-center">
<span
className={
editNode
? "input-edit-node input-dialog"
@ -54,21 +48,6 @@ export default function CodeAreaComponent({
>
{myValue !== "" ? myValue : "Type something..."}
</span>
<button
onClick={() => {
openPopUp(
<CodeAreaModal
setNodeClass={setNodeClass}
value={myValue}
nodeClass={nodeClass}
setValue={(t: string) => {
setMyValue(t);
onChange(t);
}}
/>
);
}}
>
{!editNode && (
<IconComponent
name="ExternalLink"
@ -78,8 +57,8 @@ export default function CodeAreaComponent({
}
/>
)}
</button>
</div>
</CodeAreaModal>
</div>
);
}

View file

@ -0,0 +1,667 @@
import { cloneDeep } from "lodash";
import { useContext, useEffect, useState } from "react";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
import AccordionComponent from "../../components/AccordionComponent";
import ShadTooltip from "../../components/ShadTooltipComponent";
import CodeAreaComponent from "../../components/codeAreaComponent";
import Dropdown from "../../components/dropdownComponent";
import FloatComponent from "../../components/floatComponent";
import InputComponent from "../../components/inputComponent";
import InputFileComponent from "../../components/inputFileComponent";
import InputListComponent from "../../components/inputListComponent";
import IntComponent from "../../components/intComponent";
import PromptAreaComponent from "../../components/promptComponent";
import TextAreaComponent from "../../components/textAreaComponent";
import ToggleShadComponent from "../../components/toggleShadComponent";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "../../components/ui/table";
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "../../components/ui/tabs";
import { darkContext } from "../../contexts/darkContext";
import { FlowType } from "../../types/flow/index";
import { classNames } from "../../utils/utils";
import IconComponent from "../genericIconComponent";
export default function CodeTabsComponent({
flow,
tabs,
activeTab,
setActiveTab,
isMessage,
tweaks,
}: {
flow?: FlowType;
tabs: any;
activeTab: string;
setActiveTab: any;
isMessage?: boolean;
tweaks?: {
tweak?: any;
tweaksList?: any;
buildContent?: any;
getValue?: any;
buildTweakObject?: any;
};
}) {
const [isCopied, setIsCopied] = useState<Boolean>(false);
const [data, setData] = useState(flow ? flow["data"]["nodes"] : null);
const [openAccordion, setOpenAccordion] = useState([]);
const { dark } = useContext(darkContext);
useEffect(() => {
if (flow && flow["data"]["nodes"]) {
setData(flow["data"]["nodes"]);
}
}, [flow]);
const copyToClipboard = () => {
if (!navigator.clipboard || !navigator.clipboard.writeText) {
return;
}
navigator.clipboard.writeText(tabs[activeTab].code).then(() => {
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 2000);
});
};
const downloadAsFile = () => {
const fileExtension = tabs[activeTab].language || ".txt";
const suggestedFileName = `${"generated-code."}${fileExtension}`;
const fileName = window.prompt("Enter the file name.", suggestedFileName);
if (!fileName) {
// user pressed cancel on prompt
return;
}
const blob = new Blob([tabs[activeTab].code], { 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);
};
function openAccordions() {
let accordionsToOpen = [];
tweaks.tweak.current.forEach((el) => {
Object.keys(el).forEach((key) => {
if (Object.keys(el[key]).length > 0) {
accordionsToOpen.push(key);
setOpenAccordion(accordionsToOpen);
}
});
});
if (accordionsToOpen.length == 0) {
setOpenAccordion([]);
}
}
return (
<Tabs
value={activeTab}
className={
"api-modal-tabs " +
(isMessage ? "dark " : "") +
(dark && isMessage ? "bg-background" : "")
}
onValueChange={(value) => {
setActiveTab(value);
if (value === "3") {
openAccordions();
}
}}
>
<div className="api-modal-tablist-div">
<TabsList>
{tabs.map((tab, index) => (
<TabsTrigger
className={
isMessage ? "data-[state=active]:bg-primary-foreground" : ""
}
key={index}
value={index.toString()}
>
{tab.name}
</TabsTrigger>
))}
</TabsList>
{Number(activeTab) < 3 && (
<div className="float-right mx-1 flex gap-2">
<button
className="flex items-center gap-1.5 rounded bg-none p-1 text-xs text-gray-500 dark:text-gray-300"
onClick={copyToClipboard}
>
{isCopied ? (
<IconComponent name="Check" className="h-4 w-4" />
) : (
<IconComponent name="Clipboard" className="h-4 w-4" />
)}
{isCopied ? "Copied!" : "Copy code"}
</button>
<button
className="flex items-center gap-1.5 rounded bg-none p-1 text-xs text-gray-500 dark:text-gray-300"
onClick={downloadAsFile}
>
<IconComponent name="Download" className="h-5 w-5" />
</button>
</div>
)}
</div>
{tabs.map((tab, index) => (
<TabsContent
value={index.toString()}
className="api-modal-tabs-content"
key={index} // Remember to add a unique key prop
>
{index < 3 ? (
<SyntaxHighlighter
className="mt-0 h-full w-full overflow-auto custom-scroll"
language={tab.mode}
style={oneDark}
>
{tab.code}
</SyntaxHighlighter>
) : index === 3 ? (
<>
<div className="api-modal-according-display">
<div
className={classNames(
"h-[70vh] w-full rounded-lg bg-muted",
1 == 1
? "overflow-scroll overflow-x-hidden custom-scroll"
: "overflow-hidden"
)}
>
{data.map((t: any, index) => (
<div className="px-3" key={index}>
{tweaks.tweaksList.current.includes(t["data"]["id"]) && (
<AccordionComponent
trigger={t["data"]["id"]}
open={openAccordion}
keyValue={t["data"]["id"]}
>
<div className="api-modal-table-arrangement">
<Table className="table-fixed bg-muted outline-1">
<TableHeader className="h-10 border-input text-xs font-medium text-ring">
<TableRow className="dark:border-b-muted">
<TableHead className="h-7 text-center">
PARAM
</TableHead>
<TableHead className="h-7 p-0 text-center">
VALUE
</TableHead>
</TableRow>
</TableHeader>
<TableBody className="p-0">
{Object.keys(t["data"]["node"]["template"])
.filter(
(n) =>
n.charAt(0) !== "_" &&
t.data.node.template[n].show &&
(t.data.node.template[n].type === "str" ||
t.data.node.template[n].type ===
"bool" ||
t.data.node.template[n].type ===
"float" ||
t.data.node.template[n].type ===
"code" ||
t.data.node.template[n].type ===
"prompt" ||
t.data.node.template[n].type ===
"file" ||
t.data.node.template[n].type === "int")
)
.map((n, i) => {
return (
<TableRow
key={i}
className="h-10 dark:border-b-muted"
>
<TableCell className="p-0 text-center text-sm text-foreground">
{n}
</TableCell>
<TableCell className="p-0 text-xs text-foreground">
<div className="m-auto w-[250px]">
{t.data.node.template[n].type ===
"str" &&
!t.data.node.template[n].options ? (
<div className="mx-auto">
{t.data.node.template[n]
.list ? (
<InputListComponent
editNode={true}
disabled={false}
value={
!t.data.node.template[n]
.value ||
t.data.node.template[n]
.value === ""
? [""]
: t.data.node.template[
n
].value
}
onChange={(k) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
return newInputList;
});
tweaks.buildTweakObject(
t["data"]["id"],
k,
t.data.node.template[n]
);
}}
/>
) : t.data.node.template[n]
.multiline ? (
<ShadTooltip
content={tweaks.buildContent(
t.data.node.template[n]
.value
)}
>
<div>
<TextAreaComponent
disabled={false}
editNode={true}
value={
!t.data.node.template[
n
].value ||
t.data.node.template[
n
].value === ""
? ""
: t.data.node
.template[n]
.value
}
onChange={(k) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
return newInputList;
});
tweaks.buildTweakObject(
t["data"]["id"],
k,
t.data.node
.template[n]
);
}}
/>
</div>
</ShadTooltip>
) : (
<InputComponent
editNode={true}
disabled={false}
password={
t.data.node.template[n]
.password ?? false
}
value={
!t.data.node.template[n]
.value ||
t.data.node.template[n]
.value === ""
? ""
: t.data.node.template[
n
].value
}
onChange={(k) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
return newInputList;
});
tweaks.buildTweakObject(
t["data"]["id"],
k,
t.data.node.template[n]
);
}}
/>
)}
</div>
) : t.data.node.template[n].type ===
"bool" ? (
<div className="ml-auto">
{" "}
<ToggleShadComponent
enabled={
t.data.node.template[n]
.value
}
setEnabled={(e) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = e;
return newInputList;
});
tweaks.buildTweakObject(
t["data"]["id"],
e,
t.data.node.template[n]
);
}}
size="small"
disabled={false}
/>
</div>
) : t.data.node.template[n].type ===
"file" ? (
<ShadTooltip
content={tweaks.buildContent(
!t.data.node.template[n]
.value ||
t.data.node.template[n]
.value === ""
? ""
: t.data.node.template[n]
.value
)}
>
<div className="mx-auto">
<InputFileComponent
editNode={true}
disabled={false}
value={
t.data.node.template[n]
.value ?? ""
}
onChange={(k: any) => {}}
fileTypes={
t.data.node.template[n]
.fileTypes
}
suffixes={
t.data.node.template[n]
.suffixes
}
onFileChange={(
k: any
) => {}}
></InputFileComponent>
</div>
</ShadTooltip>
) : t.data.node.template[n].type ===
"float" ? (
<div className="mx-auto">
<FloatComponent
disabled={false}
editNode={true}
value={
!t.data.node.template[n]
.value ||
t.data.node.template[n]
.value === ""
? ""
: t.data.node.template[n]
.value
}
onChange={(k) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
return newInputList;
});
tweaks.buildTweakObject(
t["data"]["id"],
k,
t.data.node.template[n]
);
}}
/>
</div>
) : t.data.node.template[n].type ===
"str" &&
t.data.node.template[n]
.options ? (
<div className="mx-auto">
<Dropdown
editNode={true}
apiModal={true}
options={
t.data.node.template[n]
.options
}
onSelect={(k) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
return newInputList;
});
tweaks.buildTweakObject(
t["data"]["id"],
k,
t.data.node.template[n]
);
}}
value={
!t.data.node.template[n]
.value ||
t.data.node.template[n]
.value === ""
? ""
: t.data.node.template[n]
.value
}
></Dropdown>
</div>
) : t.data.node.template[n].type ===
"int" ? (
<div className="mx-auto">
<IntComponent
disabled={false}
editNode={true}
value={
!t.data.node.template[n]
.value ||
t.data.node.template[n]
.value === ""
? ""
: t.data.node.template[n]
.value
}
onChange={(k) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
return newInputList;
});
tweaks.buildTweakObject(
t["data"]["id"],
k,
t.data.node.template[n]
);
}}
/>
</div>
) : t.data.node.template[n].type ===
"prompt" ? (
<ShadTooltip
content={tweaks.buildContent(
!t.data.node.template[n]
.value ||
t.data.node.template[n]
.value === ""
? ""
: t.data.node.template[n]
.value
)}
>
<div className="mx-auto">
<PromptAreaComponent
editNode={true}
disabled={false}
value={
!t.data.node.template[n]
.value ||
t.data.node.template[n]
.value === ""
? ""
: t.data.node.template[
n
].value
}
onChange={(k) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
return newInputList;
});
tweaks.buildTweakObject(
t["data"]["id"],
k,
t.data.node.template[n]
);
}}
/>
</div>
</ShadTooltip>
) : t.data.node.template[n].type ===
"code" ? (
<ShadTooltip
content={tweaks.buildContent(
tweaks.getValue(
t.data.node.template[n]
.value,
t.data,
t.data.node.template[n]
)
)}
>
<div className="mx-auto">
<CodeAreaComponent
disabled={false}
editNode={true}
value={
!t.data.node.template[n]
.value ||
t.data.node.template[n]
.value === ""
? ""
: t.data.node.template[
n
].value
}
onChange={(k) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
return newInputList;
});
tweaks.buildTweakObject(
t["data"]["id"],
k,
t.data.node.template[n]
);
}}
/>
</div>
</ShadTooltip>
) : t.data.node.template[n].type ===
"Any" ? (
"-"
) : (
<div className="hidden"></div>
)}
</div>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
</AccordionComponent>
)}
{tweaks.tweaksList.current.length === 0 && (
<>
<div className="pt-3">
No tweaks are available for this flow.
</div>
</>
)}
</div>
))}
</div>
</div>
</>
) : null}
</TabsContent>
))}
</Tabs>
);
}

View file

@ -1,6 +1,5 @@
import { Listbox, Transition } from "@headlessui/react";
import { Fragment, useContext, useEffect, useState } from "react";
import { PopUpContext } from "../../contexts/popUpContext";
import { Fragment, useEffect, useState } from "react";
import { DropDownComponentType } from "../../types/components";
import { classNames } from "../../utils/utils";
import IconComponent from "../genericIconComponent";
@ -13,15 +12,13 @@ export default function Dropdown({
numberOfOptions = 0,
apiModal = false,
}: DropDownComponentType) {
const { closePopUp } = useContext(PopUpContext);
let [internalValue, setInternalValue] = useState(
value === "" || !value ? "Choose an option" : value
);
useEffect(() => {
setInternalValue(value === "" || !value ? "Choose an option" : value);
}, [closePopUp]);
}, [value]);
return (
<>

View file

@ -1,19 +1,12 @@
import { useContext, useEffect, useState } from "react";
import { PopUpContext } from "../../contexts/popUpContext";
import { TabsContext } from "../../contexts/tabsContext";
import { useEffect } from "react";
import { FloatComponentType } from "../../types/components";
export default function FloatComponent({
value,
onChange,
disableCopyPaste = false,
disabled,
editNode = false,
}: FloatComponentType) {
const [myValue, setMyValue] = useState(value ?? "");
const { setDisableCopyPaste } = useContext(TabsContext);
const { closePopUp } = useContext(PopUpContext);
const step = 0.1;
const min = 0;
const max = 1;
@ -21,24 +14,13 @@ export default function FloatComponent({
// Clear component state
useEffect(() => {
if (disabled) {
setMyValue("");
onChange("");
}
}, [disabled, onChange]);
useEffect(() => {
setMyValue(value);
}, [closePopUp]);
return (
<div className={"w-full " + (disabled ? "float-component-pointer" : "")}>
<input
onFocus={() => {
if (disableCopyPaste) setDisableCopyPaste(true);
}}
onBlur={() => {
if (disableCopyPaste) setDisableCopyPaste(false);
}}
type="number"
step={step}
min={min}
@ -51,17 +33,17 @@ export default function FloatComponent({
}
}}
max={max}
value={myValue}
value={value ?? ""}
className={
editNode
"nopan nodrag noundo nocopy " +
(editNode
? "input-edit-node"
: "input-primary" + (disabled ? " input-disable " : "")
: "input-primary" + (disabled ? " input-disable " : ""))
}
placeholder={
editNode ? "Number 0 to 1" : "Type a number from zero to one"
}
onChange={(e) => {
setMyValue(e.target.value);
onChange(e.target.value);
}}
/>

View file

@ -1,5 +1,4 @@
import { useContext } from "react";
import { PopUpContext } from "../../../../contexts/popUpContext";
import { useContext, useState } from "react";
import { TabsContext } from "../../../../contexts/tabsContext";
import {
DropdownMenu,
@ -17,10 +16,10 @@ import IconComponent from "../../../genericIconComponent";
import { Button } from "../../../ui/button";
export const MenuBar = ({ flows, tabId }) => {
const { updateFlow, setTabId, addFlow } = useContext(TabsContext);
const { addFlow } = useContext(TabsContext);
const { setErrorData } = useContext(alertContext);
const { openPopUp } = useContext(PopUpContext);
const { undo, redo } = useContext(undoRedoContext);
const [openSettings, setOpenSettings] = useState(false);
const navigate = useNavigate();
@ -62,9 +61,10 @@ export const MenuBar = ({ flows, tabId }) => {
<IconComponent name="Plus" className="header-menu-options" />
New
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
openPopUp(<FlowSettingsModal />);
setOpenSettings(true);
}}
className="cursor-pointer"
>
@ -74,6 +74,7 @@ export const MenuBar = ({ flows, tabId }) => {
/>
Settings
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
undo();
@ -94,6 +95,10 @@ export const MenuBar = ({ flows, tabId }) => {
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<FlowSettingsModal
open={openSettings}
setOpen={setOpenSettings}
></FlowSettingsModal>
</div>
</div>
);

View file

@ -1,56 +1,37 @@
import { useContext, useEffect, useState } from "react";
import { PopUpContext } from "../../contexts/popUpContext";
import { TabsContext } from "../../contexts/tabsContext";
import { useEffect, useState } from "react";
import { InputComponentType } from "../../types/components";
import { classNames } from "../../utils/utils";
export default function InputComponent({
value,
onChange,
disableCopyPaste = false,
disabled,
password,
editNode = false,
}: InputComponentType) {
const [myValue, setMyValue] = useState(value ?? "");
const [pwdVisible, setPwdVisible] = useState(false);
const { setDisableCopyPaste } = useContext(TabsContext);
const { closePopUp } = useContext(PopUpContext);
// Clear component state
useEffect(() => {
if (disabled) {
setMyValue("");
onChange("");
}
}, [disabled, onChange]);
useEffect(() => {
setMyValue(value ?? "");
}, [closePopUp]);
return (
<div className={disabled ? "input-component-div" : "relative"}>
<input
value={myValue}
onFocus={() => {
if (disableCopyPaste) setDisableCopyPaste(true);
}}
onBlur={() => {
if (disableCopyPaste) setDisableCopyPaste(false);
}}
value={value}
className={classNames(
disabled ? " input-disable " : "",
password && !pwdVisible && myValue !== ""
? " text-clip password "
: "",
password && !pwdVisible && value !== "" ? " text-clip password " : "",
editNode ? " input-edit-node " : " input-primary ",
password && editNode ? "pr-8" : "",
password && !editNode ? "pr-10" : ""
password && !editNode ? "pr-10" : "",
"nopan nodrag noundo nocopy"
)}
placeholder={password && editNode ? "Key" : "Type something..."}
onChange={(e) => {
setMyValue(e.target.value);
onChange(e.target.value);
}}
/>

View file

@ -1,8 +1,7 @@
import { useContext, useEffect, useState } from "react";
import { useEffect } from "react";
import { InputListComponentType } from "../../types/components";
import _ from "lodash";
import { PopUpContext } from "../../contexts/popUpContext";
import IconComponent from "../genericIconComponent";
export default function InputListComponent({
@ -10,21 +9,12 @@ export default function InputListComponent({
onChange,
disabled,
editNode = false,
onAddInput,
}: InputListComponentType) {
const [inputList, setInputList] = useState(value ?? [""]);
const { closePopUp } = useContext(PopUpContext);
useEffect(() => {
if (disabled) {
setInputList([""]);
onChange([""]);
}
}, [disabled, onChange]);
useEffect(() => {
setInputList(value);
}, [closePopUp]);
}, [disabled]);
return (
<div
@ -33,36 +23,31 @@ export default function InputListComponent({
"flex flex-col gap-3"
}
>
{inputList.map((i, idx) => {
{value.map((i, idx) => {
return (
<div key={idx} className="flex w-full gap-3">
<input
type="text"
value={i}
className={
editNode
"nopan nodrag noundo nocopy " +
(editNode
? "input-edit-node "
: "input-primary " + (disabled ? "input-disable" : "")
: "input-primary " + (disabled ? "input-disable" : ""))
}
placeholder="Type something..."
onChange={(e) => {
setInputList((old) => {
let newInputList = _.cloneDeep(old);
let newInputList = _.cloneDeep(value);
newInputList[idx] = e.target.value;
return newInputList;
});
onChange(inputList);
onChange(newInputList);
}}
/>
{idx === inputList.length - 1 ? (
{idx === value.length - 1 ? (
<button
onClick={() => {
setInputList((old) => {
let newInputList = _.cloneDeep(old);
let newInputList = _.cloneDeep(value);
newInputList.push("");
return newInputList;
});
onChange(inputList);
onChange(newInputList);
}}
>
<IconComponent
@ -73,12 +58,9 @@ export default function InputListComponent({
) : (
<button
onClick={() => {
setInputList((old) => {
let newInputList = _.cloneDeep(old);
let newInputList = _.cloneDeep(value);
newInputList.splice(idx, 1);
return newInputList;
});
onChange(inputList);
onChange(newInputList);
}}
>
<IconComponent

View file

@ -1,32 +1,21 @@
import { useContext, useEffect, useState } from "react";
import { PopUpContext } from "../../contexts/popUpContext";
import { TabsContext } from "../../contexts/tabsContext";
import { useEffect } from "react";
import { FloatComponentType } from "../../types/components";
export default function IntComponent({
value,
onChange,
disableCopyPaste = false,
disabled,
editNode = false,
}: FloatComponentType) {
const [myValue, setMyValue] = useState(value ?? "");
const { setDisableCopyPaste } = useContext(TabsContext);
const min = 0;
const { closePopUp } = useContext(PopUpContext);
// Clear component state
useEffect(() => {
if (disabled) {
setMyValue("");
onChange("");
}
}, [disabled, onChange]);
useEffect(() => {
setMyValue(value);
}, [closePopUp]);
return (
<div
className={
@ -35,12 +24,6 @@ export default function IntComponent({
}
>
<input
onFocus={() => {
if (disableCopyPaste) setDisableCopyPaste(true);
}}
onBlur={() => {
if (disableCopyPaste) setDisableCopyPaste(false);
}}
onKeyDown={(event) => {
if (
event.key !== "Backspace" &&
@ -67,15 +50,15 @@ export default function IntComponent({
e.target.value = min.toString();
}
}}
value={myValue}
value={value ?? ""}
className={
editNode
"nopan nodrag noundo nocopy " +
(editNode
? " input-edit-node "
: " input-primary " + (disabled ? " input-disable" : "")
: " input-primary " + (disabled ? " input-disable" : ""))
}
placeholder={editNode ? "Integer number" : "Type an integer number"}
onChange={(e) => {
setMyValue(e.target.value);
onChange(e.target.value);
}}
/>

View file

@ -1,8 +1,6 @@
import { useContext, useEffect, useState } from "react";
import { useEffect } from "react";
import { TypeModal } from "../../constants/enums";
import { PopUpContext } from "../../contexts/popUpContext";
import { typesContext } from "../../contexts/typesContext";
import { postValidatePrompt } from "../../controllers/API";
import GenericModal from "../../modals/genericModal";
import { TextAreaComponentType } from "../../types/components";
@ -17,18 +15,13 @@ export default function PromptAreaComponent({
disabled,
editNode = false,
}: TextAreaComponentType) {
const [myValue, setMyValue] = useState(value);
const { openPopUp } = useContext(PopUpContext);
const { reactFlowInstance } = useContext(typesContext);
useEffect(() => {
if (disabled) {
setMyValue("");
onChange("");
}
}, [disabled, onChange]);
}, [disabled]);
useEffect(() => {
setMyValue(value);
if (value !== "" && !editNode) {
postValidatePrompt(field_name, value, nodeClass).then((apiReturn) => {
if (apiReturn.data) {
@ -37,28 +30,23 @@ export default function PromptAreaComponent({
}
});
}
}, [value, reactFlowInstance]);
}, []);
return (
<div className={disabled ? "pointer-events-none w-full " : " w-full"}>
<div className="flex w-full items-center">
<span
onClick={() => {
openPopUp(
<GenericModal
type={TypeModal.PROMPT}
value={myValue}
value={value}
buttonText="Check & Save"
modalTitle="Edit Prompt"
setValue={(t: string) => {
setMyValue(t);
onChange(t);
}}
nodeClass={nodeClass}
setNodeClass={setNodeClass}
/>
);
}}
>
<div className="flex w-full items-center">
<span
className={
editNode
? "input-edit-node input-dialog"
@ -66,27 +54,8 @@ export default function PromptAreaComponent({
" input-primary text-muted-foreground "
}
>
{myValue !== "" ? myValue : "Type your prompt here"}
{value !== "" ? value : "Type your prompt here..."}
</span>
<button
onClick={() => {
openPopUp(
<GenericModal
field_name={field_name}
type={TypeModal.PROMPT}
value={myValue}
buttonText="Check & Save"
modalTitle="Edit Prompt"
setValue={(t: string) => {
setMyValue(t);
onChange(t);
}}
nodeClass={nodeClass}
setNodeClass={setNodeClass}
/>
);
}}
>
{!editNode && (
<IconComponent
name="ExternalLink"
@ -96,8 +65,8 @@ export default function PromptAreaComponent({
}
/>
)}
</button>
</div>
</GenericModal>
</div>
);
}

View file

@ -1,7 +1,5 @@
import { useContext, useEffect, useState } from "react";
import { useEffect } from "react";
import { TypeModal } from "../../constants/enums";
import { PopUpContext } from "../../contexts/popUpContext";
import { TabsContext } from "../../contexts/tabsContext";
import GenericModal from "../../modals/genericModal";
import { TextAreaComponentType } from "../../types/components";
import IconComponent from "../genericIconComponent";
@ -12,60 +10,38 @@ export default function TextAreaComponent({
disabled,
editNode = false,
}: TextAreaComponentType) {
const [myValue, setMyValue] = useState(value);
const { openPopUp, closePopUp } = useContext(PopUpContext);
const { setDisableCopyPaste } = useContext(TabsContext);
// Clear text area
useEffect(() => {
if (disabled) {
setMyValue("");
onChange("");
}
}, [disabled, onChange]);
useEffect(() => {
setMyValue(value);
}, [closePopUp]);
}, [disabled]);
return (
<div className={disabled ? "pointer-events-none w-full " : " w-full"}>
<div className="flex w-full items-center">
<input
value={myValue}
onFocus={() => {
setDisableCopyPaste(true);
}}
onBlur={() => {
setDisableCopyPaste(false);
}}
value={value}
className={
editNode
(editNode
? " input-edit-node "
: " input-primary " + (disabled ? " input-disable" : "")
: " input-primary " + (disabled ? " input-disable" : "")) +
" nopan nodrag noundo nocopy w-full"
}
placeholder={"Type something..."}
onChange={(e) => {
setMyValue(e.target.value);
onChange(e.target.value);
}}
/>
<button
onClick={() => {
openPopUp(
<div>
<GenericModal
type={TypeModal.TEXT}
buttonText="Finishing Editing"
modalTitle="Edit Text"
value={myValue}
value={value}
setValue={(t: string) => {
setMyValue(t);
onChange(t);
}}
/>
);
}}
>
{!editNode && (
<IconComponent
@ -76,7 +52,8 @@ export default function TextAreaComponent({
}
/>
)}
</button>
</GenericModal>
</div>
</div>
</div>
);

View file

@ -25,6 +25,7 @@ export default function ToggleShadComponent({
scaleX = 1;
scaleY = 1;
}
return (
<div className={disabled ? "pointer-events-none cursor-not-allowed " : ""}>
<Switch

View file

@ -44,7 +44,7 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed z-50 grid w-full gap-3 rounded-b-lg border bg-background p-6 shadow-lg animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:max-w-lg sm:rounded-lg sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0",
"noundo nocopy fixed z-50 flex w-full flex-col gap-3 rounded-b-lg border bg-background p-6 shadow-lg animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:max-w-lg sm:rounded-lg sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0",
className
)}
{...props}
@ -116,10 +116,10 @@ DialogDescription.displayName = DialogPrimitive.Description.displayName;
export {
Dialog,
DialogTrigger,
DialogContent,
DialogHeader,
DialogFooter,
DialogTitle,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
};

View file

@ -10,7 +10,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
"nopan nodrag noundo nocopy flex h-10 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}

View file

@ -57,7 +57,7 @@ export default function RenameLabel(props) {
ref={inputRef}
onInput={resizeInput}
className={cn(
"rounded-md bg-transparent px-2 outline-ring hover:outline focus:border-none focus:outline active:outline",
"nopan nodrag noundo nocopy rounded-md bg-transparent px-2 outline-ring hover:outline focus:border-none focus:outline active:outline",
props.className
)}
onBlur={() => {

View file

@ -13,7 +13,7 @@ const TabsList = React.forwardRef<
<TabsPrimitive.List
ref={ref}
className={cn(
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
"inline-flex h-10 items-center justify-center rounded-md p-1 text-muted-foreground",
className
)}
{...props}
@ -51,4 +51,4 @@ const TabsContent = React.forwardRef<
));
TabsContent.displayName = TabsPrimitive.Content.displayName;
export { Tabs, TabsList, TabsTrigger, TabsContent };
export { Tabs, TabsContent, TabsList, TabsTrigger };

View file

@ -42,8 +42,6 @@ const TabsContextInitialValue: TabsContextType = {
uploadFlow: () => {},
hardReset: () => {},
saveFlow: async (flow: FlowType) => {},
disableCopyPaste: false,
setDisableCopyPaste: (state: boolean) => {},
lastCopiedSelection: null,
setLastCopiedSelection: (selection: any) => {},
tabsState: {},
@ -585,16 +583,12 @@ export function TabsProvider({ children }: { children: ReactNode }) {
}
}
const [disableCopyPaste, setDisableCopyPaste] = useState(false);
return (
<TabsContext.Provider
value={{
saveFlow,
lastCopiedSelection,
setLastCopiedSelection,
disableCopyPaste,
setDisableCopyPaste,
hardReset,
tabId,
setTabId,

View file

@ -7,6 +7,7 @@ import {
useState,
} from "react";
import { Edge, Node, useReactFlow } from "reactflow";
import { isWrappedWithClass } from "../utils/utils";
import { TabsContext } from "./tabsContext";
type undoRedoContextType = {
@ -20,14 +21,6 @@ type UseUndoRedoOptions = {
enableShortcuts: boolean;
};
type UseUndoRedo = (options?: UseUndoRedoOptions) => {
undo: () => void;
redo: () => void;
takeSnapshot: () => void;
canUndo: boolean;
canRedo: boolean;
};
type HistoryItem = {
nodes: Node[];
edges: Edge[];
@ -156,18 +149,22 @@ export function UndoRedoProvider({ children }) {
}
const keyDownHandler = (event: KeyboardEvent) => {
if (!isWrappedWithClass(event, "noundo")) {
if (
event.key === "z" &&
(event.ctrlKey || event.metaKey) &&
event.shiftKey
) {
event.preventDefault();
redo();
} else if (event.key === "y" && (event.ctrlKey || event.metaKey)) {
event.preventDefault(); // prevent the default action
redo();
} else if (event.key === "z" && (event.ctrlKey || event.metaKey)) {
event.preventDefault();
undo();
}
}
};
document.addEventListener("keydown", keyDownHandler);

View file

@ -145,6 +145,9 @@ code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
pre {
font-family: inherit;
}
/* The style below sets the cursor property of the element with the class .react-flow__pane to the default cursor.
The cursor: default; property value restores the browser's default cursor style for the targeted element. By applying this style, the element will no longer have a custom cursor appearance such as "grab" or any other custom cursor defined elsewhere in the application. Instead, it will revert to the default cursor style determined by the browser, typically an arrow-shaped cursor. */
@ -240,11 +243,11 @@ The cursor: default; property value restores the browser's default cursor style
@apply focus:placeholder-transparent focus:ring-ring focus:border-ring
}
.input-primary {
@apply bg-background block border-border form-input px-3 placeholder:text-muted-foreground rounded-md shadow-sm sm:text-sm truncate w-full;
@apply bg-background block text-left border-border form-input px-3 placeholder:text-muted-foreground rounded-md shadow-sm sm:text-sm truncate w-full;
}
.input-edit-node{
@apply input-primary border-border placeholder:text-center pt-0.5 pb-0.5 text-center
@apply input-primary border-border pt-0.5 pb-0.5 text-left w-full
}
.input-search{
@apply input-primary pr-7 mx-2
@ -547,7 +550,7 @@ The cursor: default; property value restores the browser's default cursor style
@apply z-10 mt-1 max-h-60 overflow-auto rounded-md bg-background py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm
}
.dropdown-component-true-options {
@apply dropdown-component-options w-[215px]
@apply dropdown-component-options lg:w-[32%]
}
.dropdown-component-false-options {
@apply dropdown-component-options w-full
@ -932,7 +935,7 @@ The cursor: default; property value restores the browser's default cursor style
}
.api-modal-tabs {
@apply w-full h-full overflow-hidden text-center bg-muted rounded-md border
@apply lg:w-full h-full flex flex-col overflow-hidden text-center bg-muted rounded-md border sm:w-[75vw]
}
.api-modal-tablist-div {
@apply flex items-center justify-between px-2

View file

@ -2,90 +2,54 @@ import "ace-builds/src-noconflict/ext-language_tools";
import "ace-builds/src-noconflict/mode-python";
import "ace-builds/src-noconflict/theme-github";
import "ace-builds/src-noconflict/theme-twilight";
import { Check, Clipboard } from "lucide-react";
import { useContext, useEffect, useRef, useState } from "react";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
import AccordionComponent from "../../components/AccordionComponent";
import ShadTooltip from "../../components/ShadTooltipComponent";
import CodeAreaComponent from "../../components/codeAreaComponent";
import Dropdown from "../../components/dropdownComponent";
import FloatComponent from "../../components/floatComponent";
import {
ReactNode,
forwardRef,
useContext,
useEffect,
useRef,
useState,
} from "react";
// import "ace-builds/webpack-resolver";
import CodeTabsComponent from "../../components/codeTabsComponent";
import IconComponent from "../../components/genericIconComponent";
import InputComponent from "../../components/inputComponent";
import InputFileComponent from "../../components/inputFileComponent";
import InputListComponent from "../../components/inputListComponent";
import IntComponent from "../../components/intComponent";
import PromptAreaComponent from "../../components/promptComponent";
import TextAreaComponent from "../../components/textAreaComponent";
import ToggleShadComponent from "../../components/toggleShadComponent";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "../../components/ui/dialog";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "../../components/ui/table";
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "../../components/ui/tabs";
import { EXPORT_CODE_DIALOG } from "../../constants/constants";
import { darkContext } from "../../contexts/darkContext";
import { PopUpContext } from "../../contexts/popUpContext";
import { TabsContext } from "../../contexts/tabsContext";
import { FlowType } from "../../types/flow/index";
import { buildTweaks } from "../../utils/reactflowUtils";
import {
classNames,
getCurlCode,
getPythonApiCode,
getPythonCode,
} from "../../utils/utils";
export default function ApiModal({ flow }: { flow: FlowType }) {
const [open, setOpen] = useState(true);
const { dark } = useContext(darkContext);
const { closePopUp, closeEdit, setCloseEdit } = useContext(PopUpContext);
import BaseModal from "../baseModal";
const ApiModal = forwardRef(
(
{
flow,
children,
}: {
flow: FlowType;
children: ReactNode;
},
ref
) => {
const [open, setOpen] = useState(false);
const [activeTab, setActiveTab] = useState("0");
const [isCopied, setIsCopied] = useState<Boolean>(false);
const [enabled, setEnabled] = useState(null);
const [openAccordion, setOpenAccordion] = useState([]);
const tweak = useRef([]);
const tweaksList = useRef([]);
const { setTweak, getTweak, tabsState } = useContext(TabsContext);
const copyToClipboard = () => {
if (!navigator.clipboard || !navigator.clipboard.writeText) {
return;
}
navigator.clipboard.writeText(tabs[activeTab].code).then(() => {
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 2000);
});
};
const pythonApiCode = getPythonApiCode(flow, tweak.current, tabsState);
const curl_code = getCurlCode(flow, tweak.current, tabsState);
const pythonCode = getPythonCode(flow, tweak.current, tabsState);
const tweaksCode = buildTweaks(flow);
const tabs = [
const [tabs, setTabs] = useState([
{
name: "cURL",
mode: "bash",
image: "https://curl.se/logo/curl-symbol-transparent.png",
language: "sh",
code: curl_code,
},
{
@ -93,58 +57,95 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
mode: "python",
image:
"https://images.squarespace-cdn.com/content/v1/5df3d8c5d2be5962e4f87890/1628015119369-OY4TV3XJJ53ECO0W2OLQ/Python+API+Training+Logo.png?format=1000w",
language: "py",
code: pythonApiCode,
},
{
name: "Python Code",
mode: "python",
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
language: "py",
code: pythonCode,
},
];
]);
function startState() {
tweak.current = [];
setTweak([]);
tweaksList.current = [];
}
useEffect(() => {
if (closeEdit !== "") {
tweak.current = getTweak;
if (tweak.current.length > 0) {
setActiveTab("3");
openAccordions();
if (flow["data"]["nodes"].length == 0) {
startState();
} else {
startTweaks();
tweak.current = [];
const t = buildTweaks(flow);
tweak.current.push(t);
}
} else {
if (tweak?.current) {
startTweaks();
}
}
}, [closeEdit]);
useEffect(() => {
filterNodes();
}, []);
if (Object.keys(tweaksCode).length > 0) {
tabs.push({
setActiveTab("0");
setTabs([
{
name: "cURL",
mode: "bash",
image: "https://curl.se/logo/curl-symbol-transparent.png",
language: "sh",
code: curl_code,
},
{
name: "Python API",
mode: "python",
image:
"https://images.squarespace-cdn.com/content/v1/5df3d8c5d2be5962e4f87890/1628015119369-OY4TV3XJJ53ECO0W2OLQ/Python+API+Training+Logo.png?format=1000w",
language: "py",
code: pythonApiCode,
},
{
name: "Python Code",
mode: "python",
language: "py",
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
code: pythonCode,
},
{
name: "Tweaks",
mode: "python",
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
language: "py",
code: pythonCode,
});
}
function setModalOpen(x: boolean) {
setOpen(x);
if (x === false) {
setCloseEdit("");
setTweak([]);
closePopUp();
}
}
function startTweaks() {
const t = buildTweaks(flow);
tweak?.current?.push(t);
},
]);
} else {
setTabs([
{
name: "cURL",
mode: "bash",
image: "https://curl.se/logo/curl-symbol-transparent.png",
language: "sh",
code: curl_code,
},
{
name: "Python API",
mode: "python",
image:
"https://images.squarespace-cdn.com/content/v1/5df3d8c5d2be5962e4f87890/1628015119369-OY4TV3XJJ53ECO0W2OLQ/Python+API+Training+Logo.png?format=1000w",
language: "py",
code: pythonApiCode,
},
{
name: "Python Code",
mode: "python",
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
language: "py",
code: pythonCode,
},
]);
}
}, [flow["data"]["nodes"], open]);
function filterNodes() {
let arrNodesWithValues = [];
@ -172,7 +173,6 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
return self.indexOf(value) === index;
});
}
function buildTweakObject(tw, changes, template) {
if (template.type === "float") {
changes = parseFloat(changes);
@ -252,482 +252,35 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
return returnValue;
}
function openAccordions() {
let accordionsToOpen = [];
tweak.current.forEach((el) => {
Object.keys(el).forEach((key) => {
if (Object.keys(el[key]).length > 0) {
accordionsToOpen.push(key);
setOpenAccordion(accordionsToOpen);
}
});
});
}
return (
<Dialog open={true} onOpenChange={setModalOpen}>
<DialogTrigger></DialogTrigger>
<DialogContent className="h-[80vh] md:max-w-[80vw]">
<DialogHeader>
<DialogTitle className="flex items-center">
<BaseModal open={open} setOpen={setOpen}>
<BaseModal.Trigger>{children}</BaseModal.Trigger>
<BaseModal.Header description={EXPORT_CODE_DIALOG}>
<span className="pr-2">Code</span>
<IconComponent
name="Code2"
className="h-6 w-6 pl-1 text-gray-800 dark:text-white"
aria-hidden="true"
/>
</DialogTitle>
<DialogDescription>{EXPORT_CODE_DIALOG}</DialogDescription>
</DialogHeader>
</BaseModal.Header>
<BaseModal.Content>
<CodeTabsComponent
flow={flow}
tabs={tabs}
activeTab={activeTab}
setActiveTab={setActiveTab}
tweaks={{
tweak,
tweaksList,
buildContent,
buildTweakObject,
getValue,
}}
/>
</BaseModal.Content>
</BaseModal>
);
}
);
<Tabs
value={activeTab}
className="api-modal-tabs"
onValueChange={(value) => {
setActiveTab(value);
if (value === "3") {
openAccordions();
}
}}
>
<div className="api-modal-tablist-div">
<TabsList>
{tabs.map((tab, index) => (
<TabsTrigger key={index} value={index.toString()}>
{tab.name}
</TabsTrigger>
))}
</TabsList>
{Number(activeTab) < 3 && (
<div className="float-right">
<button
className="flex items-center gap-1.5 rounded bg-none p-1 text-xs text-gray-500 dark:text-gray-300"
onClick={copyToClipboard}
>
{isCopied ? <Check size={18} /> : <Clipboard size={15} />}
{isCopied ? "Copied!" : "Copy code"}
</button>
</div>
)}
</div>
{tabs.map((tab, index) => (
<TabsContent
value={index.toString()}
className="api-modal-tabs-content"
key={index} // Remember to add a unique key prop
>
{index < 3 ? (
<SyntaxHighlighter
className="h-[60vh] w-full overflow-auto custom-scroll"
language={tab.mode}
style={oneDark}
>
{tab.code}
</SyntaxHighlighter>
) : index === 3 ? (
<>
<div className="api-modal-according-display">
<div
className={classNames(
"h-[60vh] w-full rounded-lg bg-muted",
1 == 1
? "overflow-scroll overflow-x-hidden custom-scroll"
: "overflow-hidden"
)}
>
{flow["data"]["nodes"].map((t: any, index) => (
<div className="px-3" key={index}>
{tweaksList.current.includes(t["data"]["id"]) && (
<AccordionComponent
trigger={t["data"]["id"]}
keyValue={t["data"]["id"]}
open={openAccordion}
>
<div className="api-modal-table-arrangement">
<Table className="table-fixed bg-muted outline-1">
<TableHeader className="h-10 border-input text-xs font-medium text-ring">
<TableRow className="dark:border-b-muted">
<TableHead className="h-7 text-center">
PARAM
</TableHead>
<TableHead className="h-7 p-0 text-center">
VALUE
</TableHead>
</TableRow>
</TableHeader>
<TableBody className="p-0">
{Object.keys(t["data"]["node"]["template"])
.filter(
(n) =>
n.charAt(0) !== "_" &&
t.data.node.template[n].show &&
(t.data.node.template[n].type ===
"str" ||
t.data.node.template[n].type ===
"bool" ||
t.data.node.template[n].type ===
"float" ||
t.data.node.template[n].type ===
"code" ||
t.data.node.template[n].type ===
"prompt" ||
t.data.node.template[n].type ===
"file" ||
t.data.node.template[n].type ===
"int")
)
.map((n, i) => {
return (
<TableRow
key={i}
className="h-10 dark:border-b-muted"
>
<TableCell className="p-0 text-center text-sm text-foreground">
{n}
</TableCell>
<TableCell className="p-0 text-xs text-foreground">
<div className="m-auto w-[250px]">
{t.data.node.template[n]
.type === "str" &&
!t.data.node.template[n]
.options ? (
<div className="mx-auto">
{t.data.node.template[n]
.list ? (
<InputListComponent
editNode={true}
disabled={false}
value={
!t.data.node.template[
n
].value ||
t.data.node.template[
n
].value === ""
? [""]
: t.data.node
.template[n]
.value
}
onChange={(k) => {}}
onAddInput={(k) => {
buildTweakObject(
t["data"]["id"],
k,
t.data.node
.template[n]
);
}}
/>
) : t.data.node.template[n]
.multiline ? (
<ShadTooltip
content={buildContent(
t.data.node.template[
n
].value
)}
>
<div>
<TextAreaComponent
disabled={false}
editNode={true}
value={getValue(
t.data.node
.template[n]
.value,
t.data,
t.data.node
.template[n]
)}
onChange={(k) => {
buildTweakObject(
t["data"]["id"],
k,
t.data.node
.template[n]
);
}}
/>
</div>
</ShadTooltip>
) : (
<InputComponent
editNode={true}
disabled={false}
password={
t.data.node.template[
n
].password ?? false
}
value={getValue(
t.data.node.template[
n
].value,
t.data,
t.data.node.template[
n
]
)}
onChange={(k) => {
buildTweakObject(
t["data"]["id"],
k,
t.data.node
.template[n]
);
}}
/>
)}
</div>
) : t.data.node.template[n]
.type === "bool" ? (
<div className="ml-auto">
{" "}
<ToggleShadComponent
enabled={
t.data.node.template[n]
.value
}
setEnabled={(e) => {
t.data.node.template[
n
].value = e;
setEnabled(e);
buildTweakObject(
t["data"]["id"],
e,
t.data.node.template[
n
]
);
}}
size="small"
disabled={false}
/>
</div>
) : t.data.node.template[n]
.type === "file" ? (
<ShadTooltip
content={buildContent(
getValue(
t.data.node.template[n]
.value,
t.data,
t.data.node.template[n]
)
)}
>
<div className="mx-auto">
<InputFileComponent
editNode={true}
disabled={false}
value={
t.data.node.template[
n
].value ?? ""
}
onChange={(
k: any
) => {}}
fileTypes={
t.data.node.template[
n
].fileTypes
}
suffixes={
t.data.node.template[
n
].suffixes
}
onFileChange={(
k: any
) => {}}
></InputFileComponent>
</div>
</ShadTooltip>
) : t.data.node.template[n]
.type === "float" ? (
<div className="mx-auto">
<FloatComponent
disabled={false}
editNode={true}
value={getValue(
t.data.node.template[n]
.value,
t.data,
t.data.node.template[n]
)}
onChange={(k) => {
buildTweakObject(
t["data"]["id"],
k,
t.data.node.template[
n
]
);
}}
/>
</div>
) : t.data.node.template[n]
.type === "str" &&
t.data.node.template[n]
.options ? (
<div className="mx-auto">
<Dropdown
editNode={true}
apiModal={true}
options={
t.data.node.template[n]
.options
}
onSelect={(k) => {
buildTweakObject(
t["data"]["id"],
k,
t.data.node.template[
n
]
);
}}
value={getValue(
t.data.node.template[n]
.value,
t.data,
t.data.node.template[n]
)}
></Dropdown>
</div>
) : t.data.node.template[n]
.type === "int" ? (
<div className="mx-auto">
<IntComponent
disabled={false}
editNode={true}
value={getValue(
t.data.node.template[n]
.value,
t.data,
t.data.node.template[n]
)}
onChange={(k) => {
buildTweakObject(
t["data"]["id"],
k,
t.data.node.template[
n
]
);
}}
/>
</div>
) : t.data.node.template[n]
.type === "prompt" ? (
<ShadTooltip
content={buildContent(
getValue(
t.data.node.template[n]
.value,
t.data,
t.data.node.template[n]
)
)}
>
<div className="mx-auto">
<PromptAreaComponent
editNode={true}
disabled={false}
value={getValue(
t.data.node.template[
n
].value,
t.data,
t.data.node.template[
n
]
)}
onChange={(k) => {
buildTweakObject(
t["data"]["id"],
k,
t.data.node
.template[n]
);
}}
/>
</div>
</ShadTooltip>
) : t.data.node.template[n]
.type === "code" ? (
<ShadTooltip
content={buildContent(
getValue(
t.data.node.template[n]
.value,
t.data,
t.data.node.template[n]
)
)}
>
<div className="mx-auto">
<CodeAreaComponent
disabled={false}
editNode={true}
value={getValue(
t.data.node.template[
n
].value,
t.data,
t.data.node.template[
n
]
)}
onChange={(k) => {
buildTweakObject(
t["data"]["id"],
k,
t.data.node
.template[n]
);
}}
/>
</div>
</ShadTooltip>
) : t.data.node.template[n]
.type === "Any" ? (
"-"
) : (
<div className="hidden"></div>
)}
</div>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
</AccordionComponent>
)}
{tweaksList.current.length === 0 && (
<>
<div className="pt-3">
No tweaks are available for this flow.
</div>
</>
)}
</div>
))}
</div>
</div>
</>
) : null}
</TabsContent>
))}
</Tabs>
</DialogContent>
</Dialog>
);
}
export default ApiModal;

View file

@ -1,4 +1,5 @@
import { useContext, useRef, useState } from "react";
import { cloneDeep } from "lodash";
import { ReactNode, forwardRef, useContext, useEffect, useState } from "react";
import CodeAreaComponent from "../../components/codeAreaComponent";
import Dropdown from "../../components/dropdownComponent";
import FloatComponent from "../../components/floatComponent";
@ -12,15 +13,6 @@ import TextAreaComponent from "../../components/textAreaComponent";
import ToggleShadComponent from "../../components/toggleShadComponent";
import { Badge } from "../../components/ui/badge";
import { Button } from "../../components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "../../components/ui/dialog";
import {
Table,
TableBody,
@ -30,95 +22,71 @@ import {
TableRow,
} from "../../components/ui/table";
import { limitScrollFieldsModal } from "../../constants/constants";
import { PopUpContext } from "../../contexts/popUpContext";
import { TabsContext } from "../../contexts/tabsContext";
import { typesContext } from "../../contexts/typesContext";
import { NodeDataType } from "../../types/flow";
import { classNames } from "../../utils/utils";
import BaseModal from "../baseModal";
export default function EditNodeModal({ data }: { data: NodeDataType }) {
const [open, setOpen] = useState(true);
const [nodeLength, setNodeLength] = useState(
Object.keys(data.node.template).filter(
(t) =>
t.charAt(0) !== "_" &&
data.node.template[t].show &&
(data.node.template[t].type === "str" ||
data.node.template[t].type === "bool" ||
data.node.template[t].type === "float" ||
data.node.template[t].type === "code" ||
data.node.template[t].type === "prompt" ||
data.node.template[t].type === "file" ||
data.node.template[t].type === "int")
).length
);
const [nodeValue, setNodeValue] = useState(null);
const { closePopUp } = useContext(PopUpContext);
const { types } = useContext(typesContext);
const ref = useRef();
const EditNodeModal = forwardRef(
(
{
data,
setData,
nodeLength,
children,
}: {
data: NodeDataType;
setData: (data: NodeDataType) => void;
nodeLength: number;
children: ReactNode;
},
ref
) => {
const [modalOpen, setModalOpen] = useState(false);
const [myData, setMyData] = useState(data);
const { setTabsState, tabId } = useContext(TabsContext);
const { reactFlowInstance } = useContext(typesContext);
let disabled =
reactFlowInstance?.getEdges().some((e) => e.targetHandle === data.id) ??
false;
if (nodeLength == 0) {
closePopUp();
}
function setModalOpen(x: boolean) {
setOpen(x);
if (x === false) {
closePopUp();
}
}
function changeAdvanced(node) {
Object.keys(data.node.template).map((n, i) => {
if (n === node.name) {
data.node.template[n].advanced = !data.node.template[n].advanced;
}
return n;
function changeAdvanced(n) {
setMyData((old) => {
let newData = cloneDeep(old);
newData.node.template[n].advanced = !newData.node.template[n].advanced;
return newData;
});
setNodeValue(!nodeValue);
}
const handleOnNewValue = (newValue: any, name) => {
data.node.template[name].value = newValue;
// Set state to pending
setTabsState((prev) => {
return {
...prev,
[tabId]: {
...prev[tabId],
isPending: true,
},
};
setMyData((old) => {
let newData = cloneDeep(old);
newData.node.template[name].value = newValue;
return newData;
});
};
useEffect(() => {
setMyData(data); // reset data to what it is on node when opening modal
}, [modalOpen]);
return (
<Dialog open={true} onOpenChange={setModalOpen}>
<DialogTrigger asChild></DialogTrigger>
<DialogContent className="sm:max-w-[600px] lg:max-w-[700px]">
<DialogHeader>
<DialogTitle className="flex items-center">
<span className="pr-2">{data.type}</span>
<Badge variant="secondary">ID: {data.id}</Badge>
</DialogTitle>
<DialogDescription asChild>
<div>
{data.node?.description}
<div className="flex pt-3">
<BaseModal size="large-h-full" open={modalOpen} setOpen={setModalOpen}>
<BaseModal.Trigger>{children}</BaseModal.Trigger>
<BaseModal.Header description={myData.node?.description}>
<span className="pr-2">{myData.type}</span>
<Badge variant="secondary">ID: {myData.id}</Badge>
</BaseModal.Header>
<BaseModal.Content>
<div className="flex pb-2">
<IconComponent
name="Variable"
className="edit-node-modal-variable "
/>
<span className="edit-node-modal-span">Parameters</span>
</div>
</div>
</DialogDescription>
</DialogHeader>
<div className="edit-node-modal-arrangement">
<div
@ -142,49 +110,51 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
</TableRow>
</TableHeader>
<TableBody className="p-0">
{Object.keys(data.node.template)
{Object.keys(myData.node.template)
.filter(
(t) =>
t.charAt(0) !== "_" &&
data.node.template[t].show &&
(data.node.template[t].type === "str" ||
data.node.template[t].type === "bool" ||
data.node.template[t].type === "float" ||
data.node.template[t].type === "code" ||
data.node.template[t].type === "prompt" ||
data.node.template[t].type === "file" ||
data.node.template[t].type === "int")
myData.node.template[t].show &&
(myData.node.template[t].type === "str" ||
myData.node.template[t].type === "bool" ||
myData.node.template[t].type === "float" ||
myData.node.template[t].type === "code" ||
myData.node.template[t].type === "prompt" ||
myData.node.template[t].type === "file" ||
myData.node.template[t].type === "int")
)
.map((n, i) => (
<TableRow key={i} className="h-10">
<TableCell className="truncate p-0 text-center text-sm text-foreground sm:px-3">
{data.node.template[n].name
? data.node.template[n].name
: data.node.template[n].display_name}
{myData.node.template[n].name
? myData.node.template[n].name
: myData.node.template[n].display_name}
</TableCell>
<TableCell className="w-[300px] p-0 text-center text-xs text-foreground ">
{data.node.template[n].type === "str" &&
!data.node.template[n].options ? (
{myData.node.template[n].type === "str" &&
!myData.node.template[n].options ? (
<div className="mx-auto">
{data.node.template[n].list ? (
{myData.node.template[n].list ? (
<InputListComponent
editNode={true}
disabled={disabled}
value={
!data.node.template[n].value ||
data.node.template[n].value === ""
!myData.node.template[n].value ||
myData.node.template[n].value === ""
? [""]
: data.node.template[n].value
: myData.node.template[n].value
}
onChange={(t: string[]) => {
handleOnNewValue(t, n);
}}
/>
) : data.node.template[n].multiline ? (
) : myData.node.template[n].multiline ? (
<TextAreaComponent
disabled={disabled}
editNode={true}
value={data.node.template[n].value ?? ""}
value={
myData.node.template[n].value ?? ""
}
onChange={(t: string) => {
handleOnNewValue(t, n);
}}
@ -194,107 +164,112 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
editNode={true}
disabled={disabled}
password={
data.node.template[n].password ?? false
myData.node.template[n].password ??
false
}
value={
myData.node.template[n].value ?? ""
}
value={data.node.template[n].value ?? ""}
onChange={(t) => {
handleOnNewValue(t, n);
}}
/>
)}
</div>
) : data.node.template[n].type === "bool" ? (
) : myData.node.template[n].type === "bool" ? (
<div className="ml-auto">
{" "}
<ToggleShadComponent
disabled={disabled}
enabled={data.node.template[n].value}
enabled={myData.node.template[n].value}
setEnabled={(t) => {
handleOnNewValue(t, n);
}}
size="small"
/>
</div>
) : data.node.template[n].type === "float" ? (
) : myData.node.template[n].type === "float" ? (
<div className="mx-auto">
<FloatComponent
disabled={disabled}
editNode={true}
value={data.node.template[n].value ?? ""}
value={myData.node.template[n].value ?? ""}
onChange={(t) => {
data.node.template[n].value = t;
handleOnNewValue(t, n);
}}
/>
</div>
) : data.node.template[n].type === "str" &&
data.node.template[n].options ? (
) : myData.node.template[n].type === "str" &&
myData.node.template[n].options ? (
<div className="mx-auto">
<Dropdown
numberOfOptions={nodeLength}
editNode={true}
options={data.node.template[n].options}
options={myData.node.template[n].options}
onSelect={(t) => handleOnNewValue(t, n)}
value={
data.node.template[n].value ??
myData.node.template[n].value ??
"Choose an option"
}
></Dropdown>
</div>
) : data.node.template[n].type === "int" ? (
) : myData.node.template[n].type === "int" ? (
<div className="mx-auto">
<IntComponent
disabled={disabled}
editNode={true}
value={data.node.template[n].value ?? ""}
value={myData.node.template[n].value ?? ""}
onChange={(t) => {
handleOnNewValue(t, n);
}}
/>
</div>
) : data.node.template[n].type === "file" ? (
) : myData.node.template[n].type === "file" ? (
<div className="mx-auto">
<InputFileComponent
editNode={true}
disabled={disabled}
value={data.node.template[n].value ?? ""}
value={myData.node.template[n].value ?? ""}
onChange={(t: string) => {
handleOnNewValue(t, n);
}}
fileTypes={data.node.template[n].fileTypes}
suffixes={data.node.template[n].suffixes}
fileTypes={
myData.node.template[n].fileTypes
}
suffixes={myData.node.template[n].suffixes}
onFileChange={(t: string) => {
handleOnNewValue(t, n);
}}
></InputFileComponent>
</div>
) : data.node.template[n].type === "prompt" ? (
) : myData.node.template[n].type === "prompt" ? (
<div className="mx-auto">
<PromptAreaComponent
field_name={n}
editNode={true}
disabled={disabled}
nodeClass={data.node}
nodeClass={myData.node}
setNodeClass={(nodeClass) => {
data.node = nodeClass;
myData.node = nodeClass;
}}
value={data.node.template[n].value ?? ""}
value={myData.node.template[n].value ?? ""}
onChange={(t: string) => {
handleOnNewValue(t, n);
}}
/>
</div>
) : data.node.template[n].type === "code" ? (
) : myData.node.template[n].type === "code" ? (
<div className="mx-auto">
<CodeAreaComponent
disabled={disabled}
editNode={true}
value={data.node.template[n].value ?? ""}
value={myData.node.template[n].value ?? ""}
onChange={(t: string) => {
handleOnNewValue(t, n);
}}
/>
</div>
) : data.node.template[n].type === "Any" ? (
) : myData.node.template[n].type === "Any" ? (
"-"
) : (
<div className="hidden"></div>
@ -303,10 +278,8 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
<TableCell className="p-0 text-right">
<div className="items-center text-center">
<ToggleShadComponent
enabled={!data.node.template[n].advanced}
setEnabled={(e) =>
changeAdvanced(data.node.template[n])
}
enabled={!myData.node.template[n].advanced}
setEnabled={(e) => changeAdvanced(n)}
disabled={disabled}
size="small"
/>
@ -320,19 +293,32 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
)}
</div>
</div>
</BaseModal.Content>
<DialogFooter>
<BaseModal.Footer>
<Button
className="mt-3"
onClick={() => {
setData(cloneDeep(myData)); //saves data with actual state of modal
setTabsState((prev) => {
return {
...prev,
[tabId]: {
...prev[tabId],
isPending: true,
},
};
});
setModalOpen(false);
}}
type="submit"
>
Save Changes
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</BaseModal.Footer>
</BaseModal>
);
}
);
export default EditNodeModal;

View file

@ -2,7 +2,6 @@ import { Dialog, Transition } from "@headlessui/react";
import { Fragment, useContext, useRef, useState } from "react";
import IconComponent from "../../components/genericIconComponent";
import { limitScrollFieldsModal } from "../../constants/constants";
import { PopUpContext } from "../../contexts/popUpContext";
import { typesContext } from "../../contexts/typesContext";
import { NodeDataType } from "../../types/flow";
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
@ -10,23 +9,14 @@ import { classNames, toTitleCase } from "../../utils/utils";
import ModalField from "./components/ModalField";
export default function NodeModal({ data }: { data: NodeDataType }) {
const [open, setOpen] = useState(true);
const { closePopUp } = useContext(PopUpContext);
const [modalOpen, setModalOpen] = useState(false);
const { types } = useContext(typesContext);
const ref = useRef();
function setModalOpen(x: boolean) {
setOpen(x);
if (x === false) {
setTimeout(() => {
closePopUp();
}, 300);
}
}
// any to avoid type conflict
const Icon: any = nodeIconsLucide[types[data.type]];
return (
<Transition.Root show={open} appear={true} as={Fragment}>
<Dialog
open={modalOpen}
as="div"
className="relative z-10"
onClose={setModalOpen}
@ -78,8 +68,7 @@ export default function NodeModal({ data }: { data: NodeDataType }) {
strokeWidth={1.5}
className="node-modal-icon"
style={{
color:
nodeColors[types[data.type]] ?? nodeColors.unknown,
color: nodeColors[types[data.type]] ?? nodeColors.unknown,
}}
/>
<div className="node-modal-title-div">
@ -158,6 +147,5 @@ export default function NodeModal({ data }: { data: NodeDataType }) {
</div>
</div>
</Dialog>
</Transition.Root>
);
}

View file

@ -1,4 +1,4 @@
import { ReactNode, useContext } from "react";
import { ReactNode } from "react";
import React from "react";
import {
@ -9,14 +9,18 @@ import {
DialogTitle,
DialogTrigger,
} from "../../components/ui/dialog";
import { PopUpContext } from "../../contexts/popUpContext";
type ContentProps = { children: ReactNode };
type HeaderProps = { children: ReactNode; description: string };
type FooterProps = { children: ReactNode };
type TriggerProps = { children: ReactNode };
const Content: React.FC<ContentProps> = ({ children }) => {
return <div className="h-full w-full">{children}</div>;
};
const Trigger: React.FC<ContentProps> = ({ children }) => {
return <>{children}</>;
};
const Header: React.FC<{ children: ReactNode; description: string }> = ({
children,
@ -29,36 +33,82 @@ const Header: React.FC<{ children: ReactNode; description: string }> = ({
</DialogHeader>
);
};
interface BaseModalProps {
children: [React.ReactElement<ContentProps>, React.ReactElement<HeaderProps>];
open: boolean;
setOpen: (open: boolean) => void;
}
function BaseModal({ open, setOpen, children }: BaseModalProps) {
const { closePopUp, setCloseEdit } = useContext(PopUpContext);
function setModalOpen(x: boolean) {
setOpen(x);
if (x === false) {
setTimeout(() => {
setCloseEdit("editcode");
closePopUp();
}, 300);
}
const Footer: React.FC<{ children: ReactNode }> = ({ children }) => {
return <>{children}</>;
};
interface BaseModalProps {
children: [
React.ReactElement<ContentProps>,
React.ReactElement<HeaderProps>,
React.ReactElement<TriggerProps>?,
React.ReactElement<FooterProps>?
];
open?: boolean;
setOpen?: (open: boolean) => void;
size?: "smaller" | "small" | "medium" | "large" | "large-h-full";
}
function BaseModal({
open,
setOpen,
children,
size = "large",
}: BaseModalProps) {
const headerChild = React.Children.toArray(children).find(
(child) => (child as React.ReactElement).type === Header
);
const triggerChild = React.Children.toArray(children).find(
(child) => (child as React.ReactElement).type === Trigger
);
const ContentChild = React.Children.toArray(children).find(
(child) => (child as React.ReactElement).type === Content
);
const ContentFooter = React.Children.toArray(children).find(
(child) => (child as React.ReactElement).type === Footer
);
let minWidth: string;
let height: string;
switch (size) {
case "smaller":
minWidth = "min-w-[40vw]";
height = "h-[25vh]";
break;
case "small":
minWidth = "min-w-[40vw]";
height = "h-[40vh]";
break;
case "medium":
minWidth = "min-w-[60vw]";
height = "h-[60vh]";
break;
case "large":
minWidth = "min-w-[80vw]";
height = "h-[80vh]";
break;
case "large-h-full":
minWidth = "min-w-[80vw]";
break;
default:
minWidth = "min-w-[80vw]";
height = "h-[80vh]";
break;
}
//UPDATE COLORS AND STYLE CLASSSES
return (
<Dialog open={true} onOpenChange={setModalOpen}>
<DialogTrigger className="hidden"></DialogTrigger>
<DialogContent className="min-w-[80vw]">
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger className="w-full" hidden={triggerChild ? false : true}>
{triggerChild}
</DialogTrigger>
<DialogContent className={minWidth}>
{headerChild}
<div className="mt-2 flex h-[80vh] w-full ">{ContentChild}</div>
<div className={`mt-2 flex flex-col ${height} w-full `}>
{ContentChild}
</div>
<div className="flex flex-row-reverse">{ContentFooter}</div>
</DialogContent>
</Dialog>
);
@ -66,4 +116,6 @@ function BaseModal({ open, setOpen, children }: BaseModalProps) {
BaseModal.Content = Content;
BaseModal.Header = Header;
BaseModal.Trigger = Trigger;
BaseModal.Footer = Footer;
export default BaseModal;

View file

@ -1,17 +1,15 @@
import { DialogTitle } from "@radix-ui/react-dialog";
import "ace-builds/src-noconflict/ace";
import "ace-builds/src-noconflict/ext-language_tools";
import "ace-builds/src-noconflict/mode-python";
import "ace-builds/src-noconflict/theme-github";
import "ace-builds/src-noconflict/theme-twilight";
import { useContext, useState } from "react";
import { ReactNode, useContext, useState } from "react";
import AceEditor from "react-ace";
import IconComponent from "../../components/genericIconComponent";
import { Button } from "../../components/ui/button";
import { CODE_PROMPT_DIALOG_SUBTITLE } from "../../constants/constants";
import { alertContext } from "../../contexts/alertContext";
import { darkContext } from "../../contexts/darkContext";
import { PopUpContext } from "../../contexts/popUpContext";
import { postValidateCode } from "../../controllers/API";
import { APIClassType } from "../../types/api";
import BaseModal from "../baseModal";
@ -21,25 +19,18 @@ export default function CodeAreaModal({
setValue,
nodeClass,
setNodeClass,
children,
}: {
setValue: (value: string) => void;
value: string;
nodeClass: APIClassType;
children: ReactNode;
setNodeClass: (Class: APIClassType) => void;
}) {
const [code, setCode] = useState(value);
const { dark } = useContext(darkContext);
const { closePopUp, setCloseEdit } = useContext(PopUpContext);
const { setErrorData, setSuccessData } = useContext(alertContext);
function setModalOpen(x: boolean) {
if (x === false) {
setCloseEdit("codearea");
closePopUp();
}
}
// Check for custom code errors
function handleClick() {
postValidateCode(code)
.then((apiReturn) => {
@ -51,7 +42,7 @@ export default function CodeAreaModal({
title: "Code is ready to run",
});
setValue(code);
setModalOpen(false);
setOpen(false);
} else {
if (funcErrors.length !== 0) {
setErrorData({
@ -79,17 +70,18 @@ export default function CodeAreaModal({
});
}
const [open, setOpen] = useState(false);
return (
<BaseModal open={true} setOpen={setModalOpen}>
<BaseModal open={open} setOpen={setOpen}>
<BaseModal.Trigger>{children}</BaseModal.Trigger>
<BaseModal.Header description={CODE_PROMPT_DIALOG_SUBTITLE}>
<DialogTitle className="flex items-center">
<span className="pr-2">Edit Code</span>
<IconComponent
name="prompts"
className="h-6 w-6 pl-1 text-primary "
aria-hidden="true"
/>
</DialogTitle>
</BaseModal.Header>
<BaseModal.Content>
<div className="flex h-full w-full flex-col transition-all">

View file

@ -1,60 +1,34 @@
import { useContext, useRef, useState } from "react";
import { ReactNode, forwardRef, useContext, useState } from "react";
import EditFlowSettings from "../../components/EditFlowSettingsComponent";
import IconComponent from "../../components/genericIconComponent";
import { Button } from "../../components/ui/button";
import { Checkbox } from "../../components/ui/checkbox";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "../../components/ui/dialog";
import { EXPORT_DIALOG_SUBTITLE } from "../../constants/constants";
import { alertContext } from "../../contexts/alertContext";
import { PopUpContext } from "../../contexts/popUpContext";
import { TabsContext } from "../../contexts/tabsContext";
import { removeApiKeys } from "../../utils/reactflowUtils";
import BaseModal from "../baseModal";
export default function ExportModal() {
const [open, setOpen] = useState(true);
const { closePopUp } = useContext(PopUpContext);
const ref = useRef();
const { setErrorData } = useContext(alertContext);
const ExportModal = forwardRef((props: { children: ReactNode }, ref) => {
const { flows, tabId, updateFlow, downloadFlow, saveFlow } =
useContext(TabsContext);
const [isMaxLength, setIsMaxLength] = useState(false);
function setModalOpen(x: boolean) {
setOpen(x);
if (x === false) {
setTimeout(() => {
closePopUp();
}, 300);
}
}
const [checked, setChecked] = useState(false);
const [name, setName] = useState(flows.find((f) => f.id === tabId).name);
const [description, setDescription] = useState(
flows.find((f) => f.id === tabId).description
);
const [open, setOpen] = useState(false);
return (
<Dialog open={true} onOpenChange={setModalOpen}>
<DialogTrigger asChild></DialogTrigger>
<DialogContent className="h-[420px] lg:max-w-[600px] ">
<DialogHeader>
<DialogTitle className="flex items-center">
<BaseModal size="smaller" open={open} setOpen={setOpen}>
<BaseModal.Trigger>{props.children}</BaseModal.Trigger>
<BaseModal.Header description={EXPORT_DIALOG_SUBTITLE}>
<span className="pr-2">Export</span>
<IconComponent
name="Download"
className="h-6 w-6 pl-1 text-foreground"
aria-hidden="true"
/>
</DialogTitle>
<DialogDescription>{EXPORT_DIALOG_SUBTITLE}</DialogDescription>
</DialogHeader>
</BaseModal.Header>
<BaseModal.Content>
<EditFlowSettings
name={name}
description={description}
@ -64,7 +38,7 @@ export default function ExportModal() {
setDescription={setDescription}
updateFlow={updateFlow}
/>
<div className="flex items-center space-x-2">
<div className="mt-3 flex items-center space-x-2">
<Checkbox
id="terms"
onCheckedChange={(event: boolean) => {
@ -75,8 +49,9 @@ export default function ExportModal() {
Save with my API keys
</label>
</div>
</BaseModal.Content>
<DialogFooter>
<BaseModal.Footer>
<Button
onClick={() => {
if (checked)
@ -91,15 +66,14 @@ export default function ExportModal() {
name,
description
);
closePopUp();
setOpen(false);
}}
type="submit"
>
Download Flow
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</BaseModal.Footer>
</BaseModal>
);
}
});
export default ExportModal;

View file

@ -2,23 +2,18 @@ import { useContext, useRef, useState } from "react";
import EditFlowSettings from "../../components/EditFlowSettingsComponent";
import IconComponent from "../../components/genericIconComponent";
import { Button } from "../../components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "../../components/ui/dialog";
import { SETTINGS_DIALOG_SUBTITLE } from "../../constants/constants";
import { alertContext } from "../../contexts/alertContext";
import { PopUpContext } from "../../contexts/popUpContext";
import { TabsContext } from "../../contexts/tabsContext";
import BaseModal from "../baseModal";
export default function FlowSettingsModal() {
const [open, setOpen] = useState(true);
const { closePopUp } = useContext(PopUpContext);
export default function FlowSettingsModal({
open,
setOpen,
}: {
open: boolean;
setOpen: (open: boolean) => void;
}) {
const { setErrorData, setSuccessData } = useContext(alertContext);
const ref = useRef();
const { flows, tabId, updateFlow, setTabsState, saveFlow } =
@ -28,34 +23,21 @@ export default function FlowSettingsModal() {
const [description, setDescription] = useState(
flows.find((f) => f.id === tabId).description
);
function setModalOpen(x: boolean) {
setOpen(x);
if (x === false) {
setTimeout(() => {
closePopUp();
}, 300);
}
}
function handleClick() {
let savedFlow = flows.find((f) => f.id === tabId);
savedFlow.name = name;
savedFlow.description = description;
saveFlow(savedFlow);
setSuccessData({ title: "Changes saved successfully" });
closePopUp();
setOpen(false);
}
return (
<Dialog open={true} onOpenChange={setModalOpen}>
<DialogTrigger asChild></DialogTrigger>
<DialogContent className="h-[390px] lg:max-w-[600px]">
<DialogHeader>
<DialogTitle className="flex items-center">
<BaseModal open={open} setOpen={setOpen} size="smaller">
<BaseModal.Header description={SETTINGS_DIALOG_SUBTITLE}>
<span className="pr-2">Settings</span>
<IconComponent name="Settings2" className="mr-2 h-4 w-4 " />
</DialogTitle>
<DialogDescription>{SETTINGS_DIALOG_SUBTITLE}</DialogDescription>
</DialogHeader>
</BaseModal.Header>
<BaseModal.Content>
<EditFlowSettings
name={name}
description={description}
@ -65,13 +47,13 @@ export default function FlowSettingsModal() {
setDescription={setDescription}
updateFlow={updateFlow}
/>
</BaseModal.Content>
<DialogFooter>
<BaseModal.Footer>
<Button onClick={handleClick} type="submit">
Save
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</BaseModal.Footer>
</BaseModal>
);
}

View file

@ -7,11 +7,11 @@ 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 { ChatMessageType } from "../../../types/chat";
import { classNames } from "../../../utils/utils";
import FileCard from "../fileComponent";
import { CodeBlock } from "./codeBlock";
export default function ChatMessage({
chat,
lockChat,
@ -78,13 +78,22 @@ export default function ChatMessage({
<div className="w-full dark:text-white">
<div className="w-full">
{useMemo(
() => (
() =>
chat.message.toString() === "" && lockChat ? (
<IconComponent
name="MoreHorizontal"
className="h-8 w-8 animate-pulse"
/>
) : (
<ReactMarkdown
remarkPlugins={[remarkGfm, remarkMath]}
rehypePlugins={[rehypeMathjax]}
className="markdown prose inline-block break-words text-primary
dark:prose-invert sm:w-[30vw] sm:max-w-[30vw] lg:w-[40vw] lg:max-w-[40vw]"
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]"
components={{
pre({ node, ...props }) {
return <>{props.children}</>;
},
code: ({
node,
inline,
@ -112,11 +121,20 @@ export default function ChatMessage({
);
return !inline ? (
<CodeBlock
key={Math.random()}
language={(match && match[1]) || ""}
value={String(children).replace(/\n$/, "")}
{...props}
<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}>

View file

@ -32,7 +32,7 @@ export default function FormModal({
setOpen,
}: {
open: boolean;
setOpen: Function;
setOpen: (open: boolean) => void;
flow: FlowType;
}) {
const { tabsState, setTabsState } = useContext(TabsContext);
@ -386,8 +386,8 @@ export default function FormModal({
}
return (
<Dialog open={open} onOpenChange={setModalOpen}>
<DialogTrigger className="hidden"></DialogTrigger>
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger hidden></DialogTrigger>
{tabsState[flow.id].formKeysData && (
<DialogContent className="min-w-[80vw]">
<DialogHeader>

View file

@ -1,10 +1,9 @@
import { useContext, useEffect, useRef, useState } from "react";
import { ReactNode, useContext, useEffect, useRef, useState } from "react";
import SanitizedHTMLWrapper from "../../components/SanitizedHTMLWrapper";
import ShadTooltip from "../../components/ShadTooltipComponent";
import IconComponent from "../../components/genericIconComponent";
import { Badge } from "../../components/ui/badge";
import { Button } from "../../components/ui/button";
import { DialogTitle } from "../../components/ui/dialog";
import { Textarea } from "../../components/ui/textarea";
import {
INVALID_CHARACTERS,
@ -15,8 +14,6 @@ import {
} from "../../constants/constants";
import { TypeModal } from "../../constants/enums";
import { alertContext } from "../../contexts/alertContext";
import { darkContext } from "../../contexts/darkContext";
import { PopUpContext } from "../../contexts/popUpContext";
import { postValidatePrompt } from "../../controllers/API";
import { APIClassType } from "../../types/api";
import {
@ -35,6 +32,7 @@ export default function GenericModal({
type,
nodeClass,
setNodeClass,
children,
}: {
field_name?: string;
setValue: (value: string) => void;
@ -42,6 +40,7 @@ export default function GenericModal({
buttonText: string;
modalTitle: string;
type: number;
children: ReactNode;
nodeClass?: APIClassType;
setNodeClass?: (Class: APIClassType) => void;
}) {
@ -51,17 +50,9 @@ export default function GenericModal({
const [inputValue, setInputValue] = useState(value);
const [isEdit, setIsEdit] = useState(true);
const [wordsHighlight, setWordsHighlight] = useState([]);
const { dark } = useContext(darkContext);
const { setErrorData, setSuccessData, setNoticeData } =
useContext(alertContext);
const { closePopUp, setCloseEdit } = useContext(PopUpContext);
const ref = useRef();
function setModalOpen(x: boolean) {
if (x === false) {
setCloseEdit("generic");
closePopUp();
}
}
const divRef = useRef(null);
const divRefPrompt = useRef(null);
@ -105,6 +96,10 @@ export default function GenericModal({
}
}, [inputValue, type]);
useEffect(() => {
setInputValue(value);
}, [value]);
const coloredContent = (inputValue || "")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
@ -138,7 +133,6 @@ export default function GenericModal({
postValidatePrompt(field_name, inputValue, nodeClass)
.then((apiReturn) => {
if (apiReturn.data) {
setNodeClass(apiReturn.data?.frontend_node);
let inputVariables = apiReturn.data.input_variables ?? [];
if (inputVariables && inputVariables.length === 0) {
setIsEdit(true);
@ -150,6 +144,7 @@ export default function GenericModal({
setSuccessData({
title: "Prompt is ready",
});
setNodeClass(apiReturn.data?.frontend_node);
setModalOpen(closeModal);
setValue(inputValue);
}
@ -170,8 +165,11 @@ export default function GenericModal({
});
}
const [modalOpen, setModalOpen] = useState(false);
return (
<BaseModal open={true} setOpen={setModalOpen}>
<BaseModal open={modalOpen} setOpen={setModalOpen}>
<BaseModal.Trigger>{children}</BaseModal.Trigger>
<BaseModal.Header
description={(() => {
switch (myModalTitle) {
@ -186,14 +184,12 @@ export default function GenericModal({
}
})()}
>
<DialogTitle className="flex items-center">
<span className="pr-2">{myModalTitle}</span>
<IconComponent
name="FileText"
className="h-6 w-6 pl-1 text-primary "
aria-hidden="true"
/>
</DialogTitle>
</BaseModal.Header>
<BaseModal.Content>
<div className="flex h-full flex-col">
@ -277,7 +273,7 @@ export default function GenericModal({
))}
</div>
</div>
<span className="mt-1 text-xs text-muted-foreground">
<span className="mt-2 text-xs text-muted-foreground">
Prompt variables can be created with any chosen name inside
curly brackets, e.g. {"{variable_name}"}
</span>
@ -287,11 +283,11 @@ export default function GenericModal({
<Button
onClick={() => {
switch (myModalType) {
case 1:
case TypeModal.TEXT:
setValue(inputValue);
setModalOpen(false);
break;
case 2:
case TypeModal.PROMPT:
!inputValue || inputValue === ""
? setModalOpen(false)
: validatePrompt(false);

View file

@ -17,7 +17,6 @@ import {
} from "../../components/ui/dialog";
import { IMPORT_DIALOG_SUBTITLE } from "../../constants/constants";
import { alertContext } from "../../contexts/alertContext";
import { PopUpContext } from "../../contexts/popUpContext";
import { TabsContext } from "../../contexts/tabsContext";
import { getExamples } from "../../controllers/API";
import { FlowType } from "../../types/flow";
@ -27,20 +26,11 @@ import ButtonBox from "./buttonBox";
export default function ImportModal() {
const [open, setOpen] = useState(true);
const { setErrorData } = useContext(alertContext);
const { closePopUp } = useContext(PopUpContext);
const ref = useRef();
const [showExamples, setShowExamples] = useState(false);
const [loadingExamples, setLoadingExamples] = useState(false);
const [examples, setExamples] = useState<FlowType[]>([]);
const { uploadFlow, addFlow } = useContext(TabsContext);
function setModalOpen(x: boolean) {
setOpen(x);
if (x === false) {
setTimeout(() => {
closePopUp();
}, 300);
}
}
function handleExamples() {
setLoadingExamples(true);
@ -57,8 +47,10 @@ export default function ImportModal() {
);
}
const [modalOpen, setModalOpen] = useState(false);
return (
<Dialog open={true} onOpenChange={setModalOpen}>
<Dialog open={modalOpen} onOpenChange={setModalOpen}>
<DialogTrigger></DialogTrigger>
<DialogContent
className={classNames(

View file

@ -27,6 +27,7 @@ import { undoRedoContext } from "../../../../contexts/undoRedoContext";
import { APIClassType } from "../../../../types/api";
import { FlowType, NodeType } from "../../../../types/flow";
import { isValidConnection } from "../../../../utils/reactflowUtils";
import { isWrappedWithClass } from "../../../../utils/utils";
import ConnectionLineComponent from "../ConnectionLineComponent";
import ExtraSidebar from "../extraSidebarComponent";
@ -38,7 +39,6 @@ export default function Page({ flow }: { flow: FlowType }) {
let {
updateFlow,
uploadFlow,
disableCopyPaste,
addFlow,
getNodeId,
paste,
@ -63,11 +63,11 @@ export default function Page({ flow }: { flow: FlowType }) {
// this effect is used to attach the global event handlers
const onKeyDown = (event: KeyboardEvent) => {
if (!isWrappedWithClass(event, "nocopy")) {
if (
(event.ctrlKey || event.metaKey) &&
event.key === "c" &&
lastSelection &&
!disableCopyPaste
lastSelection
) {
event.preventDefault();
setLastCopiedSelection(_.cloneDeep(lastSelection));
@ -75,8 +75,7 @@ export default function Page({ flow }: { flow: FlowType }) {
if (
(event.ctrlKey || event.metaKey) &&
event.key === "v" &&
lastCopiedSelection &&
!disableCopyPaste
lastCopiedSelection
) {
event.preventDefault();
let bounds = reactFlowWrapper.current.getBoundingClientRect();
@ -92,6 +91,7 @@ export default function Page({ flow }: { flow: FlowType }) {
) {
event.preventDefault();
}
}
};
const handleMouseMove = (event) => {
setPosition({ x: event.clientX, y: event.clientY });
@ -124,7 +124,7 @@ export default function Page({ flow }: { flow: FlowType }) {
updateFlow(flow);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [nodes, edges]);
}, [edges]);
//update flow when tabs change
useEffect(() => {
setNodes(flow?.data?.nodes ?? []);
@ -355,8 +355,6 @@ export default function Page({ flow }: { flow: FlowType }) {
setLastSelection(flow);
}, []);
const { setDisableCopyPaste } = useContext(TabsContext);
return (
<div className="flex h-full overflow-hidden">
<ExtraSidebar />
@ -378,15 +376,6 @@ export default function Page({ flow }: { flow: FlowType }) {
});
}}
edges={edges}
onPaneClick={() => {
setDisableCopyPaste(false);
}}
onPaneMouseLeave={() => {
setDisableCopyPaste(true);
}}
onPaneMouseEnter={() => {
setDisableCopyPaste(false);
}}
onNodesChange={onNodesChangeMod}
onEdgesChange={onEdgesChangeMod}
onConnect={onConnect}
@ -407,9 +396,6 @@ export default function Page({ flow }: { flow: FlowType }) {
onDrop={onDrop}
onNodesDelete={onDelete}
onSelectionChange={onSelectionChange}
nodesDraggable={!disableCopyPaste}
panOnDrag={!disableCopyPaste}
zoomOnDoubleClick={!disableCopyPaste}
className="theme-attribution"
minZoom={0.01}
maxZoom={8}

View file

@ -1,10 +1,8 @@
import { Search } from "lucide-react";
import { useContext, useState } from "react";
import ShadTooltip from "../../../../components/ShadTooltipComponent";
import IconComponent from "../../../../components/genericIconComponent";
import { Separator } from "../../../../components/ui/separator";
import { alertContext } from "../../../../contexts/alertContext";
import { PopUpContext } from "../../../../contexts/popUpContext";
import { TabsContext } from "../../../../contexts/tabsContext";
import { typesContext } from "../../../../contexts/typesContext";
import ApiModal from "../../../../modals/ApiModal";
@ -20,7 +18,6 @@ import DisclosureComponent from "../DisclosureComponent";
export default function ExtraSidebar() {
const { data } = useContext(typesContext);
const { openPopUp } = useContext(PopUpContext);
const { flows, tabId, uploadFlow, tabsState, saveFlow } =
useContext(TabsContext);
const { setSuccessData, setErrorData } = useContext(alertContext);
@ -58,6 +55,7 @@ export default function ExtraSidebar() {
return ret;
});
}
const flow = flows.find((f) => f.id === tabId);
return (
<div className="side-bar-arrangement">
@ -74,31 +72,27 @@ export default function ExtraSidebar() {
</ShadTooltip>
<ShadTooltip content="Export" side="top">
<button
className={classNames("extra-side-bar-buttons")}
onClick={(event) => {
openPopUp(<ExportModal />);
}}
>
<ExportModal>
<div className={classNames("extra-side-bar-buttons")}>
<IconComponent name="FileDown" className="side-bar-button-size" />
</button>
</div>
</ExportModal>
</ShadTooltip>
<ShadTooltip content="Code" side="top">
<button
className={classNames("extra-side-bar-buttons")}
onClick={(event) => {
openPopUp(<ApiModal flow={flows.find((f) => f.id === tabId)} />);
}}
>
{flow && flow.data && (
<ApiModal flow={flow}>
<div className={classNames("extra-side-bar-buttons")}>
<IconComponent name="Code2" className="side-bar-button-size" />
</button>
</div>
</ApiModal>
)}
</ShadTooltip>
<ShadTooltip content="Save" side="top">
<button
className="extra-side-bar-buttons"
onClick={(event) => {
saveFlow(flows.find((f) => f.id === tabId));
saveFlow(flow);
setSuccessData({ title: "Changes saved successfully" });
}}
disabled={!isPending}
@ -120,7 +114,7 @@ export default function ExtraSidebar() {
name="search"
id="search"
placeholder="Search"
className="input-search"
className="nopan nodrag noundo nocopy input-search"
onChange={(e) => {
handleSearchInput(e.target.value);
// Set search input state
@ -128,7 +122,11 @@ export default function ExtraSidebar() {
}}
/>
<div className="search-icon">
<Search size={20} strokeWidth={1.5} className="text-primary" />
<IconComponent
name="Search"
className={"h-5 w-5 stroke-[1.5] text-primary"}
aria-hidden="true"
/>
</div>
</div>

View file

@ -6,24 +6,24 @@ import { TabsContext } from "../../../../contexts/tabsContext";
import EditNodeModal from "../../../../modals/EditNodeModal";
import { classNames } from "../../../../utils/utils";
const NodeToolbarComponent = (props) => {
export default function NodeToolbarComponent({ data, setData, deleteNode }) {
const [nodeLength, setNodeLength] = useState(
Object.keys(props.data.node.template).filter(
Object.keys(data.node.template).filter(
(t) =>
t.charAt(0) !== "_" &&
props.data.node.template[t].show &&
(props.data.node.template[t].type === "str" ||
props.data.node.template[t].type === "bool" ||
props.data.node.template[t].type === "float" ||
props.data.node.template[t].type === "code" ||
props.data.node.template[t].type === "prompt" ||
props.data.node.template[t].type === "file" ||
props.data.node.template[t].type === "Any" ||
props.data.node.template[t].type === "int")
data.node.template[t].show &&
(data.node.template[t].type === "str" ||
data.node.template[t].type === "bool" ||
data.node.template[t].type === "float" ||
data.node.template[t].type === "code" ||
data.node.template[t].type === "prompt" ||
data.node.template[t].type === "file" ||
data.node.template[t].type === "Any" ||
data.node.template[t].type === "int")
).length
);
const { setLastCopiedSelection, paste } = useContext(TabsContext);
const { paste } = useContext(TabsContext);
const reactFlowInstance = useReactFlow();
return (
<>
@ -33,7 +33,7 @@ const NodeToolbarComponent = (props) => {
<button
className="relative inline-flex items-center rounded-l-md 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={() => {
props.deleteNode(props.data.id);
deleteNode(data.id);
}}
>
<IconComponent name="Trash2" className="h-4 w-4" />
@ -49,14 +49,14 @@ const NodeToolbarComponent = (props) => {
event.preventDefault();
paste(
{
nodes: [reactFlowInstance.getNode(props.data.id)],
nodes: [reactFlowInstance.getNode(data.id)],
edges: [],
},
{
x: 50,
y: 10,
paneX: reactFlowInstance.getNode(props.data.id).position.x,
paneY: reactFlowInstance.getNode(props.data.id).position.y,
paneX: reactFlowInstance.getNode(data.id).position.x,
paneY: reactFlowInstance.getNode(data.id).position.y,
}
);
}}
@ -67,25 +67,23 @@ const NodeToolbarComponent = (props) => {
<ShadTooltip
content={
props.data.node.documentation === ""
? "Coming Soon"
: "Documentation"
data.node.documentation === "" ? "Coming Soon" : "Documentation"
}
side="top"
>
<a
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" +
(props.data.node.documentation === ""
(data.node.documentation === ""
? " text-muted-foreground"
: " text-foreground")
)}
target="_blank"
rel="noopener noreferrer"
href={props.data.node.documentation}
href={data.node.documentation}
// deactivate link if no documentation is provided
onClick={(event) => {
if (props.data.node.documentation === "") {
if (data.node.documentation === "") {
event.preventDefault();
}
}}
@ -95,28 +93,27 @@ const NodeToolbarComponent = (props) => {
</ShadTooltip>
<ShadTooltip content="Edit" side="top">
<button
<div>
<EditNodeModal
data={data}
setData={setData}
nodeLength={nodeLength}
>
<div
className={classNames(
"relative -ml-px inline-flex items-center rounded-r-md 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" +
(nodeLength == 0
? " text-muted-foreground"
: " text-foreground")
)}
onClick={(event) => {
if (nodeLength == 0) {
event.preventDefault();
}
event.preventDefault();
props.openPopUp(<EditNodeModal data={props.data} />);
}}
>
<IconComponent name="Settings2" className="h-4 w-4 " />
</button>
</div>
</EditNodeModal>
</div>
</ShadTooltip>
</span>
</div>
</>
);
};
export default NodeToolbarComponent;
}

View file

@ -7,7 +7,6 @@ export type InputComponentType = {
disabled?: boolean;
onChange: (value: string) => void;
password: boolean;
disableCopyPaste?: boolean;
editNode?: boolean;
onChangePass?: (value: boolean | boolean) => void;
showPass?: boolean;
@ -28,6 +27,7 @@ export type DropDownComponentType = {
};
export type ParameterComponentType = {
data: NodeDataType;
setData: (value: NodeDataType) => void;
title: string;
id: string;
color: string;
@ -45,7 +45,6 @@ export type InputListComponentType = {
onChange: (value: string[]) => void;
disabled: boolean;
editNode?: boolean;
onAddInput?: (value?: string[]) => void;
};
export type TextAreaComponentType = {
@ -93,7 +92,6 @@ export type DisclosureComponentType = {
export type FloatComponentType = {
value: string;
disabled?: boolean;
disableCopyPaste?: boolean;
onChange: (value: string) => void;
editNode?: boolean;
};

View file

@ -20,9 +20,6 @@ export type TabsContextType = {
uploadFlows: () => void;
uploadFlow: (newFlow?: boolean, file?: File) => void;
hardReset: () => void;
//disable CopyPaste
disableCopyPaste: boolean;
setDisableCopyPaste: (value: boolean) => void;
getNodeId: (nodeType: string) => string;
tabsState: TabsState;
setTabsState: Dispatch<SetStateAction<TabsState>>;

View file

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

View file

@ -85,6 +85,9 @@ export function checkUpperWords(str: string) {
return words.join(" ");
}
export const isWrappedWithClass = (event: any, className: string | undefined) =>
event.target.closest(`.${className}`);
export function groupByFamily(data, baseClasses, left, type) {
let parentOutput: string;
let arrOfParent: string[] = [];
@ -272,7 +275,7 @@ export function buildTweakObject(tweak) {
});
});
const tweakString = JSON.stringify(tweak, null, 2);
const tweakString = JSON.stringify(tweak.at(-1), null, 2);
return tweakString;
}