Merge branch 'cz/mergeAll' into shortcuts_settings

This commit is contained in:
Gabriel Luiz Freitas Almeida 2024-06-09 06:27:26 -07:00 • committed by GitHub
commit 90a8adca29
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
132 changed files with 600 additions and 295 deletions

View file

@ -86,6 +86,10 @@ def update_frontend_node_with_template_values(frontend_node, raw_frontend_node):
update_template_values(frontend_node["template"], raw_frontend_node["template"]) update_template_values(frontend_node["template"], raw_frontend_node["template"])
old_code = raw_frontend_node["template"]["code"]["value"]
new_code = frontend_node["template"]["code"]["value"]
frontend_node["edited"] = old_code != new_code
return frontend_node return frontend_node

View file

@ -2,6 +2,7 @@ import hashlib
from http import HTTPStatus from http import HTTPStatus
from io import BytesIO from io import BytesIO
from uuid import UUID from uuid import UUID
from pathlib import Path
from fastapi import APIRouter, Depends, HTTPException, UploadFile from fastapi import APIRouter, Depends, HTTPException, UploadFile
from fastapi.responses import StreamingResponse from fastapi.responses import StreamingResponse
@ -99,6 +100,47 @@ async def download_image(file_name: str, flow_id: UUID, storage_service: Storage
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@router.get("/profile_pictures/{folder_name}/{file_name}")
async def download_profile_picture(
folder_name: str,
file_name: str,
storage_service: StorageService = Depends(get_storage_service),
):
try:
extension = file_name.split(".")[-1]
config_dir = get_storage_service().settings_service.settings.config_dir
config_path = Path(config_dir)
folder_path = config_path / 'profile_pictures' / folder_name
content_type = build_content_type_from_extension(extension)
file_content = await storage_service.get_file(flow_id=folder_path, file_name=file_name)
return StreamingResponse(BytesIO(file_content), media_type=content_type)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get("/profile_pictures/list")
async def list_profile_pictures(storage_service: StorageService = Depends(get_storage_service)):
try:
config_dir = get_storage_service().settings_service.settings.config_dir
config_path = Path(config_dir)
people_path = config_path / "profile_pictures/People"
space_path = config_path / "profile_pictures/Space"
people = await storage_service.list_files(flow_id=people_path)
space = await storage_service.list_files(flow_id=space_path)
files = [Path("People") / i for i in people]
files += [Path("Space") / i for i in space]
return {"files": files}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get("/list/{flow_id}") @router.get("/list/{flow_id}")
async def list_files( async def list_files(
flow_id: UUID = Depends(get_flow_id), storage_service: StorageService = Depends(get_storage_service) flow_id: UUID = Depends(get_flow_id), storage_service: StorageService = Depends(get_storage_service)

View file

@ -117,6 +117,21 @@ async def get_transactions(
dicts = monitor_service.get_transactions( dicts = monitor_service.get_transactions(
source=source, target=target, status=status, order_by=order_by, flow_id=flow_id source=source, target=target, status=status, order_by=order_by, flow_id=flow_id
) )
return [TransactionModelResponse(**d) for d in dicts] result = []
for d in dicts:
d = TransactionModelResponse(
index=d["index"],
timestamp=d["timestamp"],
vertex_id=d["vertex_id"],
inputs=d["inputs"],
outputs=d["outputs"],
status=d["status"],
error=d["error"],
flow_id=d["flow_id"],
source=d["vertex_id"],
target=d["target_id"],
)
result.append(d)
return result
except Exception as e: except Exception as e:
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))

View file

@ -769,11 +769,13 @@ class Graph:
next_runnable_vertices, top_level_vertices = await self.get_next_and_top_level_vertices( next_runnable_vertices, top_level_vertices = await self.get_next_and_top_level_vertices(
lock, set_cache_coro, vertex lock, set_cache_coro, vertex
) )
log_transaction(vertex, status="success") flow_id = self.flow_id
log_transaction(flow_id, vertex, status="success")
return next_runnable_vertices, top_level_vertices, result_dict, params, valid, artifacts, vertex return next_runnable_vertices, top_level_vertices, result_dict, params, valid, artifacts, vertex
except Exception as exc: except Exception as exc:
logger.exception(f"Error building vertex: {exc}") logger.exception(f"Error building vertex: {exc}")
log_transaction(vertex, status="failure", error=str(exc)) flow_id = self.flow_id
log_transaction(flow_id, vertex, status="failure", error=str(exc))
raise exc raise exc
async def get_next_and_top_level_vertices( async def get_next_and_top_level_vertices(

View file

@ -529,12 +529,13 @@ class Vertex:
Returns: Returns:
The built result if use_result is True, else the built object. The built result if use_result is True, else the built object.
""" """
flow_id = self.graph.flow_id
if not self._built: if not self._built:
log_transaction(vertex=self, target=requester, status="error") log_transaction(flow_id, vertex=self, target=requester, status="error")
raise ValueError(f"Component {self.display_name} has not been built yet") raise ValueError(f"Component {self.display_name} has not been built yet")
result = self._built_result if self.use_result else self._built_object result = self._built_result if self.use_result else self._built_object
log_transaction(vertex=self, target=requester, status="success") log_transaction(flow_id, vertex=self, target=requester, status="success")
return result return result
async def _build_vertex_and_update_params(self, key, vertex: "Vertex"): async def _build_vertex_and_update_params(self, key, vertex: "Vertex"):

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View file

@ -1,5 +1,6 @@
import logging import logging
import os import os
import shutil
from collections import defaultdict from collections import defaultdict
from copy import deepcopy from copy import deepcopy
from datetime import datetime, timezone from datetime import datetime, timezone
@ -20,7 +21,7 @@ from langflow.services.database.models.user.crud import get_user_by_username
from langflow.services.deps import get_settings_service, session_scope from langflow.services.deps import get_settings_service, session_scope
from langflow.services.database.models.folder.utils import create_default_folder_if_it_doesnt_exist from langflow.services.database.models.folder.utils import create_default_folder_if_it_doesnt_exist
from langflow.services.deps import get_variable_service from langflow.services.deps import get_variable_service, get_storage_service
STARTER_FOLDER_NAME = "Starter Projects" STARTER_FOLDER_NAME = "Starter Projects"
@ -104,6 +105,25 @@ def load_starter_projects() -> list[tuple[Path, dict]]:
return starter_projects return starter_projects
def copy_profile_pictures():
config_dir = get_storage_service().settings_service.settings.config_dir
origin = Path(__file__).parent / "profile_pictures"
target = Path(config_dir) / "profile_pictures"
if not os.path.exists(origin):
raise ValueError(f"The source folder '{origin}' does not exist.")
if not os.path.exists(target):
os.makedirs(target)
try:
shutil.copytree(origin, target, dirs_exist_ok=True)
logger.debug(f"Folder copied from '{origin}' to '{target}'")
except Exception as e:
logger.error(f"Error copying the folder: {e}")
def get_project_data(project): def get_project_data(project):
project_name = project.get("name") project_name = project.get("name")
project_description = project.get("description") project_description = project.get("description")
@ -286,6 +306,7 @@ def create_or_update_starter_projects():
new_folder = create_starter_folder(session) new_folder = create_starter_folder(session)
starter_projects = load_starter_projects() starter_projects = load_starter_projects()
delete_start_projects(session, new_folder.id) delete_start_projects(session, new_folder.id)
copy_profile_pictures()
for project_path, project in starter_projects: for project_path, project in starter_projects:
( (
project_name, project_name,

View file

@ -85,7 +85,9 @@ def update_params_with_load_from_db_fields(
logger.info(f"Using environment variable {params[field]} for {field}") logger.info(f"Using environment variable {params[field]} for {field}")
if key is None: if key is None:
logger.warning(f"Could not get value for {field}. Setting it to None.") logger.warning(f"Could not get value for {field}. Setting it to None.")
params[field] = key
if field != "session_id":
params[field] = key
except Exception as exc: except Exception as exc:
logger.error(f"Failed to get value for {field} from custom component. Setting it to None. Error: {exc}") logger.error(f"Failed to get value for {field} from custom component. Setting it to None. Error: {exc}")

View file

@ -14,9 +14,10 @@ class TransactionModel(BaseModel):
vertex_id: str vertex_id: str
target_id: str | None = None target_id: str | None = None
inputs: dict inputs: dict
outputs: dict outputs: Optional[dict] = None
status: str status: str
error: Optional[str] = None error: Optional[str] = None
flow_id: Optional[str] = Field(default=None, alias="flow_id")
class Config: class Config:
from_attributes = True from_attributes = True
@ -41,9 +42,12 @@ class TransactionModelResponse(BaseModel):
timestamp: Optional[datetime] = Field(default_factory=datetime.now, alias="timestamp") timestamp: Optional[datetime] = Field(default_factory=datetime.now, alias="timestamp")
vertex_id: str vertex_id: str
inputs: dict inputs: dict
outputs: dict outputs: Optional[dict] = None
status: str status: str
error: Optional[str] = None error: Optional[str] = None
flow_id: Optional[str] = Field(default=None, alias="flow_id")
source: Optional[str] = None
target: Optional[str] = None
class Config: class Config:
from_attributes = True from_attributes = True

View file

@ -168,7 +168,9 @@ class MonitorService(Service):
order_by: Optional[str] = "timestamp", order_by: Optional[str] = "timestamp",
flow_id: Optional[str] = None, flow_id: Optional[str] = None,
): ):
query = "SELECT index,flow_id, source, target, target_args, status, error, timestamp FROM transactions" query = (
"SELECT index,flow_id, status, error, timestamp, vertex_id, inputs, outputs, target_id FROM transactions"
)
conditions = [] conditions = []
if source: if source:
conditions.append(f"source = '{source}'") conditions.append(f"source = '{source}'")
@ -183,7 +185,7 @@ class MonitorService(Service):
query += " WHERE " + " AND ".join(conditions) query += " WHERE " + " AND ".join(conditions)
if order_by: if order_by:
query += f" ORDER BY {order_by}" query += f" ORDER BY {order_by} DESC"
with duckdb.connect(str(self.db_path)) as conn: with duckdb.connect(str(self.db_path)) as conn:
df = conn.execute(query).df() df = conn.execute(query).df()

View file

@ -178,7 +178,7 @@ def build_clean_params(target: "Vertex") -> dict:
return params return params
def log_transaction(vertex: "Vertex", status, target: Optional["Vertex"] = None, error=None): def log_transaction(flow_id, vertex: "Vertex", status, target: Optional["Vertex"] = None, error=None):
try: try:
monitor_service = get_monitor_service() monitor_service = get_monitor_service()
clean_params = build_clean_params(vertex) clean_params = build_clean_params(vertex)
@ -186,10 +186,11 @@ def log_transaction(vertex: "Vertex", status, target: Optional["Vertex"] = None,
"vertex_id": str(vertex.id), "vertex_id": str(vertex.id),
"target_id": str(target.id) if target else None, "target_id": str(target.id) if target else None,
"inputs": clean_params, "inputs": clean_params,
"outputs": vertex.result.model_dump_json(), "outputs": vertex.result.model_dump_json() if vertex.result else None,
"timestamp": monitor_service.get_timestamp(), "timestamp": monitor_service.get_timestamp(),
"status": status, "status": status,
"error": error, "error": error,
"flow_id": flow_id,
} }
monitor_service.add_row(table_name="transactions", data=data) monitor_service.add_row(table_name="transactions", data=data)
except Exception as e: except Exception as e:

View file

@ -174,3 +174,12 @@ body {
border: none !important; border: none !important;
outline: none; outline: none;
} }
/* selected */
.react-flow__edge.selected .react-flow__edge-path {
stroke: var(--selected) !important;
}
.react-flow__edge .react-flow__edge-path {
stroke: var(--connection) !important;
}

View file

@ -4,7 +4,7 @@ import SwitchOutputView from "./components/switchOutputView";
export default function OutputModal({ open, setOpen, nodeId }): JSX.Element { export default function OutputModal({ open, setOpen, nodeId }): JSX.Element {
return ( return (
<BaseModal open={open} setOpen={setOpen} size="medium"> <BaseModal open={open} setOpen={setOpen} size="medium-tall">
<BaseModal.Header description="Inspect the output of the component below."> <BaseModal.Header description="Inspect the output of the component below.">
<div className="flex items-center"> <div className="flex items-center">
<span className="pr-2">Component Output</span> <span className="pr-2">Component Output</span>

View file

@ -274,7 +274,9 @@ export default function ParameterComponent({
: "Please build the component first" : "Please build the component first"
} }
> >
<button <Button
variant="none"
size="none"
disabled={!displayOutputPreview || unknownOutput} disabled={!displayOutputPreview || unknownOutput}
onClick={() => setOpenOutputModal(true)} onClick={() => setOpenOutputModal(true)}
data-testid={`output-inspection-${title.toLowerCase()}`} data-testid={`output-inspection-${title.toLowerCase()}`}
@ -288,7 +290,7 @@ export default function ParameterComponent({
)} )}
name={"ScanEye"} name={"ScanEye"}
/> />
</button> </Button>
</ShadTooltip> </ShadTooltip>
)} )}
</div> </div>

View file

@ -10,6 +10,7 @@ import {
RUN_TIMESTAMP_PREFIX, RUN_TIMESTAMP_PREFIX,
STATUS_BUILD, STATUS_BUILD,
STATUS_BUILDING, STATUS_BUILDING,
TOOLTIP_OUTDATED_NODE,
} from "../../constants/constants"; } from "../../constants/constants";
import { BuildStatus } from "../../constants/enums"; import { BuildStatus } from "../../constants/enums";
import { countHandlesFn } from "../helpers/count-handles"; import { countHandlesFn } from "../helpers/count-handles";
@ -35,6 +36,8 @@ import getFieldTitle from "../utils/get-field-title";
import sortFields from "../utils/sort-fields"; import sortFields from "../utils/sort-fields";
import isWrappedWithClass from "../../pages/FlowPage/components/PageComponent/utils/is-wrapped-with-class"; import isWrappedWithClass from "../../pages/FlowPage/components/PageComponent/utils/is-wrapped-with-class";
import ParameterComponent from "./components/parameterComponent"; import ParameterComponent from "./components/parameterComponent";
import { postCustomComponent } from "../../controllers/API";
import { cloneDeep } from "lodash";
export default function GenericNode({ export default function GenericNode({
data, data,
@ -204,6 +207,33 @@ export default function GenericNode({
setShowNode(data.showNode ?? true); setShowNode(data.showNode ?? true);
}, [data.showNode]); }, [data.showNode]);
const [loadingUpdate, setLoadingUpdate] = useState(false);
const handleUpdateCode = () => {
setLoadingUpdate(true);
takeSnapshot();
// to update we must get the code from the templates in useTypesStore
const thisNodeTemplate = templates[data.type]?.template;
// if the template does not have a code key
// return
if (!thisNodeTemplate?.code) return;
const currentCode = thisNodeTemplate.code.value;
if (data.node) {
postCustomComponent(currentCode, data.node)
.then((apiReturn) => {
const { data } = apiReturn;
if (data && updateNodeCode) {
updateNodeCode(data, currentCode, "code");
setLoadingUpdate(false);
}
})
.catch((err) => {
console.log(err);
});
}
};
const memoizedNodeToolbarComponent = useMemo(() => { const memoizedNodeToolbarComponent = useMemo(() => {
return ( return (
<NodeToolbar> <NodeToolbar>
@ -226,8 +256,6 @@ export default function GenericNode({
showNode={showNode} showNode={showNode}
openAdvancedModal={false} openAdvancedModal={false}
onCloseAdvancedModal={() => {}} onCloseAdvancedModal={() => {}}
updateNodeCode={updateNodeCode}
isOutdated={isOutdated}
selected={selected} selected={selected}
/> />
</NodeToolbar> </NodeToolbar>
@ -448,54 +476,71 @@ export default function GenericNode({
</div> </div>
{showNode && ( {showNode && (
<> <>
<ShadTooltip <div className="flex flex-shrink-0 items-center gap-1">
content={ {isOutdated && (
buildStatus === BuildStatus.BUILDING ? ( <ShadTooltip content={TOOLTIP_OUTDATED_NODE}>
<span> {STATUS_BUILDING} </span> <Button
) : !validationStatus ? ( onClick={handleUpdateCode}
<span className="flex">{STATUS_BUILD}</span> variant="secondary"
) : ( className={"h-9 px-1.5"}
<div className="max-h-100 p-2"> loading={loadingUpdate}
<div> >
{lastRunTime && ( <IconComponent
<div className="justify-left flex font-normal text-muted-foreground"> name="AlertTriangle"
<div>{RUN_TIMESTAMP_PREFIX}</div> className="h-5 w-5 text-status-yellow"
<div className="ml-1 text-status-blue"> />
{lastRunTime} </Button>
</ShadTooltip>
)}
<ShadTooltip
content={
buildStatus === BuildStatus.BUILDING ? (
<span> {STATUS_BUILDING} </span>
) : !validationStatus ? (
<span className="flex">{STATUS_BUILD}</span>
) : (
<div className="max-h-100 p-2">
<div>
{lastRunTime && (
<div className="justify-left flex font-normal text-muted-foreground">
<div>{RUN_TIMESTAMP_PREFIX}</div>
<div className="ml-1 text-status-blue">
{lastRunTime}
</div>
</div> </div>
)}
</div>
<div className="justify-left flex font-normal text-muted-foreground">
<div>Duration:</div>
<div className="ml-1 text-status-blue">
{validationStatus?.data.duration}
</div> </div>
)}
</div>
<div className="justify-left flex font-normal text-muted-foreground">
<div>Duration:</div>
<div className="ml-1 text-status-blue">
{validationStatus?.data.duration}
</div> </div>
</div> </div>
</div> )
) }
} side="bottom"
side="bottom"
>
<Button
onClick={() => {
if (buildStatus === BuildStatus.BUILDING || isBuilding)
return;
setValidationStatus(null);
buildFlow({ stopNodeId: data.id });
}}
variant="secondary"
className={"group h-9 px-1.5"}
> >
<div <Button
data-testid={ onClick={() => {
`button_run_` + data?.node?.display_name.toLowerCase() if (buildStatus === BuildStatus.BUILDING || isBuilding)
} return;
setValidationStatus(null);
buildFlow({ stopNodeId: data.id });
}}
variant="secondary"
className={"group h-9 px-1.5"}
> >
{renderIconStatus()} <div
</div> data-testid={
</Button> `button_run_` + data?.node?.display_name.toLowerCase()
</ShadTooltip> }
>
{renderIconStatus()}
</div>
</Button>
</ShadTooltip>
</div>
</> </>
)} )}
</div> </div>

View file

@ -23,10 +23,11 @@ const useCheckCodeValidity = (
if (!thisNodeTemplate.code) return; if (!thisNodeTemplate.code) return;
const currentCode = thisNodeTemplate.code?.value; const currentCode = thisNodeTemplate.code?.value;
const thisNodesCode = data.node!.template?.code?.value; const thisNodesCode = data.node!.template?.code?.value;
const componentsToIgnore = ["Custom Component", "Prompt"]; const componentsToIgnore = ["CustomComponent", "Prompt"];
if ( if (
currentCode !== thisNodesCode && currentCode !== thisNodesCode &&
!componentsToIgnore.includes(data.node!.display_name) !componentsToIgnore.includes(data.type) &&
!(data.node?.edited ?? false)
) { ) {
setIsOutdated(true); setIsOutdated(true);
} else { } else {

View file

@ -19,6 +19,7 @@ const useUpdateNodeCode = (
node: newNodeClass, node: newNodeClass,
description: newNodeClass.description ?? dataNode.description, description: newNodeClass.description ?? dataNode.description,
display_name: newNodeClass.display_name ?? dataNode.display_name, display_name: newNodeClass.display_name ?? dataNode.display_name,
edited: false,
}; };
newNode.data.node.template[name].value = code; newNode.data.node.template[name].value = code;

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -1,10 +1,12 @@
import TableAutoCellRender from "../tableComponent/components/tableAutoCellRender";
export default function ArrayReader({ array }: { array: any[] }): JSX.Element { export default function ArrayReader({ array }: { array: any[] }): JSX.Element {
//TODO check array type //TODO check array type
return ( return (
<div> <div>
<ul> <ul>
{array.map((item, index) => ( {array.map((item, index) => (
<li key={index}>{item}</li> <li key={index}>{<TableAutoCellRender value={item} />}</li>
))} ))}
</ul> </ul>
</div> </div>

View file

@ -217,7 +217,7 @@ export default function CollectionCardComponent({
data-testid={`card-${convertTestName(data.name)}`} data-testid={`card-${convertTestName(data.name)}`}
//TODO check color schema //TODO check color schema
className={cn( className={cn(
"group relative flex min-h-[11rem] flex-col justify-between overflow-hidden transition-all hover:bg-muted/50 hover:shadow-md hover:dark:bg-[#ffffff10]", "group relative flex h-[11rem] flex-col justify-between overflow-hidden transition-all hover:bg-muted/50 hover:shadow-md hover:dark:bg-[#ffffff10]",
disabled ? "pointer-events-none opacity-50" : "", disabled ? "pointer-events-none opacity-50" : "",
onClick ? "cursor-pointer" : "", onClick ? "cursor-pointer" : "",
isSelectedCard ? "border border-selected" : "", isSelectedCard ? "border border-selected" : "",

View file

@ -1,21 +0,0 @@
import { gradients } from "../../utils/styleUtils";
export default function GradientChooserComponent({ value, onChange }) {
return (
<div className="flex flex-wrap items-center justify-start gap-2">
{gradients.map((gradient, idx) => (
<div
onClick={() => {
onChange(gradient);
}}
className={
"duration-400 h-12 w-12 cursor-pointer rounded-full transition-all " +
gradient +
(value === gradient ? " shadow-lg ring-2 ring-primary" : "")
}
key={idx}
></div>
))}
</div>
);
}

View file

@ -1,9 +1,12 @@
import { useContext } from "react"; import { useContext } from "react";
import profileCircle from "../../assets/profile-circle.png";
import { FaDiscord, FaGithub } from "react-icons/fa"; import { FaDiscord, FaGithub } from "react-icons/fa";
import { RiTwitterXFill } from "react-icons/ri"; import { RiTwitterXFill } from "react-icons/ri";
import { Link, useLocation, useNavigate, useParams } from "react-router-dom"; import { Link, useLocation, useNavigate, useParams } from "react-router-dom";
import AlertDropdown from "../../alerts/alertDropDown"; import AlertDropdown from "../../alerts/alertDropDown";
import { import {
BACKEND_URL,
BASE_URL_API,
LOCATIONS_TO_RETURN, LOCATIONS_TO_RETURN,
USER_PROJECTS_HEADER, USER_PROJECTS_HEADER,
} from "../../constants/constants"; } from "../../constants/constants";
@ -192,34 +195,35 @@ export default function Header(): JSX.Element {
variant="none" variant="none"
size="none" size="none"
data-testid="user-profile-settings" data-testid="user-profile-settings"
className={ >
"h-7 w-7 rounded-full focus-visible:outline-0 " + <img
(userData?.profile_image ?? src={
(userData?.id `${BACKEND_URL.slice(
? gradients[ 0,
parseInt(userData?.id ?? "", 30) % gradients.length BACKEND_URL.length - 1,
] )}${BASE_URL_API}files/profile_pictures/${userData?.profile_image ?? "Space/046-rocket.png"}` ??
: "bg-gray-500")) profileCircle
} }
/> className="h-7 w-7 focus-visible:outline-0 "
/>
</Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent> <DropdownMenuContent>
{!autoLogin && ( {!autoLogin && (
<> <>
<DropdownMenuLabel> <DropdownMenuLabel>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div <img
className={ src={
"h-5 w-5 rounded-full focus-visible:outline-0 " + `${BACKEND_URL.slice(
(userData?.profile_image ?? 0,
(userData?.id BACKEND_URL.length - 1,
? gradients[ )}${BASE_URL_API}files/profile_pictures/${userData?.profile_image}` ??
parseInt(userData?.id ?? "", 30) % profileCircle
gradients.length
]
: "bg-gray-500"))
} }
className="h-5 w-5 focus-visible:outline-0 "
/> />
{userData?.username ?? "User"} {userData?.username ?? "User"}
</div> </div>
</DropdownMenuLabel> </DropdownMenuLabel>

Some files were not shown because too many files have changed in this diff Show more