Merge branch 'bug/component_share' into dev
This commit is contained in:
commit
54064737e8
16 changed files with 216 additions and 170 deletions
|
|
@ -1,11 +1,12 @@
|
|||
import warnings
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, List
|
||||
|
||||
from fastapi import HTTPException
|
||||
from platformdirs import user_cache_dir
|
||||
|
||||
from langflow.services.store.schema import StoreComponentCreate
|
||||
from langflow.services.store.utils import get_lf_version_from_pypi
|
||||
import warnings
|
||||
|
||||
from platformdirs import user_cache_dir
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.services.database.models.flow.model import Flow
|
||||
|
|
@ -62,7 +63,7 @@ def build_input_keys_response(langchain_object, artifacts):
|
|||
return input_keys_response
|
||||
|
||||
|
||||
def update_frontend_node_with_template_values(frontend_node, raw_template_data):
|
||||
def update_frontend_node_with_template_values(frontend_node, raw_frontend_node):
|
||||
"""
|
||||
Updates the given frontend node with values from the raw template data.
|
||||
|
||||
|
|
@ -70,19 +71,28 @@ def update_frontend_node_with_template_values(frontend_node, raw_template_data):
|
|||
:param raw_template_data: A dict representing raw template data.
|
||||
:return: Updated frontend node.
|
||||
"""
|
||||
if not is_valid_data(frontend_node, raw_template_data):
|
||||
if not is_valid_data(frontend_node, raw_frontend_node):
|
||||
return frontend_node
|
||||
|
||||
update_template_values(frontend_node["template"], raw_template_data.template)
|
||||
# Check if the display_name is different than "CustomComponent"
|
||||
# if so, update the display_name in the frontend_node
|
||||
if raw_frontend_node["display_name"] != "CustomComponent":
|
||||
frontend_node["display_name"] = raw_frontend_node["display_name"]
|
||||
|
||||
update_template_values(frontend_node["template"], raw_frontend_node["template"])
|
||||
|
||||
return frontend_node
|
||||
|
||||
|
||||
def is_valid_data(frontend_node, raw_template_data):
|
||||
def raw_frontend_data_is_valid(raw_frontend_data):
|
||||
"""Check if the raw frontend data is valid for processing."""
|
||||
return "template" in raw_frontend_data and "display_name" in raw_frontend_data
|
||||
|
||||
|
||||
def is_valid_data(frontend_node, raw_frontend_data):
|
||||
"""Check if the data is valid for processing."""
|
||||
return (
|
||||
frontend_node and "template" in frontend_node and raw_template_data and hasattr(raw_template_data, "template")
|
||||
)
|
||||
|
||||
return frontend_node and "template" in frontend_node and raw_frontend_data_is_valid(raw_frontend_data)
|
||||
|
||||
|
||||
def update_template_values(frontend_template, raw_template):
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@ from typing import Annotated, Optional, Union
|
|||
|
||||
import sqlalchemy as sa
|
||||
from fastapi import APIRouter, Body, Depends, HTTPException, UploadFile, status
|
||||
from loguru import logger
|
||||
from sqlmodel import select
|
||||
|
||||
from langflow.api.utils import update_frontend_node_with_template_values
|
||||
from langflow.api.v1.schemas import (
|
||||
CustomComponentCode,
|
||||
|
|
@ -20,8 +23,6 @@ from langflow.services.cache.utils import save_uploaded_file
|
|||
from langflow.services.database.models.flow import Flow
|
||||
from langflow.services.database.models.user.model import User
|
||||
from langflow.services.deps import get_session, get_session_service, get_settings_service, get_task_service
|
||||
from loguru import logger
|
||||
from sqlmodel import select
|
||||
|
||||
try:
|
||||
from langflow.worker import process_graph_cached_task
|
||||
|
|
@ -31,9 +32,10 @@ except ImportError:
|
|||
raise NotImplementedError("Celery is not installed")
|
||||
|
||||
|
||||
from langflow.services.task.service import TaskService
|
||||
from sqlmodel import Session
|
||||
|
||||
from langflow.services.task.service import TaskService
|
||||
|
||||
# build router
|
||||
router = APIRouter(tags=["Base"])
|
||||
|
||||
|
|
@ -218,7 +220,7 @@ async def custom_component(
|
|||
|
||||
built_frontend_node = build_custom_component_template(component, user_id=user.id)
|
||||
|
||||
built_frontend_node = update_frontend_node_with_template_values(built_frontend_node, raw_code)
|
||||
built_frontend_node = update_frontend_node_with_template_values(built_frontend_node, raw_code.frontend_node)
|
||||
return built_frontend_node
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,11 +3,12 @@ from pathlib import Path
|
|||
from typing import Any, Dict, List, Optional, Union
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
from langflow.services.database.models.api_key.model import ApiKeyRead
|
||||
from langflow.services.database.models.base import orjson_dumps
|
||||
from langflow.services.database.models.flow import FlowCreate, FlowRead
|
||||
from langflow.services.database.models.user import UserRead
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
|
||||
class BuildStatus(Enum):
|
||||
|
|
@ -157,7 +158,7 @@ class StreamData(BaseModel):
|
|||
class CustomComponentCode(BaseModel):
|
||||
code: str
|
||||
field: Optional[str] = None
|
||||
template: Optional[dict] = None
|
||||
frontend_node: Optional[dict] = None
|
||||
|
||||
|
||||
class CustomComponentResponseError(BaseModel):
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ from langflow.field_typing import Data
|
|||
|
||||
|
||||
class Component(CustomComponent):
|
||||
display_name: str = "Custom Component"
|
||||
description: str = "Create any custom component you want!"
|
||||
documentation: str = "http://docs.langflow.org/components/custom"
|
||||
|
||||
def build_config(self):
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ from langflow.utils import validate
|
|||
|
||||
|
||||
class CustomComponent(Component):
|
||||
display_name: Optional[str] = "Custom Component"
|
||||
description: Optional[str] = "Custom Component"
|
||||
display_name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
code: Optional[str] = None
|
||||
field_config: dict = {}
|
||||
code_class_base_inheritance: ClassVar[str] = "CustomComponent"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ from typing import Optional
|
|||
from langflow.template.field.base import TemplateField
|
||||
from langflow.template.frontend_node.base import FrontendNode
|
||||
from langflow.template.template.base import Template
|
||||
from pydantic import field_serializer
|
||||
|
||||
DEFAULT_CUSTOM_COMPONENT_CODE = """from langflow import CustomComponent
|
||||
from typing import Optional, List, Dict, Union
|
||||
|
|
@ -47,7 +46,7 @@ class Component(CustomComponent):
|
|||
|
||||
class CustomComponentFrontendNode(FrontendNode):
|
||||
name: str = "CustomComponent"
|
||||
display_name: str = "Custom Component"
|
||||
display_name: Optional[str] = "CustomComponent"
|
||||
beta: bool = True
|
||||
template: Template = Template(
|
||||
type_name="CustomComponent",
|
||||
|
|
@ -67,9 +66,3 @@ class CustomComponentFrontendNode(FrontendNode):
|
|||
)
|
||||
description: Optional[str] = None
|
||||
base_classes: list[str] = []
|
||||
|
||||
@field_serializer("display_name")
|
||||
def process_display_name(self, display_name: str) -> str:
|
||||
"""Sets the display name of the frontend node."""
|
||||
|
||||
return display_name
|
||||
|
|
|
|||
|
|
@ -156,11 +156,16 @@ export default function ParameterComponent({
|
|||
};
|
||||
|
||||
const handleNodeClass = (newNodeClass: APIClassType, code?: string): void => {
|
||||
if (!data.node) return;
|
||||
if (data.node!.template[name].value !== newNodeClass.template[name].value) {
|
||||
takeSnapshot();
|
||||
}
|
||||
data.node = newNodeClass;
|
||||
data.node.template[name].value = code;
|
||||
data.node! = {
|
||||
...newNodeClass,
|
||||
description: newNodeClass.description ?? data.node!.description,
|
||||
display_name: newNodeClass.display_name ?? data.node!.display_name,
|
||||
};
|
||||
data.node!.template[name].value = code;
|
||||
updateNodeInternals(data.id);
|
||||
// Set state to pending
|
||||
//@ts-ignore
|
||||
|
|
@ -174,19 +179,22 @@ export default function ParameterComponent({
|
|||
}
|
||||
renderTooltips();
|
||||
let flow = flows.find((flow) => flow.id === tabId);
|
||||
if (reactFlowInstance && flow && flow.data) {
|
||||
cleanEdges({
|
||||
flow: {
|
||||
edges: flow.data!.edges,
|
||||
nodes: flow.data!.nodes,
|
||||
},
|
||||
updateEdge: (edge) => {
|
||||
reactFlowInstance.setEdges(edge);
|
||||
updateNodeInternals(data.id);
|
||||
},
|
||||
});
|
||||
updateFlow(flow);
|
||||
}
|
||||
setTimeout(() => {
|
||||
//timeout necessary because ReactFlow updates are not async
|
||||
if (reactFlowInstance && flow && flow.data) {
|
||||
cleanEdges({
|
||||
flow: {
|
||||
edges: flow.data!.edges,
|
||||
nodes: flow.data!.nodes,
|
||||
},
|
||||
updateEdge: (edge) => {
|
||||
reactFlowInstance.setEdges(edge);
|
||||
updateNodeInternals(data.id);
|
||||
},
|
||||
});
|
||||
updateFlow(flow);
|
||||
}
|
||||
}, 50);
|
||||
};
|
||||
|
||||
const [errorDuplicateKey, setErrorDuplicateKey] = useState(false);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,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, getFieldTitle } from "../../utils/utils";
|
||||
import { classNames, cn, getFieldTitle } from "../../utils/utils";
|
||||
import ParameterComponent from "./components/parameterComponent";
|
||||
|
||||
export default function GenericNode({
|
||||
|
|
@ -107,6 +107,8 @@ export default function GenericNode({
|
|||
|
||||
const showNode = data.showNode ?? true;
|
||||
|
||||
const nameEditable = data.node?.flow || data.type === "CustomComponent";
|
||||
|
||||
return (
|
||||
<>
|
||||
<NodeToolbar>
|
||||
|
|
@ -164,7 +166,7 @@ export default function GenericNode({
|
|||
/>
|
||||
{showNode && (
|
||||
<div className="generic-node-tooltip-div">
|
||||
{data.node?.flow && inputName ? (
|
||||
{nameEditable && inputName ? (
|
||||
<div>
|
||||
<InputComponent
|
||||
onBlur={() => {
|
||||
|
|
@ -172,6 +174,7 @@ export default function GenericNode({
|
|||
if (nodeName.trim() !== "") {
|
||||
setNodeName(nodeName);
|
||||
data.node!.display_name = nodeName;
|
||||
updateNodeInternals(data.id);
|
||||
} else {
|
||||
setNodeName(data.node!.display_name);
|
||||
}
|
||||
|
|
@ -194,7 +197,7 @@ export default function GenericNode({
|
|||
<div className="generic-node-tooltip-div pr-2 text-primary">
|
||||
{data.node?.display_name}
|
||||
</div>
|
||||
{data.node?.flow && (
|
||||
{nameEditable && (
|
||||
<IconComponent
|
||||
name="Pencil"
|
||||
className="h-4 w-4 text-ring"
|
||||
|
|
@ -361,57 +364,62 @@ export default function GenericNode({
|
|||
<div
|
||||
className={
|
||||
showNode
|
||||
? "generic-node-desc overflow-hidden " +
|
||||
(data.node?.description !== "" ? "py-5" : "pb-5")
|
||||
? "overflow-hidden " +
|
||||
(data.node?.description === "" && !nameEditable
|
||||
? "pb-5"
|
||||
: "py-5")
|
||||
: ""
|
||||
}
|
||||
>
|
||||
{data.node?.description !== "" &&
|
||||
showNode &&
|
||||
data.node?.flow &&
|
||||
inputDescription ? (
|
||||
<Textarea
|
||||
autoFocus
|
||||
onBlur={() => {
|
||||
setInputDescription(false);
|
||||
if (nodeDescription.trim() !== "") {
|
||||
<div className="generic-node-desc">
|
||||
{showNode && nameEditable && inputDescription ? (
|
||||
<Textarea
|
||||
autoFocus
|
||||
onBlur={() => {
|
||||
setInputDescription(false);
|
||||
setNodeDescription(nodeDescription);
|
||||
data.node!.description = nodeDescription;
|
||||
} else {
|
||||
setNodeDescription(data.node!.description);
|
||||
}
|
||||
}}
|
||||
value={nodeDescription}
|
||||
onChange={(e) => setNodeDescription(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
handleKeyDown(e, nodeDescription, "");
|
||||
if (
|
||||
e.key === "Enter" &&
|
||||
e.shiftKey === false &&
|
||||
e.ctrlKey === false &&
|
||||
e.altKey === false
|
||||
) {
|
||||
setInputDescription(false);
|
||||
if (nodeDescription.trim() !== "") {
|
||||
updateNodeInternals(data.id);
|
||||
}}
|
||||
value={nodeDescription}
|
||||
onChange={(e) => setNodeDescription(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
handleKeyDown(e, nodeDescription, "");
|
||||
if (
|
||||
e.key === "Enter" &&
|
||||
e.shiftKey === false &&
|
||||
e.ctrlKey === false &&
|
||||
e.altKey === false
|
||||
) {
|
||||
setInputDescription(false);
|
||||
setNodeDescription(nodeDescription);
|
||||
data.node!.description = nodeDescription;
|
||||
} else {
|
||||
setNodeDescription(data.node!.description);
|
||||
updateNodeInternals(data.id);
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className="generic-node-desc-text truncate-multiline word-break-break-word"
|
||||
onDoubleClick={() => {
|
||||
setInputDescription(true);
|
||||
takeSnapshot();
|
||||
}}
|
||||
>
|
||||
{data.node?.description}
|
||||
</div>
|
||||
)}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className={cn(
|
||||
"generic-node-desc-text truncate-multiline word-break-break-word",
|
||||
(data.node?.description === "" ||
|
||||
!data.node?.description) &&
|
||||
nameEditable
|
||||
? "font-light italic"
|
||||
: ""
|
||||
)}
|
||||
onDoubleClick={() => {
|
||||
setInputDescription(true);
|
||||
takeSnapshot();
|
||||
}}
|
||||
>
|
||||
{(data.node?.description === "" || !data.node?.description) &&
|
||||
nameEditable
|
||||
? "Double Click to Edit Description"
|
||||
: data.node?.description}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<>
|
||||
{Object.keys(data.node!.template)
|
||||
.filter((templateField) => templateField.charAt(0) !== "_")
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { Input } from "../../components/ui/input";
|
|||
import { Label } from "../../components/ui/label";
|
||||
import { Textarea } from "../../components/ui/textarea";
|
||||
import { InputProps } from "../../types/components";
|
||||
import { cn } from "../../utils/utils";
|
||||
|
||||
export const EditFlowSettings: React.FC<InputProps> = ({
|
||||
name,
|
||||
|
|
@ -21,47 +22,65 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
|||
} else {
|
||||
setIsMaxLength(false);
|
||||
}
|
||||
setName(value);
|
||||
setName!(value);
|
||||
};
|
||||
|
||||
const handleDescriptionChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setDescription(event.target.value);
|
||||
setDescription!(event.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Label>
|
||||
<div className="edit-flow-arrangement">
|
||||
<span className="font-medium">Name</span>{" "}
|
||||
<span className="font-medium">Name{setName ? "" : ":"}</span>{" "}
|
||||
{isMaxLength && (
|
||||
<span className="edit-flow-span">Character limit reached</span>
|
||||
)}
|
||||
</div>
|
||||
<Input
|
||||
className="nopan nodelete nodrag noundo nocopy mt-2 font-normal"
|
||||
onChange={handleNameChange}
|
||||
type="text"
|
||||
name="name"
|
||||
value={name ?? ""}
|
||||
placeholder="Flow name"
|
||||
id="name"
|
||||
maxLength={maxLength}
|
||||
/>
|
||||
{setName ? (
|
||||
<Input
|
||||
className="nopan nodelete nodrag noundo nocopy mt-2 font-normal"
|
||||
onChange={handleNameChange}
|
||||
type="text"
|
||||
name="name"
|
||||
value={name ?? ""}
|
||||
placeholder="Flow name"
|
||||
id="name"
|
||||
maxLength={maxLength}
|
||||
/>
|
||||
) : (
|
||||
<span className="font-normal text-muted-foreground word-break-break-word">
|
||||
{name}
|
||||
</span>
|
||||
)}
|
||||
</Label>
|
||||
<Label>
|
||||
<div className="edit-flow-arrangement mt-3">
|
||||
<span className="font-medium ">Description (optional)</span>
|
||||
<span className="font-medium ">
|
||||
Description{setDescription ? " (optional)" : ":"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Textarea
|
||||
name="description"
|
||||
id="description"
|
||||
onChange={handleDescriptionChange}
|
||||
value={description!}
|
||||
placeholder="Flow description"
|
||||
className="mt-2 max-h-[100px] font-normal"
|
||||
rows={3}
|
||||
/>
|
||||
{setDescription ? (
|
||||
<Textarea
|
||||
name="description"
|
||||
id="description"
|
||||
onChange={handleDescriptionChange}
|
||||
value={description!}
|
||||
placeholder="Flow description"
|
||||
className="mt-2 max-h-[100px] font-normal"
|
||||
rows={3}
|
||||
/>
|
||||
) : (
|
||||
<span
|
||||
className={cn(
|
||||
"font-normal text-muted-foreground word-break-break-word",
|
||||
description === "" ? "font-light italic" : ""
|
||||
)}
|
||||
>
|
||||
{description === "" ? "No description" : description}
|
||||
</span>
|
||||
)}
|
||||
</Label>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -357,10 +357,10 @@ export async function postCustomComponent(
|
|||
code: string,
|
||||
apiClass: APIClassType
|
||||
): Promise<AxiosResponse<APIClassType>> {
|
||||
let template = apiClass.template;
|
||||
// let template = apiClass.template;
|
||||
return await api.post(`${BASE_URL_API}custom_component`, {
|
||||
code,
|
||||
template,
|
||||
frontend_node: apiClass,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,11 +44,9 @@ export default function ShareModal({
|
|||
const { setSuccessData, setErrorData } = useContext(alertContext);
|
||||
const { reactFlowInstance } = useContext(typesContext);
|
||||
const [checked, setChecked] = useState(false);
|
||||
const [name, setName] = useState(component?.name ?? "");
|
||||
const [description, setDescription] = useState(component?.description ?? "");
|
||||
const [internalOpen, internalSetOpen] = useState(children ? false : true);
|
||||
const [openConfirmationModal, setOpenConfirmationModal] = useState(false);
|
||||
const nameComponent = is_component ? "Component" : "Flow";
|
||||
const nameComponent = is_component ? "component" : "flow";
|
||||
|
||||
const [tags, setTags] = useState<{ id: string; name: string }[]>([]);
|
||||
const [loadingTags, setLoadingTags] = useState<boolean>(false);
|
||||
|
|
@ -61,6 +59,9 @@ export default function ShareModal({
|
|||
|
||||
const [loadingNames, setLoadingNames] = useState(false);
|
||||
|
||||
const name = component?.name ?? "";
|
||||
const description = component?.description ?? "";
|
||||
|
||||
useEffect(() => {
|
||||
if (open || internalOpen) {
|
||||
if (hasApiKey && hasStore) {
|
||||
|
|
@ -94,11 +95,6 @@ export default function ShareModal({
|
|||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setName(component?.name ?? "");
|
||||
setDescription(component?.description ?? "");
|
||||
}, [component, open, internalOpen]);
|
||||
|
||||
const handleShareComponent = async (update = false) => {
|
||||
//remove file names from flows before sharing
|
||||
removeFileNameFromComponents(component);
|
||||
|
|
@ -120,11 +116,9 @@ export default function ShareModal({
|
|||
is_component: is_component,
|
||||
});
|
||||
|
||||
await saveFlow(flow!, true);
|
||||
|
||||
function successShare() {
|
||||
if (is_component) {
|
||||
addFlow(true, flow);
|
||||
if (!is_component) {
|
||||
saveFlow(flow!, true);
|
||||
}
|
||||
setSuccessData({
|
||||
title: `${nameComponent} shared successfully`,
|
||||
|
|
@ -223,13 +217,9 @@ export default function ShareModal({
|
|||
/>
|
||||
</BaseModal.Header>
|
||||
<BaseModal.Content>
|
||||
<EditFlowSettings
|
||||
name={name}
|
||||
invalidNameList={unavaliableNames.map((element) => element.name)}
|
||||
description={description}
|
||||
setName={setName}
|
||||
setDescription={setDescription}
|
||||
/>
|
||||
<div className="w-full rounded-lg border border-border p-4">
|
||||
<EditFlowSettings name={name} description={description} />
|
||||
</div>
|
||||
<div className="mt-3 flex h-8 w-full">
|
||||
<TagsSelector
|
||||
tags={tags}
|
||||
|
|
@ -297,8 +287,8 @@ export default function ShareModal({
|
|||
</>
|
||||
) : (
|
||||
<>
|
||||
{is_component && !loadingNames ? "Save and " : ""}Share{" "}
|
||||
{!is_component && !loadingNames ? "Flow" : ""}
|
||||
Share{" "}
|
||||
{!loadingNames && (!is_component ? "Flow" : "Component")}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -282,6 +282,17 @@ export default function Page({
|
|||
let newX = _.cloneDeep(node);
|
||||
return newX;
|
||||
});
|
||||
//@ts-ignore
|
||||
setTabsState((prev: FlowsState) => {
|
||||
return {
|
||||
...prev,
|
||||
[tabId]: {
|
||||
...prev[tabId],
|
||||
isPending: true,
|
||||
},
|
||||
};
|
||||
});
|
||||
saveCurrentFlowTimeout();
|
||||
},
|
||||
[setEdges, setNodes, takeSnapshot, addEdge]
|
||||
);
|
||||
|
|
|
|||
|
|
@ -77,7 +77,15 @@ export default function NodeToolbarComponent({
|
|||
|
||||
useEffect(() => {
|
||||
setFlowComponent(createFlowComponent(cloneDeep(data), version));
|
||||
}, [data, data.node, showModalAdvanced]);
|
||||
}, [
|
||||
data,
|
||||
data.node,
|
||||
data.node?.display_name,
|
||||
data.node?.description,
|
||||
data.node?.template,
|
||||
showModalAdvanced,
|
||||
showconfirmShare,
|
||||
]);
|
||||
|
||||
const handleSelectChange = (event) => {
|
||||
switch (event) {
|
||||
|
|
|
|||
|
|
@ -311,10 +311,10 @@
|
|||
@apply hover:text-accent-foreground hover:transition-all;
|
||||
}
|
||||
.generic-node-desc {
|
||||
@apply h-full w-full text-foreground;
|
||||
@apply h-full px-5 mb-4 w-full text-foreground;
|
||||
}
|
||||
.generic-node-desc-text {
|
||||
@apply w-full px-5 mb-4 text-sm text-muted-foreground;
|
||||
@apply w-full text-sm text-muted-foreground;
|
||||
}
|
||||
|
||||
.alert-icon {
|
||||
|
|
|
|||
|
|
@ -240,8 +240,8 @@ export type InputProps = {
|
|||
name: string | null;
|
||||
description: string | null;
|
||||
maxLength?: number;
|
||||
setName: (name: string) => void;
|
||||
setDescription: (description: string) => void;
|
||||
setName?: (name: string) => void;
|
||||
setDescription?: (description: string) => void;
|
||||
invalidNameList?: string[];
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -41,37 +41,35 @@ export function cleanEdges({
|
|||
const targetNode = nodes.find((node) => node.id === edge.target);
|
||||
if (!sourceNode || !targetNode) {
|
||||
newEdges = newEdges.filter((edg) => edg.id !== edge.id);
|
||||
return;
|
||||
}
|
||||
// check if the source and target handle still exists
|
||||
if (sourceNode && targetNode) {
|
||||
const sourceHandle = edge.sourceHandle; //right
|
||||
const targetHandle = edge.targetHandle; //left
|
||||
if (targetHandle) {
|
||||
const targetHandleObject: targetHandleType =
|
||||
scapeJSONParse(targetHandle);
|
||||
const field = targetHandleObject.fieldName;
|
||||
const id: targetHandleType = {
|
||||
type: targetNode.data.node!.template[field]?.type,
|
||||
fieldName: field,
|
||||
id: targetNode.data.id,
|
||||
inputTypes: targetNode.data.node!.template[field]?.input_types,
|
||||
};
|
||||
if (targetNode.data.node!.template[field]?.proxy) {
|
||||
id.proxy = targetNode.data.node!.template[field]?.proxy;
|
||||
}
|
||||
if (scapedJSONStringfy(id) !== targetHandle) {
|
||||
newEdges = newEdges.filter((e) => e.id !== edge.id);
|
||||
}
|
||||
const sourceHandle = edge.sourceHandle; //right
|
||||
const targetHandle = edge.targetHandle; //left
|
||||
if (targetHandle) {
|
||||
const targetHandleObject: targetHandleType = scapeJSONParse(targetHandle);
|
||||
const field = targetHandleObject.fieldName;
|
||||
const id: targetHandleType = {
|
||||
type: targetNode.data.node!.template[field]?.type,
|
||||
fieldName: field,
|
||||
id: targetNode.data.id,
|
||||
inputTypes: targetNode.data.node!.template[field]?.input_types,
|
||||
};
|
||||
if (targetNode.data.node!.template[field]?.proxy) {
|
||||
id.proxy = targetNode.data.node!.template[field]?.proxy;
|
||||
}
|
||||
if (sourceHandle) {
|
||||
const id: sourceHandleType = {
|
||||
id: sourceNode.data.id,
|
||||
baseClasses: sourceNode.data.node!.base_classes,
|
||||
dataType: sourceNode.data.type,
|
||||
};
|
||||
if (scapedJSONStringfy(id) !== sourceHandle) {
|
||||
newEdges = newEdges.filter((e) => e.id !== edge.id);
|
||||
}
|
||||
if (scapedJSONStringfy(id) !== targetHandle) {
|
||||
newEdges = newEdges.filter((e) => e.id !== edge.id);
|
||||
}
|
||||
}
|
||||
if (sourceHandle) {
|
||||
const id: sourceHandleType = {
|
||||
id: sourceNode.data.id,
|
||||
baseClasses: sourceNode.data.node!.base_classes,
|
||||
dataType: sourceNode.data.type,
|
||||
};
|
||||
if (scapedJSONStringfy(id) !== sourceHandle) {
|
||||
newEdges = newEdges.filter((e) => e.id !== edge.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -761,7 +759,7 @@ export function generateNodeFromFlow(
|
|||
display_name: "Group",
|
||||
documentation: "",
|
||||
base_classes: outputNode!.data.node!.base_classes,
|
||||
description: "double click to edit description",
|
||||
description: "",
|
||||
template: generateNodeTemplate(data),
|
||||
flow: data,
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue