Merge branch 'zustand/io/migration' into refactor/flowToolbar
This commit is contained in:
commit
c2182630b3
137 changed files with 4571 additions and 1203 deletions
|
|
@ -23,14 +23,16 @@ import {
|
|||
OUTPUT_HANDLER_HOVER,
|
||||
TOOLTIP_EMPTY,
|
||||
} from "../../../../constants/constants";
|
||||
import { postCustomComponentUpdate } from "../../../../controllers/API";
|
||||
import useAlertStore from "../../../../stores/alertStore";
|
||||
import useFlowStore from "../../../../stores/flowStore";
|
||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||
import { useTypesStore } from "../../../../stores/typesStore";
|
||||
import { APIClassType } from "../../../../types/api";
|
||||
import { APIClassType, ResponseErrorTypeAPI } from "../../../../types/api";
|
||||
import { ParameterComponentType } from "../../../../types/components";
|
||||
import { NodeDataType } from "../../../../types/flow";
|
||||
import {
|
||||
handleUpdateValues,
|
||||
throttledHandleUpdateValues,
|
||||
} from "../../../../utils/parameterUtils";
|
||||
import {
|
||||
convertObjToArray,
|
||||
convertValuesToNumbers,
|
||||
|
|
@ -86,81 +88,65 @@ export default function ParameterComponent({
|
|||
|
||||
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
|
||||
|
||||
const handleUpdateValues = async (
|
||||
name: string,
|
||||
data: NodeDataType,
|
||||
delayAnimation: boolean = true
|
||||
) => {
|
||||
setIsLoading(true);
|
||||
const code = data.node?.template["code"]?.value;
|
||||
if (!code) {
|
||||
console.error("Code not found in the template");
|
||||
return;
|
||||
}
|
||||
|
||||
await postCustomComponentUpdate(
|
||||
code,
|
||||
name,
|
||||
data.node?.template[name]?.value
|
||||
)
|
||||
.then((res) => {
|
||||
if (res.status === 200 && data.node?.template) {
|
||||
setNode(data.id, (oldNode) => {
|
||||
let newNode = cloneDeep(oldNode);
|
||||
|
||||
newNode.data = {
|
||||
...newNode.data,
|
||||
};
|
||||
|
||||
newNode.data.node.template = res.data.template;
|
||||
|
||||
return newNode;
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error occurred while updating the node:", error);
|
||||
setErrorData({
|
||||
title: "Error while updating the Component",
|
||||
list: [error.response.data.detail.error ?? "Unknown error"],
|
||||
});
|
||||
});
|
||||
|
||||
renderTooltips();
|
||||
if (delayAnimation) {
|
||||
try {
|
||||
// Wait for at least 500 milliseconds
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
// Continue with the request
|
||||
// If the request takes longer than 500 milliseconds, it will not wait an additional 500 milliseconds
|
||||
} catch (error) {
|
||||
console.error("Error occurred while waiting for refresh:", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
} else setIsLoading(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
function fetchData() {
|
||||
async function fetchData() {
|
||||
if (
|
||||
data.node?.template[name]?.refresh &&
|
||||
Object.keys(data.node?.template[name]?.options ?? {}).length === 0
|
||||
// options can be undefined but not an empty array
|
||||
(data.node?.template[name]?.options?.length ?? 0) === 0
|
||||
) {
|
||||
handleUpdateValues(name, data, false);
|
||||
setIsLoading(true);
|
||||
try {
|
||||
let newTemplate = await handleUpdateValues(name, data);
|
||||
if (newTemplate) {
|
||||
setNode(data.id, (oldNode) => {
|
||||
let newNode = cloneDeep(oldNode);
|
||||
newNode.data = {
|
||||
...newNode.data,
|
||||
};
|
||||
newNode.data.node.template = newTemplate;
|
||||
return newNode;
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
let responseError = error as ResponseErrorTypeAPI;
|
||||
setErrorData({
|
||||
title: "Error while updating the Component",
|
||||
list: [responseError.response.data.detail.error ?? "Unknown error"],
|
||||
});
|
||||
}
|
||||
setIsLoading(false);
|
||||
renderTooltips();
|
||||
}
|
||||
}
|
||||
fetchData();
|
||||
}, []);
|
||||
const handleOnNewValue = (
|
||||
const handleOnNewValue = async (
|
||||
newValue: string | string[] | boolean | Object[]
|
||||
): void => {
|
||||
): Promise<void> => {
|
||||
if (data.node!.template[name].value !== newValue) {
|
||||
takeSnapshot();
|
||||
}
|
||||
const shouldUpdate =
|
||||
data.node?.template[name].refresh &&
|
||||
data.node!.template[name].value !== newValue;
|
||||
|
||||
data.node!.template[name].value = newValue; // necessary to enable ctrl+z inside the input
|
||||
|
||||
let newTemplate;
|
||||
if (shouldUpdate) {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
newTemplate = await throttledHandleUpdateValues(name, data);
|
||||
} catch (error) {
|
||||
let responseError = error as ResponseErrorTypeAPI;
|
||||
setErrorData({
|
||||
title: "Error while updating the Component",
|
||||
list: [responseError.response.data.detail.error ?? "Unknown error"],
|
||||
});
|
||||
}
|
||||
setIsLoading(false);
|
||||
// this de
|
||||
}
|
||||
setNode(data.id, (oldNode) => {
|
||||
let newNode = cloneDeep(oldNode);
|
||||
|
||||
|
|
@ -168,7 +154,9 @@ export default function ParameterComponent({
|
|||
...newNode.data,
|
||||
};
|
||||
|
||||
newNode.data.node.template[name].value = newValue;
|
||||
if (data.node?.template[name].refresh && newTemplate) {
|
||||
newNode.data.node.template = newTemplate;
|
||||
} else newNode.data.node.template[name].value = newValue;
|
||||
|
||||
return newNode;
|
||||
});
|
||||
|
|
@ -300,11 +288,14 @@ export default function ParameterComponent({
|
|||
refHtml.current = <span>{TOOLTIP_EMPTY}</span>;
|
||||
}
|
||||
}
|
||||
// If optionalHandle is an empty list, then it is not an optional handle
|
||||
if (optionalHandle && optionalHandle.length === 0) {
|
||||
optionalHandle = null;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
renderTooltips();
|
||||
}, [tooltipTitle, flow]);
|
||||
|
||||
return !showNode ? (
|
||||
left && LANGFLOW_SUPPORTED_TYPES.has(type ?? "") && !optionalHandle ? (
|
||||
<></>
|
||||
|
|
@ -449,7 +440,14 @@ export default function ParameterComponent({
|
|||
!data.node?.template[name].options ? (
|
||||
<div className="mt-2 w-full">
|
||||
{data.node?.template[name].list ? (
|
||||
<div className="w-5/6 flex-grow">
|
||||
<div
|
||||
className={
|
||||
// Commenting this out until we have a better
|
||||
// way to display
|
||||
// (data.node?.template[name].refresh ? "w-5/6 " : "") +
|
||||
"flex-grow"
|
||||
}
|
||||
>
|
||||
<InputListComponent
|
||||
disabled={disabled}
|
||||
value={
|
||||
|
|
@ -460,7 +458,7 @@ export default function ParameterComponent({
|
|||
}
|
||||
onChange={handleOnNewValue}
|
||||
/>
|
||||
{data.node?.template[name].refresh && (
|
||||
{/* {data.node?.template[name].refresh && (
|
||||
<div className="w-1/6">
|
||||
<RefreshButton
|
||||
isLoading={isLoading}
|
||||
|
|
@ -472,7 +470,7 @@ export default function ParameterComponent({
|
|||
id={"refresh-button-" + name}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
)} */}
|
||||
</div>
|
||||
) : data.node?.template[name].multiline ? (
|
||||
<TextAreaComponent
|
||||
|
|
@ -484,7 +482,12 @@ export default function ParameterComponent({
|
|||
/>
|
||||
) : (
|
||||
<div className="mt-2 flex w-full items-center">
|
||||
<div className="w-5/6 flex-grow">
|
||||
<div
|
||||
className={
|
||||
"flex-grow " +
|
||||
(data.node?.template[name].refresh ? "w-5/6" : "")
|
||||
}
|
||||
>
|
||||
<InputComponent
|
||||
id={"input-" + name}
|
||||
disabled={disabled}
|
||||
|
|
@ -643,8 +646,14 @@ export default function ParameterComponent({
|
|||
onChange={(newValue) => {
|
||||
const valueToNumbers = convertValuesToNumbers(newValue);
|
||||
setErrorDuplicateKey(hasDuplicateKeys(valueToNumbers));
|
||||
handleOnNewValue(valueToNumbers);
|
||||
// if data.node?.template[name].list is true, then the value is an array of objects
|
||||
// else we need to get the first object of the array
|
||||
|
||||
if (data.node?.template[name].list) {
|
||||
handleOnNewValue(valueToNumbers);
|
||||
} else handleOnNewValue(valueToNumbers[0]);
|
||||
}}
|
||||
isList={data.node?.template[name].list ?? false}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ import {
|
|||
RUN_TIMESTAMP_PREFIX,
|
||||
STATUS_BUILD,
|
||||
STATUS_BUILDING,
|
||||
priorityFields,
|
||||
} from "../../constants/constants";
|
||||
import { BuildStatus } from "../../constants/enums";
|
||||
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import { useDarkStore } from "../../stores/darkStore";
|
||||
import useFlowStore from "../../stores/flowStore";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
|
|
@ -24,7 +24,7 @@ import { validationStatusType } from "../../types/components";
|
|||
import { NodeDataType } from "../../types/flow";
|
||||
import { handleKeyDown, scapedJSONStringfy } from "../../utils/reactflowUtils";
|
||||
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
|
||||
import { classNames, cn, getFieldTitle } from "../../utils/utils";
|
||||
import { classNames, cn, getFieldTitle, sortFields } from "../../utils/utils";
|
||||
import ParameterComponent from "./components/parameterComponent";
|
||||
|
||||
export default function GenericNode({
|
||||
|
|
@ -43,6 +43,7 @@ export default function GenericNode({
|
|||
const flowPool = useFlowStore((state) => state.flowPool);
|
||||
const buildFlow = useFlowStore((state) => state.buildFlow);
|
||||
const setNode = useFlowStore((state) => state.setNode);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const name = nodeIconsLucide[data.type] ? data.type : types[data.type];
|
||||
const [inputName, setInputName] = useState(false);
|
||||
const [nodeName, setNodeName] = useState(data.node!.display_name);
|
||||
|
|
@ -64,6 +65,18 @@ export default function GenericNode({
|
|||
|
||||
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
|
||||
|
||||
if (!data.node!.template) {
|
||||
setErrorData({
|
||||
title: `Error in component ${data.node!.display_name}`,
|
||||
list: [
|
||||
`The component ${data.node!.display_name} has no template.`,
|
||||
`Please contact the developer of the component to fix this issue.`,
|
||||
],
|
||||
});
|
||||
takeSnapshot();
|
||||
deleteNode(data.id);
|
||||
}
|
||||
|
||||
function countHandles(): void {
|
||||
let count = Object.keys(data.node!.template)
|
||||
.filter((templateField) => templateField.charAt(0) !== "_")
|
||||
|
|
@ -157,7 +170,7 @@ export default function GenericNode({
|
|||
const iconElement = data?.node?.icon;
|
||||
const iconColor = nodeColors[types[data.type]];
|
||||
const iconName =
|
||||
iconElement || (data.node?.flow ? "group_components" : name);
|
||||
iconElement || (data.node?.flow ? "group_components" : name);
|
||||
const iconClassName = `generic-node-icon ${
|
||||
!showNode ? " absolute inset-x-6 h-12 w-12 " : ""
|
||||
}`;
|
||||
|
|
@ -636,15 +649,7 @@ export default function GenericNode({
|
|||
<>
|
||||
{Object.keys(data.node!.template)
|
||||
.filter((templateField) => templateField.charAt(0) !== "_")
|
||||
.sort((a, b) => {
|
||||
if (priorityFields.has(a.toLowerCase())) {
|
||||
return -1;
|
||||
} else if (priorityFields.has(b.toLowerCase())) {
|
||||
return 1;
|
||||
} else {
|
||||
return a.localeCompare(b);
|
||||
}
|
||||
})
|
||||
.sort((a, b) => sortFields(a, b, data.node?.field_order ?? []))
|
||||
.map((templateField: string, idx) => (
|
||||
<div key={idx}>
|
||||
{data.node!.template[templateField].show &&
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 12 KiB |
|
|
@ -50,9 +50,7 @@ export default function AccordionComponent({
|
|||
{trigger}
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<div className="AccordionContent flex flex-col">
|
||||
{children}
|
||||
</div>
|
||||
<div className="AccordionContent flex flex-col">{children}</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export default function CrashErrorComponent({
|
|||
<CardContent className="grid">
|
||||
<div>
|
||||
<p>
|
||||
Please report errors with detailed tracebacks on the{" "}
|
||||
Please report errors with detailed tracebacks on the{" "}
|
||||
<a
|
||||
href="https://github.com/logspace-ai/langflow/issues"
|
||||
target="_blank"
|
||||
|
|
@ -37,7 +37,7 @@ export default function CrashErrorComponent({
|
|||
</a>{" "}
|
||||
page.
|
||||
<br></br>
|
||||
Thank you!
|
||||
Thank you!
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
|
|
|||
26
src/frontend/src/components/NewFLowCard2/index.tsx
Normal file
26
src/frontend/src/components/NewFLowCard2/index.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { Card, CardContent } from "../ui/card";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
import { cn } from "../../utils/utils";
|
||||
|
||||
export default function NewFlowCardComponent() {
|
||||
const addFlow = useFlowsManagerStore((state) => state.addFlow);
|
||||
const navigate = useNavigate();
|
||||
return (
|
||||
<Card onClick={() => {
|
||||
addFlow(true).then((id) => {
|
||||
navigate("/flow/" + id);
|
||||
});
|
||||
}} className="pt-4 w-80 h-72 cursor-pointer">
|
||||
<CardContent className="w-full h-full flex flex-col items-center align-middle justify-center">
|
||||
|
||||
<IconComponent
|
||||
className={cn("h-12 w-12 text-muted-foreground")}
|
||||
name="SquarePen"
|
||||
/>
|
||||
<div className="text-center text-muted-foreground"> Create from scratch</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
33
src/frontend/src/components/NewFlowCardComponent/index.tsx
Normal file
33
src/frontend/src/components/NewFlowCardComponent/index.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { useNavigate } from "react-router-dom";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { cn } from "../../utils/utils";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
import { Card, CardContent } from "../ui/card";
|
||||
|
||||
export default function NewFlowCardComponent({}: {}) {
|
||||
const addFlow = useFlowsManagerStore((state) => state.addFlow);
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={cn(
|
||||
"group relative flex h-48 w-2/6 flex-col justify-between overflow-hidden transition-all hover:shadow-md"
|
||||
)}
|
||||
>
|
||||
<CardContent className="flex h-full w-full items-center justify-center align-middle">
|
||||
<button
|
||||
onClick={() => {
|
||||
addFlow(true).then((id) => {
|
||||
navigate("/flow/" + id);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<IconComponent
|
||||
className={cn("h-12 w-12 text-muted-foreground")}
|
||||
name="PlusCircle"
|
||||
/>
|
||||
</button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
|
@ -126,7 +126,7 @@ export default function CodeTabsComponent({
|
|||
<Tabs
|
||||
value={activeTab}
|
||||
className={
|
||||
"api-modal-tabs m-0 inset-0 " +
|
||||
"api-modal-tabs inset-0 m-0 " +
|
||||
(isMessage ? "dark " : "") +
|
||||
(dark && isMessage ? "bg-background" : "")
|
||||
}
|
||||
|
|
@ -195,7 +195,7 @@ export default function CodeTabsComponent({
|
|||
<SyntaxHighlighter
|
||||
language={tab.language}
|
||||
style={oneDark}
|
||||
className="mt-0 h-full overflow-auto custom-scroll rounded-sm text-left"
|
||||
className="mt-0 h-full overflow-auto rounded-sm text-left custom-scroll"
|
||||
>
|
||||
{tab.code}
|
||||
</SyntaxHighlighter>
|
||||
|
|
|
|||
100
src/frontend/src/components/exampleComponent/index.tsx
Normal file
100
src/frontend/src/components/exampleComponent/index.tsx
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
import { useNavigate } from "react-router-dom";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { FlowType } from "../../types/flow";
|
||||
import { updateIds } from "../../utils/reactflowUtils";
|
||||
import { cn } from "../../utils/utils";
|
||||
import ShadTooltip from "../ShadTooltipComponent";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
import { Button } from "../ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "../ui/card";
|
||||
|
||||
export default function CollectionCardComponent({
|
||||
flow,
|
||||
}: {
|
||||
flow: FlowType;
|
||||
authorized?: boolean;
|
||||
}) {
|
||||
const addFlow = useFlowsManagerStore((state) => state.addFlow);
|
||||
const navigate = useNavigate();
|
||||
const emojiRegex = /\p{Emoji}/u;
|
||||
const isEmoji = (str: string) => emojiRegex.test(str);
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={cn(
|
||||
"group relative flex h-48 w-2/6 flex-col justify-between overflow-hidden transition-all hover:shadow-md"
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
<CardHeader>
|
||||
<div>
|
||||
<CardTitle className="flex w-full items-center justify-between gap-3 text-xl">
|
||||
{flow.icon && isEmoji(flow.icon) && (
|
||||
<div
|
||||
className="flex items-center justify-center rounded-md p-2 align-middle"
|
||||
style={{ backgroundColor: flow.icon_bg_color }}
|
||||
>
|
||||
<div className="h-7 w-7 pl-0.5">{flow.icon}</div>
|
||||
</div>
|
||||
)}
|
||||
{(!flow.icon || !isEmoji(flow.icon)) && (
|
||||
<div
|
||||
className="flex items-center justify-center rounded-md p-2 align-middle"
|
||||
style={{ backgroundColor: flow.icon_bg_color }}
|
||||
>
|
||||
<IconComponent
|
||||
className={cn("h-7 w-7 flex-shrink-0 text-flow-icon")}
|
||||
name={flow.icon || "Group"}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<ShadTooltip content={flow.name}>
|
||||
<div className="w-full truncate">{flow.name}</div>
|
||||
</ShadTooltip>
|
||||
</CardTitle>
|
||||
</div>
|
||||
<CardDescription className="pb-2 pt-2">
|
||||
<ShadTooltip
|
||||
side="bottom"
|
||||
styleClasses="z-50"
|
||||
content={flow.description}
|
||||
>
|
||||
<div className="truncate-doubleline">{flow.description}</div>
|
||||
</ShadTooltip>
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</div>
|
||||
|
||||
<CardFooter>
|
||||
<div className="flex w-full items-center justify-between gap-2">
|
||||
<div className="flex w-full flex-wrap justify-end gap-2">
|
||||
<Button
|
||||
onClick={() => {
|
||||
updateIds(flow.data!);
|
||||
addFlow(true, flow).then((id) => {
|
||||
navigate("/flow/" + id);
|
||||
});
|
||||
}}
|
||||
tabIndex={-1}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="whitespace-nowrap "
|
||||
>
|
||||
<IconComponent
|
||||
name="ExternalLink"
|
||||
className="main-page-nav-button select-none"
|
||||
/>
|
||||
Select Flow
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
|
@ -12,10 +12,11 @@ export default function KeypairListComponent({
|
|||
disabled,
|
||||
editNode = false,
|
||||
duplicateKey,
|
||||
isList = true,
|
||||
}: KeyPairListComponentType): JSX.Element {
|
||||
useEffect(() => {
|
||||
if (disabled && value.length > 0 && value[0] !== "") {
|
||||
onChange([""]);
|
||||
onChange([{ "": "" }]);
|
||||
}
|
||||
}, [disabled]);
|
||||
|
||||
|
|
@ -79,6 +80,7 @@ export default function KeypairListComponent({
|
|||
: "keypair" + (index + 100).toString()
|
||||
}
|
||||
type="text"
|
||||
disabled={disabled}
|
||||
value={obj[key]}
|
||||
className={editNode ? "input-edit-node" : ""}
|
||||
placeholder="Type a value..."
|
||||
|
|
@ -87,7 +89,7 @@ export default function KeypairListComponent({
|
|||
}
|
||||
/>
|
||||
|
||||
{index === ref.current.length - 1 ? (
|
||||
{isList && index === ref.current.length - 1 ? (
|
||||
<button
|
||||
onClick={() => {
|
||||
let newInputList = _.cloneDeep(ref.current);
|
||||
|
|
@ -110,7 +112,7 @@ export default function KeypairListComponent({
|
|||
className={"h-4 w-4 hover:text-accent-foreground"}
|
||||
/>
|
||||
</button>
|
||||
) : (
|
||||
) : isList ? (
|
||||
<button
|
||||
onClick={() => {
|
||||
let newInputList = _.cloneDeep(ref.current);
|
||||
|
|
@ -133,6 +135,8 @@ export default function KeypairListComponent({
|
|||
className="h-4 w-4 hover:text-status-red"
|
||||
/>
|
||||
</button>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ export default function ChatMessage({
|
|||
)}
|
||||
</div>
|
||||
{!chat.isSend ? (
|
||||
<div className="form-modal-chat-text-position flex-grow min-w-96">
|
||||
<div className="form-modal-chat-text-position min-w-96 flex-grow">
|
||||
<div className="form-modal-chat-text">
|
||||
{hidden && chat.thought && chat.thought !== "" && (
|
||||
<div
|
||||
|
|
@ -155,9 +155,9 @@ export default function ChatMessage({
|
|||
/>
|
||||
)}
|
||||
{chat.thought && chat.thought !== "" && !hidden && <br></br>}
|
||||
<div className="w-full flex flex-col">
|
||||
<div className="w-full flex flex-col dark:text-white">
|
||||
<div className="w-full flex flex-col">
|
||||
<div className="flex w-full flex-col">
|
||||
<div className="flex w-full flex-col dark:text-white">
|
||||
<div className="flex w-full flex-col">
|
||||
{useMemo(
|
||||
() =>
|
||||
chatMessage === "" && lockChat ? (
|
||||
|
|
@ -169,7 +169,7 @@ export default function ChatMessage({
|
|||
<Markdown
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
rehypePlugins={[rehypeMathjax]}
|
||||
className="markdown flex flex-col prose text-primary word-break-break-word
|
||||
className="markdown prose flex flex-col text-primary word-break-break-word
|
||||
dark:prose-invert"
|
||||
components={{
|
||||
pre({ node, ...props }) {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ const DialogContent = React.forwardRef<
|
|||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex flex-col fixed left-[50%] top-[50%] z-50 w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full",
|
||||
"fixed left-[50%] top-[50%] z-50 flex w-full max-w-lg translate-x-[-50%] translate-y-[-50%] flex-col gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
36
src/frontend/src/components/undrawCards/index.tsx
Normal file
36
src/frontend/src/components/undrawCards/index.tsx
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
|
||||
import { useNavigate } from "react-router-dom";
|
||||
/// <reference types="vite-plugin-svgr/client" />
|
||||
//@ts-ignore
|
||||
import { ReactComponent as TransferFiles } from "../../assets/undraw_transfer_files_re_a2a9.svg"
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { FlowType } from "../../types/flow"
|
||||
import { updateIds } from "../../utils/reactflowUtils";
|
||||
import ShadTooltip from "../ShadTooltipComponent"
|
||||
import { Card, CardContent, CardDescription, CardFooter, CardTitle } from "../ui/card"
|
||||
|
||||
export default function UndrawCardComponent({
|
||||
flow
|
||||
}: { flow: FlowType }) {
|
||||
const addFlow = useFlowsManagerStore((state) => state.addFlow);
|
||||
const navigate = useNavigate();
|
||||
return (
|
||||
<Card onClick={() => {
|
||||
updateIds(flow.data!);
|
||||
addFlow(true, flow).then((id) => {
|
||||
navigate("/flow/" + id);
|
||||
});
|
||||
}} className="pt-4 w-80 h-72 cursor-pointer">
|
||||
<CardContent className="w-full h-full">
|
||||
<div className="rounded-md border border-border bg-background w-full h-full flex flex-col items-center align-middle justify-center">
|
||||
<TransferFiles style={{ width: '80%', height: '80%', preserveAspectRatio: 'xMidYMid meet' }} /> </div>
|
||||
</CardContent>
|
||||
<CardDescription className="px-4 pb-4">
|
||||
<CardTitle>{flow.name}</CardTitle>
|
||||
<ShadTooltip side="bottom" styleClasses="z-50" content={flow.description}>
|
||||
<div className="pt-1 truncate-doubleline">{flow.description}</div>
|
||||
</ShadTooltip>
|
||||
</CardDescription>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
|
@ -58,4 +58,3 @@ export const FLOW_BUILD_SUCCESS_ALERT = `Flow built successfully`;
|
|||
export const SAVE_SUCCESS_ALERT = "Changes saved successfully!";
|
||||
|
||||
// Generic Node
|
||||
|
||||
|
|
|
|||
|
|
@ -727,3 +727,15 @@ export const STATUS_BUILD = "Build to validate status.";
|
|||
export const STATUS_BUILDING = "Building...";
|
||||
export const SAVED_HOVER = "Last saved at ";
|
||||
export const RUN_TIMESTAMP_PREFIX = "Last Run: ";
|
||||
export const STARTER_FOLDER_NAME = "Starter Projects";
|
||||
export const PRIORITY_SIDEBAR_ORDER = [
|
||||
"saved_components",
|
||||
"inputs",
|
||||
"outputs",
|
||||
"prompts",
|
||||
"data",
|
||||
"prompt",
|
||||
"models",
|
||||
"helpers",
|
||||
"experimental",
|
||||
];
|
||||
|
|
|
|||
|
|
@ -370,7 +370,7 @@ export async function postCustomComponent(
|
|||
export async function postCustomComponentUpdate(
|
||||
code: string,
|
||||
field: string,
|
||||
field_value: string
|
||||
field_value: any
|
||||
): Promise<AxiosResponse<APIClassType>> {
|
||||
return await api.post(`${BASE_URL_API}custom_component/update`, {
|
||||
code,
|
||||
|
|
|
|||
|
|
@ -98,6 +98,9 @@ const ApiModal = forwardRef(
|
|||
let arrNodesWithValues: string[] = [];
|
||||
|
||||
flow["data"]!["nodes"].forEach((node) => {
|
||||
if (!node["data"]["node"]["template"]) {
|
||||
return;
|
||||
}
|
||||
Object.keys(node["data"]["node"]["template"])
|
||||
.filter(
|
||||
(templateField) =>
|
||||
|
|
|
|||
|
|
@ -35,8 +35,7 @@ export default function DeleteConfirmationModal({
|
|||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<span>
|
||||
Confirm deletion of {description ?? "component"}?
|
||||
<br></br>
|
||||
Confirm deletion of {description ?? "component"}?<br></br>
|
||||
Note: This action is irreversible.
|
||||
</span>
|
||||
<DialogFooter>
|
||||
|
|
|
|||
|
|
@ -165,8 +165,19 @@ const EditNodeModal = forwardRef(
|
|||
)
|
||||
) ?? false;
|
||||
return (
|
||||
<TableRow key={index} className={"h-10 " + ((templateParam==="code" && myData.node?.template[templateParam].type==="code") || (templateParam.includes("code") && myData.node?.template[templateParam].proxy) ? " hidden " : "")
|
||||
}>
|
||||
<TableRow
|
||||
key={index}
|
||||
className={
|
||||
"h-10 " +
|
||||
((templateParam === "code" &&
|
||||
myData.node?.template[templateParam].type ===
|
||||
"code") ||
|
||||
(templateParam.includes("code") &&
|
||||
myData.node?.template[templateParam].proxy)
|
||||
? " hidden "
|
||||
: "")
|
||||
}
|
||||
>
|
||||
<TableCell className="truncate p-0 text-center text-sm text-foreground sm:px-3">
|
||||
<ShadTooltip
|
||||
content={
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import { postCustomComponent, postValidateCode } from "../../controllers/API";
|
|||
import useAlertStore from "../../stores/alertStore";
|
||||
import { useDarkStore } from "../../stores/darkStore";
|
||||
import useFlowStore from "../../stores/flowStore";
|
||||
import { CodeErrorDataTypeAPI } from "../../types/api";
|
||||
import { codeAreaModalPropsType } from "../../types/components";
|
||||
import BaseModal from "../baseModal";
|
||||
|
||||
|
|
@ -50,7 +51,7 @@ export default function CodeAreaModal({
|
|||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const [error, setError] = useState<{
|
||||
detail: { error: string | undefined; traceback: string | undefined };
|
||||
detail: CodeErrorDataTypeAPI;
|
||||
} | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ export default function ShareModal({
|
|||
</span>
|
||||
<br></br>
|
||||
<span className=" text-xs text-destructive ">
|
||||
Note: This action is irreversible.
|
||||
Note: This action is irreversible.
|
||||
</span>
|
||||
</ConfirmationModal.Content>
|
||||
</ConfirmationModal>
|
||||
|
|
@ -204,7 +204,9 @@ export default function ShareModal({
|
|||
{children ? children : <></>}
|
||||
</BaseModal.Trigger>
|
||||
<BaseModal.Header
|
||||
description={`Publish ${is_component ? "your component" : "workflow"} to the Langflow Store.`}
|
||||
description={`Publish ${
|
||||
is_component ? "your component" : "workflow"
|
||||
} to the Langflow Store.`}
|
||||
>
|
||||
<span className="pr-2">Share</span>
|
||||
<IconComponent
|
||||
|
|
@ -239,7 +241,8 @@ export default function ShareModal({
|
|||
</label>
|
||||
</div>
|
||||
<span className=" text-xs text-destructive ">
|
||||
<b>Attention:</b> API keys in specified fields are automatically removed upon sharing.
|
||||
<b>Attention:</b> API keys in specified fields are automatically
|
||||
removed upon sharing.
|
||||
</span>
|
||||
</BaseModal.Content>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,10 @@
|
|||
import { PRIORITY_SIDEBAR_ORDER } from "../../../../constants/constants";
|
||||
|
||||
export function sortKeys(a: string, b: string) {
|
||||
// Define the order of specific keys
|
||||
const order = [
|
||||
"saved_components",
|
||||
"inputs",
|
||||
"outputs",
|
||||
"prompts",
|
||||
"data",
|
||||
"models",
|
||||
"utilities",
|
||||
];
|
||||
const indexA = order.indexOf(a.toLowerCase());
|
||||
const indexB = order.indexOf(b.toLowerCase());
|
||||
|
||||
const indexA = PRIORITY_SIDEBAR_ORDER.indexOf(a.toLowerCase());
|
||||
const indexB = PRIORITY_SIDEBAR_ORDER.indexOf(b.toLowerCase());
|
||||
|
||||
// Check if both keys are in the predefined order
|
||||
if (indexA !== -1 && indexB !== -1) {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ export default function ComponentsComponent({
|
|||
const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow);
|
||||
const removeFlow = useFlowsManagerStore((state) => state.removeFlow);
|
||||
const isLoading = useFlowsManagerStore((state) => state.isLoading);
|
||||
const setExamples = useFlowsManagerStore((state) => state.setExamples);
|
||||
const flows = useFlowsManagerStore((state) => state.flows);
|
||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
|
|
@ -35,7 +36,7 @@ export default function ComponentsComponent({
|
|||
|
||||
useEffect(() => {
|
||||
if (isLoading) return;
|
||||
const all = flows
|
||||
let all = flows
|
||||
.filter((f) => (f.is_component ?? false) === is_component)
|
||||
.sort((a, b) => {
|
||||
if (a?.updated_at && b?.updated_at) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import { Group, ToyBrick } from "lucide-react";
|
||||
import { useEffect } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Outlet, useLocation, useNavigate } from "react-router-dom";
|
||||
import DropdownButton from "../../components/DropdownButtonComponent";
|
||||
import NewFlowCardComponent from "../../components/NewFLowCard2";;
|
||||
import ExampleCardComponent from "../../components/exampleComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import PageLayout from "../../components/pageLayout";
|
||||
import SidebarNav from "../../components/sidebarComponent";
|
||||
|
|
@ -11,9 +13,11 @@ import {
|
|||
MY_COLLECTION_DESC,
|
||||
USER_PROJECTS_HEADER,
|
||||
} from "../../constants/constants";
|
||||
import BaseModal from "../../modals/baseModal";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { downloadFlows } from "../../utils/reactflowUtils";
|
||||
import UndrawCardComponent from "../../components/undrawCards";
|
||||
export default function HomePage(): JSX.Element {
|
||||
const addFlow = useFlowsManagerStore((state) => state.addFlow);
|
||||
const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow);
|
||||
|
|
@ -25,6 +29,8 @@ export default function HomePage(): JSX.Element {
|
|||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const location = useLocation();
|
||||
const pathname = location.pathname;
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const examples = useFlowsManagerStore((state) => state.examples);
|
||||
const is_component = pathname === "/components";
|
||||
const dropdownOptions = [
|
||||
{
|
||||
|
|
@ -98,11 +104,7 @@ export default function HomePage(): JSX.Element {
|
|||
</Button>
|
||||
<DropdownButton
|
||||
firstButtonName="New Project"
|
||||
onFirstBtnClick={() => {
|
||||
addFlow(true).then((id) => {
|
||||
navigate("/flow/" + id);
|
||||
});
|
||||
}}
|
||||
onFirstBtnClick={() => setOpenModal(true)}
|
||||
options={dropdownOptions}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -116,6 +118,28 @@ export default function HomePage(): JSX.Element {
|
|||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
<BaseModal open={openModal} setOpen={setOpenModal}>
|
||||
<BaseModal.Header
|
||||
description={"Select a template below"}
|
||||
>
|
||||
<span className="pr-2" data-testid="modal-title">
|
||||
Get Started
|
||||
</span>
|
||||
{/* <IconComponent
|
||||
name="Group"
|
||||
className="h-6 w-6 stroke-2 text-primary "
|
||||
aria-hidden="true"
|
||||
/> */}
|
||||
</BaseModal.Header>
|
||||
<BaseModal.Content>
|
||||
<div className="flex h-full w-full flex-wrap gap-3 overflow-auto p-4 custom-scroll">
|
||||
{examples.map((example, idx) => {
|
||||
return <UndrawCardComponent key={idx} flow={example} />;
|
||||
})}
|
||||
<NewFlowCardComponent />
|
||||
</div>
|
||||
</BaseModal.Content>
|
||||
</BaseModal>
|
||||
</PageLayout>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -514,7 +514,6 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
get().setIsBuilding(false);
|
||||
},
|
||||
onBuildStart: (idList) => {
|
||||
console.log("onBuildStart", idList);
|
||||
useFlowStore.getState().updateBuildStatus(idList, BuildStatus.BUILDING);
|
||||
},
|
||||
validateNodes: validateSubgraph,
|
||||
|
|
@ -536,7 +535,6 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
runId: string;
|
||||
} | null
|
||||
) => {
|
||||
console.log("updateVerticesBuild", vertices);
|
||||
set({ verticesBuild: vertices });
|
||||
},
|
||||
verticesBuild: null,
|
||||
|
|
@ -563,7 +561,6 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
});
|
||||
},
|
||||
updateBuildStatus: (nodeIdList: string[], status: BuildStatus) => {
|
||||
console.log("updateBuildStatus", nodeIdList, status);
|
||||
const newFlowBuildStatus = { ...get().flowBuildStatus };
|
||||
nodeIdList.forEach((id) => {
|
||||
newFlowBuildStatus[id] = {
|
||||
|
|
@ -573,7 +570,6 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
const timestamp_string = new Date(Date.now()).toLocaleString();
|
||||
newFlowBuildStatus[id].timestamp = timestamp_string;
|
||||
}
|
||||
console.log("updateBuildStatus", newFlowBuildStatus);
|
||||
});
|
||||
set({ flowBuildStatus: newFlowBuildStatus });
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { AxiosError } from "axios";
|
|||
import { cloneDeep } from "lodash";
|
||||
import { Edge, Node, Viewport, XYPosition } from "reactflow";
|
||||
import { create } from "zustand";
|
||||
import { STARTER_FOLDER_NAME } from "../constants/constants";
|
||||
import {
|
||||
deleteFlowFromDatabase,
|
||||
readFlowsFromDatabase,
|
||||
|
|
@ -37,6 +38,10 @@ const past = {};
|
|||
const future = {};
|
||||
|
||||
const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
||||
examples: [],
|
||||
setExamples: (examples: FlowType[]) => {
|
||||
set({ examples });
|
||||
},
|
||||
currentFlowId: "",
|
||||
setCurrentFlowId: (currentFlowId: string) => {
|
||||
set((state) => ({
|
||||
|
|
@ -62,7 +67,16 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
|||
.then((dbData) => {
|
||||
if (dbData) {
|
||||
const { data, flows } = processFlows(dbData, false);
|
||||
get().setFlows(flows);
|
||||
get().setExamples(
|
||||
flows.filter(
|
||||
(f) => f.folder === STARTER_FOLDER_NAME && !f.user_id
|
||||
)
|
||||
);
|
||||
get().setFlows(
|
||||
flows.filter(
|
||||
(f) => !(f.folder === STARTER_FOLDER_NAME && !f.user_id)
|
||||
)
|
||||
);
|
||||
useTypesStore.setState((state) => ({
|
||||
data: { ...state.data, ["saved_components"]: data },
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ export type APIClassType = {
|
|||
official?: boolean;
|
||||
frozen?: boolean;
|
||||
flow?: FlowType;
|
||||
field_order?: string[];
|
||||
[key: string]:
|
||||
| Array<string>
|
||||
| string
|
||||
|
|
@ -158,3 +159,17 @@ export type VertexDataTypeAPI = {
|
|||
timedelta?: number;
|
||||
duration?: string;
|
||||
};
|
||||
|
||||
export type CodeErrorDataTypeAPI = {
|
||||
error: string | undefined;
|
||||
traceback: string | undefined;
|
||||
};
|
||||
|
||||
// the error above is inside this error.response.data.detail.error
|
||||
// which comes from a request to the API
|
||||
// to type the error we need to know the structure of the object
|
||||
|
||||
// error that has a response, that has a data, that has a detail, that has an error
|
||||
export type ResponseErrorTypeAPI = {
|
||||
response: { data: { detail: CodeErrorDataTypeAPI } };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ export type KeyPairListComponentType = {
|
|||
editNode?: boolean;
|
||||
duplicateKey?: boolean;
|
||||
editNodeModal?: boolean;
|
||||
isList?: boolean;
|
||||
};
|
||||
|
||||
export type DictComponentType = {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@ export type FlowType = {
|
|||
updated_at?: string;
|
||||
date_created?: string;
|
||||
parent?: string;
|
||||
folder?: string;
|
||||
user_id?: string;
|
||||
icon?: string;
|
||||
icon_bg_color?: string;
|
||||
};
|
||||
|
||||
export type NodeType = {
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ export type FlowsManagerStoreType = {
|
|||
undo: () => void;
|
||||
redo: () => void;
|
||||
takeSnapshot: () => void;
|
||||
examples: Array<FlowType>;
|
||||
setExamples: (examples: FlowType[]) => void;
|
||||
};
|
||||
|
||||
export type UseUndoRedoOptions = {
|
||||
|
|
|
|||
35
src/frontend/src/utils/parameterUtils.ts
Normal file
35
src/frontend/src/utils/parameterUtils.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import { throttle } from "lodash";
|
||||
import { postCustomComponentUpdate } from "../controllers/API";
|
||||
import { ResponseErrorTypeAPI } from "../types/api";
|
||||
import { NodeDataType } from "../types/flow";
|
||||
|
||||
export const handleUpdateValues = async (name: string, data: NodeDataType) => {
|
||||
const code = data.node?.template["code"]?.value;
|
||||
if (!code) {
|
||||
console.error("Code not found in the template");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let newTemplate = await postCustomComponentUpdate(
|
||||
code,
|
||||
name,
|
||||
data.node?.template[name]?.value
|
||||
)
|
||||
.then((res) => {
|
||||
console.log("res", res);
|
||||
if (res.status === 200 && data.node?.template) {
|
||||
return res.data.template;
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
throw error;
|
||||
});
|
||||
return newTemplate;
|
||||
} catch (error) {
|
||||
console.error("Error occurred while updating the node:", error);
|
||||
let errorType = error as ResponseErrorTypeAPI;
|
||||
throw errorType;
|
||||
}
|
||||
};
|
||||
|
||||
export const throttledHandleUpdateValues = throttle(handleUpdateValues, 10);
|
||||
|
|
@ -42,15 +42,14 @@ import {
|
|||
EyeOff,
|
||||
File,
|
||||
FileDown,
|
||||
SquarePen,
|
||||
FileSearch,
|
||||
FileSearch2,
|
||||
FileSliders,
|
||||
FileText,
|
||||
FileType2,
|
||||
FileUp,
|
||||
Fingerprint,
|
||||
FlaskConical,
|
||||
FolderOpen,
|
||||
FolderPlus,
|
||||
FormInput,
|
||||
Forward,
|
||||
|
|
@ -67,7 +66,6 @@ import {
|
|||
Key,
|
||||
Laptop2,
|
||||
Layers,
|
||||
Lightbulb,
|
||||
Link,
|
||||
Loader2,
|
||||
Lock,
|
||||
|
|
@ -91,11 +89,12 @@ import {
|
|||
Pin,
|
||||
Play,
|
||||
Plus,
|
||||
PlusCircle,
|
||||
PlusSquare,
|
||||
PocketKnife,
|
||||
Redo,
|
||||
RefreshCcw,
|
||||
Repeat,
|
||||
Rocket,
|
||||
Save,
|
||||
SaveAll,
|
||||
Scissors,
|
||||
|
|
@ -127,10 +126,8 @@ import {
|
|||
UserPlus2,
|
||||
Users2,
|
||||
Variable,
|
||||
Wand,
|
||||
Wand2,
|
||||
Workflow,
|
||||
Wrench,
|
||||
X,
|
||||
XCircle,
|
||||
Zap,
|
||||
|
|
@ -235,7 +232,8 @@ export const nodeColors: { [char: string]: string } = {
|
|||
textsplitters: "#B47CB5",
|
||||
toolkits: "#DB2C2C",
|
||||
wrappers: "#E6277A",
|
||||
utilities: "#31A3CC",
|
||||
helpers: "#31A3CC",
|
||||
experimental: "#E6277A",
|
||||
langchain_utilities: "#31A3CC",
|
||||
output_parsers: "#E6A627",
|
||||
str: "#31a3cc",
|
||||
|
|
@ -266,7 +264,8 @@ export const nodeNames: { [char: string]: string } = {
|
|||
wrappers: "Wrappers",
|
||||
textsplitters: "Text Splitters",
|
||||
retrievers: "Retrievers",
|
||||
utilities: "Helpers",
|
||||
helpers: "Helpers",
|
||||
experimental: "Experimental",
|
||||
langchain_utilities: "Utilities",
|
||||
output_parsers: "Output Parsers",
|
||||
custom_components: "Custom",
|
||||
|
|
@ -358,7 +357,8 @@ export const nodeIconsLucide: iconsType = {
|
|||
toolkits: Package2,
|
||||
textsplitters: Scissors,
|
||||
wrappers: Gift,
|
||||
utilities: Wand2,
|
||||
helpers: Wand2,
|
||||
experimental: FlaskConical,
|
||||
langchain_utilities: PocketKnife,
|
||||
WolframAlphaAPIWrapper: SvgWolfram,
|
||||
output_parsers: Compass,
|
||||
|
|
@ -376,6 +376,7 @@ export const nodeIconsLucide: iconsType = {
|
|||
XCircle,
|
||||
Info,
|
||||
CheckCircle2,
|
||||
SquarePen,
|
||||
Zap,
|
||||
MessagesSquare,
|
||||
ExternalLink,
|
||||
|
|
@ -400,6 +401,8 @@ export const nodeIconsLucide: iconsType = {
|
|||
Circle,
|
||||
CircleDot,
|
||||
Clipboard,
|
||||
PlusCircle,
|
||||
PlusSquare,
|
||||
Code2,
|
||||
Variable,
|
||||
Snowflake,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import clsx, { ClassValue } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { priorityFields } from "../constants/constants";
|
||||
import { ADJECTIVES, DESCRIPTIONS, NOUNS } from "../flow_constants";
|
||||
import {
|
||||
APIDataType,
|
||||
|
|
@ -143,7 +144,7 @@ export function groupByFamily(
|
|||
// se existir o flow
|
||||
for (const node of flow) {
|
||||
// para cada node do flow
|
||||
if (node!.data!.node!.flow) break; // não faz nada se o node for um group
|
||||
if (node!.data!.node!.flow || !node!.data!.node!.template) break; // não faz nada se o node for um group
|
||||
const nodeData = node.data;
|
||||
|
||||
const foundNode = checkedNodes.get(nodeData.type); // verifica se o tipo do node já foi checado
|
||||
|
|
@ -642,3 +643,42 @@ export function getFieldTitle(
|
|||
? template[templateField].display_name!
|
||||
: template[templateField].name ?? templateField;
|
||||
}
|
||||
|
||||
export function sortFields(a, b, fieldOrder) {
|
||||
// Early return for empty fields
|
||||
if (!a && !b) return 0;
|
||||
if (!a) return 1;
|
||||
if (!b) return -1;
|
||||
|
||||
// Normalize the case to ensure case-insensitive comparison
|
||||
const normalizedFieldA = a.toLowerCase();
|
||||
const normalizedFieldB = b.toLowerCase();
|
||||
|
||||
const aIsPriority = priorityFields.has(normalizedFieldA);
|
||||
const bIsPriority = priorityFields.has(normalizedFieldB);
|
||||
|
||||
// Sort by priority
|
||||
if (aIsPriority && !bIsPriority) return -1;
|
||||
if (!aIsPriority && bIsPriority) return 1;
|
||||
|
||||
// Check if either field is in the fieldOrder array
|
||||
const indexOfA = fieldOrder.indexOf(normalizedFieldA);
|
||||
const indexOfB = fieldOrder.indexOf(normalizedFieldB);
|
||||
|
||||
// If both fields are in fieldOrder, sort by their order in the array
|
||||
if (indexOfA !== -1 && indexOfB !== -1) {
|
||||
return indexOfA - indexOfB;
|
||||
}
|
||||
|
||||
// If only one of the fields is in fieldOrder, that field comes first
|
||||
if (indexOfA !== -1) {
|
||||
return -1;
|
||||
}
|
||||
if (indexOfB !== -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Default case for fields not in priorityFields and not found in fieldOrder
|
||||
// You might want to sort them alphabetically or in another specific manner
|
||||
return a.localeCompare(b);
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue