Formatting project
This commit is contained in:
parent
1a9ed4d94b
commit
86eb9af794
21 changed files with 470 additions and 492 deletions
|
|
@ -59,8 +59,7 @@ export default function ParameterComponent({
|
||||||
data.node.template[name]?.value ?? false
|
data.node.template[name]?.value ?? false
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {}, [closePopUp, data.node.template]);
|
||||||
}, [closePopUp, data.node.template]);
|
|
||||||
|
|
||||||
const { reactFlowInstance } = useContext(typesContext);
|
const { reactFlowInstance } = useContext(typesContext);
|
||||||
let disabled =
|
let disabled =
|
||||||
|
|
@ -69,216 +68,209 @@ export default function ParameterComponent({
|
||||||
const [myData, setMyData] = useState(useContext(typesContext).data);
|
const [myData, setMyData] = useState(useContext(typesContext).data);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const groupedObj = groupByFamily(myData, tooltipTitle);
|
const groupedObj = groupByFamily(myData, tooltipTitle);
|
||||||
refHtml.current = groupedObj.map((item, i) => (
|
refHtml.current = groupedObj.map((item, i) => (
|
||||||
<span
|
<span
|
||||||
key={i}
|
key={i}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
i > 0 ? "items-center flex mt-3" : "items-center flex"
|
i > 0 ? "items-center flex mt-3" : "items-center flex"
|
||||||
)}
|
)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="h-5 w-5"
|
||||||
|
style={{
|
||||||
|
color: nodeColors[item.family],
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div
|
{React.createElement(nodeIcons[item.family])}
|
||||||
className="h-5 w-5"
|
</div>
|
||||||
style={{
|
<span className="ps-2 text-gray-950">
|
||||||
color: nodeColors[item.family],
|
{nodeNames[item.family] ?? ""}{" "}
|
||||||
}}
|
<span className={classNames(left ? "hidden" : "")}>
|
||||||
>
|
{" "}
|
||||||
{React.createElement(nodeIcons[item.family])}
|
-
|
||||||
</div>
|
{item.type.split(", ").length > 2
|
||||||
<span className="ps-2 text-gray-950">
|
? item.type.split(", ").map((el, i) => (
|
||||||
{nodeNames[item.family] ?? ""}{" "}
|
<>
|
||||||
<span className={classNames(left ? "hidden" : "")}>
|
<span key={i}>
|
||||||
{" "}
|
{i == item.type.split(", ").length - 1
|
||||||
-
|
? el
|
||||||
{item.type.split(", ").length > 2
|
: (el += `, `)}
|
||||||
? item.type.split(", ").map((el, i) => (
|
</span>
|
||||||
<>
|
{i % 2 == 0 && i > 0 && <br></br>}
|
||||||
<span key={i}>
|
</>
|
||||||
{i == item.type.split(", ").length - 1
|
))
|
||||||
? el
|
: item.type}
|
||||||
: (el += `, `)}
|
|
||||||
</span>
|
|
||||||
{i % 2 == 0 && i > 0 && <br></br>}
|
|
||||||
</>
|
|
||||||
))
|
|
||||||
: item.type}
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
));
|
</span>
|
||||||
|
));
|
||||||
}, [tooltipTitle]);
|
}, [tooltipTitle]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className="w-full flex flex-wrap justify-between items-center bg-muted dark:bg-gray-800 dark:text-white mt-1 px-5 py-2"
|
className="w-full flex flex-wrap justify-between items-center bg-muted dark:bg-gray-800 dark:text-white mt-1 px-5 py-2"
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
<div className={"text-sm truncate w-full " + (left ? "" : "text-end")}>
|
<div
|
||||||
{title}
|
className={"text-sm truncate w-full " + (left ? "" : "text-end")}
|
||||||
<span className="text-red-600">{required ? " *" : ""}</span>
|
|
||||||
</div>
|
|
||||||
{left &&
|
|
||||||
(type === "str" ||
|
|
||||||
type === "bool" ||
|
|
||||||
type === "float" ||
|
|
||||||
type === "code" ||
|
|
||||||
type === "prompt" ||
|
|
||||||
type === "file" ||
|
|
||||||
type === "int") ? (
|
|
||||||
<></>
|
|
||||||
) : (
|
|
||||||
<ShadTooltip
|
|
||||||
delayDuration={0}
|
|
||||||
content={refHtml.current}
|
|
||||||
side={left ? "left" : "right"}
|
|
||||||
open={refHtml?.current?.length > 0}
|
|
||||||
>
|
>
|
||||||
<Handle
|
{title}
|
||||||
type={left ? "target" : "source"}
|
<span className="text-red-600">{required ? " *" : ""}</span>
|
||||||
position={left ? Position.Left : Position.Right}
|
</div>
|
||||||
id={id}
|
{left &&
|
||||||
isValidConnection={(connection) =>
|
(type === "str" ||
|
||||||
isValidConnection(connection, reactFlowInstance)
|
type === "bool" ||
|
||||||
}
|
type === "float" ||
|
||||||
className={classNames(
|
type === "code" ||
|
||||||
left ? "-ml-0.5 " : "-mr-0.5 ",
|
type === "prompt" ||
|
||||||
"w-3 h-3 rounded-full border-2 bg-white dark:bg-gray-800"
|
type === "file" ||
|
||||||
)}
|
type === "int") ? (
|
||||||
style={{
|
<></>
|
||||||
borderColor: color,
|
) : (
|
||||||
top: position,
|
<ShadTooltip
|
||||||
}}
|
delayDuration={0}
|
||||||
></Handle>
|
content={refHtml.current}
|
||||||
</ShadTooltip>
|
side={left ? "left" : "right"}
|
||||||
)}
|
open={refHtml?.current?.length > 0}
|
||||||
|
>
|
||||||
{left === true &&
|
<Handle
|
||||||
type === "str" &&
|
type={left ? "target" : "source"}
|
||||||
!data.node.template[name].options ? (
|
position={left ? Position.Left : Position.Right}
|
||||||
<div className="mt-2 w-full">
|
id={id}
|
||||||
{data.node.template[name].list ? (
|
isValidConnection={(connection) =>
|
||||||
<InputListComponent
|
isValidConnection(connection, reactFlowInstance)
|
||||||
disabled={disabled}
|
|
||||||
value={
|
|
||||||
!data.node.template[name].value ||
|
|
||||||
data.node.template[name].value === ""
|
|
||||||
? [""]
|
|
||||||
: data.node.template[name].value
|
|
||||||
}
|
}
|
||||||
onChange={(t: string[]) => {
|
className={classNames(
|
||||||
data.node.template[name].value = t;
|
left ? "-ml-0.5 " : "-mr-0.5 ",
|
||||||
|
"w-3 h-3 rounded-full border-2 bg-white dark:bg-gray-800"
|
||||||
|
)}
|
||||||
|
style={{
|
||||||
|
borderColor: color,
|
||||||
|
top: position,
|
||||||
}}
|
}}
|
||||||
/>
|
></Handle>
|
||||||
) : data.node.template[name].multiline ? (
|
</ShadTooltip>
|
||||||
<TextAreaComponent
|
)}
|
||||||
|
|
||||||
|
{left === true &&
|
||||||
|
type === "str" &&
|
||||||
|
!data.node.template[name].options ? (
|
||||||
|
<div className="mt-2 w-full">
|
||||||
|
{data.node.template[name].list ? (
|
||||||
|
<InputListComponent
|
||||||
|
disabled={disabled}
|
||||||
|
value={
|
||||||
|
!data.node.template[name].value ||
|
||||||
|
data.node.template[name].value === ""
|
||||||
|
? [""]
|
||||||
|
: data.node.template[name].value
|
||||||
|
}
|
||||||
|
onChange={(t: string[]) => {
|
||||||
|
data.node.template[name].value = t;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : data.node.template[name].multiline ? (
|
||||||
|
<TextAreaComponent
|
||||||
|
disabled={disabled}
|
||||||
|
value={data.node.template[name].value ?? ""}
|
||||||
|
onChange={(t: string) => {
|
||||||
|
data.node.template[name].value = t;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<InputComponent
|
||||||
|
disabled={disabled}
|
||||||
|
disableCopyPaste={true}
|
||||||
|
password={data.node.template[name].password ?? false}
|
||||||
|
value={data.node.template[name].value ?? ""}
|
||||||
|
onChange={(t) => {
|
||||||
|
data.node.template[name].value = t;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : left === true && type === "bool" ? (
|
||||||
|
<div className="mt-2">
|
||||||
|
<ToggleComponent
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
value={data.node.template[name].value ?? ""}
|
enabled={enabled}
|
||||||
onChange={(t: string) => {
|
setEnabled={(t) => {
|
||||||
data.node.template[name].value = t;
|
data.node.template[name].value = t;
|
||||||
|
setEnabled(t);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
</div>
|
||||||
<InputComponent
|
) : left === true && type === "float" ? (
|
||||||
|
<div className="mt-2 w-full">
|
||||||
|
<FloatComponent
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
disableCopyPaste={true}
|
disableCopyPaste={true}
|
||||||
password={data.node.template[name].password ?? false}
|
|
||||||
value={data.node.template[name].value ?? ""}
|
value={data.node.template[name].value ?? ""}
|
||||||
onChange={(t) => {
|
onChange={(t) => {
|
||||||
data.node.template[name].value = t;
|
data.node.template[name].value = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
</div>
|
||||||
</div>
|
) : left === true &&
|
||||||
) : left === true && type === "bool" ? (
|
type === "str" &&
|
||||||
<div className="mt-2">
|
data.node.template[name].options ? (
|
||||||
<ToggleComponent
|
<div className="w-full">
|
||||||
|
<Dropdown
|
||||||
|
options={data.node.template[name].options}
|
||||||
|
onSelect={(newValue) =>
|
||||||
|
(data.node.template[name].value = newValue)
|
||||||
|
}
|
||||||
|
value={data.node.template[name].value ?? "Choose an option"}
|
||||||
|
></Dropdown>
|
||||||
|
</div>
|
||||||
|
) : left === true && type === "code" ? (
|
||||||
|
<CodeAreaComponent
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
enabled={enabled}
|
value={data.node.template[name].value ?? ""}
|
||||||
setEnabled={(t) => {
|
onChange={(t: string) => {
|
||||||
data.node.template[name].value = t;
|
data.node.template[name].value = t;
|
||||||
setEnabled(t);
|
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
) : left === true && type === "file" ? (
|
||||||
) : left === true && type === "float" ? (
|
<InputFileComponent
|
||||||
<div className="mt-2 w-full">
|
|
||||||
<FloatComponent
|
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
disableCopyPaste={true}
|
|
||||||
value={data.node.template[name].value ?? ""}
|
value={data.node.template[name].value ?? ""}
|
||||||
onChange={(t) => {
|
onChange={(t: string) => {
|
||||||
data.node.template[name].value = t;
|
data.node.template[name].value = t;
|
||||||
|
}}
|
||||||
|
fileTypes={data.node.template[name].fileTypes}
|
||||||
|
suffixes={data.node.template[name].suffixes}
|
||||||
|
onFileChange={(t: string) => {
|
||||||
|
data.node.template[name].content = t;
|
||||||
|
}}
|
||||||
|
></InputFileComponent>
|
||||||
|
) : left === true && type === "int" ? (
|
||||||
|
<div className="mt-2 w-full">
|
||||||
|
<IntComponent
|
||||||
|
disabled={disabled}
|
||||||
|
disableCopyPaste={true}
|
||||||
|
value={data.node.template[name].value ?? ""}
|
||||||
|
onChange={(t) => {
|
||||||
|
data.node.template[name].value = t;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : left === true &&
|
) : left === true && type === "prompt" ? (
|
||||||
type === "str" &&
|
<PromptAreaComponent
|
||||||
data.node.template[name].options ? (
|
|
||||||
<div className="w-full">
|
|
||||||
<Dropdown
|
|
||||||
options={data.node.template[name].options}
|
|
||||||
onSelect={(newValue) =>
|
|
||||||
(data.node.template[name].value = newValue)
|
|
||||||
}
|
|
||||||
value={data.node.template[name].value ?? "Choose an option"}
|
|
||||||
></Dropdown>
|
|
||||||
</div>
|
|
||||||
) : left === true && type === "code" ? (
|
|
||||||
<CodeAreaComponent
|
|
||||||
disabled={disabled}
|
|
||||||
value={data.node.template[name].value ?? ""}
|
|
||||||
onChange={(t: string) => {
|
|
||||||
data.node.template[name].value = t;
|
|
||||||
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : left === true && type === "file" ? (
|
|
||||||
<InputFileComponent
|
|
||||||
disabled={disabled}
|
|
||||||
value={data.node.template[name].value ?? ""}
|
|
||||||
onChange={(t: string) => {
|
|
||||||
data.node.template[name].value = t;
|
|
||||||
}}
|
|
||||||
fileTypes={data.node.template[name].fileTypes}
|
|
||||||
suffixes={data.node.template[name].suffixes}
|
|
||||||
onFileChange={(t: string) => {
|
|
||||||
data.node.template[name].content = t;
|
|
||||||
|
|
||||||
}}
|
|
||||||
></InputFileComponent>
|
|
||||||
) : left === true && type === "int" ? (
|
|
||||||
<div className="mt-2 w-full">
|
|
||||||
<IntComponent
|
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
disableCopyPaste={true}
|
|
||||||
value={data.node.template[name].value ?? ""}
|
value={data.node.template[name].value ?? ""}
|
||||||
onChange={(t) => {
|
onChange={(t: string) => {
|
||||||
data.node.template[name].value = t;
|
data.node.template[name].value = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
) : (
|
||||||
) : left === true && type === "prompt" ? (
|
<></>
|
||||||
<PromptAreaComponent
|
)}
|
||||||
disabled={disabled}
|
</>
|
||||||
value={data.node.template[name].value ?? ""}
|
</div>
|
||||||
onChange={(t: string) => {
|
|
||||||
data.node.template[name].value = t;
|
|
||||||
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -101,8 +101,7 @@ export default function GenericNode({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {}, [closePopUp, data.node.template]);
|
||||||
}, [closePopUp, data.node.template]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,13 @@ const ShadTooltip = (props) => {
|
||||||
<Tooltip delayDuration={props.delayDuration}>
|
<Tooltip delayDuration={props.delayDuration}>
|
||||||
<TooltipTrigger asChild>{props.children}</TooltipTrigger>
|
<TooltipTrigger asChild>{props.children}</TooltipTrigger>
|
||||||
|
|
||||||
<TooltipContent
|
<TooltipContent
|
||||||
side={props.side}
|
side={props.side}
|
||||||
avoidCollisions={false}
|
avoidCollisions={false}
|
||||||
sticky="always"
|
sticky="always"
|
||||||
>
|
>
|
||||||
{props.content}
|
{props.content}
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,25 @@ import { Link } from "react-router-dom";
|
||||||
import { TabsContext } from "../../contexts/tabsContext";
|
import { TabsContext } from "../../contexts/tabsContext";
|
||||||
import { FlowType } from "../../types/flow";
|
import { FlowType } from "../../types/flow";
|
||||||
import { gradients } from "../../utils";
|
import { gradients } from "../../utils";
|
||||||
import { CardTitle, CardDescription, CardFooter, Card, CardHeader } from "../ui/card";
|
import {
|
||||||
|
CardTitle,
|
||||||
|
CardDescription,
|
||||||
|
CardFooter,
|
||||||
|
Card,
|
||||||
|
CardHeader,
|
||||||
|
} from "../ui/card";
|
||||||
|
|
||||||
|
export const CardComponent = ({
|
||||||
export const CardComponent = ({ flow, id, onDelete, button }: { flow: FlowType; id: string, onDelete?: () => void, button?: JSX.Element }) => {
|
flow,
|
||||||
|
id,
|
||||||
|
onDelete,
|
||||||
|
button,
|
||||||
|
}: {
|
||||||
|
flow: FlowType;
|
||||||
|
id: string;
|
||||||
|
onDelete?: () => void;
|
||||||
|
button?: JSX.Element;
|
||||||
|
}) => {
|
||||||
const { removeFlow } = useContext(TabsContext);
|
const { removeFlow } = useContext(TabsContext);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -20,19 +35,14 @@ export const CardComponent = ({ flow, id, onDelete, button }: { flow: FlowType;
|
||||||
"rounded-full w-8 h-8 flex items-center justify-center text-2xl " +
|
"rounded-full w-8 h-8 flex items-center justify-center text-2xl " +
|
||||||
gradients[parseInt(flow.id.slice(0, 6), 16) % gradients.length]
|
gradients[parseInt(flow.id.slice(0, 6), 16) % gradients.length]
|
||||||
}
|
}
|
||||||
>
|
></span>
|
||||||
</span>
|
<span className="flex-1 truncate-doubleline">{flow.name}</span>
|
||||||
<span className="flex-1 truncate-doubleline">
|
|
||||||
{flow.name}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
{onDelete &&
|
{onDelete && (
|
||||||
<button
|
<button onClick={onDelete}>
|
||||||
onClick={onDelete}
|
<Trash2 className="w-5 text-primary opacity-0 group-hover:opacity-100 transition-all" />
|
||||||
>
|
</button>
|
||||||
<Trash2 className="w-5 text-primary opacity-0 group-hover:opacity-100 transition-all" />
|
)}
|
||||||
</button>
|
|
||||||
}
|
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription className="pt-2 pb-2">
|
<CardDescription className="pt-2 pb-2">
|
||||||
<div className="truncate-doubleline">
|
<div className="truncate-doubleline">
|
||||||
|
|
@ -53,10 +63,7 @@ export const CardComponent = ({ flow, id, onDelete, button }: { flow: FlowType;
|
||||||
<span className="text-base"> </span>OpenAI+
|
<span className="text-base"> </span>OpenAI+
|
||||||
</Badge> */}
|
</Badge> */}
|
||||||
</div>
|
</div>
|
||||||
{button &&
|
{button && button}
|
||||||
button
|
|
||||||
}
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ export default function Dropdown({
|
||||||
<>
|
<>
|
||||||
<div className={editNode ? "mt-1" : "relative mt-1"}>
|
<div className={editNode ? "mt-1" : "relative mt-1"}>
|
||||||
<Listbox.Button
|
<Listbox.Button
|
||||||
|
|
||||||
className={
|
className={
|
||||||
editNode
|
editNode
|
||||||
? "relative pr-8 placeholder:text-center block w-full pt-0.5 pb-0.5 form-input dark:bg-gray-900 dark:text-gray-300 dark:border-gray-600 rounded-md shadow-sm sm:text-sm border-gray-300 border-1" +
|
? "relative pr-8 placeholder:text-center block w-full pt-0.5 pb-0.5 form-input dark:bg-gray-900 dark:text-gray-300 dark:border-gray-600 rounded-md shadow-sm sm:text-sm border-gray-300 border-1" +
|
||||||
|
|
@ -38,12 +37,10 @@ export default function Dropdown({
|
||||||
INPUT_STYLE
|
INPUT_STYLE
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
||||||
<span className="block truncate w-full">{internalValue}</span>
|
<span className="block truncate w-full">{internalValue}</span>
|
||||||
<span
|
<span
|
||||||
className={
|
className={
|
||||||
|
"pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"
|
||||||
"pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ChevronUpDownIcon
|
<ChevronUpDownIcon
|
||||||
|
|
|
||||||
|
|
@ -49,15 +49,11 @@ export const MenuBar = ({ flows, tabId }) => {
|
||||||
</Link>
|
</Link>
|
||||||
<div className="flex items-center font-medium text-sm rounded-md py-1 px-1.5 gap-0.5">
|
<div className="flex items-center font-medium text-sm rounded-md py-1 px-1.5 gap-0.5">
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger >
|
<DropdownMenuTrigger>
|
||||||
<Button
|
<Button className="gap-2 flex items-center" variant="primary">
|
||||||
className="gap-2 flex items-center"
|
{current_flow.name}
|
||||||
variant="primary"
|
<ChevronDown className="w-4 h-4" />
|
||||||
>
|
</Button>
|
||||||
{current_flow.name}
|
|
||||||
<ChevronDown className="w-4 h-4" />
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent className="w-44">
|
<DropdownMenuContent className="w-44">
|
||||||
<DropdownMenuLabel>Edit</DropdownMenuLabel>
|
<DropdownMenuLabel>Edit</DropdownMenuLabel>
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,7 @@ export default function Header() {
|
||||||
<span className="text-2xl ml-4">⛓️</span>
|
<span className="text-2xl ml-4">⛓️</span>
|
||||||
</Link>
|
</Link>
|
||||||
{flows.findIndex((f) => tabId === f.id) !== -1 && tabId !== "" && (
|
{flows.findIndex((f) => tabId === f.id) !== -1 && tabId !== "" && (
|
||||||
<MenuBar
|
<MenuBar flows={flows} tabId={tabId} />
|
||||||
flows={flows}
|
|
||||||
tabId={tabId}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,7 @@ const buttonVariants = cva(
|
||||||
"border border-input hover:bg-accent hover:text-accent-foreground",
|
"border border-input hover:bg-accent hover:text-accent-foreground",
|
||||||
primary:
|
primary:
|
||||||
"border bg-background text-secondary-foreground hover:bg-background/80 hover:shadow-sm",
|
"border bg-background text-secondary-foreground hover:bg-background/80 hover:shadow-sm",
|
||||||
secondary:
|
secondary: "bg-muted text-secondary-foreground hover:bg-secondary/80",
|
||||||
"bg-muted text-secondary-foreground hover:bg-secondary/80",
|
|
||||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||||
link: "underline-offset-4 hover:underline text-primary",
|
link: "underline-offset-4 hover:underline text-primary",
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -129,4 +129,3 @@ export const EXPORT_CODE_DIALOG =
|
||||||
*/
|
*/
|
||||||
export const INPUT_STYLE =
|
export const INPUT_STYLE =
|
||||||
" focus:ring-1 focus:ring-offset-1 focus:ring-ring focus:outline-none ";
|
" focus:ring-1 focus:ring-offset-1 focus:ring-ring focus:outline-none ";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,17 @@ export default function ContextWrapper({ children }: { children: ReactNode }) {
|
||||||
<>
|
<>
|
||||||
<ReactFlowProvider>
|
<ReactFlowProvider>
|
||||||
<DarkProvider>
|
<DarkProvider>
|
||||||
<TypesProvider>
|
<TypesProvider>
|
||||||
<LocationProvider>
|
<LocationProvider>
|
||||||
<AlertProvider>
|
<AlertProvider>
|
||||||
<TabsProvider>
|
<TabsProvider>
|
||||||
<UndoRedoProvider>
|
<UndoRedoProvider>
|
||||||
<PopUpProvider>{children}</PopUpProvider>
|
<PopUpProvider>{children}</PopUpProvider>
|
||||||
</UndoRedoProvider>
|
</UndoRedoProvider>
|
||||||
</TabsProvider>
|
</TabsProvider>
|
||||||
</AlertProvider>
|
</AlertProvider>
|
||||||
</LocationProvider>
|
</LocationProvider>
|
||||||
</TypesProvider>
|
</TypesProvider>
|
||||||
</DarkProvider>
|
</DarkProvider>
|
||||||
</ReactFlowProvider>
|
</ReactFlowProvider>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -549,7 +549,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
uploadFlows,
|
uploadFlows,
|
||||||
uploadFlow,
|
uploadFlow,
|
||||||
getNodeId,
|
getNodeId,
|
||||||
paste
|
paste,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,10 @@
|
||||||
import { createContext, useCallback, useContext, useEffect, useState } from "react";
|
import {
|
||||||
|
createContext,
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
import { Edge, Node, useReactFlow } from "reactflow";
|
import { Edge, Node, useReactFlow } from "reactflow";
|
||||||
import { cloneDeep } from "lodash";
|
import { cloneDeep } from "lodash";
|
||||||
import { TabsContext } from "./tabsContext";
|
import { TabsContext } from "./tabsContext";
|
||||||
|
|
@ -10,22 +16,22 @@ type undoRedoContextType = {
|
||||||
};
|
};
|
||||||
|
|
||||||
type UseUndoRedoOptions = {
|
type UseUndoRedoOptions = {
|
||||||
maxHistorySize: number;
|
maxHistorySize: number;
|
||||||
enableShortcuts: boolean;
|
enableShortcuts: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type UseUndoRedo = (options?: UseUndoRedoOptions) => {
|
type UseUndoRedo = (options?: UseUndoRedoOptions) => {
|
||||||
undo: () => void;
|
undo: () => void;
|
||||||
redo: () => void;
|
redo: () => void;
|
||||||
takeSnapshot: () => void;
|
takeSnapshot: () => void;
|
||||||
canUndo: boolean;
|
canUndo: boolean;
|
||||||
canRedo: boolean;
|
canRedo: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type HistoryItem = {
|
type HistoryItem = {
|
||||||
nodes: Node[];
|
nodes: Node[];
|
||||||
edges: Edge[];
|
edges: Edge[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialValue = {
|
const initialValue = {
|
||||||
undo: () => {},
|
undo: () => {},
|
||||||
|
|
@ -34,152 +40,144 @@ const initialValue = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultOptions: UseUndoRedoOptions = {
|
const defaultOptions: UseUndoRedoOptions = {
|
||||||
maxHistorySize: 100,
|
maxHistorySize: 100,
|
||||||
enableShortcuts: true,
|
enableShortcuts: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const undoRedoContext = createContext<undoRedoContextType>(initialValue);
|
export const undoRedoContext = createContext<undoRedoContextType>(initialValue);
|
||||||
|
|
||||||
export function UndoRedoProvider({ children }) {
|
export function UndoRedoProvider({ children }) {
|
||||||
const { tabId, flows } = useContext(TabsContext);
|
const { tabId, flows } = useContext(TabsContext);
|
||||||
|
|
||||||
const [past, setPast] = useState<HistoryItem[][]>(flows.map(() => []));
|
const [past, setPast] = useState<HistoryItem[][]>(flows.map(() => []));
|
||||||
const [future, setFuture] = useState<HistoryItem[][]>(flows.map(() => []));
|
const [future, setFuture] = useState<HistoryItem[][]>(flows.map(() => []));
|
||||||
const [tabIndex, setTabIndex] = useState(flows.findIndex((f) => f.id === tabId));
|
const [tabIndex, setTabIndex] = useState(
|
||||||
|
flows.findIndex((f) => f.id === tabId)
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// whenever the flows variable changes, we need to add one array to the past and future states
|
// whenever the flows variable changes, we need to add one array to the past and future states
|
||||||
setPast((old) => flows.map((f, i) => (old[i] ? old[i] : [])));
|
setPast((old) => flows.map((f, i) => (old[i] ? old[i] : [])));
|
||||||
setFuture((old) => flows.map((f, i) => (old[i] ? old[i] : [])));
|
setFuture((old) => flows.map((f, i) => (old[i] ? old[i] : [])));
|
||||||
setTabIndex(flows.findIndex((f) => f.id === tabId));
|
setTabIndex(flows.findIndex((f) => f.id === tabId));
|
||||||
|
}, [flows, tabId]);
|
||||||
|
|
||||||
}, [flows, tabId]);
|
const { setNodes, setEdges, getNodes, getEdges } = useReactFlow();
|
||||||
|
|
||||||
const { setNodes, setEdges, getNodes, getEdges } = useReactFlow();
|
const takeSnapshot = useCallback(() => {
|
||||||
|
// push the current graph to the past state
|
||||||
|
console.log(past);
|
||||||
|
console.log(tabIndex);
|
||||||
|
setPast((old) => {
|
||||||
|
let newPast = cloneDeep(old);
|
||||||
|
newPast[tabIndex] = old[tabIndex].slice(
|
||||||
|
old[tabIndex].length - defaultOptions.maxHistorySize + 1,
|
||||||
|
old[tabIndex].length
|
||||||
|
);
|
||||||
|
newPast[tabIndex].push({ nodes: getNodes(), edges: getEdges() });
|
||||||
|
return newPast;
|
||||||
|
});
|
||||||
|
|
||||||
const takeSnapshot = useCallback(() => {
|
// whenever we take a new snapshot, the redo operations need to be cleared to avoid state mismatches
|
||||||
// push the current graph to the past state
|
setFuture((old) => {
|
||||||
console.log(past);
|
let newFuture = cloneDeep(old);
|
||||||
console.log(tabIndex);
|
newFuture[tabIndex] = [];
|
||||||
|
return newFuture;
|
||||||
|
});
|
||||||
|
}, [getNodes, getEdges, past, future, flows, tabId, setPast, setFuture]);
|
||||||
|
|
||||||
|
const undo = useCallback(() => {
|
||||||
|
// get the last state that we want to go back to
|
||||||
|
const pastState = past[tabIndex][past[tabIndex].length - 1];
|
||||||
|
|
||||||
|
if (pastState) {
|
||||||
|
// first we remove the state from the history
|
||||||
setPast((old) => {
|
setPast((old) => {
|
||||||
let newPast = cloneDeep(old);
|
let newPast = cloneDeep(old);
|
||||||
newPast[tabIndex] = old[tabIndex].slice(
|
newPast[tabIndex] = old[tabIndex].slice(0, old[tabIndex].length - 1);
|
||||||
old[tabIndex].length - defaultOptions.maxHistorySize + 1,
|
return newPast;
|
||||||
old[tabIndex].length
|
});
|
||||||
);
|
// we store the current graph for the redo operation
|
||||||
|
setFuture((old) => {
|
||||||
|
let newFuture = cloneDeep(old);
|
||||||
|
newFuture[tabIndex] = old[tabIndex];
|
||||||
|
newFuture[tabIndex].push({ nodes: getNodes(), edges: getEdges() });
|
||||||
|
return newFuture;
|
||||||
|
});
|
||||||
|
// now we can set the graph to the past state
|
||||||
|
setNodes(pastState.nodes);
|
||||||
|
setEdges(pastState.edges);
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
setNodes,
|
||||||
|
setEdges,
|
||||||
|
getNodes,
|
||||||
|
getEdges,
|
||||||
|
future,
|
||||||
|
past,
|
||||||
|
setFuture,
|
||||||
|
setPast,
|
||||||
|
tabIndex,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const redo = useCallback(() => {
|
||||||
|
const futureState = future[tabIndex][future[tabIndex].length - 1];
|
||||||
|
|
||||||
|
if (futureState) {
|
||||||
|
setFuture((old) => {
|
||||||
|
let newFuture = cloneDeep(old);
|
||||||
|
newFuture[tabIndex] = old[tabIndex].slice(0, old[tabIndex].length - 1);
|
||||||
|
return newFuture;
|
||||||
|
});
|
||||||
|
setPast((old) => {
|
||||||
|
let newPast = cloneDeep(old);
|
||||||
|
newPast[tabIndex] = old[tabIndex];
|
||||||
newPast[tabIndex].push({ nodes: getNodes(), edges: getEdges() });
|
newPast[tabIndex].push({ nodes: getNodes(), edges: getEdges() });
|
||||||
return newPast;
|
return newPast;
|
||||||
});
|
});
|
||||||
|
setNodes(futureState.nodes);
|
||||||
|
setEdges(futureState.edges);
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
future,
|
||||||
|
past,
|
||||||
|
setFuture,
|
||||||
|
setPast,
|
||||||
|
setNodes,
|
||||||
|
setEdges,
|
||||||
|
getNodes,
|
||||||
|
getEdges,
|
||||||
|
future,
|
||||||
|
tabIndex,
|
||||||
|
]);
|
||||||
|
|
||||||
// whenever we take a new snapshot, the redo operations need to be cleared to avoid state mismatches
|
useEffect(() => {
|
||||||
setFuture((old) => {
|
// this effect is used to attach the global event handlers
|
||||||
let newFuture = cloneDeep(old);
|
if (!defaultOptions.enableShortcuts) {
|
||||||
newFuture[tabIndex] = [];
|
return;
|
||||||
return newFuture;
|
}
|
||||||
});
|
|
||||||
}, [
|
|
||||||
getNodes,
|
|
||||||
getEdges,
|
|
||||||
past,
|
|
||||||
future,
|
|
||||||
flows,
|
|
||||||
tabId,
|
|
||||||
setPast,
|
|
||||||
setFuture,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const undo = useCallback(() => {
|
const keyDownHandler = (event: KeyboardEvent) => {
|
||||||
// get the last state that we want to go back to
|
if (
|
||||||
const pastState = past[tabIndex][past[tabIndex].length - 1];
|
event.key === "z" &&
|
||||||
|
(event.ctrlKey || event.metaKey) &&
|
||||||
if (pastState) {
|
event.shiftKey
|
||||||
// first we remove the state from the history
|
) {
|
||||||
setPast((old) => {
|
redo();
|
||||||
let newPast = cloneDeep(old);
|
} else if (event.key === "y" && (event.ctrlKey || event.metaKey)) {
|
||||||
newPast[tabIndex] = old[tabIndex].slice(0, old[tabIndex].length - 1);
|
event.preventDefault(); // prevent the default action
|
||||||
return newPast;
|
redo();
|
||||||
});
|
} else if (event.key === "z" && (event.ctrlKey || event.metaKey)) {
|
||||||
// we store the current graph for the redo operation
|
undo();
|
||||||
setFuture((old) => {
|
|
||||||
let newFuture = cloneDeep(old);
|
|
||||||
newFuture[tabIndex] = old[tabIndex];
|
|
||||||
newFuture[tabIndex].push({ nodes: getNodes(), edges: getEdges() });
|
|
||||||
return newFuture;
|
|
||||||
});
|
|
||||||
// now we can set the graph to the past state
|
|
||||||
setNodes(pastState.nodes);
|
|
||||||
setEdges(pastState.edges);
|
|
||||||
}
|
}
|
||||||
}, [
|
};
|
||||||
setNodes,
|
|
||||||
setEdges,
|
|
||||||
getNodes,
|
|
||||||
getEdges,
|
|
||||||
future,
|
|
||||||
past,
|
|
||||||
setFuture,
|
|
||||||
setPast,
|
|
||||||
tabIndex,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const redo = useCallback(() => {
|
document.addEventListener("keydown", keyDownHandler);
|
||||||
const futureState = future[tabIndex][future[tabIndex].length - 1];
|
|
||||||
|
|
||||||
if (futureState) {
|
return () => {
|
||||||
setFuture((old) => {
|
document.removeEventListener("keydown", keyDownHandler);
|
||||||
let newFuture = cloneDeep(old);
|
};
|
||||||
newFuture[tabIndex] = old[tabIndex].slice(0, old[tabIndex].length - 1);
|
}, [undo, redo]);
|
||||||
return newFuture;
|
|
||||||
});
|
|
||||||
setPast((old) => {
|
|
||||||
let newPast = cloneDeep(old);
|
|
||||||
newPast[tabIndex] = old[tabIndex];
|
|
||||||
newPast[tabIndex].push({ nodes: getNodes(), edges: getEdges() });
|
|
||||||
return newPast;
|
|
||||||
});
|
|
||||||
setNodes(futureState.nodes);
|
|
||||||
setEdges(futureState.edges);
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
future,
|
|
||||||
past,
|
|
||||||
setFuture,
|
|
||||||
setPast,
|
|
||||||
setNodes,
|
|
||||||
setEdges,
|
|
||||||
getNodes,
|
|
||||||
getEdges,
|
|
||||||
future,
|
|
||||||
tabIndex,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// this effect is used to attach the global event handlers
|
|
||||||
if (!defaultOptions.enableShortcuts) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const keyDownHandler = (event: KeyboardEvent) => {
|
|
||||||
if (
|
|
||||||
event.key === "z" &&
|
|
||||||
(event.ctrlKey || event.metaKey) &&
|
|
||||||
event.shiftKey
|
|
||||||
) {
|
|
||||||
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)) {
|
|
||||||
undo();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener("keydown", keyDownHandler);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener("keydown", keyDownHandler);
|
|
||||||
};
|
|
||||||
}, [undo, redo]);
|
|
||||||
return (
|
return (
|
||||||
<undoRedoContext.Provider
|
<undoRedoContext.Provider
|
||||||
value={{
|
value={{
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,6 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
|
||||||
}
|
}
|
||||||
onChange={(t: string[]) => {
|
onChange={(t: string[]) => {
|
||||||
data.node.template[n].value = t;
|
data.node.template[n].value = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : data.node.template[n].multiline ? (
|
) : data.node.template[n].multiline ? (
|
||||||
|
|
@ -182,7 +181,6 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
|
||||||
value={data.node.template[n].value ?? ""}
|
value={data.node.template[n].value ?? ""}
|
||||||
onChange={(t: string) => {
|
onChange={(t: string) => {
|
||||||
data.node.template[n].value = t;
|
data.node.template[n].value = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -195,7 +193,6 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
|
||||||
value={data.node.template[n].value ?? ""}
|
value={data.node.template[n].value ?? ""}
|
||||||
onChange={(t) => {
|
onChange={(t) => {
|
||||||
data.node.template[n].value = t;
|
data.node.template[n].value = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
@ -208,7 +205,6 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
|
||||||
setEnabled={(e) => {
|
setEnabled={(e) => {
|
||||||
data.node.template[n].value = e;
|
data.node.template[n].value = e;
|
||||||
setEnabled(e);
|
setEnabled(e);
|
||||||
|
|
||||||
}}
|
}}
|
||||||
disabled={false}
|
disabled={false}
|
||||||
/>
|
/>
|
||||||
|
|
@ -221,7 +217,6 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
|
||||||
value={data.node.template[n].value ?? ""}
|
value={data.node.template[n].value ?? ""}
|
||||||
onChange={(t) => {
|
onChange={(t) => {
|
||||||
data.node.template[n].value = t;
|
data.node.template[n].value = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -249,7 +244,6 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
|
||||||
value={data.node.template[n].value ?? ""}
|
value={data.node.template[n].value ?? ""}
|
||||||
onChange={(t) => {
|
onChange={(t) => {
|
||||||
data.node.template[n].value = t;
|
data.node.template[n].value = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -266,7 +260,6 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
|
||||||
suffixes={data.node.template[n].suffixes}
|
suffixes={data.node.template[n].suffixes}
|
||||||
onFileChange={(t: string) => {
|
onFileChange={(t: string) => {
|
||||||
data.node.template[n].content = t;
|
data.node.template[n].content = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
></InputFileComponent>
|
></InputFileComponent>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -278,7 +271,6 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
|
||||||
value={data.node.template[n].value ?? ""}
|
value={data.node.template[n].value ?? ""}
|
||||||
onChange={(t: string) => {
|
onChange={(t: string) => {
|
||||||
data.node.template[n].value = t;
|
data.node.template[n].value = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,6 @@ export default function ModalField({
|
||||||
}
|
}
|
||||||
onChange={(t: string[]) => {
|
onChange={(t: string[]) => {
|
||||||
data.node.template[name].value = t;
|
data.node.template[name].value = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : data.node.template[name].multiline ? (
|
) : data.node.template[name].multiline ? (
|
||||||
|
|
@ -79,7 +78,6 @@ export default function ModalField({
|
||||||
value={data.node.template[name].value ?? ""}
|
value={data.node.template[name].value ?? ""}
|
||||||
onChange={(t: string) => {
|
onChange={(t: string) => {
|
||||||
data.node.template[name].value = t;
|
data.node.template[name].value = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -89,7 +87,6 @@ export default function ModalField({
|
||||||
value={data.node.template[name].value ?? ""}
|
value={data.node.template[name].value ?? ""}
|
||||||
onChange={(t) => {
|
onChange={(t) => {
|
||||||
data.node.template[name].value = t;
|
data.node.template[name].value = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
@ -103,7 +100,6 @@ export default function ModalField({
|
||||||
setEnabled={(t) => {
|
setEnabled={(t) => {
|
||||||
data.node.template[name].value = t;
|
data.node.template[name].value = t;
|
||||||
setEnabled(t);
|
setEnabled(t);
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -114,7 +110,6 @@ export default function ModalField({
|
||||||
value={data.node.template[name].value ?? ""}
|
value={data.node.template[name].value ?? ""}
|
||||||
onChange={(t) => {
|
onChange={(t) => {
|
||||||
data.node.template[name].value = t;
|
data.node.template[name].value = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -133,7 +128,6 @@ export default function ModalField({
|
||||||
value={data.node.template[name].value ?? ""}
|
value={data.node.template[name].value ?? ""}
|
||||||
onChange={(t) => {
|
onChange={(t) => {
|
||||||
data.node.template[name].value = t;
|
data.node.template[name].value = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -149,7 +143,6 @@ export default function ModalField({
|
||||||
suffixes={data.node.template[name].suffixes}
|
suffixes={data.node.template[name].suffixes}
|
||||||
onFileChange={(t: string) => {
|
onFileChange={(t: string) => {
|
||||||
data.node.template[name].content = t;
|
data.node.template[name].content = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
></InputFileComponent>
|
></InputFileComponent>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -160,7 +153,6 @@ export default function ModalField({
|
||||||
value={data.node.template[name].value ?? ""}
|
value={data.node.template[name].value ?? ""}
|
||||||
onChange={(t: string) => {
|
onChange={(t: string) => {
|
||||||
data.node.template[name].value = t;
|
data.node.template[name].value = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -171,7 +163,6 @@ export default function ModalField({
|
||||||
value={data.node.template[name].value ?? ""}
|
value={data.node.template[name].value ?? ""}
|
||||||
onChange={(t: string) => {
|
onChange={(t: string) => {
|
||||||
data.node.template[name].value = t;
|
data.node.template[name].value = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import EditFlowSettings from "../../components/nameInputComponent";
|
||||||
import { Label } from "../../components/ui/label";
|
import { Label } from "../../components/ui/label";
|
||||||
import { Input } from "../../components/ui/input";
|
import { Input } from "../../components/ui/input";
|
||||||
import { Textarea } from "../../components/ui/textarea";
|
import { Textarea } from "../../components/ui/textarea";
|
||||||
|
import { Download } from "lucide-react";
|
||||||
|
|
||||||
export default function ExportModal() {
|
export default function ExportModal() {
|
||||||
const [open, setOpen] = useState(true);
|
const [open, setOpen] = useState(true);
|
||||||
|
|
@ -48,7 +49,7 @@ export default function ExportModal() {
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="flex items-center">
|
<DialogTitle className="flex items-center">
|
||||||
<span className="pr-2">Export</span>
|
<span className="pr-2">Export</span>
|
||||||
<ArrowDownTrayIcon
|
<Download
|
||||||
className="h-6 w-6 text-gray-800 pl-1 dark:text-white"
|
className="h-6 w-6 text-gray-800 pl-1 dark:text-white"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -85,24 +85,33 @@ export default function CommunityPage() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span className="flex pb-8 px-6 w-[70%] text-muted-foreground">
|
<span className="flex pb-8 px-6 w-[70%] text-muted-foreground">
|
||||||
Discover and learn from shared examples by the LangFlow community. We welcome new example contributions that can help our community explore new and powerful features.
|
Discover and learn from shared examples by the LangFlow community. We
|
||||||
|
welcome new example contributions that can help our community explore
|
||||||
|
new and powerful features.
|
||||||
</span>
|
</span>
|
||||||
<div className="w-full p-4 grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
<div className="w-full p-4 grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||||
{!loadingExamples &&
|
{!loadingExamples &&
|
||||||
examples.map((flow, idx) => (
|
examples.map((flow, idx) => (
|
||||||
<CardComponent key={idx} flow={flow} id={flow.id} button={<Button
|
<CardComponent
|
||||||
variant="outline"
|
key={idx}
|
||||||
size="sm"
|
flow={flow}
|
||||||
className="whitespace-nowrap "
|
id={flow.id}
|
||||||
onClick={() => {
|
button={
|
||||||
addFlow(flow, true).then((id) => {
|
<Button
|
||||||
navigate("/flow/" + id);
|
variant="outline"
|
||||||
});
|
size="sm"
|
||||||
}}
|
className="whitespace-nowrap "
|
||||||
>
|
onClick={() => {
|
||||||
<GitFork className="w-4 mr-2" />
|
addFlow(flow, true).then((id) => {
|
||||||
Fork Example
|
navigate("/flow/" + id);
|
||||||
</Button>}/>
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<GitFork className="w-4 mr-2" />
|
||||||
|
Fork Example
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -317,53 +317,53 @@ export default function Page({ flow }: { flow: FlowType }) {
|
||||||
<div className="w-full h-full" ref={reactFlowWrapper}>
|
<div className="w-full h-full" ref={reactFlowWrapper}>
|
||||||
{Object.keys(templates).length > 0 &&
|
{Object.keys(templates).length > 0 &&
|
||||||
Object.keys(types).length > 0 ? (
|
Object.keys(types).length > 0 ? (
|
||||||
<div className="w-full h-full">
|
<div className="w-full h-full">
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
onMove={() => {
|
onMove={() => {
|
||||||
updateFlow({
|
updateFlow({
|
||||||
...flow,
|
...flow,
|
||||||
data: reactFlowInstance.toObject(),
|
data: reactFlowInstance.toObject(),
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
edges={edges}
|
edges={edges}
|
||||||
onPaneClick={() => {
|
onPaneClick={() => {
|
||||||
setDisableCopyPaste(false);
|
setDisableCopyPaste(false);
|
||||||
}}
|
}}
|
||||||
onPaneMouseLeave={() => {
|
onPaneMouseLeave={() => {
|
||||||
setDisableCopyPaste(true);
|
setDisableCopyPaste(true);
|
||||||
}}
|
}}
|
||||||
onNodesChange={onNodesChange}
|
onNodesChange={onNodesChange}
|
||||||
onEdgesChange={onEdgesChangeMod}
|
onEdgesChange={onEdgesChangeMod}
|
||||||
onConnect={onConnect}
|
onConnect={onConnect}
|
||||||
disableKeyboardA11y={true}
|
disableKeyboardA11y={true}
|
||||||
onLoad={setReactFlowInstance}
|
onLoad={setReactFlowInstance}
|
||||||
onInit={setReactFlowInstance}
|
onInit={setReactFlowInstance}
|
||||||
nodeTypes={nodeTypes}
|
nodeTypes={nodeTypes}
|
||||||
onEdgeUpdate={onEdgeUpdate}
|
onEdgeUpdate={onEdgeUpdate}
|
||||||
onEdgeUpdateStart={onEdgeUpdateStart}
|
onEdgeUpdateStart={onEdgeUpdateStart}
|
||||||
onEdgeUpdateEnd={onEdgeUpdateEnd}
|
onEdgeUpdateEnd={onEdgeUpdateEnd}
|
||||||
onNodeDragStart={onNodeDragStart}
|
onNodeDragStart={onNodeDragStart}
|
||||||
onSelectionDragStart={onSelectionDragStart}
|
onSelectionDragStart={onSelectionDragStart}
|
||||||
onSelectionEnd={onSelectionEnd}
|
onSelectionEnd={onSelectionEnd}
|
||||||
onSelectionStart={onSelectionStart}
|
onSelectionStart={onSelectionStart}
|
||||||
onEdgesDelete={onEdgesDelete}
|
onEdgesDelete={onEdgesDelete}
|
||||||
connectionLineComponent={ConnectionLineComponent}
|
connectionLineComponent={ConnectionLineComponent}
|
||||||
onDragOver={onDragOver}
|
onDragOver={onDragOver}
|
||||||
onDrop={onDrop}
|
onDrop={onDrop}
|
||||||
onNodesDelete={onDelete}
|
onNodesDelete={onDelete}
|
||||||
onSelectionChange={onSelectionChange}
|
onSelectionChange={onSelectionChange}
|
||||||
nodesDraggable={!disableCopyPaste}
|
nodesDraggable={!disableCopyPaste}
|
||||||
panOnDrag={!disableCopyPaste}
|
panOnDrag={!disableCopyPaste}
|
||||||
zoomOnDoubleClick={!disableCopyPaste}
|
zoomOnDoubleClick={!disableCopyPaste}
|
||||||
selectNodesOnDrag={false}
|
selectNodesOnDrag={false}
|
||||||
className="theme-attribution"
|
className="theme-attribution"
|
||||||
>
|
>
|
||||||
<Background className="dark:bg-gray-900" />
|
<Background className="dark:bg-gray-900" />
|
||||||
<Controls className="[&>button]:text-black [&>button]:dark:bg-gray-800 hover:[&>button]:dark:bg-gray-700 [&>button]:dark:text-gray-400 [&>button]:dark:fill-gray-400 [&>button]:dark:border-gray-600"></Controls>
|
<Controls className="[&>button]:text-black [&>button]:dark:bg-gray-800 hover:[&>button]:dark:bg-gray-700 [&>button]:dark:text-gray-400 [&>button]:dark:fill-gray-400 [&>button]:dark:border-gray-600"></Controls>
|
||||||
</ReactFlow>
|
</ReactFlow>
|
||||||
<Chat flow={flow} reactFlowInstance={reactFlowInstance} />
|
<Chat flow={flow} reactFlowInstance={reactFlowInstance} />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<></>
|
<></>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,19 @@ import Page from "./components/PageComponent";
|
||||||
import { TabsContext } from "../../contexts/tabsContext";
|
import { TabsContext } from "../../contexts/tabsContext";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
|
||||||
export default function FlowPage(){
|
export default function FlowPage() {
|
||||||
const { flows, tabId, setTabId } = useContext(TabsContext);
|
const { flows, tabId, setTabId } = useContext(TabsContext);
|
||||||
const {id} = useParams();
|
const { id } = useParams();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTabId(id);
|
setTabId(id);
|
||||||
}, [id])
|
}, [id]);
|
||||||
return (
|
return (
|
||||||
<div className="h-full w-full overflow-hidden">
|
<div className="h-full w-full overflow-hidden">
|
||||||
{flows.length > 0 && tabId !== "" && flows.findIndex(flow => flow.id === tabId) !== -1 &&
|
{flows.length > 0 &&
|
||||||
<Page flow={flows.find(flow => flow.id === tabId)} />
|
tabId !== "" &&
|
||||||
}
|
flows.findIndex((flow) => flow.id === tabId) !== -1 && (
|
||||||
|
<Page flow={flows.find((flow) => flow.id === tabId)} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
import { Route, Routes } from "react-router-dom"
|
import { Route, Routes } from "react-router-dom";
|
||||||
import HomePage from "./pages/MainPage";
|
import HomePage from "./pages/MainPage";
|
||||||
import FlowPage from "./pages/FlowPage";
|
import FlowPage from "./pages/FlowPage";
|
||||||
import CommunityPage from "./pages/CommunityPage";
|
import CommunityPage from "./pages/CommunityPage";
|
||||||
|
|
||||||
const Router = () => {
|
const Router = () => {
|
||||||
return(
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<HomePage/>}/>
|
<Route path="/" element={<HomePage />} />
|
||||||
<Route path="/community" element={<CommunityPage/>}/>
|
<Route path="/community" element={<CommunityPage />} />
|
||||||
<Route path="/flow/:id/">
|
<Route path="/flow/:id/">
|
||||||
<Route path="" element={<FlowPage/>}/>
|
<Route path="" element={<FlowPage />} />
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Router;
|
export default Router;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue