Merge branch 'dev' into docs-how-to-contribute-components

This commit is contained in:
Mendon Kissling 2024-05-06 11:33:13 -04:00
commit fbae9f59c0
86 changed files with 691 additions and 521 deletions

90
.eslintrc.json Normal file
View file

@ -0,0 +1,90 @@
{
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:prettier/recommended"
],
"plugins": [
"react",
"import-helpers",
"prettier"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": [
"./tsconfig.node.json",
"./tsconfig.json"
],
"extraFileExtensions:": [
".mdx"
],
"extensions:": [
".mdx"
]
},
"env": {
"browser": true,
"es2021": true
},
"settings": {
"react": {
"version": "detect"
}
},
"rules": {
"no-console": "warn",
"no-self-assign": "warn",
"no-self-compare": "warn",
"complexity": [
"error",
{
"max": 15
}
],
"indent": [
"error",
2,
{
"SwitchCase": 1
}
],
"no-dupe-keys": "error",
"no-invalid-regexp": "error",
"no-undef": "error",
"no-return-assign": "error",
"no-redeclare": "error",
"no-empty": "error",
"no-await-in-loop": "error",
"react/react-in-jsx-scope": 0,
"node/exports-style": [
"error",
"module.exports"
],
"node/file-extension-in-import": [
"error",
"always"
],
"node/prefer-global/buffer": [
"error",
"always"
],
"node/prefer-global/console": [
"error",
"always"
],
"node/prefer-global/process": [
"error",
"always"
],
"node/prefer-global/url-search-params": [
"error",
"always"
],
"node/prefer-global/url": [
"error",
"always"
],
"node/prefer-promises/dns": "error",
"node/prefer-promises/fs": "error"
}
}

44
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,44 @@
fail_fast: true
repos:
- repo: https://github.com/pre-commit/mirrors-eslint
rev: "v9.1.1"
hooks:
- id: eslint
files: \.[jt]sx?$ # *.js, *.jsx, *.ts and *.tsx
types: [file]
args: ["--fix", "--no-warn-ignored"]
additional_dependencies:
- eslint@9.1.1
- eslint-plugin-prettier
- eslint-config-prettier
- prettier
- eslint-plugin-react@latest
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0
hooks:
- id: check-case-conflict
- id: end-of-file-fixer
- id: mixed-line-ending
args:
- --fix=lf
- id: trailing-whitespace
- id: pretty-format-json
exclude: ^tsconfig.*.json
args:
- --autofix
- --indent=4
- --no-sort-keys
- id: check-merge-conflict
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.4.2
hooks:
# Run the linter.
- id: ruff
# Python
files: \.py$
types: [file]
# Run the formatter.
- id: ruff-format
files: \.py$
types: [file]

View file

@ -135,6 +135,7 @@ frontendc:
install_backend: install_backend:
@echo 'Installing backend dependencies' @echo 'Installing backend dependencies'
@poetry install @poetry install
@poetry run pre-commit install
backend: backend:
@echo 'Setting up the environment' @echo 'Setting up the environment'

90
poetry.lock generated
View file

@ -828,6 +828,17 @@ files = [
[package.dependencies] [package.dependencies]
pycparser = "*" pycparser = "*"
[[package]]
name = "cfgv"
version = "3.4.0"
description = "Validate configuration and produce human readable error messages."
optional = false
python-versions = ">=3.8"
files = [
{file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
{file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
]
[[package]] [[package]]
name = "chardet" name = "chardet"
version = "5.2.0" version = "5.2.0"
@ -1604,6 +1615,17 @@ files = [
{file = "diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc"}, {file = "diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc"},
] ]
[[package]]
name = "distlib"
version = "0.3.8"
description = "Distribution utilities"
optional = false
python-versions = "*"
files = [
{file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"},
{file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"},
]
[[package]] [[package]]
name = "distro" name = "distro"
version = "1.9.0" version = "1.9.0"
@ -3143,6 +3165,20 @@ files = [
{file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"}, {file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"},
] ]
[[package]]
name = "identify"
version = "2.5.36"
description = "File identification library for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "identify-2.5.36-py2.py3-none-any.whl", hash = "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa"},
{file = "identify-2.5.36.tar.gz", hash = "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d"},
]
[package.extras]
license = ["ukkonen"]
[[package]] [[package]]
name = "idna" name = "idna"
version = "3.7" version = "3.7"
@ -5229,6 +5265,20 @@ plot = ["matplotlib"]
tgrep = ["pyparsing"] tgrep = ["pyparsing"]
twitter = ["twython"] twitter = ["twython"]
[[package]]
name = "nodeenv"
version = "1.8.0"
description = "Node.js virtual environment builder"
optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*"
files = [
{file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"},
{file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"},
]
[package.dependencies]
setuptools = "*"
[[package]] [[package]]
name = "numexpr" name = "numexpr"
version = "2.10.0" version = "2.10.0"
@ -6215,6 +6265,24 @@ dev = ["black", "flake8", "flake8-print", "isort", "pre-commit"]
sentry = ["django", "sentry-sdk"] sentry = ["django", "sentry-sdk"]
test = ["coverage", "flake8", "freezegun (==0.3.15)", "mock (>=2.0.0)", "pylint", "pytest", "pytest-timeout"] test = ["coverage", "flake8", "freezegun (==0.3.15)", "mock (>=2.0.0)", "pylint", "pytest", "pytest-timeout"]
[[package]]
name = "pre-commit"
version = "3.7.0"
description = "A framework for managing and maintaining multi-language pre-commit hooks."
optional = false
python-versions = ">=3.9"
files = [
{file = "pre_commit-3.7.0-py2.py3-none-any.whl", hash = "sha256:5eae9e10c2b5ac51577c3452ec0a490455c45a0533f7960f993a0d01e59decab"},
{file = "pre_commit-3.7.0.tar.gz", hash = "sha256:e209d61b8acdcf742404408531f0c37d49d2c734fd7cff2d6076083d191cb060"},
]
[package.dependencies]
cfgv = ">=2.0.0"
identify = ">=1.0.0"
nodeenv = ">=0.11.1"
pyyaml = ">=5.1"
virtualenv = ">=20.10.0"
[[package]] [[package]]
name = "prometheus-client" name = "prometheus-client"
version = "0.20.0" version = "0.20.0"
@ -9561,6 +9629,26 @@ files = [
{file = "vine-5.1.0.tar.gz", hash = "sha256:8b62e981d35c41049211cf62a0a1242d8c1ee9bd15bb196ce38aefd6799e61e0"}, {file = "vine-5.1.0.tar.gz", hash = "sha256:8b62e981d35c41049211cf62a0a1242d8c1ee9bd15bb196ce38aefd6799e61e0"},
] ]
[[package]]
name = "virtualenv"
version = "20.26.1"
description = "Virtual Python Environment builder"
optional = false
python-versions = ">=3.7"
files = [
{file = "virtualenv-20.26.1-py3-none-any.whl", hash = "sha256:7aa9982a728ae5892558bff6a2839c00b9ed145523ece2274fad6f414690ae75"},
{file = "virtualenv-20.26.1.tar.gz", hash = "sha256:604bfdceaeece392802e6ae48e69cec49168b9c5f4a44e483963f9242eb0e78b"},
]
[package.dependencies]
distlib = ">=0.3.7,<1"
filelock = ">=3.12.2,<4"
platformdirs = ">=3.9.1,<5"
[package.extras]
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
[[package]] [[package]]
name = "watchfiles" name = "watchfiles"
version = "0.21.0" version = "0.21.0"
@ -10253,4 +10341,4 @@ local = ["ctransformers", "llama-cpp-python", "sentence-transformers"]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = ">=3.10,<3.12" python-versions = ">=3.10,<3.12"
content-hash = "bec34397b534f882551511558c76785c7cd67e6a1eefc1d45f6a64d97175d886" content-hash = "b3d424bc8e83a9f10a8e71f95e2499b3018711d8edf7a594814b9388e5393a84"

View file

@ -108,6 +108,7 @@ respx = "^0.21.1"
pytest-instafail = "^0.5.0" pytest-instafail = "^0.5.0"
pytest-asyncio = "^0.23.0" pytest-asyncio = "^0.23.0"
pytest-profiling = "^1.7.0" pytest-profiling = "^1.7.0"
pre-commit = "^3.7.0"
[tool.poetry.extras] [tool.poetry.extras]
deploy = ["celery", "redis", "flower"] deploy = ["celery", "redis", "flower"]

View file

@ -32,90 +32,6 @@ case "$OS" in
;; ;;
esac esac
echo "Detected Operating System: $OS"
# Installation of pipx based on the detected OS
install_pipx() {
case $1 in
macOS)
# macOS installation using Homebrew
command -v brew >/dev/null 2>&1 || exit_with_message "Homebrew is not installed. Please install Homebrew first."
echo "Installing pipx using Homebrew..."
brew install pipx
pipx ensurepath
;;
Linux)
# Linux installation. Further checks are needed to distinguish between distributions
if grep -qEi "(ubuntu|debian)" /etc/*release; then
echo "Installing pipx on Ubuntu/Debian..."
sudo apt update
sudo apt install pipx -y
elif grep -qEi "fedora" /etc/*release; then
echo "Installing pipx on Fedora..."
sudo dnf install pipx -y
else
echo "Installing pipx using pip (other Linux distributions)..."
python3 -m pip install --user pipx
fi
pipx ensurepath
;;
*)
exit_with_message "Unsupported operating system for pipx installation."
;;
esac
}
# Function to fetch the latest version of pipx from GitHub and compare with the installed version
check_for_pipx_update() {
echo "Checking for updates to pipx..."
# Fetch the latest version of pipx, ensuring only to capture the numeric version without 'v' prefix.
local latest_version=$(curl -s https://api.github.com/repos/pypa/pipx/releases/latest | grep '"tag_name":' | sed -E 's/.*"tag_name": "v?([^"]+)".*/\1/')
# Extract the current installed version of pipx.
local current_version=$(pipx --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
if [[ "$latest_version" == "$current_version" ]]; then
echo "You have the latest version of pipx ($current_version)."
else
echo "A newer version of pipx ($latest_version) is available. You have $current_version. Do you want to update? (yes/no)"
read -r user_input
if [[ "$user_input" == "yes" ]]; then
echo "Updating pipx..."
case "$OS" in
macOS)
brew upgrade pipx
;;
Linux)
if grep -qEi "(ubuntu|debian)" /etc/*release; then
sudo apt update
sudo apt install --only-upgrade pipx -y
elif grep -qEi "fedora" /etc/*release; then
sudo dnf upgrade pipx -y
else
python3 -m pip install --user --upgrade pipx
fi
;;
*)
exit_with_message "Unsupported operating system for pipx update."
;;
esac
pipx ensurepath
echo "pipx updated to version $latest_version"
else
echo "Not updating pipx at this time."
fi
fi
}
# Now, modify the existing check to call check_for_pipx_update even if pipx is installed
if ! command -v pipx &> /dev/null; then
echo "Pipx is not installed. Installing..."
install_pipx "$OS"
echo "Pipx installed successfully."
else
echo "Pipx is already installed."
check_for_pipx_update
fi
echo "Checking Poetry installation..." echo "Checking Poetry installation..."
@ -124,7 +40,7 @@ if ! command -v poetry &> /dev/null
then then
echo "Poetry is not installed. Installing..." echo "Poetry is not installed. Installing..."
# Also install python 3.10 and use # Also install python 3.10 and use
pipx install poetry --python python3.10 --fetch-missing-python curl -sSL https://install.python-poetry.org | python3 -
echo "Poetry installed successfully." echo "Poetry installed successfully."
else else
echo "Poetry is already installed." echo "Poetry is already installed."
@ -146,3 +62,5 @@ else
echo "Poetry version is $1 or higher. No need to update." echo "Poetry version is $1 or higher. No need to update."
fi fi

View file

@ -205,17 +205,12 @@ async def build_and_cache_graph_from_db(
flow_id: str, flow_id: str,
session: Session, session: Session,
chat_service: "ChatService", chat_service: "ChatService",
graph: Optional[Graph] = None,
): ):
"""Build and cache the graph.""" """Build and cache the graph."""
flow: Optional[Flow] = session.get(Flow, flow_id) flow: Optional[Flow] = session.get(Flow, flow_id)
if not flow or not flow.data: if not flow or not flow.data:
raise ValueError("Invalid flow ID") raise ValueError("Invalid flow ID")
other_graph = Graph.from_payload(flow.data, flow_id) graph = Graph.from_payload(flow.data, flow_id)
if graph is None:
graph = other_graph
else:
graph = graph.update(other_graph)
await chat_service.set_cache(flow_id, graph) await chat_service.set_cache(flow_id, graph)
return graph return graph

View file

@ -79,13 +79,8 @@ async def retrieve_vertices_order(
""" """
try: try:
# First, we need to check if the flow_id is in the cache # First, we need to check if the flow_id is in the cache
graph = None
if not data: if not data:
if cache := await chat_service.get_cache(flow_id): graph = await build_and_cache_graph_from_db(flow_id=flow_id, session=session, chat_service=chat_service)
graph = cache.get("result")
graph = await build_and_cache_graph_from_db(
flow_id=flow_id, session=session, chat_service=chat_service, graph=graph
)
else: else:
graph = await build_and_cache_graph_from_data( graph = await build_and_cache_graph_from_data(
flow_id=flow_id, graph_data=data.model_dump(), chat_service=chat_service flow_id=flow_id, graph_data=data.model_dump(), chat_service=chat_service

View file

@ -93,14 +93,14 @@ class APIRequest(CustomComponent):
self, self,
method: str, method: str,
urls: List[str], urls: List[str],
_headers: Optional[Record] = None, headers: Optional[Record] = None,
body: Optional[Record] = None, body: Optional[Record] = None,
timeout: int = 5, timeout: int = 5,
) -> List[Record]: ) -> List[Record]:
if _headers is None: if headers is None:
headers = {} headers_dict = {}
else: else:
headers = _headers.data headers_dict = headers.data
bodies = [] bodies = []
if body: if body:
@ -114,7 +114,7 @@ class APIRequest(CustomComponent):
bodies += [None] * (len(urls) - len(bodies)) # type: ignore bodies += [None] * (len(urls) - len(bodies)) # type: ignore
async with httpx.AsyncClient() as client: async with httpx.AsyncClient() as client:
results = await asyncio.gather( results = await asyncio.gather(
*[self.make_request(client, method, u, headers, rec, timeout) for u, rec in zip(urls, bodies)] *[self.make_request(client, method, u, headers_dict, rec, timeout) for u, rec in zip(urls, bodies)]
) )
self.status = results self.status = results
return results return results

View file

@ -1,48 +0,0 @@
from pathlib import Path
from typing import Any, Dict
from langflow.base.data.utils import TEXT_FILE_TYPES, parse_text_file_to_record
from langflow.interface.custom.custom_component import CustomComponent
from langflow.schema import Record
class FileInput(CustomComponent):
display_name = "File Input"
description = "A generic file input."
icon = "file-text"
def build_config(self) -> Dict[str, Any]:
return {
"path": {
"display_name": "Path",
"field_type": "file",
"file_types": TEXT_FILE_TYPES,
"info": f"Supported file types: {', '.join(TEXT_FILE_TYPES)}",
},
"silent_errors": {
"display_name": "Silent Errors",
"advanced": True,
"info": "If true, errors will not raise an exception.",
},
}
def load_file(self, path: str, silent_errors: bool = False) -> Record:
resolved_path = self.resolve_path(path)
path_obj = Path(resolved_path)
extension = path_obj.suffix[1:].lower()
if extension == "doc":
raise ValueError("doc files are not supported. Please save as .docx")
if extension not in TEXT_FILE_TYPES:
raise ValueError(f"Unsupported file type: {extension}")
record = parse_text_file_to_record(resolved_path, silent_errors)
self.status = record if record else "No data"
return record or Record()
def build(
self,
path: str,
silent_errors: bool = False,
) -> Record:
record = self.load_file(path, silent_errors)
self.status = record
return record

View file

@ -1,17 +0,0 @@
from langflow.base.io.text import TextComponent
from langflow.field_typing.constants import Data, NestedDict
class JsonInput(TextComponent):
display_name = "JSON Input"
description = "JSON Input."
def build_config(self):
return {
"input_value": {
"display_name": "JSON",
"field_type": "NestedDict"
}
}
def build(self, input_value: NestedDict) -> NestedDict:
return input_value

View file

@ -1,19 +0,0 @@
from langflow.base.io.text import TextComponent
from langflow.field_typing.constants import Data
class KeyPairInput(TextComponent):
display_name = "Dictionary Input"
description = "Dictionary Input."
def build_config(self):
return {
"input_value": {
"display_name": "Dictionaries",
"field_type": "dict",
"list": True
}
}
def build(self, input_value: dict) -> dict:
return input_value

View file

@ -1,13 +0,0 @@
# from langflow.field_typing import Data
from langflow.schema import Record
from langflow.interface.custom.custom_component import CustomComponent
class StringListInput(CustomComponent):
display_name = "String List Input"
def build_config(self):
return {"input_value": {"display_name": "String List Input", "field_type": "str", "list": True}}
def build(self, input_value: list) -> Record:
return Record(data=input_value)

View file

@ -1,17 +0,0 @@
from typing import Optional
from langflow.base.io.text import TextComponent
from langflow.field_typing import Text, Data
class CSVOutput(TextComponent):
display_name = "CSV Output"
description = "Used view csv files"
field_config = {
"input_value": {"display_name": "csv","info":"A csv blob","input_types":["Data"]},
"separator": {"display_name": "separator","info":"The separator used in the csv file","input_types":["Text"], "field_type":"Text","default_value":";","options":[";", ",", "|"]},
}
def build(self, input_value: Data, separator) -> Data:
return {"data": input_value, "separator": separator}

View file

@ -1,15 +0,0 @@
from typing import Optional
from langflow.base.io.text import TextComponent
from langflow.field_typing import Text
class ImageOutput(TextComponent):
display_name = "Image Output"
description = "Used view image files"
field_config = {
"input_value": {"display_name": "image","info":"A image url","input_types":["Text"]},
}
def build(self, input_value: Text) -> Text:
return input_value

View file

@ -1,17 +0,0 @@
from langflow.base.io.text import TextComponent
from langflow.field_typing.constants import Data, NestedDict
class JsonOutput(TextComponent):
display_name = "JSON Output"
description = "JSON Output."
def build_config(self):
return {
"input_value": {
"display_name": "JSON",
"field_type": "NestedDict"
}
}
def build(self, input_value: NestedDict) -> NestedDict:
return input_value

View file

@ -1,19 +0,0 @@
from langflow.base.io.text import TextComponent
from langflow.field_typing.constants import Data
class KeyPairOutput(TextComponent):
display_name = "Dictionary Output"
description = "Dictionary Output."
def build_config(self):
return {
"input_value": {
"display_name": "Dictionaries",
"field_type": "dict",
"list": True
}
}
def build(self, input_value: dict) -> dict:
return input_value

View file

@ -1,16 +0,0 @@
from typing import Optional
from langflow.base.io.text import TextComponent
from langflow.field_typing import Text
class PDFOutput(TextComponent):
display_name = "PDF Output"
description = "Used view pdf files"
field_config = {
"input_value": {"display_name": "pdf","info":"A pdf url","input_types":["Text"]},
}
def build(self, input_value: Text) -> Text:
return input_value

View file

@ -1,13 +0,0 @@
# from langflow.field_typing import Data
from langflow.schema import Record
from langflow.interface.custom.custom_component import CustomComponent
class StringListOutput(CustomComponent):
display_name = "String List Output"
def build_config(self):
return {"input_value": {"display_name": "String List Output", "field_type": "str", "list": True}}
def build(self, input_value: list) -> Record:
return Record(data=input_value)

View file

@ -445,9 +445,16 @@ class Graph:
vertex = self.get_vertex(vertex_id) vertex = self.get_vertex(vertex_id)
vertex.set_state(state) vertex.set_state(state)
def mark_branch(self, vertex_id: str, state: str): def mark_branch(self, vertex_id: str, state: str, visited: Optional[set] = None):
"""Marks a branch of the graph.""" """Marks a branch of the graph."""
if visited is None:
visited = set()
visited.add(vertex_id)
if vertex_id in visited:
return
self.mark_vertex(vertex_id, state) self.mark_vertex(vertex_id, state)
for child_id in self.parent_child_map[vertex_id]: for child_id in self.parent_child_map[vertex_id]:
self.mark_branch(child_id, state) self.mark_branch(child_id, state)

View file

@ -185,7 +185,7 @@ async def instantiate_custom_component(params, user_id, vertex):
# Call the build method directly if it's sync # Call the build method directly if it's sync
build_result = custom_component.build(**params_copy) build_result = custom_component.build(**params_copy)
custom_repr = custom_component.custom_repr() custom_repr = custom_component.custom_repr()
if not custom_repr and isinstance(build_result, (dict, Record, str)): if custom_repr is None and isinstance(build_result, (dict, Record, str)):
custom_repr = build_result custom_repr = build_result
if not isinstance(custom_repr, str): if not isinstance(custom_repr, str):
custom_repr = str(custom_repr) custom_repr = str(custom_repr)

View file

@ -59,7 +59,7 @@ def get_id_from_search_string(search_string: str) -> Optional[str]:
Returns: Returns:
Optional[str]: The extracted ID, or None if no ID is found. Optional[str]: The extracted ID, or None if no ID is found.
""" """
possible_id = search_string possible_id: Optional[str] = search_string
if "www.langflow.store/store/" in search_string: if "www.langflow.store/store/" in search_string:
possible_id = search_string.split("/")[-1] possible_id = search_string.split("/")[-1]

View file

@ -1,7 +1,24 @@
{ {
"extends": ["eslint:recommended", "plugin:node/recommended"], "extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:prettier/recommended"
],
"plugins": ["react", "import-helpers", "prettier"],
"parser": "@typescript-eslint/parser",
"parserOptions": { "parserOptions": {
"ecmaVersion": 2018 "project": ["./tsconfig.node.json", "./tsconfig.json"],
"extraFileExtensions:": [".mdx"],
"extensions:": [".mdx"]
},
"env": {
"browser": true,
"es2021": true
},
"settings": {
"react": {
"version": "detect"
}
}, },
"rules": { "rules": {
"no-console": "warn", "no-console": "warn",
@ -16,6 +33,7 @@
"no-redeclare": "error", "no-redeclare": "error",
"no-empty": "error", "no-empty": "error",
"no-await-in-loop": "error", "no-await-in-loop": "error",
"react/react-in-jsx-scope": 0,
"node/exports-style": ["error", "module.exports"], "node/exports-style": ["error", "module.exports"],
"node/file-extension-in-import": ["error", "always"], "node/file-extension-in-import": ["error", "always"],
"node/prefer-global/buffer": ["error", "always"], "node/prefer-global/buffer": ["error", "always"],

View file

@ -99,6 +99,7 @@
"prettier-plugin-organize-imports": "^3.2.3", "prettier-plugin-organize-imports": "^3.2.3",
"prettier-plugin-tailwindcss": "^0.3.0", "prettier-plugin-tailwindcss": "^0.3.0",
"pretty-quick": "^3.1.3", "pretty-quick": "^3.1.3",
"simple-git-hooks": "^2.11.1",
"tailwindcss": "^3.3.3", "tailwindcss": "^3.3.3",
"tailwindcss-dotted-background": "^1.1.0", "tailwindcss-dotted-background": "^1.1.0",
"typescript": "^5.2.2", "typescript": "^5.2.2",
@ -11917,6 +11918,16 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/simple-git-hooks": {
"version": "2.11.1",
"resolved": "https://registry.npmjs.org/simple-git-hooks/-/simple-git-hooks-2.11.1.tgz",
"integrity": "sha512-tgqwPUMDcNDhuf1Xf6KTUsyeqGdgKMhzaH4PAZZuzguOgTl5uuyeYe/8mWgAr6IBxB5V06uqEf6Dy37gIWDtDg==",
"dev": true,
"hasInstallScript": true,
"bin": {
"simple-git-hooks": "cli.js"
}
},
"node_modules/sisteransi": { "node_modules/sisteransi": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",

View file

@ -78,6 +78,9 @@
"format": "npx prettier --write \"./**/*.{js,jsx,ts,tsx,json,md}\" --ignore-path .prettierignore", "format": "npx prettier --write \"./**/*.{js,jsx,ts,tsx,json,md}\" --ignore-path .prettierignore",
"type-check": "tsc --noEmit --pretty --project tsconfig.json && vite" "type-check": "tsc --noEmit --pretty --project tsconfig.json && vite"
}, },
"simple-git-hooks": {
"pre-commit": "npx pretty-quick --staged"
},
"eslintConfig": { "eslintConfig": {
"extends": [ "extends": [
"react-app", "react-app",
@ -121,6 +124,7 @@
"prettier-plugin-organize-imports": "^3.2.3", "prettier-plugin-organize-imports": "^3.2.3",
"prettier-plugin-tailwindcss": "^0.3.0", "prettier-plugin-tailwindcss": "^0.3.0",
"pretty-quick": "^3.1.3", "pretty-quick": "^3.1.3",
"simple-git-hooks": "^2.11.1",
"tailwindcss": "^3.3.3", "tailwindcss": "^3.3.3",
"tailwindcss-dotted-background": "^1.1.0", "tailwindcss-dotted-background": "^1.1.0",
"typescript": "^5.2.2", "typescript": "^5.2.2",

View file

@ -6,7 +6,7 @@ import "./App.css";
import ErrorAlert from "./alerts/error"; import ErrorAlert from "./alerts/error";
import NoticeAlert from "./alerts/notice"; import NoticeAlert from "./alerts/notice";
import SuccessAlert from "./alerts/success"; import SuccessAlert from "./alerts/success";
import CrashErrorComponent from "./components/CrashErrorComponent"; import CrashErrorComponent from "./components/crashErrorComponent";
import FetchErrorComponent from "./components/fetchErrorComponent"; import FetchErrorComponent from "./components/fetchErrorComponent";
import LoadingComponent from "./components/loadingComponent"; import LoadingComponent from "./components/loadingComponent";
import { import {

View file

@ -5,6 +5,7 @@ import useAlertStore from "../../stores/alertStore";
import { useGlobalVariablesStore } from "../../stores/globalVariables"; import { useGlobalVariablesStore } from "../../stores/globalVariables";
import { useTypesStore } from "../../stores/typesStore"; import { useTypesStore } from "../../stores/typesStore";
import { ResponseErrorDetailAPI } from "../../types/api"; import { ResponseErrorDetailAPI } from "../../types/api";
import { sortByName } from "../../utils/utils";
import ForwardedIconComponent from "../genericIconComponent"; import ForwardedIconComponent from "../genericIconComponent";
import InputComponent from "../inputComponent"; import InputComponent from "../inputComponent";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
@ -22,15 +23,20 @@ export default function AddNewVariableButton({ children }): JSX.Element {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const setErrorData = useAlertStore((state) => state.setErrorData); const setErrorData = useAlertStore((state) => state.setErrorData);
const componentFields = useTypesStore((state) => state.ComponentFields); const componentFields = useTypesStore((state) => state.ComponentFields);
const unavaliableFields =new Set(Object.keys(useGlobalVariablesStore( const unavaliableFields = new Set(
(state) => state.unavaliableFields Object.keys(useGlobalVariablesStore((state) => state.unavaliableFields)),
)));
const availableFields = Array.from(componentFields).filter(
(field) => !unavaliableFields.has(field)
); );
const availableFields = () => {
const fields = Array.from(componentFields).filter(
(field) => !unavaliableFields.has(field),
);
return sortByName(fields);
};
const addGlobalVariable = useGlobalVariablesStore( const addGlobalVariable = useGlobalVariablesStore(
(state) => state.addGlobalVariable (state) => state.addGlobalVariable,
); );
function handleSaveVariable() { function handleSaveVariable() {
@ -97,6 +103,7 @@ export default function AddNewVariableButton({ children }): JSX.Element {
password={false} password={false}
options={["Generic", "Credential"]} options={["Generic", "Credential"]}
placeholder="Choose a type for the variable..." placeholder="Choose a type for the variable..."
id={"type-global-variables"}
></InputComponent> ></InputComponent>
<Label>Value</Label> <Label>Value</Label>
<Textarea <Textarea
@ -112,8 +119,9 @@ export default function AddNewVariableButton({ children }): JSX.Element {
setSelectedOptions={(value) => setFields(value)} setSelectedOptions={(value) => setFields(value)}
selectedOptions={fields} selectedOptions={fields}
password={false} password={false}
options={availableFields} options={availableFields()}
placeholder="Choose a field for the variable..." placeholder="Choose a field for the variable..."
id={"apply-to-fields"}
></InputComponent> ></InputComponent>
</div> </div>
</BaseModal.Content> </BaseModal.Content>

View file

@ -1,17 +1,16 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { getComponent, postLikeComponent } from "../../controllers/API"; import { getComponent, postLikeComponent } from "../../controllers/API";
import DeleteConfirmationModal from "../../modals/DeleteConfirmationModal";
import IOModal from "../../modals/IOModal"; import IOModal from "../../modals/IOModal";
import DeleteConfirmationModal from "../../modals/deleteConfirmationModal";
import useAlertStore from "../../stores/alertStore"; import useAlertStore from "../../stores/alertStore";
import useFlowStore from "../../stores/flowStore"; import useFlowStore from "../../stores/flowStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore"; import useFlowsManagerStore from "../../stores/flowsManagerStore";
import { useStoreStore } from "../../stores/storeStore"; import { useStoreStore } from "../../stores/storeStore";
import { storeComponent } from "../../types/store"; import { storeComponent } from "../../types/store";
import cloneFLowWithParent from "../../utils/storeUtils"; import cloneFLowWithParent from "../../utils/storeUtils";
import { cn } from "../../utils/utils"; import { cn, convertTestName } from "../../utils/utils";
import ShadTooltip from "../ShadTooltipComponent";
import IconComponent from "../genericIconComponent"; import IconComponent from "../genericIconComponent";
import { Badge } from "../ui/badge"; import ShadTooltip from "../shadTooltipComponent";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
import { import {
Card, Card,
@ -48,11 +47,11 @@ export default function CollectionCardComponent({
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [loadingLike, setLoadingLike] = useState(false); const [loadingLike, setLoadingLike] = useState(false);
const [liked_by_user, setLiked_by_user] = useState( const [liked_by_user, setLiked_by_user] = useState(
data?.liked_by_user ?? false data?.liked_by_user ?? false,
); );
const [likes_count, setLikes_count] = useState(data?.liked_by_count ?? 0); const [likes_count, setLikes_count] = useState(data?.liked_by_count ?? 0);
const [downloads_count, setDownloads_count] = useState( const [downloads_count, setDownloads_count] = useState(
data?.downloads_count ?? 0 data?.downloads_count ?? 0,
); );
const currentFlow = useFlowsManagerStore((state) => state.currentFlow); const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow); const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
@ -61,8 +60,9 @@ export default function CollectionCardComponent({
const setNodes = useFlowStore((state) => state.setNodes); const setNodes = useFlowStore((state) => state.setNodes);
const setEdges = useFlowStore((state) => state.setEdges); const setEdges = useFlowStore((state) => state.setEdges);
const [openPlayground, setOpenPlayground] = useState(false); const [openPlayground, setOpenPlayground] = useState(false);
const [openDelete, setOpenDelete] = useState(false);
const setCurrentFlowId = useFlowsManagerStore( const setCurrentFlowId = useFlowsManagerStore(
(state) => state.setCurrentFlowId (state) => state.setCurrentFlowId,
); );
const [loadingPlayground, setLoadingPlayground] = useState(false); const [loadingPlayground, setLoadingPlayground] = useState(false);
@ -82,8 +82,8 @@ export default function CollectionCardComponent({
} else { } else {
setNodes([], true); setNodes([], true);
setEdges([], true); setEdges([], true);
cleanFlowPool();
} }
cleanFlowPool();
} }
}, [openPlayground]); }, [openPlayground]);
@ -168,10 +168,12 @@ export default function CollectionCardComponent({
return ( return (
<> <>
<Card <Card
data-testid={`card-${convertTestName(data.name)}`}
//TODO check color schema
className={cn( className={cn(
"group relative flex min-h-[11rem] flex-col justify-between overflow-hidden transition-all hover:shadow-md", "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]",
disabled ? "pointer-events-none opacity-50" : "", disabled ? "pointer-events-none opacity-50" : "",
onClick ? "cursor-pointer" : "" onClick ? "cursor-pointer" : "",
)} )}
onClick={onClick} onClick={onClick}
> >
@ -184,7 +186,7 @@ export default function CollectionCardComponent({
"flex-shrink-0", "flex-shrink-0",
data.is_component data.is_component
? "mx-0.5 h-6 w-6 text-component-icon" ? "mx-0.5 h-6 w-6 text-component-icon"
: "h-7 w-7 flex-shrink-0 text-flow-icon" : "h-7 w-7 flex-shrink-0 text-flow-icon",
)} )}
name={data.is_component ? "ToyBrick" : "Group"} name={data.is_component ? "ToyBrick" : "Group"}
/> />
@ -236,16 +238,18 @@ export default function CollectionCardComponent({
)} )}
{onDelete && data?.metadata === undefined && ( {onDelete && data?.metadata === undefined && (
<DeleteConfirmationModal <button
onConfirm={() => { className="z-50"
onDelete(); onClick={(e) => {
e.stopPropagation();
setOpenDelete(true);
}} }}
> >
<IconComponent <IconComponent
name="Trash2" name="Trash2"
className="h-5 w-5 text-primary opacity-0 transition-all hover:text-destructive group-hover:opacity-100" className="h-5 w-5 text-primary opacity-0 transition-all hover:text-destructive group-hover:opacity-100"
/> />
</DeleteConfirmationModal> </button>
)} )}
</CardTitle> </CardTitle>
</div> </div>
@ -266,7 +270,7 @@ export default function CollectionCardComponent({
</span> </span>
)} )}
<div className="flex w-full flex-1 flex-wrap gap-2"> <div className="flex w-full flex-1 flex-wrap gap-2">
{data.tags && {/* {data.tags &&
data.tags.length > 0 && data.tags.length > 0 &&
data.tags.map((tag, index) => ( data.tags.map((tag, index) => (
<Badge <Badge
@ -277,7 +281,7 @@ export default function CollectionCardComponent({
> >
{tag.name} {tag.name}
</Badge> </Badge>
))} ))} */}
</div> </div>
</div> </div>
@ -288,8 +292,8 @@ export default function CollectionCardComponent({
</div> </div>
<CardFooter> <CardFooter>
<div className="flex w-full items-center justify-between gap-2"> <div className="z-50 flex w-full items-center justify-between gap-2">
<div className="flex w-full flex-wrap items-end justify-between gap-2"> <div className="flex w-full flex-wrap items-end justify-end gap-2">
{playground && data?.metadata !== undefined ? ( {playground && data?.metadata !== undefined ? (
<Button <Button
disabled={loadingPlayground} disabled={loadingPlayground}
@ -297,7 +301,7 @@ export default function CollectionCardComponent({
tabIndex={-1} tabIndex={-1}
variant="outline" variant="outline"
size="sm" size="sm"
className="gap-2 whitespace-nowrap" className="z-50 gap-2 whitespace-nowrap"
data-testid={"playground-flow-button-" + data.id} data-testid={"playground-flow-button-" + data.id}
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
@ -326,9 +330,7 @@ export default function CollectionCardComponent({
)} )}
Playground Playground
</Button> </Button>
) : ( ) : undefined}
<div></div>
)}
{data.liked_by_count != undefined && ( {data.liked_by_count != undefined && (
<div className="flex gap-0.5"> <div className="flex gap-0.5">
{onDelete && data?.metadata !== undefined ? ( {onDelete && data?.metadata !== undefined ? (
@ -351,10 +353,11 @@ export default function CollectionCardComponent({
} }
> >
<IconComponent <IconComponent
data-testid={`delete-${convertTestName(data.name)}`}
name="Trash2" name="Trash2"
className={cn( className={cn(
"h-5 w-5", "h-5 w-5",
!authorized ? " text-ring" : "" !authorized ? " text-ring" : "",
)} )}
/> />
</Button> </Button>
@ -389,7 +392,7 @@ export default function CollectionCardComponent({
liked_by_user liked_by_user
? "fill-destructive stroke-destructive" ? "fill-destructive stroke-destructive"
: "", : "",
!authorized ? " text-ring" : "" !authorized ? " text-ring" : "",
)} )}
/> />
</Button> </Button>
@ -427,22 +430,21 @@ export default function CollectionCardComponent({
} }
className={cn( className={cn(
loading ? "h-5 w-5 animate-spin" : "h-5 w-5", loading ? "h-5 w-5 animate-spin" : "h-5 w-5",
!authorized ? " text-ring" : "" !authorized ? " text-ring" : "",
)} )}
/> />
</Button> </Button>
</ShadTooltip> </ShadTooltip>
</div> </div>
)} )}
{button && button}
{playground && data?.metadata === undefined && ( {playground && data?.metadata === undefined && (
<Button <Button
disabled={loadingPlayground} disabled={loadingPlayground}
key={data.id} key={data.id}
tabIndex={-1} tabIndex={-1}
variant="outline" variant="primary"
size="sm" size="sm"
className="gap-2 whitespace-nowrap" className="gap-2 whitespace-nowrap bg-muted"
data-testid={"playground-flow-button-" + data.id} data-testid={"playground-flow-button-" + data.id}
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
@ -485,6 +487,17 @@ export default function CollectionCardComponent({
<></> <></>
</IOModal> </IOModal>
)} )}
{openDelete && (
<DeleteConfirmationModal
open={openDelete}
setOpen={setOpenDelete}
onConfirm={() => {
if (onDelete) onDelete();
}}
>
<></>
</DeleteConfirmationModal>
)}
</> </>
); );
} }

View file

@ -1,7 +1,7 @@
import { Transition } from "@headlessui/react"; import { Transition } from "@headlessui/react";
import { useEffect, useMemo, useRef, useState } from "react"; import { useEffect, useMemo, useRef, useState } from "react";
import ApiModal from "../../modals/ApiModal";
import IOModal from "../../modals/IOModal"; import IOModal from "../../modals/IOModal";
import ApiModal from "../../modals/apiModal";
import ShareModal from "../../modals/shareModal"; import ShareModal from "../../modals/shareModal";
import useFlowStore from "../../stores/flowStore"; import useFlowStore from "../../stores/flowStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore"; import useFlowsManagerStore from "../../stores/flowsManagerStore";
@ -50,7 +50,7 @@ export default function FlowToolbar(): JSX.Element {
"relative inline-flex h-full w-full items-center justify-center gap-[4px] bg-muted px-5 py-3 text-sm font-semibold text-foreground transition-all duration-150 ease-in-out hover:bg-background hover:bg-hover ", "relative inline-flex h-full w-full items-center justify-center gap-[4px] bg-muted px-5 py-3 text-sm font-semibold text-foreground transition-all duration-150 ease-in-out hover:bg-background hover:bg-hover ",
!hasApiKey || !validApiKey || !hasStore !hasApiKey || !validApiKey || !hasStore
? " button-disable text-muted-foreground " ? " button-disable text-muted-foreground "
: "" : "",
)} )}
> >
<ForwardedIconComponent <ForwardedIconComponent
@ -59,14 +59,14 @@ export default function FlowToolbar(): JSX.Element {
"-m-0.5 -ml-1 h-6 w-6", "-m-0.5 -ml-1 h-6 w-6",
!hasApiKey || !validApiKey || !hasStore !hasApiKey || !validApiKey || !hasStore
? "extra-side-bar-save-disable" ? "extra-side-bar-save-disable"
: "" : "",
)} )}
/> />
Share Share
</button> </button>
</ShareModal> </ShareModal>
), ),
[hasApiKey, validApiKey, currentFlow, hasStore] [hasApiKey, validApiKey, currentFlow, hasStore],
); );
return ( return (
@ -103,12 +103,10 @@ export default function FlowToolbar(): JSX.Element {
className={`relative inline-flex w-full cursor-not-allowed items-center justify-center gap-1 px-5 py-3 text-sm font-semibold text-muted-foreground transition-all duration-150 ease-in-out ease-in-out`} className={`relative inline-flex w-full cursor-not-allowed items-center justify-center gap-1 px-5 py-3 text-sm font-semibold text-muted-foreground transition-all duration-150 ease-in-out ease-in-out`}
> >
<ForwardedIconComponent <ForwardedIconComponent
name="Zap" name="BotMessageSquareIcon"
className={ className={" h-5 w-5 transition-all"}
"message-button-icon h-5 w-5 fill-muted-foreground stroke-muted-foreground transition-all"
}
/> />
Run Playground
</div> </div>
)} )}
</div> </div>
@ -120,7 +118,7 @@ export default function FlowToolbar(): JSX.Element {
<ApiModal flow={currentFlow}> <ApiModal flow={currentFlow}>
<div <div
className={classNames( className={classNames(
"relative inline-flex w-full items-center justify-center gap-1 px-5 py-3 text-sm font-semibold text-foreground transition-all duration-150 ease-in-out hover:bg-hover" "relative inline-flex w-full items-center justify-center gap-1 px-5 py-3 text-sm font-semibold text-foreground transition-all duration-150 ease-in-out hover:bg-hover",
)} )}
> >
<ForwardedIconComponent <ForwardedIconComponent

View file

@ -2,7 +2,6 @@ import { cloneDeep } from "lodash";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism"; import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
import AccordionComponent from "../../components/AccordionComponent";
import CodeAreaComponent from "../../components/codeAreaComponent"; import CodeAreaComponent from "../../components/codeAreaComponent";
import Dropdown from "../../components/dropdownComponent"; import Dropdown from "../../components/dropdownComponent";
import FloatComponent from "../../components/floatComponent"; import FloatComponent from "../../components/floatComponent";
@ -36,11 +35,12 @@ import {
hasDuplicateKeys, hasDuplicateKeys,
} from "../../utils/reactflowUtils"; } from "../../utils/reactflowUtils";
import { classNames } from "../../utils/utils"; import { classNames } from "../../utils/utils";
import ShadTooltip from "../ShadTooltipComponent"; import AccordionComponent from "../accordionComponent";
import DictComponent from "../dictComponent"; import DictComponent from "../dictComponent";
import IconComponent from "../genericIconComponent"; import IconComponent from "../genericIconComponent";
import KeypairListComponent from "../keypairListComponent";
import InputComponent from "../inputComponent"; import InputComponent from "../inputComponent";
import KeypairListComponent from "../keypairListComponent";
import ShadTooltip from "../shadTooltipComponent";
export default function CodeTabsComponent({ export default function CodeTabsComponent({
flow, flow,

View file

@ -69,7 +69,7 @@ function CsvOutputComponent({
if (file) { if (file) {
const { rowData: data, colDefs: columns } = convertCSVToData( const { rowData: data, colDefs: columns } = convertCSVToData(
file, file,
separator separator,
); );
setRowData(data); setRowData(data);
setColDefs(columns); setColDefs(columns);
@ -114,14 +114,14 @@ function CsvOutputComponent({
(params: any) => { (params: any) => {
updateRowHeight(params); updateRowHeight(params);
}, },
[updateRowHeight] [updateRowHeight],
); );
const onGridSizeChanged = useCallback( const onGridSizeChanged = useCallback(
(params: any) => { (params: any) => {
updateRowHeight(params); updateRowHeight(params);
}, },
[updateRowHeight] [updateRowHeight],
); );
return ( return (
@ -167,6 +167,7 @@ function CsvOutputComponent({
onFirstDataRendered={onFirstDataRendered} onFirstDataRendered={onFirstDataRendered}
onGridSizeChanged={onGridSizeChanged} onGridSizeChanged={onGridSizeChanged}
scrollbarWidth={8} scrollbarWidth={8}
overlayNoRowsTemplate="No data available"
/> />
</div> </div>
)} )}

View file

@ -3,8 +3,8 @@ import useFlowsManagerStore from "../../stores/flowsManagerStore";
import { FlowType } from "../../types/flow"; import { FlowType } from "../../types/flow";
import { updateIds } from "../../utils/reactflowUtils"; import { updateIds } from "../../utils/reactflowUtils";
import { cn } from "../../utils/utils"; import { cn } from "../../utils/utils";
import ShadTooltip from "../ShadTooltipComponent";
import IconComponent from "../genericIconComponent"; import IconComponent from "../genericIconComponent";
import ShadTooltip from "../shadTooltipComponent";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
import { import {
Card, Card,

View file

@ -17,8 +17,8 @@ import useAlertStore from "../../../../stores/alertStore";
import useFlowStore from "../../../../stores/flowStore"; import useFlowStore from "../../../../stores/flowStore";
import useFlowsManagerStore from "../../../../stores/flowsManagerStore"; import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
import { cn } from "../../../../utils/utils"; import { cn } from "../../../../utils/utils";
import ShadTooltip from "../../../ShadTooltipComponent";
import IconComponent from "../../../genericIconComponent"; import IconComponent from "../../../genericIconComponent";
import ShadTooltip from "../../../shadTooltipComponent";
import { Button } from "../../../ui/button"; import { Button } from "../../../ui/button";
export const MenuBar = ({ export const MenuBar = ({

View file

@ -174,6 +174,7 @@ export default function Header(): JSX.Element {
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<button <button
data-testid="user-profile-settings"
className={ className={
"h-7 w-7 rounded-full focus-visible:outline-0 " + "h-7 w-7 rounded-full focus-visible:outline-0 " +
(userData?.profile_image ?? (userData?.profile_image ??

View file

@ -247,6 +247,7 @@ export default function InputComponent({
</PopoverContentWithoutPortal> </PopoverContentWithoutPortal>
</Popover> </Popover>
<div <div
data-testid={"popover-anchor-" + id}
className={cn( className={cn(
"pointer-events-auto absolute inset-y-0 h-full w-full cursor-pointer", "pointer-events-auto absolute inset-y-0 h-full w-full cursor-pointer",
((selectedOption !== "" || !onChange) && setSelectedOption) || ((selectedOption !== "" || !onChange) && setSelectedOption) ||

View file

@ -1,6 +1,6 @@
import { useEffect } from "react"; import { useEffect } from "react";
import { deleteGlobalVariable } from "../../controllers/API"; import { deleteGlobalVariable } from "../../controllers/API";
import DeleteConfirmationModal from "../../modals/DeleteConfirmationModal"; import DeleteConfirmationModal from "../../modals/deleteConfirmationModal";
import useAlertStore from "../../stores/alertStore"; import useAlertStore from "../../stores/alertStore";
import { useGlobalVariablesStore } from "../../stores/globalVariables"; import { useGlobalVariablesStore } from "../../stores/globalVariables";
import { InputGlobalComponentType } from "../../types/components"; import { InputGlobalComponentType } from "../../types/components";
@ -49,7 +49,10 @@ export default function InputGlobalComponent({
!data.node?.template[name].value && !data.node?.template[name].value &&
data.node?.template[name].display_name data.node?.template[name].display_name
) { ) {
if (unavaliableFields[data.node?.template[name].display_name!] && !disabled) { if (
unavaliableFields[data.node?.template[name].display_name!] &&
!disabled
) {
setTimeout(() => { setTimeout(() => {
setDb(true); setDb(true);
onChange(unavaliableFields[data.node?.template[name].display_name!]); onChange(unavaliableFields[data.node?.template[name].display_name!]);

View file

@ -5,6 +5,7 @@ import { ComponentPropsWithoutRef, ElementRef, forwardRef } from "react";
import { useDarkStore } from "../../stores/darkStore"; import { useDarkStore } from "../../stores/darkStore";
import "../../style/ag-theme-shadcn.css"; // Custom CSS applied to the grid import "../../style/ag-theme-shadcn.css"; // Custom CSS applied to the grid
import { cn } from "../../utils/utils"; import { cn } from "../../utils/utils";
import { Card, CardContent } from "../ui/card";
const TableComponent = forwardRef< const TableComponent = forwardRef<
ElementRef<typeof AgGridReact>, ElementRef<typeof AgGridReact>,
@ -17,10 +18,18 @@ const TableComponent = forwardRef<
<div <div
className={cn( className={cn(
dark ? "ag-theme-quartz-dark" : "ag-theme-quartz", dark ? "ag-theme-quartz-dark" : "ag-theme-quartz",
"ag-theme-shadcn flex h-full flex-col" "ag-theme-shadcn flex h-full flex-col",
)} // applying the grid theme )} // applying the grid theme
> >
<AgGridReact ref={ref} {...props} /> <Card x-chunk="dashboard-04-chunk-2" className="pt-4">
<CardContent>
<AgGridReact
overlayNoRowsTemplate="No data available"
ref={ref}
{...props}
/>
</CardContent>
</Card>
</div> </div>
</div> </div>
); );

View file

@ -1,7 +1,6 @@
import { cloneDeep } from "lodash"; import { cloneDeep } from "lodash";
import React, { ReactNode, useEffect, useRef, useState } from "react"; import React, { ReactNode, useEffect, useRef, useState } from "react";
import { Handle, Position, useUpdateNodeInternals } from "reactflow"; import { Handle, Position, useUpdateNodeInternals } from "reactflow";
import ShadTooltip from "../../../../components/ShadTooltipComponent";
import CodeAreaComponent from "../../../../components/codeAreaComponent"; import CodeAreaComponent from "../../../../components/codeAreaComponent";
import DictComponent from "../../../../components/dictComponent"; import DictComponent from "../../../../components/dictComponent";
import Dropdown from "../../../../components/dropdownComponent"; import Dropdown from "../../../../components/dropdownComponent";
@ -13,6 +12,7 @@ import InputListComponent from "../../../../components/inputListComponent";
import IntComponent from "../../../../components/intComponent"; import IntComponent from "../../../../components/intComponent";
import KeypairListComponent from "../../../../components/keypairListComponent"; import KeypairListComponent from "../../../../components/keypairListComponent";
import PromptAreaComponent from "../../../../components/promptComponent"; import PromptAreaComponent from "../../../../components/promptComponent";
import ShadTooltip from "../../../../components/shadTooltipComponent";
import TextAreaComponent from "../../../../components/textAreaComponent"; import TextAreaComponent from "../../../../components/textAreaComponent";
import ToggleShadComponent from "../../../../components/toggleShadComponent"; import ToggleShadComponent from "../../../../components/toggleShadComponent";
import { Button } from "../../../../components/ui/button"; import { Button } from "../../../../components/ui/button";

View file

@ -1,9 +1,9 @@
import { cloneDeep } from "lodash"; import { cloneDeep } from "lodash";
import { useCallback, useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import { NodeToolbar, useUpdateNodeInternals } from "reactflow"; import { NodeToolbar, useUpdateNodeInternals } from "reactflow";
import ShadTooltip from "../../components/ShadTooltipComponent";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import InputComponent from "../../components/inputComponent"; import InputComponent from "../../components/inputComponent";
import ShadTooltip from "../../components/shadTooltipComponent";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
import Checkmark from "../../components/ui/checkmark"; import Checkmark from "../../components/ui/checkmark";
import Loading from "../../components/ui/loading"; import Loading from "../../components/ui/loading";

View file

@ -6,9 +6,9 @@ import remarkGfm from "remark-gfm";
import remarkMath from "remark-math"; import remarkMath from "remark-math";
import MaleTechnology from "../../../../../assets/male-technologist.png"; import MaleTechnology from "../../../../../assets/male-technologist.png";
import Robot from "../../../../../assets/robot.png"; import Robot from "../../../../../assets/robot.png";
import SanitizedHTMLWrapper from "../../../../../components/SanitizedHTMLWrapper";
import CodeTabsComponent from "../../../../../components/codeTabsComponent"; import CodeTabsComponent from "../../../../../components/codeTabsComponent";
import IconComponent from "../../../../../components/genericIconComponent"; import IconComponent from "../../../../../components/genericIconComponent";
import SanitizedHTMLWrapper from "../../../../../components/sanitizedHTMLWrapper";
import useAlertStore from "../../../../../stores/alertStore"; import useAlertStore from "../../../../../stores/alertStore";
import useFlowStore from "../../../../../stores/flowStore"; import useFlowStore from "../../../../../stores/flowStore";
import { chatMessagePropsType } from "../../../../../types/components"; import { chatMessagePropsType } from "../../../../../types/components";
@ -271,7 +271,7 @@ dark:prose-invert"
</div> </div>
</div> </div>
) : ( ) : (
<div> <div className="form-modal-chat-text-position min-w-96 flex-grow">
{template ? ( {template ? (
<> <>
<button <button
@ -324,6 +324,7 @@ dark:prose-invert"
</> </>
) : ( ) : (
<span <span
className="prose text-primary word-break-break-word dark:prose-invert"
data-testid={ data-testid={
"chat-message-" + chat.sender_name + "-" + chatMessage "chat-message-" + chat.sender_name + "-" + chatMessage
} }

View file

@ -1,7 +1,7 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import AccordionComponent from "../../components/AccordionComponent"; import AccordionComponent from "../../components/accordionComponent";
import ShadTooltip from "../../components/ShadTooltipComponent";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import ShadTooltip from "../../components/shadTooltipComponent";
import { Badge } from "../../components/ui/badge"; import { Badge } from "../../components/ui/badge";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
import { import {
@ -34,25 +34,25 @@ export default function IOModal({
}: IOModalPropsType): JSX.Element { }: IOModalPropsType): JSX.Element {
const allNodes = useFlowStore((state) => state.nodes); const allNodes = useFlowStore((state) => state.nodes);
const inputs = useFlowStore((state) => state.inputs).filter( const inputs = useFlowStore((state) => state.inputs).filter(
(input) => input.type !== "ChatInput" (input) => input.type !== "ChatInput",
); );
const chatInput = useFlowStore((state) => state.inputs).find( const chatInput = useFlowStore((state) => state.inputs).find(
(input) => input.type === "ChatInput" (input) => input.type === "ChatInput",
); );
const outputs = useFlowStore((state) => state.outputs).filter( const outputs = useFlowStore((state) => state.outputs).filter(
(output) => output.type !== "ChatOutput" (output) => output.type !== "ChatOutput",
); );
const chatOutput = useFlowStore((state) => state.outputs).find( const chatOutput = useFlowStore((state) => state.outputs).find(
(output) => output.type === "ChatOutput" (output) => output.type === "ChatOutput",
); );
const nodes = useFlowStore((state) => state.nodes).filter( const nodes = useFlowStore((state) => state.nodes).filter(
(node) => (node) =>
inputs.some((input) => input.id === node.id) || inputs.some((input) => input.id === node.id) ||
outputs.some((output) => output.id === node.id) outputs.some((output) => output.id === node.id),
); );
const haveChat = chatInput || chatOutput; const haveChat = chatInput || chatOutput;
const [selectedTab, setSelectedTab] = useState( const [selectedTab, setSelectedTab] = useState(
inputs.length > 0 ? 1 : outputs.length > 0 ? 2 : 0 inputs.length > 0 ? 1 : outputs.length > 0 ? 2 : 0,
); );
function startView() { function startView() {
@ -117,7 +117,7 @@ export default function IOModal({
return ( return (
<BaseModal <BaseModal
size={selectedTab === 0 ? "large-thin" : "large"} size={selectedTab === 0 ? "sm-thin" : "md-thin"}
open={open} open={open}
setOpen={setOpen} setOpen={setOpen}
disable={disable} disable={disable}
@ -140,7 +140,7 @@ export default function IOModal({
{selectedTab !== 0 && ( {selectedTab !== 0 && (
<div <div
className={cn( className={cn(
"mr-6 flex h-full w-2/6 flex-shrink-0 flex-col justify-start transition-all duration-300" "mr-6 flex h-full w-2/6 flex-shrink-0 flex-col justify-start transition-all duration-300",
)} )}
> >
<Tabs <Tabs
@ -173,11 +173,11 @@ export default function IOModal({
</div> </div>
{nodes {nodes
.filter((node) => .filter((node) =>
inputs.some((input) => input.id === node.id) inputs.some((input) => input.id === node.id),
) )
.map((node, index) => { .map((node, index) => {
const input = inputs.find( const input = inputs.find(
(input) => input.id === node.id (input) => input.id === node.id,
)!; )!;
return ( return (
<div <div
@ -241,11 +241,11 @@ export default function IOModal({
</div> </div>
{nodes {nodes
.filter((node) => .filter((node) =>
outputs.some((output) => output.id === node.id) outputs.some((output) => output.id === node.id),
) )
.map((node, index) => { .map((node, index) => {
const output = outputs.find( const output = outputs.find(
(output) => output.id === node.id (output) => output.id === node.id,
)!; )!;
return ( return (
<div <div
@ -308,7 +308,7 @@ export default function IOModal({
<div <div
className={cn( className={cn(
"flex h-full w-full flex-col items-start gap-4 pt-4", "flex h-full w-full flex-col items-start gap-4 pt-4",
!selectedViewField ? "hidden" : "" !selectedViewField ? "hidden" : "",
)} )}
> >
<div className="font-xl flex items-center justify-center gap-3 font-semibold"> <div className="font-xl flex items-center justify-center gap-3 font-semibold">
@ -327,7 +327,7 @@ export default function IOModal({
</div> </div>
<div className="h-full w-full"> <div className="h-full w-full">
{inputs.some( {inputs.some(
(input) => input.id === selectedViewField.id (input) => input.id === selectedViewField.id,
) ? ( ) ? (
<IOFieldView <IOFieldView
type={InputOutput.INPUT} type={InputOutput.INPUT}
@ -349,7 +349,7 @@ export default function IOModal({
<div <div
className={cn( className={cn(
"flex h-full w-full", "flex h-full w-full",
selectedViewField ? "hidden" : "" selectedViewField ? "hidden" : "",
)} )}
> >
{haveChat ? ( {haveChat ? (
@ -384,7 +384,7 @@ export default function IOModal({
"h-4 w-4", "h-4 w-4",
isBuilding isBuilding
? "animate-spin" ? "animate-spin"
: "fill-current text-medium-indigo" : "fill-current text-medium-indigo",
)} )}
/> />
Run Flow Run Flow

View file

@ -63,7 +63,7 @@ interface BaseModalProps {
React.ReactElement<ContentProps>, React.ReactElement<ContentProps>,
React.ReactElement<HeaderProps>, React.ReactElement<HeaderProps>,
React.ReactElement<TriggerProps>?, React.ReactElement<TriggerProps>?,
React.ReactElement<FooterProps>? React.ReactElement<FooterProps>?,
]; ];
open?: boolean; open?: boolean;
setOpen?: (open: boolean) => void; setOpen?: (open: boolean) => void;
@ -78,6 +78,8 @@ interface BaseModalProps {
| "large-h-full" | "large-h-full"
| "small-h-full" | "small-h-full"
| "medium-h-full" | "medium-h-full"
| "md-thin"
| "sm-thin"
| "smaller-h-full"; | "smaller-h-full";
disable?: boolean; disable?: boolean;
@ -93,16 +95,16 @@ function BaseModal({
type = "dialog", type = "dialog",
}: BaseModalProps) { }: BaseModalProps) {
const headerChild = React.Children.toArray(children).find( const headerChild = React.Children.toArray(children).find(
(child) => (child as React.ReactElement).type === Header (child) => (child as React.ReactElement).type === Header,
); );
const triggerChild = React.Children.toArray(children).find( const triggerChild = React.Children.toArray(children).find(
(child) => (child as React.ReactElement).type === Trigger (child) => (child as React.ReactElement).type === Trigger,
); );
const ContentChild = React.Children.toArray(children).find( const ContentChild = React.Children.toArray(children).find(
(child) => (child as React.ReactElement).type === Content (child) => (child as React.ReactElement).type === Content,
); );
const ContentFooter = React.Children.toArray(children).find( const ContentFooter = React.Children.toArray(children).find(
(child) => (child as React.ReactElement).type === Footer (child) => (child as React.ReactElement).type === Footer,
); );
let minWidth: string; let minWidth: string;
@ -147,6 +149,17 @@ function BaseModal({
minWidth = "min-w-[65vw]"; minWidth = "min-w-[65vw]";
height = "h-[80vh]"; height = "h-[80vh]";
break; break;
case "md-thin":
minWidth = "min-w-[85vw]";
height = "h-[70vh]";
break;
case "sm-thin":
minWidth = "min-w-[65vw]";
height = "h-[70vh]";
break;
case "large-h-full": case "large-h-full":
minWidth = "min-w-[80vw]"; minWidth = "min-w-[80vw]";
break; break;

View file

@ -1,5 +1,5 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import ShadTooltip from "../../components/ShadTooltipComponent"; import ShadTooltip from "../../components/shadTooltipComponent";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
import { import {
ConfirmationModalType, ConfirmationModalType,

View file

@ -15,14 +15,18 @@ export default function DeleteConfirmationModal({
onConfirm, onConfirm,
description, description,
asChild, asChild,
open,
setOpen,
}: { }: {
children: JSX.Element; children: JSX.Element;
onConfirm: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void; onConfirm: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
description?: string; description?: string;
asChild?: boolean; asChild?: boolean;
open?: boolean;
setOpen?: (open: boolean) => void;
}) { }) {
return ( return (
<Dialog> <Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild={asChild} tabIndex={-1}> <DialogTrigger asChild={asChild} tabIndex={-1}>
{children} {children}
</DialogTrigger> </DialogTrigger>

View file

@ -1,6 +1,5 @@
import { cloneDeep } from "lodash"; import { cloneDeep } from "lodash";
import { forwardRef, useEffect, useState } from "react"; import { forwardRef, useEffect, useState } from "react";
import ShadTooltip from "../../components/ShadTooltipComponent";
import CodeAreaComponent from "../../components/codeAreaComponent"; import CodeAreaComponent from "../../components/codeAreaComponent";
import DictComponent from "../../components/dictComponent"; import DictComponent from "../../components/dictComponent";
import Dropdown from "../../components/dropdownComponent"; import Dropdown from "../../components/dropdownComponent";
@ -12,6 +11,7 @@ import InputListComponent from "../../components/inputListComponent";
import IntComponent from "../../components/intComponent"; import IntComponent from "../../components/intComponent";
import KeypairListComponent from "../../components/keypairListComponent"; import KeypairListComponent from "../../components/keypairListComponent";
import PromptAreaComponent from "../../components/promptComponent"; import PromptAreaComponent from "../../components/promptComponent";
import ShadTooltip from "../../components/shadTooltipComponent";
import TextAreaComponent from "../../components/textAreaComponent"; import TextAreaComponent from "../../components/textAreaComponent";
import ToggleShadComponent from "../../components/toggleShadComponent"; import ToggleShadComponent from "../../components/toggleShadComponent";
import { Badge } from "../../components/ui/badge"; import { Badge } from "../../components/ui/badge";

View file

@ -1,5 +1,5 @@
import { ReactNode, forwardRef, useEffect, useState } from "react"; import { ReactNode, forwardRef, useEffect, useState } from "react";
import EditFlowSettings from "../../components/EditFlowSettingsComponent"; import EditFlowSettings from "../../components/editFlowSettingsComponent";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
import { Checkbox } from "../../components/ui/checkbox"; import { Checkbox } from "../../components/ui/checkbox";

View file

@ -1,5 +1,5 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import EditFlowSettings from "../../components/EditFlowSettingsComponent"; import EditFlowSettings from "../../components/editFlowSettingsComponent";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
import { SETTINGS_DIALOG_SUBTITLE } from "../../constants/constants"; import { SETTINGS_DIALOG_SUBTITLE } from "../../constants/constants";

View file

@ -1,7 +1,7 @@
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import SanitizedHTMLWrapper from "../../components/SanitizedHTMLWrapper";
import ShadTooltip from "../../components/ShadTooltipComponent";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import SanitizedHTMLWrapper from "../../components/sanitizedHTMLWrapper";
import ShadTooltip from "../../components/shadTooltipComponent";
import { Badge } from "../../components/ui/badge"; import { Badge } from "../../components/ui/badge";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
import { Textarea } from "../../components/ui/textarea"; import { Textarea } from "../../components/ui/textarea";

View file

@ -1,6 +1,6 @@
import { Loader2 } from "lucide-react"; import { Loader2 } from "lucide-react";
import { ReactNode, useEffect, useMemo, useState } from "react"; import { ReactNode, useEffect, useMemo, useState } from "react";
import EditFlowSettings from "../../components/EditFlowSettingsComponent"; import EditFlowSettings from "../../components/editFlowSettingsComponent";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import { TagsSelector } from "../../components/tagsSelectorComponent"; import { TagsSelector } from "../../components/tagsSelectorComponent";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
@ -23,8 +23,8 @@ import {
removeGlobalVariableFromComponents, removeGlobalVariableFromComponents,
} from "../../utils/reactflowUtils"; } from "../../utils/reactflowUtils";
import { getTagsIds } from "../../utils/storeUtils"; import { getTagsIds } from "../../utils/storeUtils";
import ConfirmationModal from "../ConfirmationModal";
import BaseModal from "../baseModal"; import BaseModal from "../baseModal";
import ConfirmationModal from "../confirmationModal";
import ExportModal from "../exportModal"; import ExportModal from "../exportModal";
export default function ShareModal({ export default function ShareModal({
@ -207,7 +207,8 @@ export default function ShareModal({
{children ? children : <></>} {children ? children : <></>}
</BaseModal.Trigger> </BaseModal.Trigger>
<BaseModal.Header <BaseModal.Header
description={`Publish ${is_component ? "your component" : "workflow" description={`Publish ${
is_component ? "your component" : "workflow"
} to the Langflow Store.`} } to the Langflow Store.`}
> >
<span className="pr-2">Share</span> <span className="pr-2">Share</span>
@ -251,7 +252,8 @@ export default function ShareModal({
<BaseModal.Footer> <BaseModal.Footer>
<div className="flex w-full justify-between gap-2"> <div className="flex w-full justify-between gap-2">
{!is_component && <ExportModal> {!is_component && (
<ExportModal>
<Button <Button
type="button" type="button"
variant="outline" variant="outline"
@ -264,8 +266,9 @@ export default function ShareModal({
Export Export
</Button> </Button>
</ExportModal> </ExportModal>
} )}
{is_component && <Button {is_component && (
<Button
type="button" type="button"
variant="outline" variant="outline"
className="gap-2" className="gap-2"
@ -277,8 +280,7 @@ export default function ShareModal({
<IconComponent name="Download" className="h-4 w-4" /> <IconComponent name="Download" className="h-4 w-4" />
Export Export
</Button> </Button>
)}
}
<Button <Button
disabled={loadingNames} disabled={loadingNames}
type="button" type="button"

View file

@ -1,10 +1,10 @@
import { cloneDeep } from "lodash"; import { cloneDeep } from "lodash";
import { useContext, useEffect, useRef, useState } from "react"; import { useContext, useEffect, useRef, useState } from "react";
import PaginatorComponent from "../../components/PaginatorComponent";
import ShadTooltip from "../../components/ShadTooltipComponent";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import Header from "../../components/headerComponent"; import Header from "../../components/headerComponent";
import LoadingComponent from "../../components/loadingComponent"; import LoadingComponent from "../../components/loadingComponent";
import PaginatorComponent from "../../components/paginatorComponent";
import ShadTooltip from "../../components/shadTooltipComponent";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
import { CheckBoxDiv } from "../../components/ui/checkbox"; import { CheckBoxDiv } from "../../components/ui/checkbox";
import { Input } from "../../components/ui/input"; import { Input } from "../../components/ui/input";
@ -35,8 +35,8 @@ import {
getUsersPage, getUsersPage,
updateUser, updateUser,
} from "../../controllers/API"; } from "../../controllers/API";
import ConfirmationModal from "../../modals/ConfirmationModal"; import ConfirmationModal from "../../modals/confirmationModal";
import UserManagementModal from "../../modals/UserManagementModal"; import UserManagementModal from "../../modals/userManagementModal";
import useAlertStore from "../../stores/alertStore"; import useAlertStore from "../../stores/alertStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore"; import useFlowsManagerStore from "../../stores/flowsManagerStore";
import { Users } from "../../types/api"; import { Users } from "../../types/api";

View file

@ -1,6 +1,6 @@
import { useContext, useEffect, useRef, useState } from "react"; import { useContext, useEffect, useRef, useState } from "react";
import ShadTooltip from "../../components/ShadTooltipComponent";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import ShadTooltip from "../../components/shadTooltipComponent";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
import { import {
Table, Table,
@ -12,8 +12,8 @@ import {
} from "../../components/ui/table"; } from "../../components/ui/table";
import { AuthContext } from "../../contexts/authContext"; import { AuthContext } from "../../contexts/authContext";
import { deleteApiKey, getApiKey } from "../../controllers/API"; import { deleteApiKey, getApiKey } from "../../controllers/API";
import ConfirmationModal from "../../modals/ConfirmationModal"; import ConfirmationModal from "../../modals/confirmationModal";
import SecretKeyModal from "../../modals/SecretKeyModal"; import SecretKeyModal from "../../modals/secretKeyModal";
import moment from "moment"; import moment from "moment";
import Header from "../../components/headerComponent"; import Header from "../../components/headerComponent";

View file

@ -11,13 +11,13 @@ import ReactFlow, {
SelectionDragHandler, SelectionDragHandler,
updateEdge, updateEdge,
} from "reactflow"; } from "reactflow";
import GenericNode from "../../../../CustomNodes/GenericNode";
import { import {
INVALID_SELECTION_ERROR_ALERT, INVALID_SELECTION_ERROR_ALERT,
UPLOAD_ALERT_LIST, UPLOAD_ALERT_LIST,
UPLOAD_ERROR_ALERT, UPLOAD_ERROR_ALERT,
WRONG_FILE_ERROR_ALERT, WRONG_FILE_ERROR_ALERT,
} from "../../../../constants/alerts_constants"; } from "../../../../constants/alerts_constants";
import GenericNode from "../../../../customNodes/genericNode";
import useAlertStore from "../../../../stores/alertStore"; import useAlertStore from "../../../../stores/alertStore";
import useFlowStore from "../../../../stores/flowStore"; import useFlowStore from "../../../../stores/flowStore";
import useFlowsManagerStore from "../../../../stores/flowsManagerStore"; import useFlowsManagerStore from "../../../../stores/flowsManagerStore";

View file

@ -1,8 +1,8 @@
import { cloneDeep } from "lodash"; import { cloneDeep } from "lodash";
import { LinkIcon, SparklesIcon } from "lucide-react"; import { LinkIcon, SparklesIcon } from "lucide-react";
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import ShadTooltip from "../../../../components/ShadTooltipComponent";
import IconComponent from "../../../../components/genericIconComponent"; import IconComponent from "../../../../components/genericIconComponent";
import ShadTooltip from "../../../../components/shadTooltipComponent";
import { Input } from "../../../../components/ui/input"; import { Input } from "../../../../components/ui/input";
import { Separator } from "../../../../components/ui/separator"; import { Separator } from "../../../../components/ui/separator";
import { PRIORITY_SIDEBAR_ORDER } from "../../../../constants/constants"; import { PRIORITY_SIDEBAR_ORDER } from "../../../../constants/constants";

View file

@ -1,9 +1,9 @@
import _, { cloneDeep } from "lodash"; import _, { cloneDeep } from "lodash";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useUpdateNodeInternals } from "reactflow"; import { useUpdateNodeInternals } from "reactflow";
import ShadTooltip from "../../../../components/ShadTooltipComponent";
import CodeAreaComponent from "../../../../components/codeAreaComponent"; import CodeAreaComponent from "../../../../components/codeAreaComponent";
import IconComponent from "../../../../components/genericIconComponent"; import IconComponent from "../../../../components/genericIconComponent";
import ShadTooltip from "../../../../components/shadTooltipComponent";
import { import {
Select, Select,
SelectContent, SelectContent,
@ -11,8 +11,8 @@ import {
SelectTrigger, SelectTrigger,
} from "../../../../components/ui/select-custom"; } from "../../../../components/ui/select-custom";
import { postCustomComponent } from "../../../../controllers/API"; import { postCustomComponent } from "../../../../controllers/API";
import ConfirmationModal from "../../../../modals/ConfirmationModal"; import ConfirmationModal from "../../../../modals/confirmationModal";
import EditNodeModal from "../../../../modals/EditNodeModal"; import EditNodeModal from "../../../../modals/editNodeModal";
import ShareModal from "../../../../modals/shareModal"; import ShareModal from "../../../../modals/shareModal";
import useAlertStore from "../../../../stores/alertStore"; import useAlertStore from "../../../../stores/alertStore";
import { useDarkStore } from "../../../../stores/darkStore"; import { useDarkStore } from "../../../../stores/darkStore";
@ -58,7 +58,7 @@ export default function NodeToolbarComponent({
data.node.template[templateField].type === "Any" || data.node.template[templateField].type === "Any" ||
data.node.template[templateField].type === "int" || data.node.template[templateField].type === "int" ||
data.node.template[templateField].type === "dict" || data.node.template[templateField].type === "dict" ||
data.node.template[templateField].type === "NestedDict") data.node.template[templateField].type === "NestedDict"),
).length; ).length;
const templates = useTypesStore((state) => state.templates); const templates = useTypesStore((state) => state.templates);
const hasStore = useStoreStore((state) => state.hasStore); const hasStore = useStoreStore((state) => state.hasStore);
@ -85,7 +85,7 @@ export default function NodeToolbarComponent({
const [showconfirmShare, setShowconfirmShare] = useState(false); const [showconfirmShare, setShowconfirmShare] = useState(false);
const [showOverrideModal, setShowOverrideModal] = useState(false); const [showOverrideModal, setShowOverrideModal] = useState(false);
const [flowComponent, setFlowComponent] = useState<FlowType>( const [flowComponent, setFlowComponent] = useState<FlowType>(
createFlowComponent(cloneDeep(data), version) createFlowComponent(cloneDeep(data), version),
); );
const openInNewTab = (url) => { const openInNewTab = (url) => {
@ -100,7 +100,7 @@ export default function NodeToolbarComponent({
const updateNodeInternals = useUpdateNodeInternals(); const updateNodeInternals = useUpdateNodeInternals();
const setLastCopiedSelection = useFlowStore( const setLastCopiedSelection = useFlowStore(
(state) => state.setLastCopiedSelection (state) => state.setLastCopiedSelection,
); );
const setSuccessData = useAlertStore((state) => state.setSuccessData); const setSuccessData = useAlertStore((state) => state.setSuccessData);
@ -150,7 +150,7 @@ export default function NodeToolbarComponent({
nodes, nodes,
edges, edges,
setNodes, setNodes,
setEdges setEdges,
); );
break; break;
case "override": case "override":
@ -174,7 +174,7 @@ export default function NodeToolbarComponent({
y: 10, y: 10,
paneX: nodes.find((node) => node.id === data.id)?.position.x, paneX: nodes.find((node) => node.id === data.id)?.position.x,
paneY: nodes.find((node) => node.id === data.id)?.position.y, paneY: nodes.find((node) => node.id === data.id)?.position.y,
} },
); );
break; break;
case "update": case "update":
@ -212,13 +212,13 @@ export default function NodeToolbarComponent({
}; };
const isSaved = flows.some((flow) => const isSaved = flows.some((flow) =>
Object.values(flow).includes(data.node?.display_name!) Object.values(flow).includes(data.node?.display_name!),
); );
const setNode = useFlowStore((state) => state.setNode); const setNode = useFlowStore((state) => state.setNode);
const handleOnNewValue = ( const handleOnNewValue = (
newValue: string | string[] | boolean | Object[] newValue: string | string[] | boolean | Object[],
): void => { ): void => {
if (data.node!.template[name].value !== newValue) { if (data.node!.template[name].value !== newValue) {
takeSnapshot(); takeSnapshot();
@ -325,7 +325,6 @@ export default function NodeToolbarComponent({
} }
if ( if (
selected && selected &&
!isGroup &&
(event.ctrlKey || event.metaKey) && (event.ctrlKey || event.metaKey) &&
event.shiftKey && event.shiftKey &&
event.key === "A" event.key === "A"
@ -394,7 +393,7 @@ export default function NodeToolbarComponent({
data-testid="save-button-modal" data-testid="save-button-modal"
className={classNames( className={classNames(
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10", "relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10",
hasCode ? " " : " rounded-l-md " hasCode ? " " : " rounded-l-md ",
)} )}
onClick={(event) => { onClick={(event) => {
event.preventDefault(); event.preventDefault();
@ -412,7 +411,7 @@ export default function NodeToolbarComponent({
<button <button
data-testid="duplicate-button-modal" data-testid="duplicate-button-modal"
className={classNames( className={classNames(
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10" "relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10",
)} )}
onClick={(event) => { onClick={(event) => {
event.preventDefault(); event.preventDefault();
@ -460,7 +459,7 @@ export default function NodeToolbarComponent({
<div <div
data-testid="more-options-modal" data-testid="more-options-modal"
className={classNames( className={classNames(
"relative -ml-px inline-flex h-8 w-[31px] items-center rounded-r-md bg-background text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10" "relative -ml-px inline-flex h-8 w-[31px] items-center rounded-r-md bg-background text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10",
)} )}
> >
<IconComponent <IconComponent

View file

@ -1,9 +1,9 @@
import { useState } from "react"; import { useState } from "react";
import { Link, useNavigate } from "react-router-dom"; import { Link, useNavigate } from "react-router-dom";
import PaginatorComponent from "../../../../components/PaginatorComponent";
import CollectionCardComponent from "../../../../components/cardComponent"; import CollectionCardComponent from "../../../../components/cardComponent";
import CardsWrapComponent from "../../../../components/cardsWrapComponent"; import CardsWrapComponent from "../../../../components/cardsWrapComponent";
import IconComponent from "../../../../components/genericIconComponent"; import IconComponent from "../../../../components/genericIconComponent";
import PaginatorComponent from "../../../../components/paginatorComponent";
import { SkeletonCardComponent } from "../../../../components/skeletonCardComponent"; import { SkeletonCardComponent } from "../../../../components/skeletonCardComponent";
import { Button } from "../../../../components/ui/button"; import { Button } from "../../../../components/ui/button";
import { import {
@ -30,7 +30,6 @@ export default function ComponentsComponent({
const [pageIndex, setPageIndex] = useState(1); const [pageIndex, setPageIndex] = useState(1);
const navigate = useNavigate(); const navigate = useNavigate();
const all: FlowType[] = flows const all: FlowType[] = flows
.filter((f) => (f.is_component ?? false) === is_component) .filter((f) => (f.is_component ?? false) === is_component)
.sort((a, b) => { .sort((a, b) => {
@ -86,12 +85,10 @@ export default function ComponentsComponent({
} }
} }
}; };
function resetFilter() { function resetFilter() {
setPageIndex(1); setPageIndex(1);
setPageSize(20); setPageSize(20);
} }
return ( return (
<CardsWrapComponent <CardsWrapComponent
onFileDrop={onFileDrop} onFileDrop={onFileDrop}

View file

@ -1,7 +1,7 @@
import { Group, ToyBrick } from "lucide-react"; import { Group, ToyBrick } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Outlet, useLocation, useNavigate } from "react-router-dom"; import { Outlet, useLocation, useNavigate } from "react-router-dom";
import DropdownButton from "../../components/DropdownButtonComponent"; import DropdownButton from "../../components/dropdownButtonComponent";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import PageLayout from "../../components/pageLayout"; import PageLayout from "../../components/pageLayout";
import SidebarNav from "../../components/sidebarComponent"; import SidebarNav from "../../components/sidebarComponent";
@ -11,7 +11,7 @@ import {
MY_COLLECTION_DESC, MY_COLLECTION_DESC,
USER_PROJECTS_HEADER, USER_PROJECTS_HEADER,
} from "../../constants/constants"; } from "../../constants/constants";
import NewFlowModal from "../../modals/NewFlowModal"; import NewFlowModal from "../../modals/newFlowModal";
import useAlertStore from "../../stores/alertStore"; import useAlertStore from "../../stores/alertStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore"; import useFlowsManagerStore from "../../stores/flowsManagerStore";
import { downloadFlows } from "../../utils/reactflowUtils"; import { downloadFlows } from "../../utils/reactflowUtils";

View file

@ -15,13 +15,13 @@ import { cn } from "../../../../utils/utils";
export default function GlobalVariablesPage() { export default function GlobalVariablesPage() {
const globalVariablesEntries = useGlobalVariablesStore( const globalVariablesEntries = useGlobalVariablesStore(
(state) => state.globalVariablesEntries (state) => state.globalVariablesEntries,
); );
const removeGlobalVariable = useGlobalVariablesStore( const removeGlobalVariable = useGlobalVariablesStore(
(state) => state.removeGlobalVariable (state) => state.removeGlobalVariable,
); );
const globalVariables = useGlobalVariablesStore( const globalVariables = useGlobalVariablesStore(
(state) => state.globalVariables (state) => state.globalVariables,
); );
const setErrorData = useAlertStore((state) => state.setErrorData); const setErrorData = useAlertStore((state) => state.setErrorData);
const getVariableId = useGlobalVariablesStore((state) => state.getVariableId); const getVariableId = useGlobalVariablesStore((state) => state.getVariableId);
@ -154,7 +154,7 @@ export default function GlobalVariablesPage() {
<IconComponent <IconComponent
name="Trash2" name="Trash2"
className={cn( className={cn(
"h-5 w-5 text-destructive group-disabled:text-primary" "h-5 w-5 text-destructive group-disabled:text-primary",
)} )}
/> />
</Button> </Button>
@ -174,6 +174,8 @@ export default function GlobalVariablesPage() {
}} }}
rowSelection="multiple" rowSelection="multiple"
suppressRowClickSelection={true} suppressRowClickSelection={true}
domLayout="autoHeight"
pagination={false}
columnDefs={colDefs} columnDefs={colDefs}
rowData={rowData} rowData={rowData}
/> />

View file

@ -2,13 +2,6 @@ import { ColDef, ColGroupDef } from "ag-grid-community";
import { useState } from "react"; import { useState } from "react";
import ForwardedIconComponent from "../../../../components/genericIconComponent"; import ForwardedIconComponent from "../../../../components/genericIconComponent";
import TableComponent from "../../../../components/tableComponent"; import TableComponent from "../../../../components/tableComponent";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "../../../../components/ui/card";
export default function ShortcutsPage() { export default function ShortcutsPage() {
const advancedShortcut = "Ctrl + Shift + A"; const advancedShortcut = "Ctrl + Shift + A";
@ -97,22 +90,17 @@ export default function ShortcutsPage() {
/> />
</h2> </h2>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
Manage Shortcuts for quick access to Manage Shortcuts for quick access to frequently used actions.
frequently used actions.
</p> </p>
</div> </div>
</div> </div>
<div className="grid gap-6 pb-8"> <div className="grid gap-6 pb-8">
<Card x-chunk="dashboard-04-chunk-2" className="pt-4">
<CardContent>
<TableComponent <TableComponent
domLayout="autoHeight" domLayout="autoHeight"
pagination={false} pagination={false}
columnDefs={colDefs} columnDefs={colDefs}
rowData={nodesRowData} rowData={nodesRowData}
/> />
</CardContent>
</Card>
</div> </div>
</div> </div>
); );

View file

@ -1,15 +1,15 @@
import { uniqueId } from "lodash"; import { uniqueId } from "lodash";
import { useContext, useEffect, useState } from "react"; import { useContext, useEffect, useState } from "react";
import PaginatorComponent from "../../components/PaginatorComponent";
import ShadTooltip from "../../components/ShadTooltipComponent";
import CollectionCardComponent from "../../components/cardComponent"; import CollectionCardComponent from "../../components/cardComponent";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import PageLayout from "../../components/pageLayout"; import PageLayout from "../../components/pageLayout";
import ShadTooltip from "../../components/shadTooltipComponent";
import { SkeletonCardComponent } from "../../components/skeletonCardComponent"; import { SkeletonCardComponent } from "../../components/skeletonCardComponent";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
import { Input } from "../../components/ui/input"; import { Input } from "../../components/ui/input";
import { Link, useNavigate, useParams } from "react-router-dom"; import { Link, useNavigate, useParams } from "react-router-dom";
import PaginatorComponent from "../../components/paginatorComponent";
import { TagsSelector } from "../../components/tagsSelectorComponent"; import { TagsSelector } from "../../components/tagsSelectorComponent";
import { Badge } from "../../components/ui/badge"; import { Badge } from "../../components/ui/badge";
import { import {
@ -29,7 +29,7 @@ import {
import { STORE_DESC, STORE_TITLE } from "../../constants/constants"; import { STORE_DESC, STORE_TITLE } from "../../constants/constants";
import { AuthContext } from "../../contexts/authContext"; import { AuthContext } from "../../contexts/authContext";
import { getStoreComponents, getStoreTags } from "../../controllers/API"; import { getStoreComponents, getStoreTags } from "../../controllers/API";
import StoreApiKeyModal from "../../modals/StoreApiKeyModal"; import StoreApiKeyModal from "../../modals/storeApiKeyModal";
import useAlertStore from "../../stores/alertStore"; import useAlertStore from "../../stores/alertStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore"; import useFlowsManagerStore from "../../stores/flowsManagerStore";
import { useStoreStore } from "../../stores/storeStore"; import { useStoreStore } from "../../stores/storeStore";

View file

@ -7,19 +7,19 @@ import { StoreGuard } from "./components/storeGuard";
import AdminPage from "./pages/AdminPage"; import AdminPage from "./pages/AdminPage";
import LoginAdminPage from "./pages/AdminPage/LoginPage"; import LoginAdminPage from "./pages/AdminPage/LoginPage";
import ApiKeysPage from "./pages/ApiKeysPage"; import ApiKeysPage from "./pages/ApiKeysPage";
import DeleteAccountPage from "./pages/DeleteAccountPage";
import FlowPage from "./pages/FlowPage"; import FlowPage from "./pages/FlowPage";
import LoginPage from "./pages/LoginPage";
import HomePage from "./pages/MainPage"; import HomePage from "./pages/MainPage";
import ComponentsComponent from "./pages/MainPage/components/components"; import ComponentsComponent from "./pages/MainPage/components/components";
import PlaygroundPage from "./pages/Playground";
import SettingsPage from "./pages/SettingsPage"; import SettingsPage from "./pages/SettingsPage";
import GeneralPage from "./pages/SettingsPage/pages/GeneralPage"; import GeneralPage from "./pages/SettingsPage/pages/GeneralPage";
import GlobalVariablesPage from "./pages/SettingsPage/pages/GlobalVariablesPage"; import GlobalVariablesPage from "./pages/SettingsPage/pages/GlobalVariablesPage";
import ShortcutsPage from "./pages/SettingsPage/pages/ShortcutsPage"; import ShortcutsPage from "./pages/SettingsPage/pages/ShortcutsPage";
import SignUp from "./pages/SignUpPage";
import StorePage from "./pages/StorePage"; import StorePage from "./pages/StorePage";
import ViewPage from "./pages/ViewPage"; import ViewPage from "./pages/ViewPage";
import DeleteAccountPage from "./pages/deleteAccountPage";
import LoginPage from "./pages/loginPage";
import SignUp from "./pages/signUpPage";
import PlaygroundPage from "./pages/Playground";
const Router = () => { const Router = () => {
return ( return (
@ -76,10 +76,16 @@ const Router = () => {
} }
/> />
<Route path="/playground/:id/"> <Route path="/playground/:id/">
element=
{
<Route
path=""
element={ element={
<Route path="" element={<ProtectedRoute> <ProtectedRoute>
<PlaygroundPage /> <PlaygroundPage />
</ProtectedRoute>} /> </ProtectedRoute>
}
/>
} }
</Route> </Route>
<Route path="/flow/:id/"> <Route path="/flow/:id/">

View file

@ -9,7 +9,9 @@
body { body {
@apply bg-background text-foreground; @apply bg-background text-foreground;
font-feature-settings: "rlig" 1, "calt" 1; font-feature-settings:
"rlig" 1,
"calt" 1;
} }
} }
@ -1018,6 +1020,9 @@
.langflow-chat-span { .langflow-chat-span {
@apply text-lg text-foreground; @apply text-lg text-foreground;
} }
.card-filter {
@apply bg-background bg-fixed opacity-20;
}
.langflow-chat-desc { .langflow-chat-desc {
@apply w-2/4 rounded-md border border-border bg-muted px-6 py-8; @apply w-2/4 rounded-md border border-border bg-muted px-6 py-8;
} }

View file

@ -116,14 +116,11 @@ export async function buildVertices({
nodes, nodes,
edges, edges,
}: BuildVerticesParams) { }: BuildVerticesParams) {
let verticesBuild = useFlowStore.getState().verticesBuild;
// if startNodeId and stopNodeId are provided // if startNodeId and stopNodeId are provided
// something is wrong // something is wrong
if (startNodeId && stopNodeId) { if (startNodeId && stopNodeId) {
return; return;
} }
if (!verticesBuild || startNodeId || stopNodeId) {
let verticesOrderResponse = await updateVerticesOrder( let verticesOrderResponse = await updateVerticesOrder(
flowId, flowId,
startNodeId, startNodeId,
@ -141,8 +138,7 @@ export async function buildVertices({
} }
} }
if (onGetOrderSuccess) onGetOrderSuccess(); if (onGetOrderSuccess) onGetOrderSuccess();
verticesBuild = useFlowStore.getState().verticesBuild; let verticesBuild = useFlowStore.getState().verticesBuild;
}
const verticesIds = verticesBuild?.verticesIds!; const verticesIds = verticesBuild?.verticesIds!;
const verticesLayers = verticesBuild?.verticesLayers!; const verticesLayers = verticesBuild?.verticesLayers!;

View file

@ -61,7 +61,7 @@ export function normalCaseToSnakeCase(str: string): string {
export function toTitleCase( export function toTitleCase(
str: string | undefined, str: string | undefined,
isNodeField?: boolean isNodeField?: boolean,
): string { ): string {
if (!str) return ""; if (!str) return "";
let result = str let result = str
@ -70,7 +70,7 @@ export function toTitleCase(
if (isNodeField) return word; if (isNodeField) return word;
if (index === 0) { if (index === 0) {
return checkUpperWords( return checkUpperWords(
word[0].toUpperCase() + word.slice(1).toLowerCase() word[0].toUpperCase() + word.slice(1).toLowerCase(),
); );
} }
return checkUpperWords(word.toLowerCase()); return checkUpperWords(word.toLowerCase());
@ -83,7 +83,7 @@ export function toTitleCase(
if (isNodeField) return word; if (isNodeField) return word;
if (index === 0) { if (index === 0) {
return checkUpperWords( return checkUpperWords(
word[0].toUpperCase() + word.slice(1).toLowerCase() word[0].toUpperCase() + word.slice(1).toLowerCase(),
); );
} }
return checkUpperWords(word.toLowerCase()); return checkUpperWords(word.toLowerCase());
@ -123,7 +123,7 @@ export function groupByFamily(
data: APIDataType, data: APIDataType,
baseClasses: string, baseClasses: string,
left: boolean, left: boolean,
flow?: NodeType[] flow?: NodeType[],
): groupedObjType[] { ): groupedObjType[] {
const baseClassesSet = new Set(baseClasses.split("\n")); const baseClassesSet = new Set(baseClasses.split("\n"));
let arrOfPossibleInputs: Array<{ let arrOfPossibleInputs: Array<{
@ -149,7 +149,7 @@ export function groupByFamily(
baseClassesSet.has(template.type)) || baseClassesSet.has(template.type)) ||
(template.input_types && (template.input_types &&
template.input_types.some((inputType) => template.input_types.some((inputType) =>
baseClassesSet.has(inputType) baseClassesSet.has(inputType),
))) )))
); );
}; };
@ -169,7 +169,7 @@ export function groupByFamily(
hasBaseClassInBaseClasses: hasBaseClassInBaseClasses:
foundNode?.hasBaseClassInBaseClasses || foundNode?.hasBaseClassInBaseClasses ||
nodeData.node!.base_classes.some((baseClass) => nodeData.node!.base_classes.some((baseClass) =>
baseClassesSet.has(baseClass) baseClassesSet.has(baseClass),
), //seta como anterior ou verifica se o node tem base class ), //seta como anterior ou verifica se o node tem base class
displayName: nodeData.node?.display_name, displayName: nodeData.node?.display_name,
}); });
@ -186,10 +186,10 @@ export function groupByFamily(
if (!foundNode) { if (!foundNode) {
foundNode = { foundNode = {
hasBaseClassInTemplate: Object.values(node!.template).some( hasBaseClassInTemplate: Object.values(node!.template).some(
checkBaseClass checkBaseClass,
), ),
hasBaseClassInBaseClasses: node!.base_classes.some((baseClass) => hasBaseClassInBaseClasses: node!.base_classes.some((baseClass) =>
baseClassesSet.has(baseClass) baseClassesSet.has(baseClass),
), ),
displayName: node?.display_name, displayName: node?.display_name,
}; };
@ -246,7 +246,7 @@ export function getRandomDescription(): string {
export function getRandomName( export function getRandomName(
retry: number = 0, retry: number = 0,
noSpace: boolean = false, noSpace: boolean = false,
maxRetries: number = 3 maxRetries: number = 3,
): string { ): string {
const left: string[] = ADJECTIVES; const left: string[] = ADJECTIVES;
const right: string[] = NOUNS; const right: string[] = NOUNS;
@ -325,7 +325,7 @@ export function getChatInputField(flowState?: FlowState) {
export function getPythonApiCode( export function getPythonApiCode(
flow: FlowType, flow: FlowType,
isAuth: boolean, isAuth: boolean,
tweak?: any[] tweak?: any[],
): string { ): string {
const flowId = flow.id; const flowId = flow.id;
@ -393,7 +393,7 @@ print(run_flow(message=message, flow_id=FLOW_ID, tweaks=TWEAKS${
export function getCurlCode( export function getCurlCode(
flow: FlowType, flow: FlowType,
isAuth: boolean, isAuth: boolean,
tweak?: any[] tweak?: any[],
): string { ): string {
const flowId = flow.id; const flowId = flow.id;
const tweaks = buildTweaks(flow); const tweaks = buildTweaks(flow);
@ -463,7 +463,7 @@ result = run_flow_from_json(flow="${flowName}.json",
export function getWidgetCode( export function getWidgetCode(
flow: FlowType, flow: FlowType,
isAuth: boolean, isAuth: boolean,
flowState?: FlowState flowState?: FlowState,
): string { ): string {
const flowId = flow.id; const flowId = flow.id;
const flowName = flow.name; const flowName = flow.name;
@ -593,7 +593,7 @@ export function checkLocalStorageKey(key: string): boolean {
export function IncrementObjectKey( export function IncrementObjectKey(
object: object, object: object,
key: string key: string,
): { newKey: string; increment: number } { ): { newKey: string; increment: number } {
let count = 1; let count = 1;
const type = removeCountFromString(key); const type = removeCountFromString(key);
@ -665,7 +665,7 @@ export function getSetFromObject(obj: object, key?: string): Set<string> {
export function getFieldTitle( export function getFieldTitle(
template: APITemplateType, template: APITemplateType,
templateField: string templateField: string,
): string { ): string {
return template[templateField].display_name return template[templateField].display_name
? template[templateField].display_name! ? template[templateField].display_name!
@ -715,3 +715,11 @@ export function freezeObject(obj: any) {
if (!obj) return obj; if (!obj) return obj;
return JSON.parse(JSON.stringify(obj)); return JSON.parse(JSON.stringify(obj));
} }
export function convertTestName(name: string): string {
return name.replace(/ /g, "-").toLowerCase();
}
export function sortByName(stringList: string[]): string[] {
return stringList.sort((a, b) => a.localeCompare(b));
}

View file

@ -0,0 +1,46 @@
import { test } from "@playwright/test";
test("shoud delete a flow", async ({ page }) => {
await page.goto("/");
await page.waitForTimeout(2000);
await page.getByText("Store").nth(0).click();
await page.getByTestId("install-Website Content QA").click();
await page.waitForTimeout(5000);
await page.getByText("My Collection").nth(0).click();
await page.getByText("Website Content QA").first().isVisible();
await page
.getByTestId("card-website-content-qa")
.first()
.hover()
.then(async () => {
await page.getByTestId("icon-Trash2").first().click();
await page.waitForTimeout(2000);
});
await page.getByText("Confirm deletion of component?").isVisible();
await page.getByText("Delete").nth(1).click();
await page.waitForTimeout(1000);
await page.getByText("Successfully").first().isVisible();
});
test("shoud delete a component", async ({ page }) => {
await page.goto("/");
await page.waitForTimeout(2000);
await page.getByText("Store").nth(0).click();
await page.getByTestId("install-Basic RAG").click();
await page.waitForTimeout(5000);
await page.getByText("My Collection").nth(0).click();
await page.getByText("Components").first().click();
await page.getByText("Basic RAG").first().isVisible();
await page
.getByTestId("card-basic-rag")
.first()
.hover()
.then(async () => {
await page.getByTestId("icon-Trash2").first().click();
await page.waitForTimeout(2000);
});
await page.getByText("Confirm deletion of component?").isVisible();
await page.getByText("Delete").nth(1).click();
await page.waitForTimeout(1000);
await page.getByText("Successfully").first().isVisible();
});

View file

@ -0,0 +1,91 @@
import { test } from "@playwright/test";
test("should see general profile gradient", async ({ page }) => {
await page.goto("/");
await page.waitForTimeout(2000);
await page.getByTestId("user-profile-settings").click();
await page.getByText("Settings").click();
await page.getByText("General").nth(2).isVisible();
await page.getByText("Profile Gradient").isVisible();
});
test("should interact with global variables", async ({ page }) => {
const randomName = Math.random().toString(36).substring(2);
await page.goto("/");
await page.waitForTimeout(2000);
await page.getByTestId("user-profile-settings").click();
await page.getByText("Settings").click();
await page.getByText("Global Variables").click();
await page.getByText("Global Variables").nth(2);
await page.getByText("Global Variables", { exact: true }).nth(1).isVisible();
await page.getByText("Add New").click();
await page
.getByPlaceholder("Insert a name for the variable...")
.fill(randomName);
await page.getByTestId("popover-anchor-type-global-variables").click();
await page.getByPlaceholder("Search options...").fill("Generic");
await page.waitForTimeout(2000);
await page.getByText("Generic", { exact: true }).last().isVisible();
await page.getByText("Generic", { exact: true }).last().click();
await page.getByTestId("popover-anchor-type-global-variables").click();
await page.waitForTimeout(2000);
await page.getByPlaceholder("Search options...").fill("Generic");
await page.getByText("Generic", { exact: true }).last().isVisible();
await page.getByText("Generic", { exact: true }).last().click();
await page
.getByPlaceholder("Insert a value for the variable...")
.fill("testtesttesttesttesttesttesttest");
await page.getByTestId("popover-anchor-apply-to-fields").click();
await page.waitForTimeout(2000);
await page.getByPlaceholder("Search options...").fill("System Message");
await page.getByText("System Message").first().click();
await page.getByPlaceholder("Search options...").fill("openAI");
await page.getByText("OpenAI API Base").first().click();
await page.getByPlaceholder("Search options...").fill("llama");
await page.getByText("Ollama").first().click();
await page.keyboard.press("Escape");
await page.getByText("Save Variable", { exact: true }).click();
await page.getByText(randomName).isVisible();
await page
.getByLabel("Press Space to toggle all rows selection (unchecked)")
.nth(0)
.click();
await page.getByTestId("icon-Trash2").click();
await page.getByText("No data available").isVisible();
});
test("should see shortcuts", async ({ page }) => {
await page.goto("/");
await page.waitForTimeout(2000);
await page.getByTestId("user-profile-settings").click();
await page.getByText("Settings").click();
await page.getByText("General").nth(2).isVisible();
await page.getByText("Shortcuts").nth(0).click();
await page.getByText("Shortcuts", { exact: true }).nth(1).isVisible();
await page
.getByText("Advanced Settings Component", { exact: true })
.isVisible();
await page.getByText("Minimize Component", { exact: true }).isVisible();
await page.getByText("Code Component", { exact: true }).isVisible();
await page.getByText("Copy Component", { exact: true }).isVisible();
await page.getByText("Duplicate Component", { exact: true }).isVisible();
await page.getByText("Share Component", { exact: true }).isVisible();
await page.getByText("Docs Component", { exact: true }).isVisible();
await page.getByText("Save Component", { exact: true }).isVisible();
await page.getByText("Delete Component", { exact: true }).isVisible();
await page.getByText("Open Playground", { exact: true }).isVisible();
await page.getByText("Undo", { exact: true }).isVisible();
await page.getByText("Redo", { exact: true }).isVisible();
});

View file

@ -393,13 +393,13 @@ def test_various_prompts(client, prompt, expected_input_variables):
def test_get_vertices_flow_not_found(client, logged_in_headers): def test_get_vertices_flow_not_found(client, logged_in_headers):
response = client.get("/api/v1/build/nonexistent_id/vertices", headers=logged_in_headers) response = client.post("/api/v1/build/nonexistent_id/vertices", headers=logged_in_headers)
assert response.status_code == 500 # Or whatever status code you've set for invalid ID assert response.status_code == 500 # Or whatever status code you've set for invalid ID
def test_get_vertices(client, added_flow_with_prompt_and_history, logged_in_headers): def test_get_vertices(client, added_flow_with_prompt_and_history, logged_in_headers):
flow_id = added_flow_with_prompt_and_history["id"] flow_id = added_flow_with_prompt_and_history["id"]
response = client.get(f"/api/v1/build/{flow_id}/vertices", headers=logged_in_headers) response = client.post(f"/api/v1/build/{flow_id}/vertices", headers=logged_in_headers)
assert response.status_code == 200 assert response.status_code == 200
assert "ids" in response.json() assert "ids" in response.json()
# The response should contain the list in this order # The response should contain the list in this order