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