Merge branch 'shortcuts_settings' of github.com:langflow-ai/langflow into shortcuts_settings
|
|
@ -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"])
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import hashlib
|
|||
from http import HTTPStatus
|
||||
from io import BytesIO
|
||||
from uuid import UUID
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, UploadFile
|
||||
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))
|
||||
|
||||
|
||||
@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}")
|
||||
async def list_files(
|
||||
flow_id: UUID = Depends(get_flow_id), storage_service: StorageService = Depends(get_storage_service)
|
||||
|
|
|
|||
|
|
@ -117,6 +117,21 @@ async def get_transactions(
|
|||
dicts = monitor_service.get_transactions(
|
||||
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:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
|
|
|||
|
|
@ -769,11 +769,13 @@ class Graph:
|
|||
next_runnable_vertices, top_level_vertices = await self.get_next_and_top_level_vertices(
|
||||
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
|
||||
except Exception as 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
|
||||
|
||||
async def get_next_and_top_level_vertices(
|
||||
|
|
|
|||
|
|
@ -529,12 +529,13 @@ class Vertex:
|
|||
Returns:
|
||||
The built result if use_result is True, else the built object.
|
||||
"""
|
||||
flow_id = self.graph.flow_id
|
||||
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")
|
||||
|
||||
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
|
||||
|
||||
async def _build_vertex_and_update_params(self, key, vertex: "Vertex"):
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
|
@ -1,5 +1,6 @@
|
|||
import logging
|
||||
import os
|
||||
import shutil
|
||||
from collections import defaultdict
|
||||
from copy import deepcopy
|
||||
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.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"
|
||||
|
|
@ -104,6 +105,25 @@ def load_starter_projects() -> list[tuple[Path, dict]]:
|
|||
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):
|
||||
project_name = project.get("name")
|
||||
project_description = project.get("description")
|
||||
|
|
@ -286,6 +306,7 @@ def create_or_update_starter_projects():
|
|||
new_folder = create_starter_folder(session)
|
||||
starter_projects = load_starter_projects()
|
||||
delete_start_projects(session, new_folder.id)
|
||||
copy_profile_pictures()
|
||||
for project_path, project in starter_projects:
|
||||
(
|
||||
project_name,
|
||||
|
|
|
|||
|
|
@ -85,7 +85,9 @@ def update_params_with_load_from_db_fields(
|
|||
logger.info(f"Using environment variable {params[field]} for {field}")
|
||||
if key is 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:
|
||||
logger.error(f"Failed to get value for {field} from custom component. Setting it to None. Error: {exc}")
|
||||
|
|
|
|||
|
|
@ -14,9 +14,10 @@ class TransactionModel(BaseModel):
|
|||
vertex_id: str
|
||||
target_id: str | None = None
|
||||
inputs: dict
|
||||
outputs: dict
|
||||
outputs: Optional[dict] = None
|
||||
status: str
|
||||
error: Optional[str] = None
|
||||
flow_id: Optional[str] = Field(default=None, alias="flow_id")
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
|
@ -41,9 +42,12 @@ class TransactionModelResponse(BaseModel):
|
|||
timestamp: Optional[datetime] = Field(default_factory=datetime.now, alias="timestamp")
|
||||
vertex_id: str
|
||||
inputs: dict
|
||||
outputs: dict
|
||||
outputs: Optional[dict] = None
|
||||
status: str
|
||||
error: Optional[str] = None
|
||||
flow_id: Optional[str] = Field(default=None, alias="flow_id")
|
||||
source: Optional[str] = None
|
||||
target: Optional[str] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
|
|
|||
|
|
@ -168,7 +168,9 @@ class MonitorService(Service):
|
|||
order_by: Optional[str] = "timestamp",
|
||||
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 = []
|
||||
if source:
|
||||
conditions.append(f"source = '{source}'")
|
||||
|
|
@ -183,7 +185,7 @@ class MonitorService(Service):
|
|||
query += " WHERE " + " AND ".join(conditions)
|
||||
|
||||
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:
|
||||
df = conn.execute(query).df()
|
||||
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ def build_clean_params(target: "Vertex") -> dict:
|
|||
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:
|
||||
monitor_service = get_monitor_service()
|
||||
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),
|
||||
"target_id": str(target.id) if target else None,
|
||||
"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(),
|
||||
"status": status,
|
||||
"error": error,
|
||||
"flow_id": flow_id,
|
||||
}
|
||||
monitor_service.add_row(table_name="transactions", data=data)
|
||||
except Exception as e:
|
||||
|
|
|
|||
|
|
@ -174,3 +174,12 @@ body {
|
|||
border: none !important;
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import SwitchOutputView from "./components/switchOutputView";
|
|||
|
||||
export default function OutputModal({ open, setOpen, nodeId }): JSX.Element {
|
||||
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.">
|
||||
<div className="flex items-center">
|
||||
<span className="pr-2">Component Output</span>
|
||||
|
|
|
|||
|
|
@ -274,7 +274,9 @@ export default function ParameterComponent({
|
|||
: "Please build the component first"
|
||||
}
|
||||
>
|
||||
<button
|
||||
<Button
|
||||
variant="none"
|
||||
size="none"
|
||||
disabled={!displayOutputPreview || unknownOutput}
|
||||
onClick={() => setOpenOutputModal(true)}
|
||||
data-testid={`output-inspection-${title.toLowerCase()}`}
|
||||
|
|
@ -288,7 +290,7 @@ export default function ParameterComponent({
|
|||
)}
|
||||
name={"ScanEye"}
|
||||
/>
|
||||
</button>
|
||||
</Button>
|
||||
</ShadTooltip>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
RUN_TIMESTAMP_PREFIX,
|
||||
STATUS_BUILD,
|
||||
STATUS_BUILDING,
|
||||
TOOLTIP_OUTDATED_NODE,
|
||||
} from "../../constants/constants";
|
||||
import { BuildStatus } from "../../constants/enums";
|
||||
import { countHandlesFn } from "../helpers/count-handles";
|
||||
|
|
@ -35,6 +36,8 @@ import getFieldTitle from "../utils/get-field-title";
|
|||
import sortFields from "../utils/sort-fields";
|
||||
import isWrappedWithClass from "../../pages/FlowPage/components/PageComponent/utils/is-wrapped-with-class";
|
||||
import ParameterComponent from "./components/parameterComponent";
|
||||
import { postCustomComponent } from "../../controllers/API";
|
||||
import { cloneDeep } from "lodash";
|
||||
|
||||
export default function GenericNode({
|
||||
data,
|
||||
|
|
@ -204,6 +207,33 @@ export default function GenericNode({
|
|||
setShowNode(data.showNode ?? true);
|
||||
}, [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(() => {
|
||||
return (
|
||||
<NodeToolbar>
|
||||
|
|
@ -226,8 +256,6 @@ export default function GenericNode({
|
|||
showNode={showNode}
|
||||
openAdvancedModal={false}
|
||||
onCloseAdvancedModal={() => {}}
|
||||
updateNodeCode={updateNodeCode}
|
||||
isOutdated={isOutdated}
|
||||
selected={selected}
|
||||
/>
|
||||
</NodeToolbar>
|
||||
|
|
@ -448,54 +476,71 @@ export default function GenericNode({
|
|||
</div>
|
||||
{showNode && (
|
||||
<>
|
||||
<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 className="flex flex-shrink-0 items-center gap-1">
|
||||
{isOutdated && (
|
||||
<ShadTooltip content={TOOLTIP_OUTDATED_NODE}>
|
||||
<Button
|
||||
onClick={handleUpdateCode}
|
||||
variant="secondary"
|
||||
className={"h-9 px-1.5"}
|
||||
loading={loadingUpdate}
|
||||
>
|
||||
<IconComponent
|
||||
name="AlertTriangle"
|
||||
className="h-5 w-5 text-status-yellow"
|
||||
/>
|
||||
</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 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 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>
|
||||
)
|
||||
}
|
||||
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"}
|
||||
)
|
||||
}
|
||||
side="bottom"
|
||||
>
|
||||
<div
|
||||
data-testid={
|
||||
`button_run_` + data?.node?.display_name.toLowerCase()
|
||||
}
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (buildStatus === BuildStatus.BUILDING || isBuilding)
|
||||
return;
|
||||
setValidationStatus(null);
|
||||
buildFlow({ stopNodeId: data.id });
|
||||
}}
|
||||
variant="secondary"
|
||||
className={"group h-9 px-1.5"}
|
||||
>
|
||||
{renderIconStatus()}
|
||||
</div>
|
||||
</Button>
|
||||
</ShadTooltip>
|
||||
<div
|
||||
data-testid={
|
||||
`button_run_` + data?.node?.display_name.toLowerCase()
|
||||
}
|
||||
>
|
||||
{renderIconStatus()}
|
||||
</div>
|
||||
</Button>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -23,10 +23,11 @@ const useCheckCodeValidity = (
|
|||
if (!thisNodeTemplate.code) return;
|
||||
const currentCode = thisNodeTemplate.code?.value;
|
||||
const thisNodesCode = data.node!.template?.code?.value;
|
||||
const componentsToIgnore = ["Custom Component", "Prompt"];
|
||||
const componentsToIgnore = ["CustomComponent", "Prompt"];
|
||||
if (
|
||||
currentCode !== thisNodesCode &&
|
||||
!componentsToIgnore.includes(data.node!.display_name)
|
||||
!componentsToIgnore.includes(data.type) &&
|
||||
!(data.node?.edited ?? false)
|
||||
) {
|
||||
setIsOutdated(true);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ const useUpdateNodeCode = (
|
|||
node: newNodeClass,
|
||||
description: newNodeClass.description ?? dataNode.description,
|
||||
display_name: newNodeClass.display_name ?? dataNode.display_name,
|
||||
edited: false,
|
||||
};
|
||||
|
||||
newNode.data.node.template[name].value = code;
|
||||
|
|
|
|||
BIN
src/frontend/src/assets/profile-circle.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
|
|
@ -1,10 +1,12 @@
|
|||
import TableAutoCellRender from "../tableComponent/components/tableAutoCellRender";
|
||||
|
||||
export default function ArrayReader({ array }: { array: any[] }): JSX.Element {
|
||||
//TODO check array type
|
||||
return (
|
||||
<div>
|
||||
<ul>
|
||||
{array.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
<li key={index}>{<TableAutoCellRender value={item} />}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ export default function CollectionCardComponent({
|
|||
data-testid={`card-${convertTestName(data.name)}`}
|
||||
//TODO check color schema
|
||||
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" : "",
|
||||
onClick ? "cursor-pointer" : "",
|
||||
isSelectedCard ? "border border-selected" : "",
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ import {
|
|||
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { UPLOAD_ERROR_ALERT } from "../../../../constants/alerts_constants";
|
||||
import { SAVED_HOVER } from "../../../../constants/constants";
|
||||
import { IS_MAC, SAVED_HOVER } from "../../../../constants/constants";
|
||||
import ExportModal from "../../../../modals/exportModal";
|
||||
import FlowLogsModal from "../../../../modals/flowLogsModal";
|
||||
import FlowSettingsModal from "../../../../modals/flowSettingsModal";
|
||||
|
|
@ -115,7 +115,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
|||
title: UPLOAD_ERROR_ALERT,
|
||||
list: [error],
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
}}
|
||||
>
|
||||
|
|
@ -139,7 +139,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
|||
>
|
||||
<IconComponent name="Undo" className="header-menu-options " />
|
||||
Undo
|
||||
{navigator.userAgent.toUpperCase().includes("MAC") ? (
|
||||
{IS_MAC ? (
|
||||
<IconComponent
|
||||
name="Command"
|
||||
className="absolute right-[1.15rem] top-[0.65em] h-3.5 w-3.5 stroke-2"
|
||||
|
|
@ -161,7 +161,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
|||
>
|
||||
<IconComponent name="Redo" className="header-menu-options " />
|
||||
Redo
|
||||
{navigator.userAgent.toUpperCase().includes("MAC") ? (
|
||||
{IS_MAC ? (
|
||||
<IconComponent
|
||||
name="Command"
|
||||
className="absolute right-[1.15rem] top-[0.65em] h-3.5 w-3.5 stroke-2"
|
||||
|
|
@ -201,7 +201,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
|||
name={isBuilding || saveLoading ? "Loader2" : "CheckCircle2"}
|
||||
className={cn(
|
||||
"h-4 w-4",
|
||||
isBuilding || saveLoading ? "animate-spin" : "animate-wiggle",
|
||||
isBuilding || saveLoading ? "animate-spin" : "animate-wiggle"
|
||||
)}
|
||||
/>
|
||||
{printByBuildStatus()}
|
||||
|
|
|
|||