merging with authentication

This commit is contained in:
Cristhian Zanforlin Lousa 2023-08-17 19:18:30 -03:00
commit bfe8053127
115 changed files with 9363 additions and 3126 deletions

View file

@ -15,7 +15,8 @@
"actboy168.tasks", "actboy168.tasks",
"GitHub.copilot", "GitHub.copilot",
"ms-python.python", "ms-python.python",
"eamodio.gitlens" "eamodio.gitlens",
"GitHub.vscode-pull-request-github"
] ]
} }
}, },

View file

@ -3,32 +3,36 @@
{ {
"name": "LangChain Dev Container", "name": "LangChain Dev Container",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/universal:2-linux", "image": "mcr.microsoft.com/devcontainers/python:1-3.10-bullseye",
// Features to add to the dev container. More info: https://containers.dev/features.
"features": { "features": {
"ghcr.io/devcontainers/features/aws-cli:1": {}, "ghcr.io/devcontainers/features/node": {},
"ghcr.io/devcontainers/features/docker-in-docker": {} "ghcr.io/devcontainers-contrib/features/poetry": {}
}, },
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "make setup_devcontainer",
"containerEnv": {
"POETRY_VIRTUALENVS_IN_PROJECT": "true"
},
// Configure tool-specific properties.
"customizations": { "customizations": {
"vscode": {"extensions": [ "vscode": {"extensions": [
"actboy168.tasks", "actboy168.tasks",
"GitHub.copilot", "GitHub.copilot",
"ms-python.python", "ms-python.python",
"sourcery.sourcery", "sourcery.sourcery",
"eamodio.gitlens" "eamodio.gitlens",
"ms-vscode.makefile-tools",
"GitHub.vscode-pull-request-github"
]} ]}
}, }
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "poetry install"
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root" // "remoteUser": "root"

20
.gitattributes vendored Normal file
View file

@ -0,0 +1,20 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text eol=lf
# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
*.c text
*.h text
*.py text
*.js text
*.ts text
*.jsx text
*.md text
*.mdx text
# Declare files that will always have CRLF line endings on checkout.
*.sln text eol=crlf
# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpg binary

View file

@ -32,15 +32,27 @@ lint:
poetry run ruff . --fix poetry run ruff . --fix
install_frontend: install_frontend:
cd src/frontend && npm install cd src/frontend && npm install;
install_frontendc:
cd src/frontend && rm -rf node_modules package-lock.json && npm install;
run_frontend: run_frontend:
cd src/frontend && npm start cd src/frontend && npm start
setup_devcontainer:
make init
make build_frontend
poetry run langflow --path src/frontend/build
frontend: frontend:
make install_frontend make install_frontend
make run_frontend make run_frontend
frontendc:
make install_frontendc
make run_frontend
install_backend: install_backend:
poetry install poetry install

View file

@ -7,3 +7,57 @@ import Admonition from '@theme/Admonition';
We appreciate your understanding as we polish our documentation – it may contain some rough edges. Share your feedback or report issues to help us improve! 🛠️📝 We appreciate your understanding as we polish our documentation – it may contain some rough edges. Share your feedback or report issues to help us improve! 🛠️📝
</p> </p>
</Admonition> </Admonition>
### BingSearchRun
Bing Search is a web search engine owned and operated by Microsoft. It provides search results for various types of content, including web pages, images, videos, and news articles. It uses a combination of algorithms and human editors to deliver search results to users.
**Params**
- **Api Wrapper:** A BingSearchAPIWrapper component that takes the search URL and a subscription key.
### Calculator
The calculator tool provides mathematical calculation capabilities to an agent by leveraging an LLMMathChain. It allows the agent to perform math when needed to answer questions.
**Params**
- **LLM:** Language Model to use in the calculation.
### GoogleSearchResults
A wrapper around Google Search. Useful for when the user needs to answer questions about with more control over the JSON data returned from the API. It returns the full JSON response configured based on the parameters passed to the API wrapper.
**Params**
- **Api Wrapper:** A GoogleSearchAPIWrapper with Google API key and CSE ID
### GoogleSearchRun
A quick wrapper around Google Search. It executes the search query and returns just the first result snippet from the highest-priority result type.
**Params**
- **Api Wrapper:** A GoogleSearchAPIWrapper with Google API key and CSE ID
### GoogleSerperRun
A low-cost Google Search API.
**Params**
- **Api Wrapper:** A GoogleSerperAPIWrapper component with API key and result keys
### InfoSQLDatabaseTool
Tool for getting metadata about a SQL database. The input to this tool is a comma-separated list of tables, and the output is the schema and sample rows for those tables. Example Input: `“table1`, `table2`, `table3”`.
**Params**
- **Db:** SQLDatabase to query.

1551
package-lock.json generated

File diff suppressed because it is too large Load diff

152
poetry.lock generated
View file

@ -146,13 +146,13 @@ files = [
[[package]] [[package]]
name = "alembic" name = "alembic"
version = "1.11.2" version = "1.11.3"
description = "A database migration tool for SQLAlchemy." description = "A database migration tool for SQLAlchemy."
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "alembic-1.11.2-py3-none-any.whl", hash = "sha256:7981ab0c4fad4fe1be0cf183aae17689fe394ff874fd2464adb774396faf0796"}, {file = "alembic-1.11.3-py3-none-any.whl", hash = "sha256:d6c96c2482740592777c400550a523bc7a9aada4e210cae2e733354ddae6f6f8"},
{file = "alembic-1.11.2.tar.gz", hash = "sha256:678f662130dc540dac12de0ea73de9f89caea9dbea138f60ef6263149bf84657"}, {file = "alembic-1.11.3.tar.gz", hash = "sha256:3db4ce81a9072e1b5aa44c2d202add24553182672a12daf21608d6f62a8f9cf9"},
] ]
[package.dependencies] [package.dependencies]
@ -165,20 +165,20 @@ tz = ["python-dateutil"]
[[package]] [[package]]
name = "anthropic" name = "anthropic"
version = "0.3.9" version = "0.3.10"
description = "Client library for the anthropic API" description = "Client library for the anthropic API"
optional = false optional = false
python-versions = ">=3.7,<4.0" python-versions = ">=3.7,<4.0"
files = [ files = [
{file = "anthropic-0.3.9-py3-none-any.whl", hash = "sha256:23e8daf266c707faa0b85328ada03dcf885e62c2f236eb0159352e29b9c4e2e3"}, {file = "anthropic-0.3.10-py3-none-any.whl", hash = "sha256:95cd73168296a4f91a5899a28660991e044322cf94442d07b99901d4ca74acd6"},
{file = "anthropic-0.3.9.tar.gz", hash = "sha256:c19b75308c07cb1ecbea03ffcdde9ba68e5ca22a22479217712396385678bde3"}, {file = "anthropic-0.3.10.tar.gz", hash = "sha256:d1f66efc541fbff0ecfd37fd4d3690f9daaa748fc42d9ded5863a10815a5d97b"},
] ]
[package.dependencies] [package.dependencies]
anyio = ">=3.5.0,<4" anyio = ">=3.5.0,<4"
distro = ">=1.7.0,<2" distro = ">=1.7.0,<2"
httpx = ">=0.23.0,<1" httpx = ">=0.23.0,<1"
pydantic = ">=1.9.0,<2.0.0" pydantic = ">=1.9.0,<3"
tokenizers = ">=0.13.0" tokenizers = ">=0.13.0"
typing-extensions = ">=4.5,<5" typing-extensions = ">=4.5,<5"
@ -792,13 +792,13 @@ sqlalchemy = ["sqlalchemy (>1.3.21,<2.0)"]
[[package]] [[package]]
name = "cohere" name = "cohere"
version = "4.20.0" version = "4.20.1"
description = "" description = ""
optional = false optional = false
python-versions = ">=3.7,<4.0" python-versions = ">=3.7,<4.0"
files = [ files = [
{file = "cohere-4.20.0-py3-none-any.whl", hash = "sha256:bebe4b1d21da0719aaa7db2cc180b5f3d0a802c19ad2d893d627971286e50497"}, {file = "cohere-4.20.1-py3-none-any.whl", hash = "sha256:4466c7abdbb168fe2893e5b5123882fe91691d0e3fe43ee254b411917f780dfc"},
{file = "cohere-4.20.0.tar.gz", hash = "sha256:a16e86981945c201bab67ad6d47e57f42a37bf2a774f6ac6b8b12233aae6409a"}, {file = "cohere-4.20.1.tar.gz", hash = "sha256:533e4a45b38dc338f8a27f24e098b652b4f5ed9f9fbb4719780d1a9fcd1af39e"},
] ]
[package.dependencies] [package.dependencies]
@ -1630,13 +1630,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"]
[[package]] [[package]]
name = "google-api-python-client" name = "google-api-python-client"
version = "2.96.0" version = "2.97.0"
description = "Google API Client Library for Python" description = "Google API Client Library for Python"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "google-api-python-client-2.96.0.tar.gz", hash = "sha256:f712373d03d338af57b9f5fe98c91f4b5baaa8765469b015bc623c4681c5bd51"}, {file = "google-api-python-client-2.97.0.tar.gz", hash = "sha256:48277291894876a1ca7ed4127e055e81f81e6343ced1b544a7200ae2c119dcd7"},
{file = "google_api_python_client-2.96.0-py2.py3-none-any.whl", hash = "sha256:38c2b61b10d15bb41ec8f89303e3837ec2d2c3e4e38de5800c05ee322492f937"}, {file = "google_api_python_client-2.97.0-py2.py3-none-any.whl", hash = "sha256:5215f4cd577753fc4192ccfbe0bb8b55d4bb5fd68fa6268ac5cf271b6305de31"},
] ]
[package.dependencies] [package.dependencies]
@ -2979,13 +2979,13 @@ test = ["psutil", "pytest", "pytest-asyncio"]
[[package]] [[package]]
name = "langsmith" name = "langsmith"
version = "0.0.22" version = "0.0.24"
description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform."
optional = false optional = false
python-versions = ">=3.8.1,<4.0" python-versions = ">=3.8.1,<4.0"
files = [ files = [
{file = "langsmith-0.0.22-py3-none-any.whl", hash = "sha256:1bc94a2e5bfa355ca15d9e658c2c2d04c8cc45c61892a1be08a7c3b40f2fd3f4"}, {file = "langsmith-0.0.24-py3-none-any.whl", hash = "sha256:f9f951d070aa1919123d700642aca9c781edfc8797a65ab1161aa12f89bed707"},
{file = "langsmith-0.0.22.tar.gz", hash = "sha256:5726c7841294db2a9e5863e20718878d16e28722bdaf3169a278ff3bda2f0be7"}, {file = "langsmith-0.0.24.tar.gz", hash = "sha256:9c066dd915752324490a735692997b0db0958f5dfc1e0a0dfbf752c6e62c7529"},
] ]
[package.dependencies] [package.dependencies]
@ -3573,33 +3573,38 @@ dill = ">=0.3.7"
[[package]] [[package]]
name = "mypy" name = "mypy"
version = "1.5.0" version = "1.5.1"
description = "Optional static typing for Python" description = "Optional static typing for Python"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "mypy-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ad3109bec37cc33654de8db30fe8ff3a1bb57ea65144167d68185e6dced9868d"}, {file = "mypy-1.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f33592ddf9655a4894aef22d134de7393e95fcbdc2d15c1ab65828eee5c66c70"},
{file = "mypy-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b4ea3a0241cb005b0ccdbd318fb99619b21ae51bcf1660b95fc22e0e7d3ba4a1"}, {file = "mypy-1.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:258b22210a4a258ccd077426c7a181d789d1121aca6db73a83f79372f5569ae0"},
{file = "mypy-1.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fe816e26e676c1311b9e04fd576543b873576d39439f7c24c8e5c7728391ecf"}, {file = "mypy-1.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9ec1f695f0c25986e6f7f8778e5ce61659063268836a38c951200c57479cc12"},
{file = "mypy-1.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:42170e68adb1603ccdc55a30068f72bcfcde2ce650188e4c1b2a93018b826735"}, {file = "mypy-1.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:abed92d9c8f08643c7d831300b739562b0a6c9fcb028d211134fc9ab20ccad5d"},
{file = "mypy-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:d145b81a8214687cfc1f85c03663a5bbe736777410e5580e54d526e7e904f564"}, {file = "mypy-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:a156e6390944c265eb56afa67c74c0636f10283429171018446b732f1a05af25"},
{file = "mypy-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c36011320e452eb30bec38b9fd3ba20569dc9545d7d4540d967f3ea1fab9c374"}, {file = "mypy-1.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6ac9c21bfe7bc9f7f1b6fae441746e6a106e48fc9de530dea29e8cd37a2c0cc4"},
{file = "mypy-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f3940cf5845b2512b3ab95463198b0cdf87975dfd17fdcc6ce9709a9abe09e69"}, {file = "mypy-1.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51cb1323064b1099e177098cb939eab2da42fea5d818d40113957ec954fc85f4"},
{file = "mypy-1.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9166186c498170e1ff478a7f540846b2169243feb95bc228d39a67a1a450cdc6"}, {file = "mypy-1.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:596fae69f2bfcb7305808c75c00f81fe2829b6236eadda536f00610ac5ec2243"},
{file = "mypy-1.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:725b57a19b7408ef66a0fd9db59b5d3e528922250fb56e50bded27fea9ff28f0"}, {file = "mypy-1.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:32cb59609b0534f0bd67faebb6e022fe534bdb0e2ecab4290d683d248be1b275"},
{file = "mypy-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:eec5c927aa4b3e8b4781840f1550079969926d0a22ce38075f6cfcf4b13e3eb4"}, {file = "mypy-1.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:159aa9acb16086b79bbb0016145034a1a05360626046a929f84579ce1666b315"},
{file = "mypy-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79c520aa24f21852206b5ff2cf746dc13020113aa73fa55af504635a96e62718"}, {file = "mypy-1.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f6b0e77db9ff4fda74de7df13f30016a0a663928d669c9f2c057048ba44f09bb"},
{file = "mypy-1.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:769ddb6bfe55c2bd9c7d6d7020885a5ea14289619db7ee650e06b1ef0852c6f4"}, {file = "mypy-1.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:26f71b535dfc158a71264e6dc805a9f8d2e60b67215ca0bfa26e2e1aa4d4d373"},
{file = "mypy-1.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbf18f8db7e5f060d61c91e334d3b96d6bb624ddc9ee8a1cde407b737acbca2c"}, {file = "mypy-1.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc3a600f749b1008cc75e02b6fb3d4db8dbcca2d733030fe7a3b3502902f161"},
{file = "mypy-1.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a2500ad063413bc873ae102cf655bf49889e0763b260a3a7cf544a0cbbf7e70a"}, {file = "mypy-1.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:26fb32e4d4afa205b24bf645eddfbb36a1e17e995c5c99d6d00edb24b693406a"},
{file = "mypy-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:84cf9f7d8a8a22bb6a36444480f4cbf089c917a4179fbf7eea003ea931944a7f"}, {file = "mypy-1.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:82cb6193de9bbb3844bab4c7cf80e6227d5225cc7625b068a06d005d861ad5f1"},
{file = "mypy-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a551ed0fc02455fe2c1fb0145160df8336b90ab80224739627b15ebe2b45e9dc"}, {file = "mypy-1.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4a465ea2ca12804d5b34bb056be3a29dc47aea5973b892d0417c6a10a40b2d65"},
{file = "mypy-1.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:372fd97293ed0076d52695849f59acbbb8461c4ab447858cdaeaf734a396d823"}, {file = "mypy-1.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9fece120dbb041771a63eb95e4896791386fe287fefb2837258925b8326d6160"},
{file = "mypy-1.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8a7444d6fcac7e2585b10abb91ad900a576da7af8f5cffffbff6065d9115813"}, {file = "mypy-1.5.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d28ddc3e3dfeab553e743e532fb95b4e6afad51d4706dd22f28e1e5e664828d2"},
{file = "mypy-1.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:35b13335c6c46a386577a51f3d38b2b5d14aa619e9633bb756bd77205e4bd09f"}, {file = "mypy-1.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:57b10c56016adce71fba6bc6e9fd45d8083f74361f629390c556738565af8eeb"},
{file = "mypy-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:2c9d570f53908cbea326ad8f96028a673b814d9dca7515bf71d95fa662c3eb6f"}, {file = "mypy-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:ff0cedc84184115202475bbb46dd99f8dcb87fe24d5d0ddfc0fe6b8575c88d2f"},
{file = "mypy-1.5.0-py3-none-any.whl", hash = "sha256:69b32d0dedd211b80f1b7435644e1ef83033a2af2ac65adcdc87c38db68a86be"}, {file = "mypy-1.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8f772942d372c8cbac575be99f9cc9d9fb3bd95c8bc2de6c01411e2c84ebca8a"},
{file = "mypy-1.5.0.tar.gz", hash = "sha256:f3460f34b3839b9bc84ee3ed65076eb827cd99ed13ed08d723f9083cada4a212"}, {file = "mypy-1.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5d627124700b92b6bbaa99f27cbe615c8ea7b3402960f6372ea7d65faf376c14"},
{file = "mypy-1.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:361da43c4f5a96173220eb53340ace68cda81845cd88218f8862dfb0adc8cddb"},
{file = "mypy-1.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:330857f9507c24de5c5724235e66858f8364a0693894342485e543f5b07c8693"},
{file = "mypy-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:c543214ffdd422623e9fedd0869166c2f16affe4ba37463975043ef7d2ea8770"},
{file = "mypy-1.5.1-py3-none-any.whl", hash = "sha256:f757063a83970d67c444f6e01d9550a7402322af3557ce7630d3c957386fa8f5"},
{file = "mypy-1.5.1.tar.gz", hash = "sha256:b031b9601f1060bf1281feab89697324726ba0c0bae9d7cd7ab4b690940f0b92"},
] ]
[package.dependencies] [package.dependencies]
@ -5890,29 +5895,42 @@ python-versions = "*"
files = [ files = [
{file = "safetensors-0.3.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b6a66989075c2891d743153e8ba9ca84ee7232c8539704488f454199b8b8f84d"}, {file = "safetensors-0.3.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b6a66989075c2891d743153e8ba9ca84ee7232c8539704488f454199b8b8f84d"},
{file = "safetensors-0.3.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:670d6bc3a3b377278ce2971fa7c36ebc0a35041c4ea23b9df750a39380800195"}, {file = "safetensors-0.3.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:670d6bc3a3b377278ce2971fa7c36ebc0a35041c4ea23b9df750a39380800195"},
{file = "safetensors-0.3.2-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:564f42838721925b5313ae864ba6caa6f4c80a9fbe63cf24310c3be98ab013cd"},
{file = "safetensors-0.3.2-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:7f80af7e4ab3188daaff12d43d078da3017a90d732d38d7af4eb08b6ca2198a5"}, {file = "safetensors-0.3.2-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:7f80af7e4ab3188daaff12d43d078da3017a90d732d38d7af4eb08b6ca2198a5"},
{file = "safetensors-0.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec30d78f20f1235b252d59cbb9755beb35a1fde8c24c89b3c98e6a1804cfd432"},
{file = "safetensors-0.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16063d94d8f600768d3c331b1e97964b1bf3772e19710105fe24ec5a6af63770"},
{file = "safetensors-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbb44e140bf2aeda98d9dde669dbec15f7b77f96a9274469b91a6cf4bcc5ec3b"}, {file = "safetensors-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbb44e140bf2aeda98d9dde669dbec15f7b77f96a9274469b91a6cf4bcc5ec3b"},
{file = "safetensors-0.3.2-cp310-cp310-win32.whl", hash = "sha256:2961c1243fd0da46aa6a1c835305cc4595486f8ac64632a604d0eb5f2de76175"}, {file = "safetensors-0.3.2-cp310-cp310-win32.whl", hash = "sha256:2961c1243fd0da46aa6a1c835305cc4595486f8ac64632a604d0eb5f2de76175"},
{file = "safetensors-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c813920482c337d1424d306e1b05824a38e3ef94303748a0a287dea7a8c4f805"}, {file = "safetensors-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c813920482c337d1424d306e1b05824a38e3ef94303748a0a287dea7a8c4f805"},
{file = "safetensors-0.3.2-cp311-cp311-macosx_10_11_universal2.whl", hash = "sha256:707df34bd9b9047e97332136ad98e57028faeccdb9cfe1c3b52aba5964cc24bf"}, {file = "safetensors-0.3.2-cp311-cp311-macosx_10_11_universal2.whl", hash = "sha256:707df34bd9b9047e97332136ad98e57028faeccdb9cfe1c3b52aba5964cc24bf"},
{file = "safetensors-0.3.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:becc5bb85b2947eae20ed23b407ebfd5277d9a560f90381fe2c42e6c043677ba"}, {file = "safetensors-0.3.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:becc5bb85b2947eae20ed23b407ebfd5277d9a560f90381fe2c42e6c043677ba"},
{file = "safetensors-0.3.2-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:30a75707be5cc9686490bde14b9a371cede4af53244ea72b340cfbabfffdf58a"},
{file = "safetensors-0.3.2-cp311-cp311-macosx_13_0_universal2.whl", hash = "sha256:54ad6af663e15e2b99e2ea3280981b7514485df72ba6d014dc22dae7ba6a5e6c"}, {file = "safetensors-0.3.2-cp311-cp311-macosx_13_0_universal2.whl", hash = "sha256:54ad6af663e15e2b99e2ea3280981b7514485df72ba6d014dc22dae7ba6a5e6c"},
{file = "safetensors-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37764b3197656ef507a266c453e909a3477dabc795962b38e3ad28226f53153b"},
{file = "safetensors-0.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4939067736783acd8391d83cd97d6c202f94181951ce697d519f9746381b6a39"},
{file = "safetensors-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ada0fac127ff8fb04834da5c6d85a8077e6a1c9180a11251d96f8068db922a17"}, {file = "safetensors-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ada0fac127ff8fb04834da5c6d85a8077e6a1c9180a11251d96f8068db922a17"},
{file = "safetensors-0.3.2-cp311-cp311-win32.whl", hash = "sha256:155b82dbe2b0ebff18cde3f76b42b6d9470296e92561ef1a282004d449fa2b4c"}, {file = "safetensors-0.3.2-cp311-cp311-win32.whl", hash = "sha256:155b82dbe2b0ebff18cde3f76b42b6d9470296e92561ef1a282004d449fa2b4c"},
{file = "safetensors-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:a86428d196959619ce90197731be9391b5098b35100a7228ef4643957648f7f5"}, {file = "safetensors-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:a86428d196959619ce90197731be9391b5098b35100a7228ef4643957648f7f5"},
{file = "safetensors-0.3.2-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:c1f8ab41ed735c5b581f451fd15d9602ff51aa88044bfa933c5fa4b1d0c644d1"}, {file = "safetensors-0.3.2-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:c1f8ab41ed735c5b581f451fd15d9602ff51aa88044bfa933c5fa4b1d0c644d1"},
{file = "safetensors-0.3.2-cp37-cp37m-macosx_13_0_x86_64.whl", hash = "sha256:bc9cfb3c9ea2aec89685b4d656f9f2296f0f0d67ecf2bebf950870e3be89b3db"}, {file = "safetensors-0.3.2-cp37-cp37m-macosx_13_0_x86_64.whl", hash = "sha256:bc9cfb3c9ea2aec89685b4d656f9f2296f0f0d67ecf2bebf950870e3be89b3db"},
{file = "safetensors-0.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ace5d471e3d78e0d93f952707d808b5ab5eac77ddb034ceb702e602e9acf2be9"},
{file = "safetensors-0.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de3e20a388b444381bcda1a3193cce51825ddca277e4cf3ed1fe8d9b2d5722cd"},
{file = "safetensors-0.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d7d70d48585fe8df00725aa788f2e64fd24a4c9ae07cd6be34f6859d0f89a9c"}, {file = "safetensors-0.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d7d70d48585fe8df00725aa788f2e64fd24a4c9ae07cd6be34f6859d0f89a9c"},
{file = "safetensors-0.3.2-cp37-cp37m-win32.whl", hash = "sha256:6ff59bc90cdc857f68b1023be9085fda6202bbe7f2fd67d06af8f976d6adcc10"}, {file = "safetensors-0.3.2-cp37-cp37m-win32.whl", hash = "sha256:6ff59bc90cdc857f68b1023be9085fda6202bbe7f2fd67d06af8f976d6adcc10"},
{file = "safetensors-0.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:8b05c93da15fa911763a89281906ca333ed800ab0ef1c7ce53317aa1a2322f19"}, {file = "safetensors-0.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:8b05c93da15fa911763a89281906ca333ed800ab0ef1c7ce53317aa1a2322f19"},
{file = "safetensors-0.3.2-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:8969cfd9e8d904e8d3c67c989e1bd9a95e3cc8980d4f95e4dcd43c299bb94253"}, {file = "safetensors-0.3.2-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:8969cfd9e8d904e8d3c67c989e1bd9a95e3cc8980d4f95e4dcd43c299bb94253"},
{file = "safetensors-0.3.2-cp38-cp38-macosx_13_0_x86_64.whl", hash = "sha256:f54148ac027556eb02187e9bc1556c4d916c99ca3cb34ca36a7d304d675035c1"}, {file = "safetensors-0.3.2-cp38-cp38-macosx_13_0_x86_64.whl", hash = "sha256:f54148ac027556eb02187e9bc1556c4d916c99ca3cb34ca36a7d304d675035c1"},
{file = "safetensors-0.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:caec25fedbcf73f66c9261984f07885680f71417fc173f52279276c7f8a5edd3"},
{file = "safetensors-0.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:50224a1d99927ccf3b75e27c3d412f7043280431ab100b4f08aad470c37cf99a"},
{file = "safetensors-0.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa98f49e95f02eb750d32c4947e7d5aa43883149ebd0414920866446525b70f0"}, {file = "safetensors-0.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa98f49e95f02eb750d32c4947e7d5aa43883149ebd0414920866446525b70f0"},
{file = "safetensors-0.3.2-cp38-cp38-win32.whl", hash = "sha256:33409df5e28a83dc5cc5547a3ac17c0f1b13a1847b1eb3bc4b3be0df9915171e"}, {file = "safetensors-0.3.2-cp38-cp38-win32.whl", hash = "sha256:33409df5e28a83dc5cc5547a3ac17c0f1b13a1847b1eb3bc4b3be0df9915171e"},
{file = "safetensors-0.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:e04a7cbbb3856159ab99e3adb14521544f65fcb8548cce773a1435a0f8d78d27"}, {file = "safetensors-0.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:e04a7cbbb3856159ab99e3adb14521544f65fcb8548cce773a1435a0f8d78d27"},
{file = "safetensors-0.3.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:7c864cf5dcbfb608c5378f83319c60cc9c97263343b57c02756b7613cd5ab4dd"}, {file = "safetensors-0.3.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:7c864cf5dcbfb608c5378f83319c60cc9c97263343b57c02756b7613cd5ab4dd"},
{file = "safetensors-0.3.2-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:14e8c19d6dc51d4f70ee33c46aff04c8ba3f95812e74daf8036c24bc86e75cae"}, {file = "safetensors-0.3.2-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:14e8c19d6dc51d4f70ee33c46aff04c8ba3f95812e74daf8036c24bc86e75cae"},
{file = "safetensors-0.3.2-cp39-cp39-macosx_13_0_arm64.whl", hash = "sha256:042a60f633c3c7009fdf6a7c182b165cb7283649d2a1e9c7a4a1c23454bd9a5b"},
{file = "safetensors-0.3.2-cp39-cp39-macosx_13_0_x86_64.whl", hash = "sha256:fafd95e5ef41e8f312e2a32b7031f7b9b2a621b255f867b221f94bb2e9f51ae8"}, {file = "safetensors-0.3.2-cp39-cp39-macosx_13_0_x86_64.whl", hash = "sha256:fafd95e5ef41e8f312e2a32b7031f7b9b2a621b255f867b221f94bb2e9f51ae8"},
{file = "safetensors-0.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ed77cf358abce2307f03634694e0b2a29822e322a1623e0b1aa4b41e871bf8b"},
{file = "safetensors-0.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d344e8b2681a33aafc197c90b0def3229b3317d749531c72fa6259d0caa5c8c"},
{file = "safetensors-0.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87ff0024ef2e5722a79af24688ce4a430f70601d0cf712a744105ed4b8f67ba5"}, {file = "safetensors-0.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87ff0024ef2e5722a79af24688ce4a430f70601d0cf712a744105ed4b8f67ba5"},
{file = "safetensors-0.3.2-cp39-cp39-win32.whl", hash = "sha256:827af9478b78977248ba93e2fd97ea307fb63f463f80cef4824460f8c2542a52"}, {file = "safetensors-0.3.2-cp39-cp39-win32.whl", hash = "sha256:827af9478b78977248ba93e2fd97ea307fb63f463f80cef4824460f8c2542a52"},
{file = "safetensors-0.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:9b09f27c456efa301f98681ea14b12f81f2637889f6336223ccab71e42c34541"}, {file = "safetensors-0.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:9b09f27c456efa301f98681ea14b12f81f2637889f6336223ccab71e42c34541"},
@ -6113,18 +6131,18 @@ files = [
[[package]] [[package]]
name = "setuptools" name = "setuptools"
version = "68.0.0" version = "68.1.0"
description = "Easily download, build, install, upgrade, and uninstall Python packages" description = "Easily download, build, install, upgrade, and uninstall Python packages"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.8"
files = [ files = [
{file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"}, {file = "setuptools-68.1.0-py3-none-any.whl", hash = "sha256:e13e1b0bc760e9b0127eda042845999b2f913e12437046e663b833aa96d89715"},
{file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"}, {file = "setuptools-68.1.0.tar.gz", hash = "sha256:d59c97e7b774979a5ccb96388efc9eb65518004537e85d52e81eaee89ab6dd91"},
] ]
[package.extras] [package.extras]
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
[[package]] [[package]]
@ -6514,13 +6532,13 @@ doc = ["reno", "sphinx", "tornado (>=4.5)"]
[[package]] [[package]]
name = "textual" name = "textual"
version = "0.32.0" version = "0.33.0"
description = "Modern Text User Interface framework" description = "Modern Text User Interface framework"
optional = true optional = true
python-versions = ">=3.7,<4.0" python-versions = ">=3.7,<4.0"
files = [ files = [
{file = "textual-0.32.0-py3-none-any.whl", hash = "sha256:81fc68406c8806bc864e2f035874a868b4ff0cf466289dce5f7b31869949383b"}, {file = "textual-0.33.0-py3-none-any.whl", hash = "sha256:698a093add0fd21c786232bcacde9ff427a9d5dc9ea5deca93437d9453e4ead1"},
{file = "textual-0.32.0.tar.gz", hash = "sha256:f7b6683bc18faee6fd3c47cfbad43fbf8273c5fecc12230d52ce5ee089021327"}, {file = "textual-0.33.0.tar.gz", hash = "sha256:e0a98b1d9c4458c5bb4269c65d0a7f3e926f197411242d2f8faf80183d47a728"},
] ]
[package.dependencies] [package.dependencies]
@ -6939,6 +6957,17 @@ files = [
{file = "types_cachetools-5.3.0.6-py3-none-any.whl", hash = "sha256:f7f8a25bfe306f2e6bc2ad0a2f949d9e72f2d91036d509c36d3810bf728bc6e1"}, {file = "types_cachetools-5.3.0.6-py3-none-any.whl", hash = "sha256:f7f8a25bfe306f2e6bc2ad0a2f949d9e72f2d91036d509c36d3810bf728bc6e1"},
] ]
[[package]]
name = "types-passlib"
version = "1.7.7.13"
description = "Typing stubs for passlib"
optional = false
python-versions = "*"
files = [
{file = "types-passlib-1.7.7.13.tar.gz", hash = "sha256:f152639f1f2103d7f59a56e2aec5f9398a75a80830991d0d68aac5c2b9c32a77"},
{file = "types_passlib-1.7.7.13-py3-none-any.whl", hash = "sha256:414b5ee9c88313357c9261cfcf816509b1e8e4673f0796bd61e9ef249f6fe076"},
]
[[package]] [[package]]
name = "types-pillow" name = "types-pillow"
version = "9.5.0.6" version = "9.5.0.6"
@ -6950,6 +6979,31 @@ files = [
{file = "types_Pillow-9.5.0.6-py3-none-any.whl", hash = "sha256:1d238abaa9d529b04941d805b7f4d3f7df30702bb14521ec507617f117406fb4"}, {file = "types_Pillow-9.5.0.6-py3-none-any.whl", hash = "sha256:1d238abaa9d529b04941d805b7f4d3f7df30702bb14521ec507617f117406fb4"},
] ]
[[package]]
name = "types-pyasn1"
version = "0.4.0.6"
description = "Typing stubs for pyasn1"
optional = false
python-versions = "*"
files = [
{file = "types-pyasn1-0.4.0.6.tar.gz", hash = "sha256:8f1965d0b79152f9d1efc89f9aa9a8cdda7cd28b2619df6737c095cbedeff98b"},
{file = "types_pyasn1-0.4.0.6-py3-none-any.whl", hash = "sha256:dd5fc818864e63a66cd714be0a7a59a493f4a81b87ee9ac978c41f1eaa9a0cef"},
]
[[package]]
name = "types-python-jose"
version = "3.3.4.8"
description = "Typing stubs for python-jose"
optional = false
python-versions = "*"
files = [
{file = "types-python-jose-3.3.4.8.tar.gz", hash = "sha256:3c316675c3cee059ccb9aff87358254344915239fa7f19cee2787155a7db14ac"},
{file = "types_python_jose-3.3.4.8-py3-none-any.whl", hash = "sha256:95592273443b45dc5cc88f7c56aa5a97725428753fb738b794e63ccb4904954e"},
]
[package.dependencies]
types-pyasn1 = "*"
[[package]] [[package]]
name = "types-pytz" name = "types-pytz"
version = "2023.3.0.1" version = "2023.3.0.1"
@ -7693,4 +7747,4 @@ local = ["ctransformers", "llama-cpp-python", "sentence-transformers"]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = ">=3.9,<3.11" python-versions = ">=3.9,<3.11"
content-hash = "0f7d3f932eb130e7f3b6d234fc36e403ef409d63cfc825ba9f4c470203425ad2" content-hash = "8ad605e7ea30f2819dbc03eac6c2e67576a98d1efa4890912414a7568fc27441"

View file

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "langflow" name = "langflow"
version = "0.4.7" version = "0.4.11"
description = "A Python package with a built-in web application" description = "A Python package with a built-in web application"
authors = ["Logspace <contact@logspace.ai>"] authors = ["Logspace <contact@logspace.ai>"]
maintainers = [ maintainers = [
@ -97,6 +97,8 @@ pandas-stubs = "^2.0.0.230412"
types-pillow = "^9.5.0.2" types-pillow = "^9.5.0.2"
types-appdirs = "^1.4.3.5" types-appdirs = "^1.4.3.5"
types-pyyaml = "^6.0.12.8" types-pyyaml = "^6.0.12.8"
types-python-jose = "^3.3.4.8"
types-passlib = "^1.7.7.13"
[tool.poetry.extras] [tool.poetry.extras]

View file

@ -1,6 +1,7 @@
import sys import sys
import time import time
import httpx import httpx
from langflow.services.manager import initialize_settings_manager
from langflow.services.utils import get_settings_manager from langflow.services.utils import get_settings_manager
from langflow.utils.util import get_number_of_workers from langflow.utils.util import get_number_of_workers
from multiprocess import Process # type: ignore from multiprocess import Process # type: ignore
@ -30,6 +31,7 @@ def update_settings(
"""Update the settings from a config file.""" """Update the settings from a config file."""
# Check for database_url in the environment variables # Check for database_url in the environment variables
initialize_settings_manager()
settings_manager = get_settings_manager() settings_manager = get_settings_manager()
if config: if config:
logger.debug(f"Loading settings from {config}") logger.debug(f"Loading settings from {config}")

View file

@ -1,5 +1,5 @@
from http import HTTPStatus from http import HTTPStatus
from typing import Annotated, Optional from typing import Annotated, Optional, Union
from langflow.services.cache.utils import save_uploaded_file from langflow.services.cache.utils import save_uploaded_file
from langflow.services.database.models.flow import Flow from langflow.services.database.models.flow import Flow
@ -44,15 +44,21 @@ def get_all():
logger.info( logger.info(
f"Building custom components from {settings_manager.settings.COMPONENTS_PATH}" f"Building custom components from {settings_manager.settings.COMPONENTS_PATH}"
) )
custom_component_dicts = [
build_langchain_custom_component_list_from_path(str(path)) custom_component_dicts = []
for path in settings_manager.settings.COMPONENTS_PATH processed_paths = []
] for path in settings_manager.settings.COMPONENTS_PATH:
if str(path) in processed_paths:
continue
custom_component_dict = build_langchain_custom_component_list_from_path(
str(path)
)
custom_component_dicts.append(custom_component_dict)
processed_paths.append(str(path))
logger.info(f"Loading {len(custom_component_dicts)} category(ies)") logger.info(f"Loading {len(custom_component_dicts)} category(ies)")
for custom_component_dict in custom_component_dicts: for custom_component_dict in custom_component_dicts:
# custom_component_dict is a dict of dicts # custom_component_dict is a dict of dicts
if not custom_component_dict:
continue
category = list(custom_component_dict.keys())[0] category = list(custom_component_dict.keys())[0]
logger.info( logger.info(
f"Loading {len(custom_component_dict[category])} component(s) from category {category}" f"Loading {len(custom_component_dict[category])} component(s) from category {category}"
@ -75,6 +81,7 @@ async def process_flow(
inputs: Optional[dict] = None, inputs: Optional[dict] = None,
tweaks: Optional[dict] = None, tweaks: Optional[dict] = None,
clear_cache: Annotated[bool, Body(embed=True)] = False, # noqa: F821 clear_cache: Annotated[bool, Body(embed=True)] = False, # noqa: F821
session_id: Annotated[Union[None, str], Body(embed=True)] = None, # noqa: F821
session: Session = Depends(get_session), session: Session = Depends(get_session),
): ):
""" """
@ -94,10 +101,10 @@ async def process_flow(
graph_data = process_tweaks(graph_data, tweaks) graph_data = process_tweaks(graph_data, tweaks)
except Exception as exc: except Exception as exc:
logger.error(f"Error processing tweaks: {exc}") logger.error(f"Error processing tweaks: {exc}")
response = process_graph_cached(graph_data, inputs, clear_cache) response, session_id = process_graph_cached(
return ProcessResponse( graph_data, inputs, clear_cache, session_id
result=response,
) )
return ProcessResponse(result=response, session_id=session_id)
except Exception as e: except Exception as e:
# Log stack trace # Log stack trace
logger.exception(e) logger.exception(e)

View file

@ -47,6 +47,7 @@ class ProcessResponse(BaseModel):
"""Process response schema.""" """Process response schema."""
result: dict result: dict
session_id: Optional[str] = None
class ChatMessage(BaseModel): class ChatMessage(BaseModel):

View file

@ -0,0 +1,82 @@
from langflow import CustomComponent
from typing import Optional
from langchain.prompts import SystemMessagePromptTemplate
from langchain.tools import Tool
from langchain.schema.memory import BaseMemory
from langchain.chat_models import ChatOpenAI
from langchain.agents.agent import AgentExecutor
from langchain.agents.openai_functions_agent.base import OpenAIFunctionsAgent
from langchain.memory.token_buffer import ConversationTokenBufferMemory
from langchain.prompts.chat import MessagesPlaceholder
from langchain.agents.agent_toolkits.conversational_retrieval.openai_functions import (
_get_default_system_message,
)
class ConversationalAgent(CustomComponent):
display_name: str = "OpenAI Conversational Agent"
description: str = "Conversational Agent that can use OpenAI's function calling API"
def build_config(self):
openai_function_models = [
"gpt-3.5-turbo-0613",
"gpt-3.5-turbo-16k-0613",
"gpt-4-0613",
"gpt-4-32k-0613",
]
return {
"tools": {"is_list": True, "display_name": "Tools"},
"memory": {"display_name": "Memory"},
"system_message": {"display_name": "System Message"},
"max_token_limit": {"display_name": "Max Token Limit"},
"model_name": {
"display_name": "Model Name",
"options": openai_function_models,
"value": openai_function_models[0],
},
"code": {"show": False},
}
def build(
self,
model_name: str,
openai_api_key: str,
openai_api_base: str,
tools: Tool,
memory: Optional[BaseMemory] = None,
system_message: Optional[SystemMessagePromptTemplate] = None,
max_token_limit: int = 2000,
) -> AgentExecutor:
llm = ChatOpenAI(
model=model_name,
openai_api_key=openai_api_key,
openai_api_base=openai_api_base,
)
if not memory:
memory_key = "chat_history"
memory = ConversationTokenBufferMemory(
memory_key=memory_key,
return_messages=True,
output_key="output",
llm=llm,
max_token_limit=max_token_limit,
)
else:
memory_key = memory.memory_key # type: ignore
_system_message = system_message or _get_default_system_message()
prompt = OpenAIFunctionsAgent.create_prompt(
system_message=_system_message, # type: ignore
extra_prompt_messages=[MessagesPlaceholder(variable_name=memory_key)],
)
agent = OpenAIFunctionsAgent(
llm=llm, tools=tools, prompt=prompt # type: ignore
)
return AgentExecutor(
agent=agent,
tools=tools, # type: ignore
memory=memory,
verbose=True,
return_intermediate_steps=True,
)

View file

@ -3,6 +3,10 @@ from typing import Any, Union
from langflow.interface.utils import extract_input_variables_from_prompt from langflow.interface.utils import extract_input_variables_from_prompt
class UnbuiltObject:
pass
def validate_prompt(prompt: str): def validate_prompt(prompt: str):
"""Validate prompt.""" """Validate prompt."""
if extract_input_variables_from_prompt(prompt): if extract_input_variables_from_prompt(prompt):

View file

@ -1,4 +1,5 @@
import ast import ast
from langflow.graph.utils import UnbuiltObject
from langflow.interface.initialize import loading from langflow.interface.initialize import loading
from langflow.interface.listing import lazy_load_dict from langflow.interface.listing import lazy_load_dict
from langflow.utils.constants import DIRECT_TYPES from langflow.utils.constants import DIRECT_TYPES
@ -22,7 +23,7 @@ class Vertex:
self.edges: List["Edge"] = [] self.edges: List["Edge"] = []
self.base_type: Optional[str] = base_type self.base_type: Optional[str] = base_type
self._parse_data() self._parse_data()
self._built_object = None self._built_object = UnbuiltObject()
self._built = False self._built = False
self.artifacts: Dict[str, Any] = {} self.artifacts: Dict[str, Any] = {}
@ -245,8 +246,14 @@ class Vertex:
""" """
Checks if the built object is None and raises a ValueError if so. Checks if the built object is None and raises a ValueError if so.
""" """
if self._built_object is None: if isinstance(self._built_object, UnbuiltObject):
raise ValueError(f"Node type {self.vertex_type} not found") raise ValueError(f"{self.vertex_type}: {self._built_object_repr()}")
elif self._built_object is None:
message = f"{self.vertex_type} returned None."
if self.base_type == "custom_components":
message += " Make sure your build method returns a component."
raise ValueError(message)
def build(self, force: bool = False) -> Any: def build(self, force: bool = False) -> Any:
if not self._built or force: if not self._built or force:

View file

@ -226,6 +226,11 @@ class PromptVertex(Vertex):
# so the prompt format doesn't break # so the prompt format doesn't break
artifacts.pop("handle_keys", None) artifacts.pop("handle_keys", None)
try: try:
if not hasattr(self._built_object, "template") and hasattr(
self._built_object, "prompt"
):
template = self._built_object.prompt.template
else:
template = self._built_object.template template = self._built_object.template
for key, value in artifacts.items(): for key, value in artifacts.items():
if value: if value:

View file

@ -8,10 +8,13 @@ from langchain.text_splitter import TextSplitter
from langchain.tools import Tool from langchain.tools import Tool
from langchain.vectorstores.base import VectorStore from langchain.vectorstores.base import VectorStore
from langchain.schema import BaseOutputParser from langchain.schema import BaseOutputParser
from langchain.schema.memory import BaseMemory
from langchain.memory.chat_memory import BaseChatMemory
from langchain.agents.agent import AgentExecutor
LANGCHAIN_BASE_TYPES = { LANGCHAIN_BASE_TYPES = {
"Chain": Chain, "Chain": Chain,
"AgentExecutor": AgentExecutor,
"Tool": Tool, "Tool": Tool,
"BaseLLM": BaseLLM, "BaseLLM": BaseLLM,
"PromptTemplate": PromptTemplate, "PromptTemplate": PromptTemplate,
@ -22,6 +25,8 @@ LANGCHAIN_BASE_TYPES = {
"Embeddings": Embeddings, "Embeddings": Embeddings,
"BaseRetriever": BaseRetriever, "BaseRetriever": BaseRetriever,
"BaseOutputParser": BaseOutputParser, "BaseOutputParser": BaseOutputParser,
"BaseMemory": BaseMemory,
"BaseChatMemory": BaseChatMemory,
} }
# Langchain base types plus Python base types # Langchain base types plus Python base types

View file

@ -51,8 +51,8 @@ class CustomComponent(Component, extra=Extra.allow):
for type_hint in TYPE_HINT_LIST: for type_hint in TYPE_HINT_LIST:
if reader._is_type_hint_used_in_args( if reader._is_type_hint_used_in_args(
"Optional", code type_hint, code
) and not reader._is_type_hint_imported("Optional", code): ) and not reader._is_type_hint_imported(type_hint, code):
error_detail = { error_detail = {
"error": "Type hint Error", "error": "Type hint Error",
"traceback": f"Type hint '{type_hint}' is used but not imported in the code.", "traceback": f"Type hint '{type_hint}' is used but not imported in the code.",

View file

@ -51,7 +51,9 @@ def handle_partial_variables(prompt, format_kwargs: Dict):
} }
# Remove handle_keys otherwise LangChain raises an error # Remove handle_keys otherwise LangChain raises an error
partial_variables.pop("handle_keys", None) partial_variables.pop("handle_keys", None)
if partial_variables and hasattr(prompt, "partial"):
return prompt.partial(**partial_variables) return prompt.partial(**partial_variables)
return prompt
def handle_variable(params: Dict, input_variable: str, format_kwargs: Dict): def handle_variable(params: Dict, input_variable: str, format_kwargs: Dict):

View file

@ -1,3 +1,4 @@
from typing import Any, Dict, Tuple
from langflow.services.cache.utils import memoize_dict from langflow.services.cache.utils import memoize_dict
from langflow.graph import Graph from langflow.graph import Graph
from langflow.utils.logger import logger from langflow.utils.logger import logger
@ -15,7 +16,7 @@ def build_langchain_object_with_caching(data_graph):
@memoize_dict(maxsize=10) @memoize_dict(maxsize=10)
def build_sorted_vertices_with_caching(data_graph): def build_sorted_vertices_with_caching(data_graph) -> Tuple[Any, Dict]:
""" """
Build langchain object from data_graph. Build langchain object from data_graph.
""" """

View file

@ -5,6 +5,7 @@ from langflow.api.v1.callback import (
) )
from langflow.processing.process import fix_memory_inputs, format_actions from langflow.processing.process import fix_memory_inputs, format_actions
from langflow.utils.logger import logger from langflow.utils.logger import logger
from langchain.agents.agent import AgentExecutor
async def get_result_and_steps(langchain_object, inputs: Union[dict, str], **kwargs): async def get_result_and_steps(langchain_object, inputs: Union[dict, str], **kwargs):
@ -20,6 +21,7 @@ async def get_result_and_steps(langchain_object, inputs: Union[dict, str], **kwa
# to display intermediate steps # to display intermediate steps
langchain_object.return_intermediate_steps = True langchain_object.return_intermediate_steps = True
try: try:
if not isinstance(langchain_object, AgentExecutor):
fix_memory_inputs(langchain_object) fix_memory_inputs(langchain_object)
except Exception as exc: except Exception as exc:
logger.error(f"Error fixing memory inputs: {exc}") logger.error(f"Error fixing memory inputs: {exc}")

View file

@ -85,39 +85,55 @@ def get_input_str_if_only_one_input(inputs: dict) -> Optional[str]:
return list(inputs.values())[0] if len(inputs) == 1 else None return list(inputs.values())[0] if len(inputs) == 1 else None
def process_graph_cached( def get_build_result(data_graph, session_id):
data_graph: Dict[str, Any], inputs: Optional[dict] = None, clear_cache=False # If session_id is provided, load the langchain_object from the session
): # using build_sorted_vertices_with_caching.get_result_by_session_id
""" # if it returns something different than None, return it
Process graph by extracting input variables and replacing ZeroShotPrompt # otherwise, build the graph and return the result
with PromptTemplate,then run the graph and return the result and thought. if session_id:
""" logger.debug(f"Loading LangChain object from session {session_id}")
# Load langchain object result = build_sorted_vertices_with_caching.get_result_by_session_id(session_id)
if result is not None:
logger.debug("Loaded LangChain object")
return result
logger.debug("Building langchain object")
return build_sorted_vertices_with_caching(data_graph)
def clear_caches_if_needed(clear_cache: bool):
if clear_cache: if clear_cache:
build_sorted_vertices_with_caching.clear_cache() build_sorted_vertices_with_caching.clear_cache()
logger.debug("Cleared cache") logger.debug("Cleared cache")
langchain_object, artifacts = build_sorted_vertices_with_caching(data_graph)
logger.debug("Loaded LangChain object")
if inputs is None:
inputs = {}
# Add artifacts to inputs
# artifacts can be documents loaded when building def load_langchain_object(
# the flow data_graph: Dict[str, Any], session_id: str
for ( ) -> Tuple[Union[Chain, VectorStore], Dict[str, Any], str]:
key, langchain_object, artifacts = get_build_result(data_graph, session_id)
value, session_id = build_sorted_vertices_with_caching.hash
) in artifacts.items(): logger.debug("Loaded LangChain object")
if key not in inputs or not inputs[key]:
inputs[key] = value
if langchain_object is None: if langchain_object is None:
# Raise user facing error
raise ValueError( raise ValueError(
"There was an error loading the langchain_object. Please, check all the nodes and try again." "There was an error loading the langchain_object. Please, check all the nodes and try again."
) )
# Generate result and thought return langchain_object, artifacts, session_id
def process_inputs(inputs: Optional[dict], artifacts: Dict[str, Any]) -> dict:
if inputs is None:
inputs = {}
for key, value in artifacts.items():
if key not in inputs or not inputs[key]:
inputs[key] = value
return inputs
def generate_result(langchain_object: Union[Chain, VectorStore], inputs: dict):
if isinstance(langchain_object, Chain): if isinstance(langchain_object, Chain):
if inputs is None: if inputs is None:
raise ValueError("Inputs must be provided for a Chain") raise ValueError("Inputs must be provided for a Chain")
@ -130,9 +146,28 @@ def process_graph_cached(
raise ValueError( raise ValueError(
f"Unknown langchain_object type: {type(langchain_object).__name__}" f"Unknown langchain_object type: {type(langchain_object).__name__}"
) )
return result return result
def process_graph_cached(
data_graph: Dict[str, Any],
inputs: Optional[dict] = None,
clear_cache=False,
session_id=None,
) -> Tuple[Any, str]:
clear_caches_if_needed(clear_cache)
# If session_id is provided, load the langchain_object from the session
# else build the graph and return the result and the new session_id
langchain_object, artifacts, session_id = load_langchain_object(
data_graph, session_id
)
processed_inputs = process_inputs(inputs, artifacts)
result = generate_result(langchain_object, processed_inputs)
return result, session_id
def load_flow_from_json( def load_flow_from_json(
flow: Union[Path, str, dict], tweaks: Optional[dict] = None, build=True flow: Union[Path, str, dict], tweaks: Optional[dict] = None, build=True
): ):

View file

@ -1,9 +1,6 @@
from fastapi import APIRouter from fastapi import APIRouter
router = APIRouter(tags=["APIKey"]) router = APIRouter(tags=["APIKey"])
@ -15,18 +12,21 @@ def get_api_key(user_id: str):
"api_keys": [ "api_keys": [
{ {
"id": "4425707e-cce4-4d1b-a54e-bd2632064657", "id": "4425707e-cce4-4d1b-a54e-bd2632064657",
"api_key": "lf-...abcd",
"name": "my api_key name - 01", "name": "my api_key name - 01",
"created_at": "2023-08-15T19:28:40.019613", "created_at": "2023-08-15T19:28:40.019613",
"last_used_at": "2023-08-16T18:38:20.875210", "last_used_at": "2023-08-16T18:38:20.875210",
}, },
{ {
"id": "6fb7282b-9f2e-4efe-9bda-0c3d8f899473", "id": "6fb7282b-9f2e-4efe-9bda-0c3d8f899473",
"api_key": "lf-...abcd",
"name": "my api_key name - 02", "name": "my api_key name - 02",
"created_at": "2023-08-15T19:41:30.077942", "created_at": "2023-08-15T19:41:30.077942",
"last_used_at": "2023-08-15T19:45:32.067899", "last_used_at": "2023-08-15T19:45:32.067899",
}, },
{ {
"id": "c55f3b32-4920-42b6-a5cd-698b4251806e", "id": "c55f3b32-4920-42b6-a5cd-698b4251806e",
"api_key": "lf-...abcd",
"name": "my api_key name - 03", "name": "my api_key name - 03",
"created_at": "2023-08-15T20:29:40.577808", "created_at": "2023-08-15T20:29:40.577808",
"last_used_at": "2023-08-15T20:29:40.577816", "last_used_at": "2023-08-15T20:29:40.577816",

View file

@ -30,6 +30,7 @@ def create_cache_folder(func):
def memoize_dict(maxsize=128): def memoize_dict(maxsize=128):
cache = OrderedDict() cache = OrderedDict()
hash_to_key = {} # Mapping from hash to cache key
def decorator(func): def decorator(func):
@functools.wraps(func) @functools.wraps(func)
@ -39,16 +40,29 @@ def memoize_dict(maxsize=128):
if key not in cache: if key not in cache:
result = func(*args, **kwargs) result = func(*args, **kwargs)
cache[key] = result cache[key] = result
hash_to_key[hashed] = key # Store the mapping
if len(cache) > maxsize: if len(cache) > maxsize:
cache.popitem(last=False) oldest_key = next(iter(cache))
oldest_hash = oldest_key[1]
del cache[oldest_key]
del hash_to_key[oldest_hash]
else: else:
result = cache[key] result = cache[key]
wrapper.session_id = hashed # Store hash in the wrapper
return result return result
def clear_cache(): def clear_cache():
cache.clear() cache.clear()
hash_to_key.clear()
def get_result_by_session_id(session_id):
key = hash_to_key.get(session_id)
return cache.get(key) if key is not None else None
wrapper.clear_cache = clear_cache # type: ignore wrapper.clear_cache = clear_cache # type: ignore
wrapper.get_result_by_session_id = get_result_by_session_id # type: ignore
wrapper.hash = None
wrapper.cache = cache # type: ignore wrapper.cache = cache # type: ignore
return wrapper return wrapper

View file

@ -18,6 +18,7 @@ class ServiceManager:
""" """
Registers a new factory. Registers a new factory.
""" """
if service_factory.service_class.name not in self.factories:
self.factories[service_factory.service_class.name] = service_factory self.factories[service_factory.service_class.name] = service_factory
def get(self, service_name: ServiceType): def get(self, service_name: ServiceType):
@ -85,3 +86,12 @@ def initialize_services():
service_manager.register_factory(database_factory.DatabaseManagerFactory()) service_manager.register_factory(database_factory.DatabaseManagerFactory())
service_manager.register_factory(cache_factory.CacheManagerFactory()) service_manager.register_factory(cache_factory.CacheManagerFactory())
service_manager.register_factory(chat_factory.ChatManagerFactory()) service_manager.register_factory(chat_factory.ChatManagerFactory())
def initialize_settings_manager():
"""
Initialize the settings manager.
"""
from langflow.services.settings import factory as settings_factory
service_manager.register_factory(settings_factory.SettingsManagerFactory())

View file

@ -1,6 +1,7 @@
import contextlib import contextlib
import json import json
import os import os
from shutil import copy2
import secrets import secrets
from typing import Optional, List from typing import Optional, List
from pathlib import Path from pathlib import Path
@ -9,7 +10,8 @@ import yaml
from pydantic import BaseSettings, root_validator, validator from pydantic import BaseSettings, root_validator, validator
from langflow.utils.logger import logger from langflow.utils.logger import logger
BASE_COMPONENTS_PATH = str(Path(__file__).parent / "components") # BASE_COMPONENTS_PATH = str(Path(__file__).parent / "components")
BASE_COMPONENTS_PATH = str(Path(__file__).parent.parent.parent / "components")
class Settings(BaseSettings): class Settings(BaseSettings):
@ -30,6 +32,9 @@ class Settings(BaseSettings):
OUTPUT_PARSERS: dict = {} OUTPUT_PARSERS: dict = {}
CUSTOM_COMPONENTS: dict = {} CUSTOM_COMPONENTS: dict = {}
# Define the default LANGFLOW_DIR
CONFIG_DIR: Optional[str] = None
DEV: bool = False DEV: bool = False
DATABASE_URL: Optional[str] = None DATABASE_URL: Optional[str] = None
CACHE: str = "InMemoryCache" CACHE: str = "InMemoryCache"
@ -54,8 +59,31 @@ class Settings(BaseSettings):
FIRST_SUPERUSER: str = "langflow" FIRST_SUPERUSER: str = "langflow"
FIRST_SUPERUSER_PASSWORD: str = "langflow" FIRST_SUPERUSER_PASSWORD: str = "langflow"
@validator("CONFIG_DIR", pre=True, allow_reuse=True)
def set_langflow_dir(cls, value):
if not value:
import appdirs
# Define the app name and author
app_name = "langflow"
app_author = "logspace"
# Get the cache directory for the application
cache_dir = appdirs.user_cache_dir(app_name, app_author)
# Create a .langflow directory inside the cache directory
value = Path(cache_dir)
value.mkdir(parents=True, exist_ok=True)
if isinstance(value, str):
value = Path(value)
if not value.exists():
value.mkdir(parents=True, exist_ok=True)
return str(value)
@validator("DATABASE_URL", pre=True) @validator("DATABASE_URL", pre=True)
def set_database_url(cls, value): def set_database_url(cls, value, values):
if not value: if not value:
logger.debug( logger.debug(
"No database_url provided, trying LANGFLOW_DATABASE_URL env variable" "No database_url provided, trying LANGFLOW_DATABASE_URL env variable"
@ -65,7 +93,28 @@ class Settings(BaseSettings):
logger.debug("Using LANGFLOW_DATABASE_URL env variable.") logger.debug("Using LANGFLOW_DATABASE_URL env variable.")
else: else:
logger.debug("No DATABASE_URL env variable, using sqlite database") logger.debug("No DATABASE_URL env variable, using sqlite database")
value = "sqlite:///./langflow.db" # Originally, we used sqlite:///./langflow.db
# so we need to migrate to the new format
# if there is a database in that location
if not values["CONFIG_DIR"]:
raise ValueError(
"CONFIG_DIR not set, please set it or provide a DATABASE_URL"
)
new_path = f"{values['CONFIG_DIR']}/langflow.db"
if Path("./langflow.db").exists():
if Path(new_path).exists():
logger.debug(f"Database already exists at {new_path}, using it")
else:
try:
logger.debug("Copying existing database to new location")
copy2("./langflow.db", new_path)
logger.debug(f"Copied existing database to {new_path}")
except Exception:
logger.error("Failed to copy database, using default path")
new_path = "./langflow.db"
value = f"sqlite:///{new_path}"
return value return value
@ -148,10 +197,15 @@ class Settings(BaseSettings):
value = json.loads(str(value)) value = json.loads(str(value))
if isinstance(value, list): if isinstance(value, list):
for item in value: for item in value:
if isinstance(item, Path):
item = str(item)
if item not in getattr(self, key): if item not in getattr(self, key):
getattr(self, key).append(item) getattr(self, key).append(item)
logger.debug(f"Extended {key}") logger.debug(f"Extended {key}")
else: else:
if isinstance(value, Path):
value = str(value)
if value not in getattr(self, key):
getattr(self, key).append(value) getattr(self, key).append(value)
logger.debug(f"Appended {key}") logger.debug(f"Appended {key}")

File diff suppressed because it is too large Load diff

View file

@ -72,7 +72,8 @@
"start": "vite", "start": "vite",
"build": "vite build", "build": "vite build",
"serve": "vite preview", "serve": "vite preview",
"format": "npx prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"" "format": "npx prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"",
"type-check": "tsc --noEmit --pretty --project tsconfig.json && vite"
}, },
"eslintConfig": { "eslintConfig": {
"extends": [ "extends": [

View file

@ -9,7 +9,7 @@ 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 Header from "./components/headerComponent"; import LoadingComponent from "./components/loadingComponent";
import { alertContext } from "./contexts/alertContext"; import { alertContext } from "./contexts/alertContext";
import { AuthContext } from "./contexts/authContext"; import { AuthContext } from "./contexts/authContext";
import { locationContext } from "./contexts/locationContext"; import { locationContext } from "./contexts/locationContext";
@ -27,6 +27,7 @@ export default function App() {
setIsStackedOpen(true); setIsStackedOpen(true);
}, [location.pathname, setCurrent, setIsStackedOpen, setShowSideBar]); }, [location.pathname, setCurrent, setIsStackedOpen, setShowSideBar]);
const { hardReset } = useContext(TabsContext); const { hardReset } = useContext(TabsContext);
const { const {
errorData, errorData,
errorOpen, errorOpen,
@ -38,6 +39,7 @@ export default function App() {
successOpen, successOpen,
setSuccessOpen, setSuccessOpen,
setErrorData, setErrorData,
loading,
} = useContext(alertContext); } = useContext(alertContext);
const navigate = useNavigate(); const navigate = useNavigate();
@ -144,7 +146,7 @@ export default function App() {
} }
}).catch((error) => { }).catch((error) => {
setAutoLogin(false); setAutoLogin(false);
if (getAuthentication && !isLoginPage) { if (getAuthentication() && !isLoginPage) {
getLoggedUser() getLoggedUser()
.then((user) => { .then((user) => {
setUserData(user); setUserData(user);
@ -167,8 +169,15 @@ export default function App() {
}} }}
FallbackComponent={CrashErrorComponent} FallbackComponent={CrashErrorComponent}
> >
{!isLoginPage && !isSignUpPage && <Header />} {loading ? (
<div className="loading-page-panel">
<LoadingComponent remSize={50} />
</div>
) : (
<>
<Router /> <Router />
</>
)}
</ErrorBoundary> </ErrorBoundary>
<div></div> <div></div>
<div className="app-div" style={{ zIndex: 999 }}> <div className="app-div" style={{ zIndex: 999 }}>

View file

@ -111,6 +111,7 @@ export default function ParameterComponent({
let groupedObj = groupByFamily(myData, tooltipTitle!, left, flow!); let groupedObj = groupByFamily(myData, tooltipTitle!, left, flow!);
if (groupedObj && groupedObj.length > 0) { if (groupedObj && groupedObj.length > 0) {
//@ts-ignore
//@ts-ignore //@ts-ignore
refHtml.current = groupedObj.map((item, index) => { refHtml.current = groupedObj.map((item, index) => {
const Icon: any = const Icon: any =

View file

@ -8,6 +8,7 @@ import { useSSE } from "../../contexts/SSEContext";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { typesContext } from "../../contexts/typesContext"; import { typesContext } from "../../contexts/typesContext";
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent"; import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
import { validationStatusType } from "../../types/components";
import { NodeDataType } from "../../types/flow"; import { NodeDataType } from "../../types/flow";
import { cleanEdges } from "../../utils/reactflowUtils"; import { cleanEdges } from "../../utils/reactflowUtils";
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils"; import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
@ -20,13 +21,14 @@ export default function GenericNode({
}: { }: {
data: NodeDataType; data: NodeDataType;
selected: boolean; selected: boolean;
}) { }): JSX.Element {
const [data, setData] = useState(olddata); const [data, setData] = useState(olddata);
const { updateFlow, flows, tabId } = useContext(TabsContext); const { updateFlow, flows, tabId } = useContext(TabsContext);
const updateNodeInternals = useUpdateNodeInternals(); const updateNodeInternals = useUpdateNodeInternals();
const { types, deleteNode, reactFlowInstance } = useContext(typesContext); const { types, deleteNode, reactFlowInstance } = useContext(typesContext);
const name = nodeIconsLucide[data.type] ? data.type : types[data.type]; const name = nodeIconsLucide[data.type] ? data.type : types[data.type];
const [validationStatus, setValidationStatus] = useState(null); const [validationStatus, setValidationStatus] =
useState<validationStatusType | null>(null);
// State for outline color // State for outline color
const { sseData, isBuilding } = useSSE(); const { sseData, isBuilding } = useSSE();
useEffect(() => { useEffect(() => {
@ -41,7 +43,7 @@ export default function GenericNode({
nodes: flow.data.nodes, nodes: flow.data.nodes,
}, },
updateEdge: (edge) => { updateEdge: (edge) => {
flow.data.edges = edge; flow.data!.edges = edge;
reactFlowInstance.setEdges(edge); reactFlowInstance.setEdges(edge);
updateNodeInternals(data.id); updateNodeInternals(data.id);
}, },
@ -77,7 +79,7 @@ export default function GenericNode({
"generic-node-div" "generic-node-div"
)} )}
> >
{data.node.beta && ( {data.node?.beta && (
<div className="beta-badge-wrapper"> <div className="beta-badge-wrapper">
<div className="beta-badge-content">BETA</div> <div className="beta-badge-content">BETA</div>
</div> </div>
@ -90,9 +92,9 @@ export default function GenericNode({
iconColor={`${nodeColors[types[data.type]]}`} iconColor={`${nodeColors[types[data.type]]}`}
/> />
<div className="generic-node-tooltip-div"> <div className="generic-node-tooltip-div">
<ShadTooltip content={data.node.display_name}> <ShadTooltip content={data.node?.display_name}>
<div className="generic-node-tooltip-div text-primary"> <div className="generic-node-tooltip-div text-primary">
{data.node.display_name} {data.node?.display_name}
</div> </div>
</ShadTooltip> </ShadTooltip>
</div> </div>
@ -117,7 +119,9 @@ export default function GenericNode({
{typeof validationStatus.params === "string" {typeof validationStatus.params === "string"
? validationStatus.params ? validationStatus.params
.split("\n") .split("\n")
.map((line, index) => <div key={index}>{line}</div>) .map((line: string, index: number) => (
<div key={index}>{line}</div>
))
: ""} : ""}
</div> </div>
) )
@ -155,20 +159,20 @@ export default function GenericNode({
</div> </div>
<div className="generic-node-desc"> <div className="generic-node-desc">
<div className="generic-node-desc-text">{data.node.description}</div> <div className="generic-node-desc-text">{data.node?.description}</div>
<> <>
{Object.keys(data.node.template) {Object.keys(data.node!.template)
.filter((templateField) => templateField.charAt(0) !== "_") .filter((templateField) => templateField.charAt(0) !== "_")
.map((templateField: string, idx) => ( .map((templateField: string, idx) => (
<div key={idx}> <div key={idx}>
{data.node.template[templateField].show && {data.node!.template[templateField].show &&
!data.node.template[templateField].advanced ? ( !data.node!.template[templateField].advanced ? (
<ParameterComponent <ParameterComponent
key={ key={
(data.node.template[templateField].input_types?.join( (data.node!.template[templateField].input_types?.join(
";" ";"
) ?? data.node.template[templateField].type) + ) ?? data.node!.template[templateField].type) +
"|" + "|" +
templateField + templateField +
"|" + "|" +
@ -178,39 +182,39 @@ export default function GenericNode({
setData={setData} setData={setData}
color={ color={
nodeColors[ nodeColors[
types[data.node.template[templateField].type] types[data.node?.template[templateField].type!]
] ?? ] ??
nodeColors[data.node.template[templateField].type] ?? nodeColors[data.node?.template[templateField].type!] ??
nodeColors.unknown nodeColors.unknown
} }
title={ title={
data.node.template[templateField].display_name data.node?.template[templateField].display_name
? data.node.template[templateField].display_name ? data.node.template[templateField].display_name
: data.node.template[templateField].name : data.node?.template[templateField].name
? toTitleCase(data.node.template[templateField].name) ? toTitleCase(data.node.template[templateField].name)
: toTitleCase(templateField) : toTitleCase(templateField)
} }
info={data.node.template[templateField].info} info={data.node?.template[templateField].info}
name={templateField} name={templateField}
tooltipTitle={ tooltipTitle={
data.node.template[templateField].input_types?.join( data.node?.template[templateField].input_types?.join(
"\n" "\n"
) ?? data.node.template[templateField].type ) ?? data.node?.template[templateField].type
} }
required={data.node.template[templateField].required} required={data.node?.template[templateField].required}
id={ id={
(data.node.template[templateField].input_types?.join( (data.node?.template[templateField].input_types?.join(
";" ";"
) ?? data.node.template[templateField].type) + ) ?? data.node?.template[templateField].type) +
"|" + "|" +
templateField + templateField +
"|" + "|" +
data.id data.id
} }
left={true} left={true}
type={data.node.template[templateField].type} type={data.node?.template[templateField].type}
optionalHandle={ optionalHandle={
data.node.template[templateField].input_types data.node?.template[templateField].input_types
} }
/> />
) : ( ) : (
@ -220,25 +224,25 @@ export default function GenericNode({
))} ))}
<div <div
className={classNames( className={classNames(
Object.keys(data.node.template).length < 1 ? "hidden" : "", Object.keys(data.node!.template).length < 1 ? "hidden" : "",
"flex-max-width justify-center" "flex-max-width justify-center"
)} )}
> >
{" "} {" "}
</div> </div>
<ParameterComponent <ParameterComponent
key={[data.type, data.id, ...data.node.base_classes].join("|")} key={[data.type, data.id, ...data.node!.base_classes].join("|")}
data={data} data={data}
setData={setData} setData={setData}
color={nodeColors[types[data.type]] ?? nodeColors.unknown} color={nodeColors[types[data.type]] ?? nodeColors.unknown}
title={ title={
data.node.output_types && data.node.output_types.length > 0 data.node?.output_types && data.node.output_types.length > 0
? data.node.output_types.join("|") ? data.node.output_types.join("|")
: data.type : data.type
} }
tooltipTitle={data.node.base_classes.join("\n")} tooltipTitle={data.node?.base_classes.join("\n")}
id={[data.type, data.id, ...data.node.base_classes].join("|")} id={[data.type, data.id, ...data.node!.base_classes].join("|")}
type={data.node.base_classes.join("|")} type={data.node?.base_classes.join("|")}
left={false} left={false}
/> />
</> </>

View file

@ -7,7 +7,7 @@ import { SingleAlertComponentType } from "../../../../types/alerts";
export default function SingleAlert({ export default function SingleAlert({
dropItem, dropItem,
removeAlert, removeAlert,
}: SingleAlertComponentType) { }: SingleAlertComponentType): JSX.Element {
const [show, setShow] = useState(true); const [show, setShow] = useState(true);
const type = dropItem.type; const type = dropItem.type;

View file

@ -9,7 +9,9 @@ import { alertContext } from "../../contexts/alertContext";
import { AlertDropdownType } from "../../types/alerts"; import { AlertDropdownType } from "../../types/alerts";
import SingleAlert from "./components/singleAlertComponent"; import SingleAlert from "./components/singleAlertComponent";
export default function AlertDropdown({ children }: AlertDropdownType) { export default function AlertDropdown({
children,
}: AlertDropdownType): JSX.Element {
const { const {
notificationList, notificationList,
clearNotificationList, clearNotificationList,

View file

@ -8,7 +8,7 @@ export default function ErrorAlert({
list = [], list = [],
id, id,
removeAlert, removeAlert,
}: ErrorAlertType) { }: ErrorAlertType): JSX.Element {
const [show, setShow] = useState(true); const [show, setShow] = useState(true);
useEffect(() => { useEffect(() => {
if (show) { if (show) {

View file

@ -2,7 +2,7 @@ import { useEffect } from "react";
export function useOnClickOutside(ref, handler) { export function useOnClickOutside(ref, handler) {
useEffect(() => { useEffect(() => {
const listener = (event) => { const listener = (event: Event) => {
// Do nothing if clicking ref's element or its children // Do nothing if clicking ref's element or its children
if (!ref.current || ref.current.contains(event.target)) { if (!ref.current || ref.current.contains(event.target)) {
return; return;

View file

@ -9,7 +9,7 @@ export default function NoticeAlert({
link = "", link = "",
id, id,
removeAlert, removeAlert,
}: NoticeAlertType) { }: NoticeAlertType): JSX.Element {
const [show, setShow] = useState(true); const [show, setShow] = useState(true);
useEffect(() => { useEffect(() => {
if (show) { if (show) {

View file

@ -7,7 +7,7 @@ export default function SuccessAlert({
title, title,
id, id,
removeAlert, removeAlert,
}: SuccessAlertType) { }: SuccessAlertType): JSX.Element {
const [show, setShow] = useState(true); const [show, setShow] = useState(true);
useEffect(() => { useEffect(() => {
if (show) { if (show) {

View file

@ -12,12 +12,12 @@ export default function AccordionComponent({
children, children,
open = [], open = [],
keyValue, keyValue,
}: AccordionComponentType) { }: AccordionComponentType): JSX.Element {
const [value, setValue] = useState( const [value, setValue] = useState(
open.length === 0 ? "" : getOpenAccordion() open.length === 0 ? "" : getOpenAccordion()
); );
function getOpenAccordion() { function getOpenAccordion(): string {
let value = ""; let value = "";
open.forEach((el) => { open.forEach((el) => {
if (el == trigger) { if (el == trigger) {
@ -28,10 +28,17 @@ export default function AccordionComponent({
return value; return value;
} }
function handleClick() { function handleClick(): void {
value === "" ? setValue(keyValue) : setValue(""); value === "" ? setValue(keyValue!) : setValue("");
} }
const handleKeyDown = (event) => {
if (event.key === "Backspace") {
event.preventDefault();
event.stopPropagation();
}
};
return ( return (
<> <>
<Accordion <Accordion
@ -39,8 +46,9 @@ export default function AccordionComponent({
className="w-full" className="w-full"
value={value} value={value}
onValueChange={setValue} onValueChange={setValue}
onKeyDown={handleKeyDown}
> >
<AccordionItem value={keyValue} className="border-b"> <AccordionItem value={keyValue!} className="border-b">
<AccordionTrigger <AccordionTrigger
onClick={() => { onClick={() => {
handleClick(); handleClick();

View file

@ -1,4 +1,9 @@
export default function CrashErrorComponent({ error, resetErrorBoundary }) { import { crashComponentPropsType } from "../../types/components";
export default function CrashErrorComponent({
error,
resetErrorBoundary,
}: crashComponentPropsType): JSX.Element {
return ( return (
<div className="fixed left-0 top-0 z-50 flex h-full w-full items-center justify-center bg-foreground bg-opacity-50"> <div className="fixed left-0 top-0 z-50 flex h-full w-full items-center justify-center bg-foreground bg-opacity-50">
<div className="flex h-1/3 min-h-fit max-w-4xl flex-col justify-evenly rounded-lg bg-background p-8 text-start shadow-lg"> <div className="flex h-1/3 min-h-fit max-w-4xl flex-col justify-evenly rounded-lg bg-background p-8 text-start shadow-lg">

View file

@ -3,19 +3,8 @@ import { Input } from "../../components/ui/input";
import { Label } from "../../components/ui/label"; import { Label } from "../../components/ui/label";
import { Textarea } from "../../components/ui/textarea"; import { Textarea } from "../../components/ui/textarea";
import { readFlowsFromDatabase } from "../../controllers/API"; import { readFlowsFromDatabase } from "../../controllers/API";
import { InputProps } from "../../types/components";
type InputProps = { import { FlowType } from "../../types/flow";
name: string | null;
description: string | null;
maxLength?: number;
flows: Array<{ id: string; name: string; description: string }>;
tabId: string;
invalidName: boolean;
setInvalidName: (invalidName: boolean) => void;
setName: (name: string) => void;
setDescription: (description: string) => void;
updateFlow: (flow: { id: string; name: string }) => void;
};
export const EditFlowSettings: React.FC<InputProps> = ({ export const EditFlowSettings: React.FC<InputProps> = ({
name, name,
@ -27,13 +16,12 @@ export const EditFlowSettings: React.FC<InputProps> = ({
tabId, tabId,
setName, setName,
setDescription, setDescription,
updateFlow, }: InputProps): JSX.Element => {
}) => {
const [isMaxLength, setIsMaxLength] = useState(false); const [isMaxLength, setIsMaxLength] = useState(false);
const nameLists = useRef([]); const nameLists = useRef<string[]>([]);
useEffect(() => { useEffect(() => {
readFlowsFromDatabase().then((flows) => { readFlowsFromDatabase().then((flows) => {
flows.forEach((flow) => { flows.forEach((flow: FlowType) => {
nameLists.current.push(flow.name); nameLists.current.push(flow.name);
}); });
}); });
@ -46,21 +34,34 @@ export const EditFlowSettings: React.FC<InputProps> = ({
} else { } else {
setIsMaxLength(false); setIsMaxLength(false);
} }
if (invalidName !== undefined) {
if (!nameLists.current.includes(value)) { if (!nameLists.current.includes(value)) {
setInvalidName(false); setInvalidName(false);
} else { } else {
setInvalidName(true); setInvalidName(true);
} }
}
if (!nameLists.current.includes(value)) {
setInvalidName!(false);
} else {
setInvalidName!(true);
}
setName(value); setName(value);
setCurrentName(value);
}; };
const [desc, setDesc] = useState( const [currentName, setCurrentName] = useState(name);
flows.find((flow) => flow.id === tabId).description
); const [currentDescription, setCurrentDescription] = useState(description);
useEffect(() => {
setCurrentName(name);
setCurrentDescription(description);
}, [name, description]);
const handleDescriptionChange = (event: ChangeEvent<HTMLTextAreaElement>) => { const handleDescriptionChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
flows.find((flow) => flow.id === tabId).description = event.target.value; flows.find((f) => f.id === tabId).description = event.target.value;
setDesc(flows.find((flow) => flow.id === tabId).description); setCurrentDescription(flows.find((f) => f.id === tabId).description);
setDescription(event.target.value); setDescription(event.target.value);
}; };
@ -81,7 +82,7 @@ export const EditFlowSettings: React.FC<InputProps> = ({
onChange={handleNameChange} onChange={handleNameChange}
type="text" type="text"
name="name" name="name"
value={name ?? ""} value={currentName ?? ""}
placeholder="File name" placeholder="File name"
id="name" id="name"
maxLength={maxLength} maxLength={maxLength}
@ -96,7 +97,7 @@ export const EditFlowSettings: React.FC<InputProps> = ({
name="description" name="description"
id="description" id="description"
onChange={handleDescriptionChange} onChange={handleDescriptionChange}
value={desc} value={currentDescription}
placeholder="Flow description" placeholder="Flow description"
className="mt-2 max-h-[100px] font-normal" className="mt-2 max-h-[100px] font-normal"
rows={3} rows={3}

View file

@ -3,16 +3,16 @@ import { RadialProgressType } from "../../types/components";
export default function RadialProgressComponent({ export default function RadialProgressComponent({
value, value,
color, color,
}: RadialProgressType) { }: RadialProgressType): JSX.Element {
const style = { const style = {
"--value": value * 100, "--value": value! * 100,
"--size": "1.5rem", "--size": "1.5rem",
"--thickness": "2px", "--thickness": "2px",
} as React.CSSProperties; } as React.CSSProperties;
return ( return (
<div className={"radial-progress " + color} style={style}> <div className={"radial-progress " + color} style={style}>
<strong className="text-[8px]">{Math.trunc(value * 100)}%</strong> <strong className="text-[8px]">{Math.trunc(value! * 100)}%</strong>
</div> </div>
); );
} }

View file

@ -3,20 +3,9 @@ import type { FC } from "react";
import React from "react"; import React from "react";
import { Tooltip as ReactTooltip } from "react-tooltip"; import { Tooltip as ReactTooltip } from "react-tooltip";
import "react-tooltip/dist/react-tooltip.css"; import "react-tooltip/dist/react-tooltip.css";
import { TooltipProps } from "../../types/components";
import { classNames } from "../../utils/utils"; import { classNames } from "../../utils/utils";
type TooltipProps = {
selector: string;
content?: string;
disabled?: boolean;
htmlContent?: React.ReactNode;
className?: string; // This should use !impornant to override the default styles eg: '!bg-white'
position?: "top" | "right" | "bottom" | "left";
clickable?: boolean;
children: React.ReactNode;
delayShow?: number;
};
const TooltipReact: FC<TooltipProps> = ({ const TooltipReact: FC<TooltipProps> = ({
selector, selector,
content, content,
@ -27,7 +16,7 @@ const TooltipReact: FC<TooltipProps> = ({
className, className,
clickable, clickable,
delayShow, delayShow,
}) => { }: TooltipProps): JSX.Element => {
return ( return (
<div className="tooltip-container"> <div className="tooltip-container">
{React.cloneElement(children as React.ReactElement, { {React.cloneElement(children as React.ReactElement, {
@ -38,7 +27,7 @@ const TooltipReact: FC<TooltipProps> = ({
content={content} content={content}
className={classNames( className={classNames(
"z-[9999] !bg-white !text-xs !font-normal !text-foreground !opacity-100 !shadow-md", "z-[9999] !bg-white !text-xs !font-normal !text-foreground !opacity-100 !shadow-md",
className className!
)} )}
place={position} place={position}
clickable={clickable} clickable={clickable}

View file

@ -1,11 +1,12 @@
import DOMPurify from "dompurify"; import DOMPurify from "dompurify";
import { SanitizedHTMLWrapperType } from "../../types/components";
const SanitizedHTMLWrapper = ({ const SanitizedHTMLWrapper = ({
className, className,
content, content,
onClick, onClick,
suppressWarning = false, suppressWarning = false,
}) => { }: SanitizedHTMLWrapperType): JSX.Element => {
const sanitizedHTML = DOMPurify.sanitize(content); const sanitizedHTML = DOMPurify.sanitize(content);
return ( return (

View file

@ -8,7 +8,7 @@ export default function ShadTooltip({
children, children,
styleClasses, styleClasses,
delayDuration = 500, delayDuration = 500,
}: ShadToolTipType) { }: ShadToolTipType): JSX.Element {
return ( return (
<Tooltip delayDuration={delayDuration}> <Tooltip delayDuration={delayDuration}>
<TooltipTrigger asChild={asChild}>{children}</TooltipTrigger> <TooltipTrigger asChild={asChild}>{children}</TooltipTrigger>

View file

@ -5,7 +5,7 @@ export default function Tooltip({
children, children,
title, title,
placement, placement,
}: TooltipComponentType) { }: TooltipComponentType): JSX.Element {
return ( return (
<LightTooltip placement={placement} title={title} arrow> <LightTooltip placement={placement} title={title} arrow>
{children} {children}

View file

@ -1,6 +1,6 @@
import { useContext } from "react"; import { useContext } from "react";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { FlowType } from "../../types/flow"; import { cardComponentPropsType } from "../../types/components";
import { gradients } from "../../utils/styleUtils"; import { gradients } from "../../utils/styleUtils";
import IconComponent from "../genericIconComponent"; import IconComponent from "../genericIconComponent";
import { import {
@ -16,12 +16,7 @@ export const CardComponent = ({
id, id,
onDelete, onDelete,
button, button,
}: { }: cardComponentPropsType): JSX.Element => {
flow: FlowType;
id: string;
onDelete?: () => void;
button?: JSX.Element;
}) => {
const { removeFlow } = useContext(TabsContext); const { removeFlow } = useContext(TabsContext);
return ( return (

View file

@ -8,6 +8,8 @@ import { postBuildInit } from "../../../controllers/API";
import { FlowType } from "../../../types/flow"; import { FlowType } from "../../../types/flow";
import { TabsContext } from "../../../contexts/tabsContext"; import { TabsContext } from "../../../contexts/tabsContext";
import { parsedDataType } from "../../../types/components";
import { TabsState } from "../../../types/tabs";
import { validateNodes } from "../../../utils/reactflowUtils"; import { validateNodes } from "../../../utils/reactflowUtils";
import RadialProgressComponent from "../../RadialProgress"; import RadialProgressComponent from "../../RadialProgress";
import IconComponent from "../../genericIconComponent"; import IconComponent from "../../genericIconComponent";
@ -21,7 +23,7 @@ export default function BuildTrigger({
flow: FlowType; flow: FlowType;
setIsBuilt: any; setIsBuilt: any;
isBuilt: boolean; isBuilt: boolean;
}) { }): JSX.Element {
const { updateSSEData, isBuilding, setIsBuilding, sseData } = useSSE(); const { updateSSEData, isBuilding, setIsBuilding, sseData } = useSSE();
const { reactFlowInstance } = useContext(typesContext); const { reactFlowInstance } = useContext(typesContext);
const { setTabsState } = useContext(TabsContext); const { setTabsState } = useContext(TabsContext);
@ -30,12 +32,12 @@ export default function BuildTrigger({
const eventClick = isBuilding ? "pointer-events-none" : ""; const eventClick = isBuilding ? "pointer-events-none" : "";
const [progress, setProgress] = useState(0); const [progress, setProgress] = useState(0);
async function handleBuild(flow: FlowType) { async function handleBuild(flow: FlowType): Promise<void> {
try { try {
if (isBuilding) { if (isBuilding) {
return; return;
} }
const errors = validateNodes(reactFlowInstance); const errors = validateNodes(reactFlowInstance!);
if (errors.length > 0) { if (errors.length > 0) {
setErrorData({ setErrorData({
title: "Oops! Looks like you missed something", title: "Oops! Looks like you missed something",
@ -69,7 +71,7 @@ export default function BuildTrigger({
const response = await postBuildInit(flow); const response = await postBuildInit(flow);
const { flowId } = response.data; const { flowId } = response.data;
// Step 2: Use the session ID to establish an SSE connection using EventSource // Step 2: Use the session ID to establish an SSE connection using EventSource
let validationResults = []; let validationResults: boolean[] = [];
let finished = false; let finished = false;
const apiUrl = `/api/v1/build/stream/${flowId}`; const apiUrl = `/api/v1/build/stream/${flowId}`;
const eventSource = new EventSource(apiUrl); const eventSource = new EventSource(apiUrl);
@ -91,7 +93,8 @@ export default function BuildTrigger({
// If the event is a log, log it // If the event is a log, log it
setSuccessData({ title: parsedData.log }); setSuccessData({ title: parsedData.log });
} else if (parsedData.input_keys !== undefined) { } else if (parsedData.input_keys !== undefined) {
setTabsState((old) => { //@ts-ignore
setTabsState((old: TabsState) => {
return { return {
...old, ...old,
[flowId]: { [flowId]: {
@ -120,13 +123,13 @@ export default function BuildTrigger({
// Step 3: Wait for the stream to finish // Step 3: Wait for the stream to finish
while (!finished) { while (!finished) {
await new Promise((resolve) => setTimeout(resolve, 100)); await new Promise((resolve) => setTimeout(resolve, 100));
finished = validationResults.length === flow.data.nodes.length; finished = validationResults.length === flow.data!.nodes.length;
} }
// Step 4: Return true if all nodes are valid, false otherwise // Step 4: Return true if all nodes are valid, false otherwise
return validationResults.every((result) => result); return validationResults.every((result) => result);
} }
function processStreamResult(parsedData) { function processStreamResult(parsedData: parsedDataType) {
// Process each chunk of data here // Process each chunk of data here
// Parse the chunk and update the context // Parse the chunk and update the context
try { try {

View file

@ -8,12 +8,18 @@ import {
FLOW_NOT_BUILT_TITLE, FLOW_NOT_BUILT_TITLE,
} from "../../../constants/constants"; } from "../../../constants/constants";
import { alertContext } from "../../../contexts/alertContext"; import { alertContext } from "../../../contexts/alertContext";
import { chatTriggerPropType } from "../../../types/components";
import IconComponent from "../../genericIconComponent"; import IconComponent from "../../genericIconComponent";
export default function ChatTrigger({ open, setOpen, isBuilt, canOpen }) { export default function ChatTrigger({
open,
setOpen,
isBuilt,
canOpen,
}: chatTriggerPropType): JSX.Element {
const { setErrorData } = useContext(alertContext); const { setErrorData } = useContext(alertContext);
function handleClick() { function handleClick(): void {
if (isBuilt) { if (isBuilt) {
if (canOpen) { if (canOpen) {
setOpen(true); setOpen(true);

View file

@ -10,7 +10,7 @@ import { getBuildStatus } from "../../controllers/API";
import FormModal from "../../modals/formModal"; import FormModal from "../../modals/formModal";
import { NodeType } from "../../types/flow"; import { NodeType } from "../../types/flow";
export default function Chat({ flow }: ChatType) { export default function Chat({ flow }: ChatType): JSX.Element {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [canOpen, setCanOpen] = useState(false); const [canOpen, setCanOpen] = useState(false);
const { tabsState, isBuilt, setIsBuilt } = useContext(TabsContext); const { tabsState, isBuilt, setIsBuilt } = useContext(TabsContext);
@ -44,11 +44,11 @@ export default function Chat({ flow }: ChatType) {
}, [flow]); }, [flow]);
const prevNodesRef = useRef<any[] | undefined>(); const prevNodesRef = useRef<any[] | undefined>();
const nodes = useNodes(); const nodes: NodeType[] = useNodes();
useEffect(() => { useEffect(() => {
const prevNodes = prevNodesRef.current; const prevNodes = prevNodesRef.current;
const currentNodes = nodes.map((node: NodeType) => const currentNodes = nodes.map((node: NodeType) =>
_.cloneDeep(node.data.node.template) _.cloneDeep(node.data.node?.template)
); );
if ( if (
tabsState && tabsState &&

View file

@ -33,7 +33,7 @@ export default function CodeAreaComponent({
dynamic={dynamic} dynamic={dynamic}
value={myValue} value={myValue}
nodeClass={nodeClass} nodeClass={nodeClass}
setNodeClass={setNodeClass} setNodeClass={setNodeClass!}
setValue={(value: string) => { setValue={(value: string) => {
setMyValue(value); setMyValue(value);
onChange(value); onChange(value);

View file

@ -29,7 +29,7 @@ import {
TabsTrigger, TabsTrigger,
} from "../../components/ui/tabs"; } from "../../components/ui/tabs";
import { darkContext } from "../../contexts/darkContext"; import { darkContext } from "../../contexts/darkContext";
import { FlowType } from "../../types/flow/index"; import { codeTabsPropsType } from "../../types/components";
import { classNames } from "../../utils/utils"; import { classNames } from "../../utils/utils";
import IconComponent from "../genericIconComponent"; import IconComponent from "../genericIconComponent";
@ -40,28 +40,15 @@ export default function CodeTabsComponent({
setActiveTab, setActiveTab,
isMessage, isMessage,
tweaks, tweaks,
}: { }: codeTabsPropsType) {
flow?: FlowType;
tabs: any;
activeTab: string;
setActiveTab: any;
isMessage?: boolean;
tweaks?: {
tweak?: any;
tweaksList?: any;
buildContent?: any;
getValue?: any;
buildTweakObject?: any;
};
}) {
const [isCopied, setIsCopied] = useState<Boolean>(false); const [isCopied, setIsCopied] = useState<Boolean>(false);
const [data, setData] = useState(flow ? flow["data"]["nodes"] : null); const [data, setData] = useState(flow ? flow["data"]!["nodes"] : null);
const [openAccordion, setOpenAccordion] = useState([]); const [openAccordion, setOpenAccordion] = useState<string[]>([]);
const { dark } = useContext(darkContext); const { dark } = useContext(darkContext);
useEffect(() => { useEffect(() => {
if (flow && flow["data"]["nodes"]) { if (flow && flow["data"]!["nodes"]) {
setData(flow["data"]["nodes"]); setData(flow["data"]!["nodes"]);
} }
}, [flow]); }, [flow]);
@ -102,8 +89,8 @@ export default function CodeTabsComponent({
}; };
function openAccordions() { function openAccordions() {
let accordionsToOpen = []; let accordionsToOpen: string[] = [];
tweaks.tweak.current.forEach((el) => { tweaks?.tweak!.current.forEach((el) => {
Object.keys(el).forEach((key) => { Object.keys(el).forEach((key) => {
if (Object.keys(el[key]).length > 0) { if (Object.keys(el[key]).length > 0) {
accordionsToOpen.push(key); accordionsToOpen.push(key);
@ -205,9 +192,9 @@ export default function CodeTabsComponent({
: "overflow-hidden" : "overflow-hidden"
)} )}
> >
{data.map((node: any, index) => ( {data?.map((node: any, index) => (
<div className="px-3" key={index}> <div className="px-3" key={index}>
{tweaks.tweaksList.current.includes( {tweaks?.tweaksList!.current.includes(
node["data"]["id"] node["data"]["id"]
) && ( ) && (
<AccordionComponent <AccordionComponent
@ -290,14 +277,14 @@ export default function CodeTabsComponent({
setData((old) => { setData((old) => {
let newInputList = let newInputList =
cloneDeep(old); cloneDeep(old);
newInputList[ newInputList![
index index
].data.node.template[ ].data.node.template[
templateField templateField
].value = target; ].value = target;
return newInputList; return newInputList;
}); });
tweaks.buildTweakObject( tweaks.buildTweakObject!(
node["data"]["id"], node["data"]["id"],
target, target,
node.data.node.template[ node.data.node.template[
@ -310,7 +297,7 @@ export default function CodeTabsComponent({
templateField templateField
].multiline ? ( ].multiline ? (
<ShadTooltip <ShadTooltip
content={tweaks.buildContent( content={tweaks.buildContent!(
node.data.node.template[ node.data.node.template[
templateField templateField
].value ].value
@ -339,14 +326,14 @@ export default function CodeTabsComponent({
setData((old) => { setData((old) => {
let newInputList = let newInputList =
cloneDeep(old); cloneDeep(old);
newInputList[ newInputList![
index index
].data.node.template[ ].data.node.template[
templateField templateField
].value = target; ].value = target;
return newInputList; return newInputList;
}); });
tweaks.buildTweakObject( tweaks.buildTweakObject!(
node["data"]["id"], node["data"]["id"],
target, target,
node.data.node node.data.node
@ -384,14 +371,14 @@ export default function CodeTabsComponent({
setData((old) => { setData((old) => {
let newInputList = let newInputList =
cloneDeep(old); cloneDeep(old);
newInputList[ newInputList![
index index
].data.node.template[ ].data.node.template[
templateField templateField
].value = target; ].value = target;
return newInputList; return newInputList;
}); });
tweaks.buildTweakObject( tweaks.buildTweakObject!(
node["data"]["id"], node["data"]["id"],
target, target,
node.data.node.template[ node.data.node.template[
@ -417,14 +404,14 @@ export default function CodeTabsComponent({
setData((old) => { setData((old) => {
let newInputList = let newInputList =
cloneDeep(old); cloneDeep(old);
newInputList[ newInputList![
index index
].data.node.template[ ].data.node.template[
templateField templateField
].value = e; ].value = e;
return newInputList; return newInputList;
}); });
tweaks.buildTweakObject( tweaks.buildTweakObject!(
node["data"]["id"], node["data"]["id"],
e, e,
node.data.node.template[ node.data.node.template[
@ -440,7 +427,7 @@ export default function CodeTabsComponent({
templateField templateField
].type === "file" ? ( ].type === "file" ? (
<ShadTooltip <ShadTooltip
content={tweaks.buildContent( content={tweaks.buildContent!(
!node.data.node.template[ !node.data.node.template[
templateField templateField
].value || ].value ||
@ -508,14 +495,14 @@ export default function CodeTabsComponent({
setData((old) => { setData((old) => {
let newInputList = let newInputList =
cloneDeep(old); cloneDeep(old);
newInputList[ newInputList![
index index
].data.node.template[ ].data.node.template[
templateField templateField
].value = target; ].value = target;
return newInputList; return newInputList;
}); });
tweaks.buildTweakObject( tweaks.buildTweakObject!(
node["data"]["id"], node["data"]["id"],
target, target,
node.data.node.template[ node.data.node.template[
@ -544,14 +531,14 @@ export default function CodeTabsComponent({
setData((old) => { setData((old) => {
let newInputList = let newInputList =
cloneDeep(old); cloneDeep(old);
newInputList[ newInputList![
index index
].data.node.template[ ].data.node.template[
templateField templateField
].value = target; ].value = target;
return newInputList; return newInputList;
}); });
tweaks.buildTweakObject( tweaks.buildTweakObject!(
node["data"]["id"], node["data"]["id"],
target, target,
node.data.node.template[ node.data.node.template[
@ -596,14 +583,14 @@ export default function CodeTabsComponent({
setData((old) => { setData((old) => {
let newInputList = let newInputList =
cloneDeep(old); cloneDeep(old);
newInputList[ newInputList![
index index
].data.node.template[ ].data.node.template[
templateField templateField
].value = target; ].value = target;
return newInputList; return newInputList;
}); });
tweaks.buildTweakObject( tweaks.buildTweakObject!(
node["data"]["id"], node["data"]["id"],
target, target,
node.data.node.template[ node.data.node.template[
@ -617,7 +604,7 @@ export default function CodeTabsComponent({
templateField templateField
].type === "prompt" ? ( ].type === "prompt" ? (
<ShadTooltip <ShadTooltip
content={tweaks.buildContent( content={tweaks.buildContent!(
!node.data.node.template[ !node.data.node.template[
templateField templateField
].value || ].value ||
@ -651,14 +638,14 @@ export default function CodeTabsComponent({
setData((old) => { setData((old) => {
let newInputList = let newInputList =
cloneDeep(old); cloneDeep(old);
newInputList[ newInputList![
index index
].data.node.template[ ].data.node.template[
templateField templateField
].value = target; ].value = target;
return newInputList; return newInputList;
}); });
tweaks.buildTweakObject( tweaks.buildTweakObject!(
node["data"]["id"], node["data"]["id"],
target, target,
node.data.node.template[ node.data.node.template[
@ -673,8 +660,8 @@ export default function CodeTabsComponent({
templateField templateField
].type === "code" ? ( ].type === "code" ? (
<ShadTooltip <ShadTooltip
content={tweaks.buildContent( content={tweaks.buildContent!(
tweaks.getValue( tweaks.getValue!(
node.data.node.template[ node.data.node.template[
templateField templateField
].value, ].value,
@ -706,14 +693,14 @@ export default function CodeTabsComponent({
setData((old) => { setData((old) => {
let newInputList = let newInputList =
cloneDeep(old); cloneDeep(old);
newInputList[ newInputList![
index index
].data.node.template[ ].data.node.template[
templateField templateField
].value = target; ].value = target;
return newInputList; return newInputList;
}); });
tweaks.buildTweakObject( tweaks.buildTweakObject!(
node["data"]["id"], node["data"]["id"],
target, target,
node.data.node.template[ node.data.node.template[
@ -742,7 +729,7 @@ export default function CodeTabsComponent({
</AccordionComponent> </AccordionComponent>
)} )}
{tweaks.tweaksList.current.length === 0 && ( {tweaks?.tweaksList!.current.length === 0 && (
<> <>
<div className="pt-3"> <div className="pt-3">
No tweaks are available for this flow. No tweaks are available for this flow.

View file

@ -11,7 +11,7 @@ export default function Dropdown({
editNode = false, editNode = false,
numberOfOptions = 0, numberOfOptions = 0,
apiModal = false, apiModal = false,
}: DropDownComponentType) { }: DropDownComponentType): JSX.Element {
let [internalValue, setInternalValue] = useState( let [internalValue, setInternalValue] = useState(
value === "" || !value ? "Choose an option" : value value === "" || !value ? "Choose an option" : value
); );

View file

@ -1,5 +1,6 @@
import { useEffect } from "react"; import { useEffect } from "react";
import { FloatComponentType } from "../../types/components"; import { FloatComponentType } from "../../types/components";
import { handleKeyDown } from "../../utils/reactflowUtils";
import { Input } from "../ui/input"; import { Input } from "../ui/input";
export default function FloatComponent({ export default function FloatComponent({
@ -7,7 +8,7 @@ export default function FloatComponent({
onChange, onChange,
disabled, disabled,
editNode = false, editNode = false,
}: FloatComponentType) { }: FloatComponentType): JSX.Element {
const step = 0.1; const step = 0.1;
const min = 0; const min = 0;
const max = 1; const max = 1;
@ -43,6 +44,9 @@ export default function FloatComponent({
onChange={(event) => { onChange={(event) => {
onChange(event.target.value); onChange(event.target.value);
}} }}
onKeyDown={(e) => {
handleKeyDown(e, value, "0");
}}
/> />
</div> </div>
); );

View file

@ -12,10 +12,11 @@ import { Link, useNavigate } from "react-router-dom";
import { alertContext } from "../../../../contexts/alertContext"; import { alertContext } from "../../../../contexts/alertContext";
import { undoRedoContext } from "../../../../contexts/undoRedoContext"; import { undoRedoContext } from "../../../../contexts/undoRedoContext";
import FlowSettingsModal from "../../../../modals/flowSettingsModal"; import FlowSettingsModal from "../../../../modals/flowSettingsModal";
import { menuBarPropsType } from "../../../../types/components";
import IconComponent from "../../../genericIconComponent"; import IconComponent from "../../../genericIconComponent";
import { Button } from "../../../ui/button"; import { Button } from "../../../ui/button";
export const MenuBar = ({ flows, tabId }) => { export const MenuBar = ({ flows, tabId }: menuBarPropsType): JSX.Element => {
const { addFlow } = useContext(TabsContext); const { addFlow } = useContext(TabsContext);
const { setErrorData } = useContext(alertContext); const { setErrorData } = useContext(alertContext);
const { undo, redo } = useContext(undoRedoContext); const { undo, redo } = useContext(undoRedoContext);
@ -25,12 +26,12 @@ export const MenuBar = ({ flows, tabId }) => {
function handleAddFlow() { function handleAddFlow() {
try { try {
addFlow(null, true).then((id) => { addFlow(undefined, true).then((id) => {
navigate("/flow/" + id); navigate("/flow/" + id);
}); });
// saveFlowStyleInDataBase(); // saveFlowStyleInDataBase();
} catch (err) { } catch (err) {
setErrorData(err); setErrorData(err as { title: string; list?: Array<string> });
} }
} }
let current_flow = flows.find((flow) => flow.id === tabId); let current_flow = flows.find((flow) => flow.id === tabId);
@ -45,7 +46,9 @@ export const MenuBar = ({ flows, tabId }) => {
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button asChild variant="primary" size="sm"> <Button asChild variant="primary" size="sm">
<div className="header-menu-bar-display"> <div className="header-menu-bar-display">
<div className="header-menu-flow-name">{current_flow.name}</div> <div className="header-menu-flow-name">
{current_flow!.name}
</div>
<IconComponent name="ChevronDown" className="h-4 w-4" /> <IconComponent name="ChevronDown" className="h-4 w-4" />
</div> </div>
</Button> </Button>

View file

@ -13,7 +13,7 @@ import { Button } from "../ui/button";
import { Separator } from "../ui/separator"; import { Separator } from "../ui/separator";
import MenuBar from "./components/menuBar"; import MenuBar from "./components/menuBar";
export default function Header() { export default function Header(): JSX.Element {
const { flows, tabId } = useContext(TabsContext); const { flows, tabId } = useContext(TabsContext);
const { dark, setDark } = useContext(darkContext); const { dark, setDark } = useContext(darkContext);
const { notificationCenter } = useContext(alertContext); const { notificationCenter } = useContext(alertContext);

View file

@ -1,6 +1,7 @@
import * as Form from "@radix-ui/react-form"; import * as Form from "@radix-ui/react-form";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { InputComponentType } from "../../types/components"; import { InputComponentType } from "../../types/components";
import { handleKeyDown } from "../../utils/reactflowUtils";
import { classNames } from "../../utils/utils"; import { classNames } from "../../utils/utils";
import { Input } from "../ui/input"; import { Input } from "../ui/input";
@ -14,7 +15,7 @@ export default function InputComponent({
editNode = false, editNode = false,
placeholder = "Type something...", placeholder = "Type something...",
className, className,
}: InputComponentType) { }: InputComponentType): JSX.Element {
const [pwdVisible, setPwdVisible] = useState(false); const [pwdVisible, setPwdVisible] = useState(false);
// Clear component state // Clear component state
@ -39,12 +40,15 @@ export default function InputComponent({
editNode ? " input-edit-node " : "", editNode ? " input-edit-node " : "",
password && editNode ? "pr-8" : "", password && editNode ? "pr-8" : "",
password && !editNode ? "pr-10" : "", password && !editNode ? "pr-10" : "",
className className!
)} )}
placeholder={password && editNode ? "Key" : placeholder} placeholder={password && editNode ? "Key" : placeholder}
onChange={(e) => { onChange={(e) => {
onChange(e.target.value); onChange(e.target.value);
}} }}
onKeyDown={(e) => {
handleKeyDown(e, value, "");
}}
/> />
</Form.Control> </Form.Control>
) : ( ) : (
@ -59,12 +63,15 @@ export default function InputComponent({
editNode ? " input-edit-node " : "", editNode ? " input-edit-node " : "",
password && editNode ? "pr-8" : "", password && editNode ? "pr-8" : "",
password && !editNode ? "pr-10" : "", password && !editNode ? "pr-10" : "",
className className!
)} )}
placeholder={password && editNode ? "Key" : placeholder} placeholder={password && editNode ? "Key" : placeholder}
onChange={(e) => { onChange={(e) => {
onChange(e.target.value); onChange(e.target.value);
}} }}
onKeyDown={(e) => {
handleKeyDown(e, value, "");
}}
/> />
)} )}
{password && ( {password && (

View file

@ -13,7 +13,7 @@ export default function InputFileComponent({
fileTypes, fileTypes,
onFileChange, onFileChange,
editNode = false, editNode = false,
}: FileComponentType) { }: FileComponentType): JSX.Element {
const [myValue, setMyValue] = useState(value); const [myValue, setMyValue] = useState(value);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const { setErrorData } = useContext(alertContext); const { setErrorData } = useContext(alertContext);
@ -41,7 +41,7 @@ export default function InputFileComponent({
setMyValue(value); setMyValue(value);
}, [value]); }, [value]);
const handleButtonClick = () => { const handleButtonClick = (): void => {
// Create a file input element // Create a file input element
const input = document.createElement("input"); const input = document.createElement("input");
input.type = "file"; input.type = "file";
@ -49,7 +49,7 @@ export default function InputFileComponent({
input.style.display = "none"; // Hidden from view input.style.display = "none"; // Hidden from view
input.multiple = false; // Allow only one file selection input.multiple = false; // Allow only one file selection
input.onchange = (event: Event) => { input.onchange = (event: Event): void => {
setLoading(true); setLoading(true);
// Get the selected file // Get the selected file

View file

@ -11,7 +11,7 @@ export default function InputListComponent({
onChange, onChange,
disabled, disabled,
editNode = false, editNode = false,
}: InputListComponentType) { }: InputListComponentType): JSX.Element {
useEffect(() => { useEffect(() => {
if (disabled) { if (disabled) {
onChange([""]); onChange([""]);
@ -39,6 +39,12 @@ export default function InputListComponent({
newInputList[idx] = event.target.value; newInputList[idx] = event.target.value;
onChange(newInputList); onChange(newInputList);
}} }}
onKeyDown={(e) => {
if (e.ctrlKey && e.key === "Backspace") {
e.preventDefault();
e.stopPropagation();
}
}}
/> />
{idx === value.length - 1 ? ( {idx === value.length - 1 ? (
<button <button

View file

@ -1,5 +1,6 @@
import { useEffect } from "react"; import { useEffect } from "react";
import { FloatComponentType } from "../../types/components"; import { FloatComponentType } from "../../types/components";
import { handleKeyDown } from "../../utils/reactflowUtils";
import { Input } from "../ui/input"; import { Input } from "../ui/input";
export default function IntComponent({ export default function IntComponent({
@ -7,7 +8,7 @@ export default function IntComponent({
onChange, onChange,
disabled, disabled,
editNode = false, editNode = false,
}: FloatComponentType) { }: FloatComponentType): JSX.Element {
const min = 0; const min = 0;
// Clear component state // Clear component state
@ -37,6 +38,7 @@ export default function IntComponent({
) { ) {
event.preventDefault(); event.preventDefault();
} }
handleKeyDown(event, value, "0");
}} }}
type="number" type="number"
step="1" step="1"

View file

@ -1,8 +1,8 @@
type LoadingComponentProps = { import { LoadingComponentProps } from "../../types/components";
remSize: number;
};
export default function LoadingComponent({ remSize }: LoadingComponentProps) { export default function LoadingComponent({
remSize,
}: LoadingComponentProps): JSX.Element {
return ( return (
<div role="status" className="m-auto w-min"> <div role="status" className="m-auto w-min">
<svg <svg

View file

@ -14,7 +14,7 @@ export default function PromptAreaComponent({
onChange, onChange,
disabled, disabled,
editNode = false, editNode = false,
}: TextAreaComponentType) { }: TextAreaComponentType): JSX.Element {
useEffect(() => { useEffect(() => {
if (disabled) { if (disabled) {
onChange(""); onChange("");
@ -23,9 +23,9 @@ export default function PromptAreaComponent({
useEffect(() => { useEffect(() => {
if (value !== "" && !editNode) { if (value !== "" && !editNode) {
postValidatePrompt(field_name, value, nodeClass).then((apiReturn) => { postValidatePrompt(field_name!, value, nodeClass!).then((apiReturn) => {
if (apiReturn.data) { if (apiReturn.data) {
setNodeClass(apiReturn.data.frontend_node); setNodeClass!(apiReturn.data.frontend_node);
// need to update reactFlowInstance to re-render the nodes. // need to update reactFlowInstance to re-render the nodes.
} }
}); });

View file

@ -10,7 +10,7 @@ export default function TextAreaComponent({
onChange, onChange,
disabled, disabled,
editNode = false, editNode = false,
}: TextAreaComponentType) { }: TextAreaComponentType): JSX.Element {
// Clear text area // Clear text area
useEffect(() => { useEffect(() => {
if (disabled) { if (disabled) {

View file

@ -7,7 +7,7 @@ export default function ToggleComponent({
enabled, enabled,
setEnabled, setEnabled,
disabled, disabled,
}: ToggleComponentType) { }: ToggleComponentType): JSX.Element {
// set component state as disabled // set component state as disabled
useEffect(() => { useEffect(() => {
if (disabled) { if (disabled) {

View file

@ -6,7 +6,7 @@ export default function ToggleShadComponent({
setEnabled, setEnabled,
disabled, disabled,
size, size,
}: ToggleComponentType) { }: ToggleComponentType): JSX.Element {
let scaleX, scaleY; let scaleX, scaleY;
switch (size) { switch (size) {
case "small": case "small":

View file

@ -23,14 +23,14 @@ export default function RenameLabel(props) {
}); });
if (inputRef.current) { if (inputRef.current) {
setTimeout(() => { setTimeout(() => {
inputRef.current.focus(); inputRef.current?.focus();
}, 100); }, 100);
} }
} }
resizeInput(); resizeInput();
}, [isRename]); }, [isRename]);
const inputRef = useRef(null); const inputRef = useRef<HTMLInputElement | null>(null);
const resizeInput = () => { const resizeInput = () => {
const input = inputRef.current; const input = inputRef.current;

View file

@ -29,6 +29,7 @@ export const INVALID_CHARACTERS = [
*/ */
export const regexHighlight = /\{([^}]+)\}/g; export const regexHighlight = /\{([^}]+)\}/g;
export const specialCharsRegex = /[!@#$%^&*()\-_=+[\]{}|;:'",.<>/?\\`´]/;
export const programmingLanguages: languageMap = { export const programmingLanguages: languageMap = {
javascript: ".js", javascript: ".js",
@ -510,6 +511,8 @@ export const URL_EXCLUDED_FROM_ERROR_RETRIES = [
"http://localhost:7860/login", "http://localhost:7860/login",
]; ];
export const skipNodeUpdate = ["CustomComponent"];
export const CONTROL_INPUT_STATE = { export const CONTROL_INPUT_STATE = {
password: "", password: "",
cnfPassword: "", cnfPassword: "",

View file

@ -1,34 +1,15 @@
import { createContext, ReactNode, useState } from "react"; import { createContext, ReactNode, useState } from "react";
import { AlertItemType } from "../types/alerts"; import { AlertItemType } from "../types/alerts";
import { alertContextType } from "../types/typesContext";
import _ from "lodash"; import _ from "lodash";
//types for alertContextType
type alertContextType = {
errorData: { title: string; list?: Array<string> };
setErrorData: (newState: { title: string; list?: Array<string> }) => void;
errorOpen: boolean;
setErrorOpen: (newState: boolean) => void;
noticeData: { title: string; link?: string };
setNoticeData: (newState: { title: string; link?: string }) => void;
noticeOpen: boolean;
setNoticeOpen: (newState: boolean) => void;
successData: { title: string };
setSuccessData: (newState: { title: string }) => void;
successOpen: boolean;
setSuccessOpen: (newState: boolean) => void;
notificationCenter: boolean;
setNotificationCenter: (newState: boolean) => void;
notificationList: Array<AlertItemType>;
pushNotificationList: (Object: AlertItemType) => void;
clearNotificationList: () => void;
removeFromNotificationList: (index: string) => void;
};
//initial values to alertContextType //initial values to alertContextType
const initialValue: alertContextType = { const initialValue: alertContextType = {
errorData: { title: "", list: [] }, errorData: { title: "", list: [] },
setErrorData: () => {}, setErrorData: () => {},
loading: true,
setLoading: () => {},
errorOpen: false, errorOpen: false,
setErrorOpen: () => {}, setErrorOpen: () => {},
noticeData: { title: "", link: "" }, noticeData: { title: "", link: "" },
@ -55,6 +36,7 @@ export function AlertProvider({ children }: { children: ReactNode }) {
list?: Array<string>; list?: Array<string>;
}>({ title: "", list: [] }); }>({ title: "", list: [] });
const [errorOpen, setErrorOpen] = useState(false); const [errorOpen, setErrorOpen] = useState(false);
const [loading, setLoading] = useState(true);
const [noticeData, setNoticeDataState] = useState<{ const [noticeData, setNoticeDataState] = useState<{
title: string; title: string;
link?: string; link?: string;
@ -65,7 +47,7 @@ export function AlertProvider({ children }: { children: ReactNode }) {
}); });
const [successOpen, setSuccessOpen] = useState(false); const [successOpen, setSuccessOpen] = useState(false);
const [notificationCenter, setNotificationCenter] = useState(false); const [notificationCenter, setNotificationCenter] = useState(false);
const [notificationList, setNotificationList] = useState([]); const [notificationList, setNotificationList] = useState<AlertItemType[]>([]);
const pushNotificationList = (notification: AlertItemType) => { const pushNotificationList = (notification: AlertItemType) => {
setNotificationList((old) => { setNotificationList((old) => {
let newNotificationList = _.cloneDeep(old); let newNotificationList = _.cloneDeep(old);
@ -141,6 +123,8 @@ export function AlertProvider({ children }: { children: ReactNode }) {
removeFromNotificationList, removeFromNotificationList,
clearNotificationList, clearNotificationList,
notificationList, notificationList,
loading,
setLoading,
pushNotificationList, pushNotificationList,
setNotificationCenter, setNotificationCenter,
notificationCenter, notificationCenter,

View file

@ -28,7 +28,7 @@ export function AuthProvider({ children }): React.ReactElement {
const [refreshToken, setRefreshToken] = useState<string | null>(null); const [refreshToken, setRefreshToken] = useState<string | null>(null);
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false); const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
const [isAdmin, setIsAdmin] = useState<boolean>(false); const [isAdmin, setIsAdmin] = useState<boolean>(false);
const [userData, setUserData] = useState<Users>(null); const [userData, setUserData] = useState<Users | null>(null);
const [autoLogin, setAutoLogin] = useState<boolean>(false); const [autoLogin, setAutoLogin] = useState<boolean>(false);
const cookies = new Cookies(); const cookies = new Cookies();

View file

@ -1,9 +1,5 @@
import { createContext, useEffect, useState } from "react"; import { createContext, useEffect, useState } from "react";
import { darkContextType } from "../types/typesContext";
type darkContextType = {
dark: {};
setDark: (newState: {}) => void;
};
const initialValue = { const initialValue = {
dark: {}, dark: {},
@ -14,13 +10,13 @@ export const darkContext = createContext<darkContextType>(initialValue);
export function DarkProvider({ children }) { export function DarkProvider({ children }) {
const [dark, setDark] = useState( const [dark, setDark] = useState(
JSON.parse(window.localStorage.getItem("isDark")) ?? false JSON.parse(window.localStorage.getItem("isDark")!) ?? false
); );
useEffect(() => { useEffect(() => {
if (dark) { if (dark) {
document.getElementById("body").classList.add("dark"); document.getElementById("body")!.classList.add("dark");
} else { } else {
document.getElementById("body").classList.remove("dark"); document.getElementById("body")!.classList.remove("dark");
} }
window.localStorage.setItem("isDark", dark.toString()); window.localStorage.setItem("isDark", dark.toString());
}, [dark]); }, [dark]);

View file

@ -1,34 +1,5 @@
import { createContext, ReactNode, useState } from "react"; import { createContext, ReactNode, useState } from "react";
import { locationContextType } from "../types/typesContext";
//types for location context
type locationContextType = {
current: Array<string>;
setCurrent: (newState: Array<string>) => void;
isStackedOpen: boolean;
setIsStackedOpen: (newState: boolean) => void;
showSideBar: boolean;
setShowSideBar: (newState: boolean) => void;
extraNavigation: {
title: string;
options?: Array<{
name: string;
href: string;
icon: any;
children?: Array<any>;
}>;
};
setExtraNavigation: (newState: {
title: string;
options?: Array<{
name: string;
href: string;
icon: any;
children?: Array<any>;
}>;
}) => void;
extraComponent: any;
setExtraComponent: (newState: any) => void;
};
//initial value for location context //initial value for location context
const initialValue = { const initialValue = {

View file

@ -7,8 +7,9 @@ import {
useRef, useRef,
useState, useState,
} from "react"; } from "react";
import { addEdge } from "reactflow"; import { Edge, Node, ReactFlowJsonObject, addEdge } from "reactflow";
import ShortUniqueId from "short-unique-id"; import ShortUniqueId from "short-unique-id";
import { skipNodeUpdate } from "../constants/constants";
import { import {
deleteFlowFromDatabase, deleteFlowFromDatabase,
downloadFlowsFromDatabase, downloadFlowsFromDatabase,
@ -18,8 +19,9 @@ import {
uploadFlowsToDatabase, uploadFlowsToDatabase,
} from "../controllers/API"; } from "../controllers/API";
import { APIClassType, APITemplateType } from "../types/api"; import { APIClassType, APITemplateType } from "../types/api";
import { FlowType, NodeType } from "../types/flow"; import { tweakType } from "../types/components";
import { TabsContextType, TabsState } from "../types/tabs"; import { FlowType, NodeDataType, NodeType } from "../types/flow";
import { TabsContextType, TabsState, errorsVarType } from "../types/tabs";
import { import {
addVersionToDuplicates, addVersionToDuplicates,
updateIds, updateIds,
@ -73,9 +75,12 @@ export function TabsProvider({ children }: { children: ReactNode }) {
const [flows, setFlows] = useState<Array<FlowType>>([]); const [flows, setFlows] = useState<Array<FlowType>>([]);
const [id, setId] = useState(uid()); const [id, setId] = useState(uid());
const { templates, reactFlowInstance } = useContext(typesContext); const { templates, reactFlowInstance } = useContext(typesContext);
const [lastCopiedSelection, setLastCopiedSelection] = useState(null); const [lastCopiedSelection, setLastCopiedSelection] = useState<{
nodes: any;
edges: any;
} | null>(null);
const [tabsState, setTabsState] = useState<TabsState>({}); const [tabsState, setTabsState] = useState<TabsState>({});
const [getTweak, setTweak] = useState([]); const [getTweak, setTweak] = useState<tweakType>([]);
const newNodeId = useRef(uid()); const newNodeId = useRef(uid());
function incrementNodeId() { function incrementNodeId() {
@ -130,8 +135,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
//get tabs from db //get tabs from db
return readFlowsFromDatabase(); return readFlowsFromDatabase();
} }
function processDBData(DbData) { function processDBData(DbData: FlowType[]) {
DbData.forEach((flow) => { DbData.forEach((flow: FlowType) => {
try { try {
if (!flow.data) { if (!flow.data) {
return; return;
@ -144,7 +149,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
}); });
} }
function processFlowEdges(flow) { function processFlowEdges(flow: FlowType) {
if (!flow.data || !flow.data.edges) return; if (!flow.data || !flow.data.edges) return;
flow.data.edges.forEach((edge) => { flow.data.edges.forEach((edge) => {
edge.className = ""; edge.className = "";
@ -153,16 +158,17 @@ export function TabsProvider({ children }: { children: ReactNode }) {
} }
function updateDisplay_name(node: NodeType, template: APIClassType) { function updateDisplay_name(node: NodeType, template: APIClassType) {
node.data.node.display_name = template["display_name"] || node.data.type; node.data.node!.display_name = template["display_name"] || node.data.type;
} }
function updateNodeDocumentation(node: NodeType, template: APIClassType) { function updateNodeDocumentation(node: NodeType, template: APIClassType) {
node.data.node.documentation = template["documentation"]; node.data.node!.documentation = template["documentation"];
} }
function processFlowNodes(flow) { function processFlowNodes(flow: FlowType) {
if (!flow.data || !flow.data.nodes) return; if (!flow.data || !flow.data.nodes) return;
flow.data.nodes.forEach((node: NodeType) => { flow.data.nodes.forEach((node: NodeType) => {
if (skipNodeUpdate.includes(node.data.type)) return;
const template = templates[node.data.type]; const template = templates[node.data.type];
if (!template) { if (!template) {
setErrorData({ title: `Unknown node type: ${node.data.type}` }); setErrorData({ title: `Unknown node type: ${node.data.type}` });
@ -180,7 +186,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
} }
function updateNodeBaseClasses(node: NodeType, template: APIClassType) { function updateNodeBaseClasses(node: NodeType, template: APIClassType) {
node.data.node.base_classes = template["base_classes"]; node.data.node!.base_classes = template["base_classes"];
} }
function updateNodeEdges( function updateNodeEdges(
@ -188,10 +194,10 @@ export function TabsProvider({ children }: { children: ReactNode }) {
node: NodeType, node: NodeType,
template: APIClassType template: APIClassType
) { ) {
flow.data.edges.forEach((edge) => { flow.data!.edges.forEach((edge) => {
if (edge.source === node.id) { if (edge.source === node.id) {
edge.sourceHandle = edge.sourceHandle edge.sourceHandle = edge.sourceHandle
.split("|") ?.split("|")
.slice(0, 2) .slice(0, 2)
.concat(template["base_classes"]) .concat(template["base_classes"])
.join("|"); .join("|");
@ -200,17 +206,17 @@ export function TabsProvider({ children }: { children: ReactNode }) {
} }
function updateNodeDescription(node: NodeType, template: APIClassType) { function updateNodeDescription(node: NodeType, template: APIClassType) {
node.data.node.description = template["description"]; node.data.node!.description = template["description"];
} }
function updateNodeTemplate(node: NodeType, template: APIClassType) { function updateNodeTemplate(node: NodeType, template: APIClassType) {
node.data.node.template = updateTemplate( node.data.node!.template = updateTemplate(
template["template"] as unknown as APITemplateType, template["template"] as unknown as APITemplateType,
node.data.node.template as APITemplateType node.data.node!.template as APITemplateType
); );
} }
function updateStateWithDbData(tabsData) { function updateStateWithDbData(tabsData: FlowType[]) {
setFlows(tabsData); setFlows(tabsData);
} }
@ -241,7 +247,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
link.download = `${ link.download = `${
flowName && flowName != "" flowName && flowName != ""
? flowName ? flowName
: flows.find((f) => f.id === tabId).name : flows.find((f) => f.id === tabId)!.name
}.json`; }.json`;
// simulate a click on the link element to trigger the download // simulate a click on the link element to trigger the download
@ -293,10 +299,10 @@ export function TabsProvider({ children }: { children: ReactNode }) {
input.onchange = (e: Event) => { input.onchange = (e: Event) => {
// check if the file type is application/json // check if the file type is application/json
if ( if (
(e.target as HTMLInputElement).files[0].type === "application/json" (e.target as HTMLInputElement).files![0].type === "application/json"
) { ) {
// get the file from the file input // get the file from the file input
const currentfile = (e.target as HTMLInputElement).files[0]; const currentfile = (e.target as HTMLInputElement).files![0];
// read the file as text // read the file as text
currentfile.text().then((text) => { currentfile.text().then((text) => {
// parse the text into a JSON object // parse the text into a JSON object
@ -319,10 +325,10 @@ export function TabsProvider({ children }: { children: ReactNode }) {
input.onchange = (event: Event) => { input.onchange = (event: Event) => {
// check if the file type is application/json // check if the file type is application/json
if ( if (
(event.target as HTMLInputElement).files[0].type === "application/json" (event.target as HTMLInputElement).files![0].type === "application/json"
) { ) {
// get the file from the file input // get the file from the file input
const file = (event.target as HTMLInputElement).files[0]; const file = (event.target as HTMLInputElement).files![0];
// read the file as text // read the file as text
const formData = new FormData(); const formData = new FormData();
formData.append("file", file); formData.append("file", file);
@ -353,15 +359,15 @@ export function TabsProvider({ children }: { children: ReactNode }) {
*/ */
function paste( function paste(
selectionInstance, selectionInstance: { nodes: Node[]; edges: Edge[] },
position: { x: number; y: number; paneX?: number; paneY?: number } position: { x: number; y: number; paneX?: number; paneY?: number }
) { ) {
let minimumX = Infinity; let minimumX = Infinity;
let minimumY = Infinity; let minimumY = Infinity;
let idsMap = {}; let idsMap = {};
let nodes = reactFlowInstance.getNodes(); let nodes: Node<NodeDataType>[] = reactFlowInstance!.getNodes();
let edges = reactFlowInstance.getEdges(); let edges = reactFlowInstance!.getEdges();
selectionInstance.nodes.forEach((node) => { selectionInstance.nodes.forEach((node: Node) => {
if (node.position.y < minimumY) { if (node.position.y < minimumY) {
minimumY = node.position.y; minimumY = node.position.y;
} }
@ -371,8 +377,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
}); });
const insidePosition = position.paneX const insidePosition = position.paneX
? { x: position.paneX + position.x, y: position.paneY + position.y } ? { x: position.paneX + position.x, y: position.paneY! + position.y }
: reactFlowInstance.project({ x: position.x, y: position.y }); : reactFlowInstance!.project({ x: position.x, y: position.y });
selectionInstance.nodes.forEach((node: NodeType) => { selectionInstance.nodes.forEach((node: NodeType) => {
// Generate a unique node ID // Generate a unique node ID
@ -384,8 +390,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
id: newId, id: newId,
type: "genericNode", type: "genericNode",
position: { position: {
x: insidePosition.x + node.position.x - minimumX, x: insidePosition.x + node.position!.x - minimumX,
y: insidePosition.y + node.position.y - minimumY, y: insidePosition.y + node.position!.y - minimumY,
}, },
data: { data: {
..._.cloneDeep(node.data), ..._.cloneDeep(node.data),
@ -398,19 +404,19 @@ export function TabsProvider({ children }: { children: ReactNode }) {
.map((node) => ({ ...node, selected: false })) .map((node) => ({ ...node, selected: false }))
.concat({ ...newNode, selected: false }); .concat({ ...newNode, selected: false });
}); });
reactFlowInstance.setNodes(nodes); reactFlowInstance!.setNodes(nodes);
selectionInstance.edges.forEach((edge) => { selectionInstance.edges.forEach((edge) => {
let source = idsMap[edge.source]; let source = idsMap[edge.source];
let target = idsMap[edge.target]; let target = idsMap[edge.target];
let sourceHandleSplitted = edge.sourceHandle.split("|"); let sourceHandleSplitted = edge.sourceHandle!.split("|");
let sourceHandle = let sourceHandle =
sourceHandleSplitted[0] + sourceHandleSplitted[0] +
"|" + "|" +
source + source +
"|" + "|" +
sourceHandleSplitted.slice(2).join("|"); sourceHandleSplitted.slice(2).join("|");
let targetHandleSplitted = edge.targetHandle.split("|"); let targetHandleSplitted = edge.targetHandle!.split("|");
let targetHandle = let targetHandle =
targetHandleSplitted.slice(0, -1).join("|") + "|" + target; targetHandleSplitted.slice(0, -1).join("|") + "|" + target;
let id = let id =
@ -438,21 +444,21 @@ export function TabsProvider({ children }: { children: ReactNode }) {
edges.map((edge) => ({ ...edge, selected: false })) edges.map((edge) => ({ ...edge, selected: false }))
); );
}); });
reactFlowInstance.setEdges(edges); reactFlowInstance!.setEdges(edges);
} }
const addFlow = async ( const addFlow = async (
flow?: FlowType, flow?: FlowType,
newProject?: Boolean newProject?: Boolean
): Promise<String> => { ): Promise<String | undefined> => {
if (newProject) { if (newProject) {
let flowData = extractDataFromFlow(flow); let flowData = extractDataFromFlow(flow!);
if (flowData.description == "") { if (flowData.description == "") {
flowData.description = getRandomDescription(); flowData.description = getRandomDescription();
} }
// Create a new flow with a default name if no flow is provided. // Create a new flow with a default name if no flow is provided.
const newFlow = createNewFlow(flowData, flow); const newFlow = createNewFlow(flowData, flow!);
processFlowEdges(newFlow); processFlowEdges(newFlow);
processFlowNodes(newFlow); processFlowNodes(newFlow);
@ -477,13 +483,13 @@ export function TabsProvider({ children }: { children: ReactNode }) {
} }
} else { } else {
paste( paste(
{ nodes: flow.data.nodes, edges: flow.data.edges }, { nodes: flow!.data!.nodes, edges: flow!.data!.edges },
{ x: 10, y: 10 } { x: 10, y: 10 }
); );
} }
}; };
const extractDataFromFlow = (flow) => { const extractDataFromFlow = (flow: FlowType) => {
let data = flow?.data ? flow.data : null; let data = flow?.data ? flow.data : null;
const description = flow?.description ? flow.description : ""; const description = flow?.description ? flow.description : "";
@ -496,18 +502,19 @@ export function TabsProvider({ children }: { children: ReactNode }) {
return { data, description }; return { data, description };
}; };
const updateEdges = (edges) => { const updateEdges = (edges: Edge[]) => {
edges.forEach((edge) => { edges.forEach((edge) => {
edge.className = edge.className =
(edge.targetHandle.split("|")[0] === "Text" (edge.targetHandle!.split("|")[0] === "Text"
? "stroke-gray-800 " ? "stroke-gray-800 "
: "stroke-gray-900 ") + " stroke-connection"; : "stroke-gray-900 ") + " stroke-connection";
edge.animated = edge.targetHandle.split("|")[0] === "Text"; edge.animated = edge.targetHandle!.split("|")[0] === "Text";
}); });
}; };
const updateNodes = (nodes, edges) => { const updateNodes = (nodes: Node[], edges: Edge[]) => {
nodes.forEach((node) => { nodes.forEach((node) => {
if (skipNodeUpdate.includes(node.data.type)) return;
const template = templates[node.data.type]; const template = templates[node.data.type];
if (!template) { if (!template) {
setErrorData({ title: `Unknown node type: ${node.data.type}` }); setErrorData({ title: `Unknown node type: ${node.data.type}` });
@ -517,8 +524,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
node.data.node.base_classes = template["base_classes"]; node.data.node.base_classes = template["base_classes"];
edges.forEach((edge) => { edges.forEach((edge) => {
if (edge.source === node.id) { if (edge.source === node.id) {
edge.sourceHandle = edge.sourceHandle edge.sourceHandle = edge
.split("|") .sourceHandle!.split("|")
.slice(0, 2) .slice(0, 2)
.concat(template["base_classes"]) .concat(template["base_classes"])
.join("|"); .join("|");
@ -533,14 +540,17 @@ export function TabsProvider({ children }: { children: ReactNode }) {
}); });
}; };
const createNewFlow = (flowData, flow) => ({ const createNewFlow = (
flowData: { data: ReactFlowJsonObject | null; description: string },
flow: FlowType
) => ({
description: flowData.description, description: flowData.description,
name: flow?.name ?? getRandomName(), name: flow?.name ?? getRandomName(),
data: flowData.data, data: flowData.data,
id: "", id: "",
}); });
const addFlowToLocalState = (newFlow) => { const addFlowToLocalState = (newFlow: FlowType) => {
setFlows((prevState) => { setFlows((prevState) => {
return [...prevState, newFlow]; return [...prevState, newFlow];
}); });
@ -591,7 +601,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
}); });
} }
} catch (err) { } catch (err) {
setErrorData(err); setErrorData(err as errorsVarType);
} }
} }

View file

@ -1,14 +1,21 @@
import { createContext, ReactNode, useEffect, useState } from "react"; import {
import { Node } from "reactflow"; createContext,
ReactNode,
useContext,
useEffect,
useState,
} from "react";
import { Node, ReactFlowInstance } from "reactflow";
import { getAll } from "../controllers/API"; import { getAll } from "../controllers/API";
import { APIKindType } from "../types/api"; import { APIKindType } from "../types/api";
import { typesContextType } from "../types/typesContext"; import { typesContextType } from "../types/typesContext";
import { alertContext } from "./alertContext";
//context to share types adn functions from nodes to flow //context to share types adn functions from nodes to flow
const initialValue: typesContextType = { const initialValue: typesContextType = {
reactFlowInstance: null, reactFlowInstance: null,
setReactFlowInstance: () => {}, setReactFlowInstance: (newState: ReactFlowInstance) => {},
deleteNode: () => {}, deleteNode: () => {},
types: {}, types: {},
setTypes: () => {}, setTypes: () => {},
@ -22,13 +29,15 @@ export const typesContext = createContext<typesContextType>(initialValue);
export function TypesProvider({ children }: { children: ReactNode }) { export function TypesProvider({ children }: { children: ReactNode }) {
const [types, setTypes] = useState({}); const [types, setTypes] = useState({});
const [reactFlowInstance, setReactFlowInstance] = useState(null); const [reactFlowInstance, setReactFlowInstance] =
useState<ReactFlowInstance | null>(null);
const [templates, setTemplates] = useState({}); const [templates, setTemplates] = useState({});
const [data, setData] = useState({}); const [data, setData] = useState({});
const { setLoading } = useContext(alertContext);
useEffect(() => { useEffect(() => {
let delay = 1000; // Start delay of 1 second let delay = 1000; // Start delay of 1 second
let intervalId = null; let intervalId: NodeJS.Timer;
let retryCount = 0; // Count of retry attempts let retryCount = 0; // Count of retry attempts
const maxRetryCount = 5; // Max retry attempts const maxRetryCount = 5; // Max retry attempts
@ -40,6 +49,7 @@ export function TypesProvider({ children }: { children: ReactNode }) {
const result = await getAll(); const result = await getAll();
// Make sure to only update the state if the component is still mounted. // Make sure to only update the state if the component is still mounted.
if (isMounted) { if (isMounted) {
setLoading(false);
setData(result.data); setData(result.data);
setTemplates( setTemplates(
Object.keys(result.data).reduce((acc, curr) => { Object.keys(result.data).reduce((acc, curr) => {
@ -69,7 +79,7 @@ export function TypesProvider({ children }: { children: ReactNode }) {
); );
} }
// Clear the interval if successful. // Clear the interval if successful.
clearInterval(intervalId); clearInterval(intervalId!);
} catch (error) { } catch (error) {
console.error("An error has occurred while fetching types."); console.error("An error has occurred while fetching types.");
} }
@ -77,21 +87,20 @@ export function TypesProvider({ children }: { children: ReactNode }) {
// Start the initial interval. // Start the initial interval.
intervalId = setInterval(getTypes, delay); intervalId = setInterval(getTypes, delay);
return () => { return () => {
// This will clear the interval when the component unmounts, or when the dependencies of the useEffect hook change. // This will clear the interval when the component unmounts, or when the dependencies of the useEffect hook change.
clearInterval(intervalId); clearInterval(intervalId!);
// Indicate that the component has been unmounted. // Indicate that the component has been unmounted.
isMounted = false; isMounted = false;
}; };
}, []); }, []);
function deleteNode(idx: string) { function deleteNode(idx: string) {
reactFlowInstance.setNodes( reactFlowInstance!.setNodes(
reactFlowInstance.getNodes().filter((node: Node) => node.id !== idx) reactFlowInstance!.getNodes().filter((node: Node) => node.id !== idx)
); );
reactFlowInstance.setEdges( reactFlowInstance!.setEdges(
reactFlowInstance reactFlowInstance!
.getEdges() .getEdges()
.filter((edge) => edge.source !== idx && edge.target !== idx) .filter((edge) => edge.source !== idx && edge.target !== idx)
); );

View file

@ -6,26 +6,15 @@ import {
useEffect, useEffect,
useState, useState,
} from "react"; } from "react";
import { Edge, Node, useReactFlow } from "reactflow"; import { useReactFlow } from "reactflow";
import {
HistoryItem,
UseUndoRedoOptions,
undoRedoContextType,
} from "../types/typesContext";
import { isWrappedWithClass } from "../utils/utils"; import { isWrappedWithClass } from "../utils/utils";
import { TabsContext } from "./tabsContext"; import { TabsContext } from "./tabsContext";
type undoRedoContextType = {
undo: () => void;
redo: () => void;
takeSnapshot: () => void;
};
type UseUndoRedoOptions = {
maxHistorySize: number;
enableShortcuts: boolean;
};
type HistoryItem = {
nodes: Node[];
edges: Edge[];
};
const initialValue = { const initialValue = {
undo: () => {}, undo: () => {},
redo: () => {}, redo: () => {},

View file

@ -36,13 +36,14 @@ function ApiInterceptor() {
const res = await renewAccessToken(refreshToken); const res = await renewAccessToken(refreshToken);
login(res.data.access_token, res.data.refresh_token); login(res.data.access_token, res.data.refresh_token);
try { try {
const accessToken = cookies.get("access_token"); if (error?.config?.headers) {
delete error.config.headers["Authorization"]; delete error.config.headers["Authorization"];
error.config.headers["Authorization"] = `Bearer ${accessToken}`; error.config.headers["Authorization"] = `Bearer ${accessToken}`;
const response = await axios.request(error.config); const response = await axios.request(error.config);
return response; return response;
}
} catch (error) { } catch (error) {
if (error.response?.status === 401) { if (axios.isAxiosError(error) && error.response?.status === 401) {
logout(); logout();
navigate("/login"); navigate("/login");
} }
@ -103,7 +104,7 @@ function ApiInterceptor() {
} }
// Function to sleep for a given duration in milliseconds // Function to sleep for a given duration in milliseconds
function sleep(ms) { function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms)); return new Promise((resolve) => setTimeout(resolve, ms));
} }

View file

@ -30,7 +30,7 @@ export async function getAll(): Promise<AxiosResponse<APIObjectType>> {
const GITHUB_API_URL = "https://api.github.com"; const GITHUB_API_URL = "https://api.github.com";
export async function getRepoStars(owner, repo) { export async function getRepoStars(owner: string, repo: string) {
try { try {
const response = await api.get(`${GITHUB_API_URL}/repos/${owner}/${repo}`); const response = await api.get(`${GITHUB_API_URL}/repos/${owner}/${repo}`);
return response.data.stargazers_count; return response.data.stargazers_count;
@ -107,7 +107,7 @@ export async function getExamples(): Promise<FlowType[]> {
export async function saveFlowToDatabase(newFlow: { export async function saveFlowToDatabase(newFlow: {
name: string; name: string;
id: string; id: string;
data: ReactFlowJsonObject; data: ReactFlowJsonObject | null;
description: string; description: string;
style?: FlowStyleType; style?: FlowStyleType;
}): Promise<FlowType> { }): Promise<FlowType> {
@ -186,7 +186,7 @@ export async function downloadFlowsFromDatabase() {
} }
} }
export async function uploadFlowsToDatabase(flows) { export async function uploadFlowsToDatabase(flows: FormData) {
try { try {
const response = await api.post(`/api/v1/flows/upload/`, flows); const response = await api.post(`/api/v1/flows/upload/`, flows);

View file

@ -1,9 +1,9 @@
import React, { forwardRef } from "react"; import React, { forwardRef } from "react";
import { ReactComponent as PowerPointSVG } from "./PowerPoint.svg"; import SvgPowerPoint from "./PowerPoint";
export const PowerPointIcon = forwardRef< export const PowerPointIcon = forwardRef<
SVGSVGElement, SVGSVGElement,
React.PropsWithChildren<{}> React.PropsWithChildren<{}>
>((props, ref) => { >((props, ref) => {
return <PowerPointSVG ref={ref} {...props} />; return <SvgPowerPoint ref={ref} {...props} />;
}); });

View file

@ -15,7 +15,9 @@ import CodeTabsComponent from "../../components/codeTabsComponent";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import { EXPORT_CODE_DIALOG } from "../../constants/constants"; import { EXPORT_CODE_DIALOG } from "../../constants/constants";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { FlowType } from "../../types/flow/index"; import { TemplateVariableType } from "../../types/api";
import { tweakType, uniqueTweakType } from "../../types/components";
import { FlowType, NodeType } from "../../types/flow/index";
import { buildTweaks } from "../../utils/reactflowUtils"; import { buildTweaks } from "../../utils/reactflowUtils";
import { import {
getCurlCode, getCurlCode,
@ -41,8 +43,8 @@ const ApiModal = forwardRef(
) => { ) => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [activeTab, setActiveTab] = useState("0"); const [activeTab, setActiveTab] = useState("0");
const tweak = useRef([]); const tweak = useRef<tweakType>([]);
const tweaksList = useRef([]); const tweaksList = useRef<string[]>([]);
const { setTweak, getTweak, tabsState } = useContext(TabsContext); const { setTweak, getTweak, tabsState } = useContext(TabsContext);
const pythonApiCode = getPythonApiCode(flow, tweak.current, tabsState); const pythonApiCode = getPythonApiCode(flow, tweak.current, tabsState);
const curl_code = getCurlCode(flow, tweak.current, tabsState); const curl_code = getCurlCode(flow, tweak.current, tabsState);
@ -65,7 +67,7 @@ const ApiModal = forwardRef(
} }
useEffect(() => { useEffect(() => {
if (flow["data"]["nodes"].length == 0) { if (flow["data"]!["nodes"].length == 0) {
startState(); startState();
} else { } else {
tweak.current = []; tweak.current = [];
@ -81,12 +83,12 @@ const ApiModal = forwardRef(
} else { } else {
setTabs(tabsArray(codesArray, 1)); setTabs(tabsArray(codesArray, 1));
} }
}, [flow["data"]["nodes"], open]); }, [flow["data"]!["nodes"], open]);
function filterNodes() { function filterNodes() {
let arrNodesWithValues = []; let arrNodesWithValues: string[] = [];
flow["data"]["nodes"].forEach((node) => { flow["data"]!["nodes"].forEach((node) => {
Object.keys(node["data"]["node"]["template"]) Object.keys(node["data"]["node"]["template"])
.filter( .filter(
(templateField) => (templateField) =>
@ -109,11 +111,15 @@ const ApiModal = forwardRef(
return self.indexOf(value) === index; return self.indexOf(value) === index;
}); });
} }
function buildTweakObject(tw, changes, template) { function buildTweakObject(
if (template.type === "float") { tw: string,
changes: string | string[] | boolean | number,
template: TemplateVariableType
) {
if (typeof changes === "string" && template.type === "float") {
changes = parseFloat(changes); changes = parseFloat(changes);
} }
if (template.type === "int") { if (typeof changes === "string" && template.type === "int") {
changes = parseInt(changes); changes = parseInt(changes);
} }
if (template.list === true && Array.isArray(changes)) { if (template.list === true && Array.isArray(changes)) {
@ -125,7 +131,7 @@ const ApiModal = forwardRef(
); );
if (existingTweak) { if (existingTweak) {
existingTweak[tw][template["name"]] = changes; existingTweak[tw][template["name"]] = changes as string;
if (existingTweak[tw][template["name"]] == template.value) { if (existingTweak[tw][template["name"]] == template.value) {
tweak.current.forEach((element) => { tweak.current.forEach((element) => {
@ -142,7 +148,7 @@ const ApiModal = forwardRef(
[tw]: { [tw]: {
[template["name"]]: changes, [template["name"]]: changes,
}, },
}; } as uniqueTweakType;
tweak.current.push(newTweak); tweak.current.push(newTweak);
} }
@ -151,15 +157,15 @@ const ApiModal = forwardRef(
const pythonCode = getPythonCode(flow, tweak.current, tabsState); const pythonCode = getPythonCode(flow, tweak.current, tabsState);
const widgetCode = getWidgetCode(flow, tabsState); const widgetCode = getWidgetCode(flow, tabsState);
tabs[0].code = curl_code; tabs![0].code = curl_code;
tabs[1].code = pythonApiCode; tabs![1].code = pythonApiCode;
tabs[2].code = pythonCode; tabs![2].code = pythonCode;
tabs[3].code = widgetCode; tabs![3].code = widgetCode;
setTweak(tweak.current); setTweak(tweak.current);
} }
function buildContent(value) { function buildContent(value: string) {
const htmlContent = ( const htmlContent = (
<div className="w-[200px]"> <div className="w-[200px]">
<span>{value != null && value != "" ? value : "None"}</span> <span>{value != null && value != "" ? value : "None"}</span>
@ -168,7 +174,11 @@ const ApiModal = forwardRef(
return htmlContent; return htmlContent;
} }
function getValue(value, node, template) { function getValue(
value: string,
node: NodeType,
template: TemplateVariableType
) {
let returnValue = value ?? ""; let returnValue = value ?? "";
if (getTweak.length > 0) { if (getTweak.length > 0) {
@ -204,7 +214,7 @@ const ApiModal = forwardRef(
<BaseModal.Content> <BaseModal.Content>
<CodeTabsComponent <CodeTabsComponent
flow={flow} flow={flow}
tabs={tabs} tabs={tabs!}
activeTab={activeTab} activeTab={activeTab}
setActiveTab={setActiveTab} setActiveTab={setActiveTab}
tweaks={{ tweaks={{

View file

@ -25,6 +25,7 @@ import { limitScrollFieldsModal } from "../../constants/constants";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { typesContext } from "../../contexts/typesContext"; import { typesContext } from "../../contexts/typesContext";
import { NodeDataType } from "../../types/flow"; import { NodeDataType } from "../../types/flow";
import { TabsState } from "../../types/tabs";
import { classNames } from "../../utils/utils"; import { classNames } from "../../utils/utils";
import BaseModal from "../baseModal"; import BaseModal from "../baseModal";
@ -53,19 +54,22 @@ const EditNodeModal = forwardRef(
?.getEdges() ?.getEdges()
.some((edge) => edge.targetHandle === data.id) ?? false; .some((edge) => edge.targetHandle === data.id) ?? false;
function changeAdvanced(templateParam) { function changeAdvanced(templateParam: string): void {
setMyData((old) => { setMyData((old) => {
let newData = cloneDeep(old); let newData = cloneDeep(old);
newData.node.template[templateParam].advanced = newData.node!.template[templateParam].advanced =
!newData.node.template[templateParam].advanced; !newData.node!.template[templateParam].advanced;
return newData; return newData;
}); });
} }
const handleOnNewValue = (newValue: any, name) => { const handleOnNewValue = (
newValue: string | string[] | boolean,
name: string
) => {
setMyData((old) => { setMyData((old) => {
let newData = cloneDeep(old); let newData = cloneDeep(old);
newData.node.template[name].value = newValue; newData.node!.template[name].value = newValue;
return newData; return newData;
}); });
}; };
@ -77,7 +81,7 @@ const EditNodeModal = forwardRef(
return ( return (
<BaseModal size="large-h-full" open={modalOpen} setOpen={setModalOpen}> <BaseModal size="large-h-full" open={modalOpen} setOpen={setModalOpen}>
<BaseModal.Trigger>{children}</BaseModal.Trigger> <BaseModal.Trigger>{children}</BaseModal.Trigger>
<BaseModal.Header description={myData.node?.description}> <BaseModal.Header description={myData.node?.description!}>
<span className="pr-2">{myData.type}</span> <span className="pr-2">{myData.type}</span>
<Badge variant="secondary">ID: {myData.id}</Badge> <Badge variant="secondary">ID: {myData.id}</Badge>
</BaseModal.Header> </BaseModal.Header>
@ -112,11 +116,11 @@ const EditNodeModal = forwardRef(
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody className="p-0"> <TableBody className="p-0">
{Object.keys(myData.node.template) {Object.keys(myData.node!.template)
.filter( .filter(
(templateParam) => (templateParam) =>
templateParam.charAt(0) !== "_" && templateParam.charAt(0) !== "_" &&
myData.node.template[templateParam].show && myData.node?.template[templateParam].show &&
(myData.node.template[templateParam].type === (myData.node.template[templateParam].type ===
"str" || "str" ||
myData.node.template[templateParam].type === myData.node.template[templateParam].type ===
@ -135,13 +139,13 @@ const EditNodeModal = forwardRef(
.map((templateParam, index) => ( .map((templateParam, index) => (
<TableRow key={index} className="h-10"> <TableRow key={index} className="h-10">
<TableCell className="truncate p-0 text-center text-sm text-foreground sm:px-3"> <TableCell className="truncate p-0 text-center text-sm text-foreground sm:px-3">
{myData.node.template[templateParam].name {myData.node?.template[templateParam].name
? myData.node.template[templateParam].name ? myData.node.template[templateParam].name
: myData.node.template[templateParam] : myData.node?.template[templateParam]
.display_name} .display_name}
</TableCell> </TableCell>
<TableCell className="w-[300px] p-0 text-center text-xs text-foreground "> <TableCell className="w-[300px] p-0 text-center text-xs text-foreground ">
{myData.node.template[templateParam].type === {myData.node?.template[templateParam].type ===
"str" && "str" &&
!myData.node.template[templateParam].options ? ( !myData.node.template[templateParam].options ? (
<div className="mx-auto"> <div className="mx-auto">
@ -171,7 +175,7 @@ const EditNodeModal = forwardRef(
myData.node.template[templateParam] myData.node.template[templateParam]
.value ?? "" .value ?? ""
} }
onChange={(value: string) => { onChange={(value: string | string[]) => {
handleOnNewValue(value, templateParam); handleOnNewValue(value, templateParam);
}} }}
/> />
@ -193,7 +197,7 @@ const EditNodeModal = forwardRef(
/> />
)} )}
</div> </div>
) : myData.node.template[templateParam].type === ) : myData.node?.template[templateParam].type ===
"bool" ? ( "bool" ? (
<div className="ml-auto"> <div className="ml-auto">
{" "} {" "}
@ -211,7 +215,7 @@ const EditNodeModal = forwardRef(
size="small" size="small"
/> />
</div> </div>
) : myData.node.template[templateParam].type === ) : myData.node?.template[templateParam].type ===
"float" ? ( "float" ? (
<div className="mx-auto"> <div className="mx-auto">
<FloatComponent <FloatComponent
@ -226,7 +230,7 @@ const EditNodeModal = forwardRef(
}} }}
/> />
</div> </div>
) : myData.node.template[templateParam].type === ) : myData.node?.template[templateParam].type ===
"str" && "str" &&
myData.node.template[templateParam].options ? ( myData.node.template[templateParam].options ? (
<div className="mx-auto"> <div className="mx-auto">
@ -246,7 +250,7 @@ const EditNodeModal = forwardRef(
} }
></Dropdown> ></Dropdown>
</div> </div>
) : myData.node.template[templateParam].type === ) : myData.node?.template[templateParam].type ===
"int" ? ( "int" ? (
<div className="mx-auto"> <div className="mx-auto">
<IntComponent <IntComponent
@ -261,7 +265,7 @@ const EditNodeModal = forwardRef(
}} }}
/> />
</div> </div>
) : myData.node.template[templateParam].type === ) : myData.node?.template[templateParam].type ===
"file" ? ( "file" ? (
<div className="mx-auto"> <div className="mx-auto">
<InputFileComponent <InputFileComponent
@ -271,7 +275,7 @@ const EditNodeModal = forwardRef(
myData.node.template[templateParam] myData.node.template[templateParam]
.value ?? "" .value ?? ""
} }
onChange={(value: string) => { onChange={(value: string | string[]) => {
handleOnNewValue(value, templateParam); handleOnNewValue(value, templateParam);
}} }}
fileTypes={ fileTypes={
@ -283,13 +287,13 @@ const EditNodeModal = forwardRef(
.suffixes .suffixes
} }
onFileChange={(filePath: string) => { onFileChange={(filePath: string) => {
data.node.template[ data.node!.template[
templateParam templateParam
].file_path = filePath; ].file_path = filePath;
}} }}
></InputFileComponent> ></InputFileComponent>
</div> </div>
) : myData.node.template[templateParam].type === ) : myData.node?.template[templateParam].type ===
"prompt" ? ( "prompt" ? (
<div className="mx-auto"> <div className="mx-auto">
<PromptAreaComponent <PromptAreaComponent
@ -304,17 +308,17 @@ const EditNodeModal = forwardRef(
myData.node.template[templateParam] myData.node.template[templateParam]
.value ?? "" .value ?? ""
} }
onChange={(value: string) => { onChange={(value: string | string[]) => {
handleOnNewValue(value, templateParam); handleOnNewValue(value, templateParam);
}} }}
/> />
</div> </div>
) : myData.node.template[templateParam].type === ) : myData.node?.template[templateParam].type ===
"code" ? ( "code" ? (
<div className="mx-auto"> <div className="mx-auto">
<CodeAreaComponent <CodeAreaComponent
dynamic={ dynamic={
data.node.template[templateParam] data.node!.template[templateParam]
.dynamic ?? false .dynamic ?? false
} }
setNodeClass={(nodeClass) => { setNodeClass={(nodeClass) => {
@ -327,12 +331,12 @@ const EditNodeModal = forwardRef(
myData.node.template[templateParam] myData.node.template[templateParam]
.value ?? "" .value ?? ""
} }
onChange={(value: string) => { onChange={(value: string | string[]) => {
handleOnNewValue(value, templateParam); handleOnNewValue(value, templateParam);
}} }}
/> />
</div> </div>
) : myData.node.template[templateParam].type === ) : myData.node?.template[templateParam].type ===
"Any" ? ( "Any" ? (
"-" "-"
) : ( ) : (
@ -343,7 +347,7 @@ const EditNodeModal = forwardRef(
<div className="items-center text-center"> <div className="items-center text-center">
<ToggleShadComponent <ToggleShadComponent
enabled={ enabled={
!myData.node.template[templateParam] !myData.node?.template[templateParam]
.advanced .advanced
} }
setEnabled={(e) => setEnabled={(e) =>
@ -369,7 +373,8 @@ const EditNodeModal = forwardRef(
className="mt-3" className="mt-3"
onClick={() => { onClick={() => {
setData(cloneDeep(myData)); //saves data with actual state of modal setData(cloneDeep(myData)); //saves data with actual state of modal
setTabsState((prev) => { //@ts-ignore
setTabsState((prev: TabsState) => {
return { return {
...prev, ...prev,
[tabId]: { [tabId]: {

View file

@ -1,180 +0,0 @@
import { useState } from "react";
import CodeAreaComponent from "../../../../components/codeAreaComponent";
import Dropdown from "../../../../components/dropdownComponent";
import FloatComponent from "../../../../components/floatComponent";
import InputComponent from "../../../../components/inputComponent";
import InputFileComponent from "../../../../components/inputFileComponent";
import InputListComponent from "../../../../components/inputListComponent";
import IntComponent from "../../../../components/intComponent";
import PromptAreaComponent from "../../../../components/promptComponent";
import TextAreaComponent from "../../../../components/textAreaComponent";
import ToggleComponent from "../../../../components/toggleComponent";
import { classNames } from "../../../../utils/utils";
export default function ModalField({
data,
title,
required,
id,
name,
type,
index,
}) {
const [enabled, setEnabled] = useState(
data.node.template[name]?.value ?? false
);
const display =
type === "str" ||
type === "int" ||
type === "prompt" ||
type === "bool" ||
type === "float" ||
type === "file" ||
type === "code";
return (
<div
className={classNames(
"flex w-full flex-row items-center justify-between",
display ? "" : "hidden",
Object.keys(data.node.template).filter(
(t) =>
t.charAt(0) !== "_" &&
data.node.template[t].advanced &&
data.node.template[t].show
).length -
1 ===
index
? "pb-4"
: ""
)}
>
{display && (
<div>
<span className="mx-2">{title}</span>
<span className="text-status-red">{required ? " *" : ""}</span>
</div>
)}
{type === "str" && !data.node.template[name].options ? (
<div className="w-1/2">
{data.node.template[name].list ? (
<InputListComponent
disabled={false}
value={
!data.node.template[name].value ||
data.node.template[name].value === ""
? [""]
: data.node.template[name].value
}
onChange={(t: string[]) => {
data.node.template[name].value = t;
}}
/>
) : data.node.template[name].multiline ? (
<TextAreaComponent
disabled={false}
value={data.node.template[name].value ?? ""}
onChange={(t: string) => {
data.node.template[name].value = t;
}}
/>
) : (
<InputComponent
disabled={false}
password={data.node.template[name].password ?? false}
value={data.node.template[name].value ?? ""}
onChange={(t) => {
data.node.template[name].value = t;
}}
/>
)}
</div>
) : type === "bool" ? (
<div className="ml-auto">
{" "}
<ToggleComponent
disabled={false}
enabled={enabled}
setEnabled={(t) => {
data.node.template[name].value = t;
setEnabled(t);
}}
size="small"
/>
</div>
) : type === "float" ? (
<div className="w-1/2">
<FloatComponent
disabled={false}
value={data.node.template[name].value ?? ""}
onChange={(t) => {
data.node.template[name].value = t;
}}
/>
</div>
) : type === "str" && data.node.template[name].options ? (
<div className="w-1/2">
<Dropdown
options={data.node.template[name].options}
onSelect={(newValue) => (data.node.template[name].value = newValue)}
value={data.node.template[name].value ?? "Choose an option"}
></Dropdown>
</div>
) : type === "int" ? (
<div className="w-1/2">
<IntComponent
disabled={false}
value={data.node.template[name].value ?? ""}
onChange={(t) => {
data.node.template[name].value = t;
}}
/>
</div>
) : type === "file" ? (
<div className="w-1/2">
<InputFileComponent
disabled={false}
value={data.node.template[name].value ?? ""}
onChange={(t: string) => {
data.node.template[name].value = t;
}}
fileTypes={data.node.template[name].fileTypes}
suffixes={data.node.template[name].suffixes}
onFileChange={(t: string) => {
data.node.template[name].file_path = t;
}}
></InputFileComponent>
</div>
) : type === "prompt" ? (
<div className="w-1/2">
<PromptAreaComponent
field_name={name}
disabled={false}
value={data.node.template[name].value ?? ""}
onChange={(t: string) => {
data.node.template[name].value = t;
}}
/>
</div>
) : type === "code" ? (
<div className="w-1/2">
<CodeAreaComponent
dynamic={data.node.template[name].dynamic ?? false}
setNodeClass={(nodeClass) => {
data.node = nodeClass;
}}
nodeClass={data.node}
disabled={false}
value={data.node.template[name].value ?? ""}
onChange={(t: string) => {
data.node.template[name].value = t;
}}
/>
</div>
) : (
<div className="hidden"></div>
)}
</div>
);
}

View file

@ -1,151 +0,0 @@
import { Dialog, Transition } from "@headlessui/react";
import { Fragment, useContext, useRef, useState } from "react";
import IconComponent from "../../components/genericIconComponent";
import { limitScrollFieldsModal } from "../../constants/constants";
import { typesContext } from "../../contexts/typesContext";
import { NodeDataType } from "../../types/flow";
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
import { classNames, toTitleCase } from "../../utils/utils";
import ModalField from "./components/ModalField";
export default function NodeModal({ data }: { data: NodeDataType }) {
const [modalOpen, setModalOpen] = useState(false);
const { types } = useContext(typesContext);
const ref = useRef();
// any to avoid type conflict
const Icon: any = nodeIconsLucide[types[data.type]];
return (
<Dialog
open={modalOpen}
as="div"
className="relative z-10"
onClose={setModalOpen}
initialFocus={ref}
>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="node-modal-div" />
</Transition.Child>
<div className="node-modal-dialog-arrangement">
<div className="node-modal-dialog-div">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="node-modal-dialog-panel">
<div className=" node-modal-dialog-panel-div">
<button
type="button"
className="node-modal-dialog-button"
onClick={() => {
setModalOpen(false);
}}
>
<span className="sr-only">Close</span>
<IconComponent
name="X"
className="h-6 w-6"
aria-hidden="true"
/>
</button>
</div>
<div className="node-modal-dialog-icon-div">
<div className="node-modal-icon-arrangement">
<Icon
strokeWidth={1.5}
className="node-modal-icon"
style={{
color: nodeColors[types[data.type]] ?? nodeColors.unknown,
}}
/>
<div className="node-modal-title-div">
<Dialog.Title as="h3" className="node-modal-title">
{data.type}
</Dialog.Title>
</div>
</div>
<div className="node-modal-template-div">
<div className="flex-max-width h-[445px]">
<div
className={classNames(
"node-modal-template",
Object.keys(data.node.template).filter(
(t) =>
t.charAt(0) !== "_" &&
data.node.template[t].advanced &&
data.node.template[t].show
).length > limitScrollFieldsModal
? "overflow-scroll overflow-x-hidden custom-scroll"
: "overflow-hidden"
)}
>
<div className="node-modal-template-column">
{Object.keys(data.node.template)
.filter(
(t) =>
t.charAt(0) !== "_" &&
data.node.template[t].advanced &&
data.node.template[t].show
)
.map((t: string, idx) => {
return (
<ModalField
key={idx}
data={data}
title={
data.node.template[t].display_name
? data.node.template[t].display_name
: data.node.template[t].name
? toTitleCase(data.node.template[t].name)
: toTitleCase(t)
}
required={data.node.template[t].required}
id={
data.node.template[t].type +
"|" +
t +
"|" +
data.id
}
name={t}
type={data.node.template[t].type}
index={idx}
/>
);
})}
</div>
</div>
</div>
</div>
<div className="node-modal-button-box">
<button
type="button"
className="node-modal-button"
onClick={() => {
setModalOpen(false);
}}
>
Done
</button>
</div>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
);
}

View file

@ -9,6 +9,7 @@ import {
DialogTitle, DialogTitle,
DialogTrigger, DialogTrigger,
} from "../../components/ui/dialog"; } from "../../components/ui/dialog";
import { modalHeaderType } from "../../types/components";
type ContentProps = { children: ReactNode }; type ContentProps = { children: ReactNode };
type HeaderProps = { children: ReactNode; description: string }; type HeaderProps = { children: ReactNode; description: string };
@ -22,10 +23,10 @@ const Trigger: React.FC<ContentProps> = ({ children }) => {
return <>{children}</>; return <>{children}</>;
}; };
const Header: React.FC<{ children: ReactNode; description: string }> = ({ const Header: React.FC<{ children: ReactNode; description: string | null }> = ({
children, children,
description, description,
}) => { }: modalHeaderType): JSX.Element => {
return ( return (
<DialogHeader> <DialogHeader>
<DialogTitle className="flex items-center">{children}</DialogTitle> <DialogTitle className="flex items-center">{children}</DialogTitle>

View file

@ -4,7 +4,7 @@ import "ace-builds/src-noconflict/mode-python";
import "ace-builds/src-noconflict/theme-github"; import "ace-builds/src-noconflict/theme-github";
import "ace-builds/src-noconflict/theme-twilight"; import "ace-builds/src-noconflict/theme-twilight";
// import "ace-builds/webpack-resolver"; // import "ace-builds/webpack-resolver";
import { ReactNode, useContext, useEffect, useState } from "react"; import { useContext, useEffect, useState } from "react";
import AceEditor from "react-ace"; import AceEditor from "react-ace";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
@ -13,7 +13,7 @@ import { alertContext } from "../../contexts/alertContext";
import { darkContext } from "../../contexts/darkContext"; import { darkContext } from "../../contexts/darkContext";
import { typesContext } from "../../contexts/typesContext"; import { typesContext } from "../../contexts/typesContext";
import { postCustomComponent, postValidateCode } from "../../controllers/API"; import { postCustomComponent, postValidateCode } from "../../controllers/API";
import { APIClassType } from "../../types/api"; import { codeAreaModalPropsType } from "../../types/components";
import BaseModal from "../baseModal"; import BaseModal from "../baseModal";
export default function CodeAreaModal({ export default function CodeAreaModal({
@ -23,27 +23,20 @@ export default function CodeAreaModal({
setNodeClass, setNodeClass,
children, children,
dynamic, dynamic,
}: { }: codeAreaModalPropsType): JSX.Element {
setValue: (value: string) => void;
value: string;
nodeClass?: APIClassType;
children: ReactNode;
setNodeClass?: (Class: APIClassType) => void;
dynamic?: boolean;
}) {
const [code, setCode] = useState(value); const [code, setCode] = useState(value);
const { dark } = useContext(darkContext); const { dark } = useContext(darkContext);
const { reactFlowInstance } = useContext(typesContext); const { reactFlowInstance } = useContext(typesContext);
const [height, setHeight] = useState(null); const [height, setHeight] = useState<string | null>(null);
const { setErrorData, setSuccessData } = useContext(alertContext); const { setErrorData, setSuccessData } = useContext(alertContext);
const [error, setError] = useState<{ const [error, setError] = useState<{
detail: { error: string; traceback: string }; detail: { error: string | undefined; traceback: string | undefined };
}>(null); } | null>(null);
useEffect(() => { useEffect(() => {
// if nodeClass.template has more fields other than code and dynamic is true // if nodeClass.template has more fields other than code and dynamic is true
// do not run handleClick // do not run handleClick
if (dynamic && Object.keys(nodeClass.template).length > 2) { if (dynamic && Object.keys(nodeClass!.template).length > 2) {
return; return;
} }
processCode(); processCode();
@ -90,7 +83,7 @@ export default function CodeAreaModal({
} }
function processDynamicField() { function processDynamicField() {
postCustomComponent(code, nodeClass) postCustomComponent(code, nodeClass!)
.then((apiReturn) => { .then((apiReturn) => {
const { data } = apiReturn; const { data } = apiReturn;
if (data) { if (data) {

View file

@ -1,4 +1,4 @@
import { ReactNode, forwardRef, useContext, useState } from "react"; import { ReactNode, forwardRef, useContext, 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";
@ -8,18 +8,19 @@ import { TabsContext } from "../../contexts/tabsContext";
import { removeApiKeys } from "../../utils/reactflowUtils"; import { removeApiKeys } from "../../utils/reactflowUtils";
import BaseModal from "../baseModal"; import BaseModal from "../baseModal";
const ExportModal = forwardRef((props: { children: ReactNode }, ref) => { const ExportModal = forwardRef(
const { flows, tabId, updateFlow, downloadFlow, saveFlow } = (props: { children: ReactNode }, ref): JSX.Element => {
useContext(TabsContext); const { flows, tabId, updateFlow, downloadFlow } = useContext(TabsContext);
const [checked, setChecked] = useState(false); const [checked, setChecked] = useState(false);
const [name, setName] = useState( const flow = flows.find((f) => f.id === tabId);
flows.find((flow) => flow.id === tabId).name useEffect(() => {
); setName(flow.name);
const [invalidName, setInvalidName] = useState(false); setDescription(flow.description);
const [description, setDescription] = useState( }, [flow.name, flow.description]);
flows.find((flow) => flow.id === tabId).description const [name, setName] = useState(flow.name);
); const [description, setDescription] = useState(flow.description);
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
return ( return (
<BaseModal size="smaller" open={open} setOpen={setOpen}> <BaseModal size="smaller" open={open} setOpen={setOpen}>
<BaseModal.Trigger>{props.children}</BaseModal.Trigger> <BaseModal.Trigger>{props.children}</BaseModal.Trigger>
@ -33,8 +34,6 @@ const ExportModal = forwardRef((props: { children: ReactNode }, ref) => {
</BaseModal.Header> </BaseModal.Header>
<BaseModal.Content> <BaseModal.Content>
<EditFlowSettings <EditFlowSettings
invalidName={invalidName}
setInvalidName={setInvalidName}
name={name} name={name}
description={description} description={description}
flows={flows} flows={flows}
@ -61,14 +60,14 @@ const ExportModal = forwardRef((props: { children: ReactNode }, ref) => {
onClick={() => { onClick={() => {
if (checked) if (checked)
downloadFlow( downloadFlow(
flows.find((flow) => flow.id === tabId), flows.find((flow) => flow.id === tabId)!,
name, name!,
description description
); );
else else
downloadFlow( downloadFlow(
removeApiKeys(flows.find((flow) => flow.id === tabId)), removeApiKeys(flows.find((flow) => flow.id === tabId)!),
name, name!,
description description
); );
setOpen(false); setOpen(false);
@ -80,5 +79,6 @@ const ExportModal = forwardRef((props: { children: ReactNode }, ref) => {
</BaseModal.Footer> </BaseModal.Footer>
</BaseModal> </BaseModal>
); );
}); }
);
export default ExportModal; export default ExportModal;

View file

@ -1,37 +1,33 @@
import { useContext, useRef, useState } from "react"; import { useContext, 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";
import { alertContext } from "../../contexts/alertContext"; import { alertContext } from "../../contexts/alertContext";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { FlowSettingsPropsType } from "../../types/components";
import BaseModal from "../baseModal"; import BaseModal from "../baseModal";
export default function FlowSettingsModal({ export default function FlowSettingsModal({
open, open,
setOpen, setOpen,
}: { }: FlowSettingsPropsType): JSX.Element {
open: boolean; const { setSuccessData } = useContext(alertContext);
setOpen: (open: boolean) => void; const { flows, tabId, updateFlow, saveFlow } = useContext(TabsContext);
}) { const flow = flows.find((f) => f.id === tabId);
const { setErrorData, setSuccessData } = useContext(alertContext); useEffect(() => {
const ref = useRef(); setName(flow.name);
const { flows, tabId, updateFlow, setTabsState, saveFlow } = setDescription(flow.description);
useContext(TabsContext); }, [flow.name, flow.description]);
const maxLength = 50; const [name, setName] = useState(flow.name);
const [name, setName] = useState( const [description, setDescription] = useState(flow.description);
flows.find((flow) => flow.id === tabId).name
);
const [description, setDescription] = useState(
flows.find((flow) => flow.id === tabId).description
);
const [invalidName, setInvalidName] = useState(false); const [invalidName, setInvalidName] = useState(false);
function handleClick() { function handleClick(): void {
let savedFlow = flows.find((flow) => flow.id === tabId); let savedFlow = flows.find((flow) => flow.id === tabId);
savedFlow.name = name; savedFlow!.name = name;
savedFlow.description = description; savedFlow!.description = description;
saveFlow(savedFlow); saveFlow(savedFlow!);
setSuccessData({ title: "Changes saved successfully" }); setSuccessData({ title: "Changes saved successfully" });
setOpen(false); setOpen(false);
} }
@ -51,7 +47,6 @@ export default function FlowSettingsModal({
tabId={tabId} tabId={tabId}
setName={setName} setName={setName}
setDescription={setDescription} setDescription={setDescription}
updateFlow={updateFlow}
/> />
</BaseModal.Content> </BaseModal.Content>

View file

@ -1,6 +1,7 @@
import { useEffect } from "react"; import { useEffect } from "react";
import IconComponent from "../../../components/genericIconComponent"; import IconComponent from "../../../components/genericIconComponent";
import { Textarea } from "../../../components/ui/textarea"; import { Textarea } from "../../../components/ui/textarea";
import { chatInputType } from "../../../types/components";
import { classNames } from "../../../utils/utils"; import { classNames } from "../../../utils/utils";
export default function ChatInput({ export default function ChatInput({
@ -10,7 +11,7 @@ export default function ChatInput({
setChatValue, setChatValue,
inputRef, inputRef,
noInput, noInput,
}) { }: chatInputType): JSX.Element {
useEffect(() => { useEffect(() => {
if (!lockChat && inputRef.current) { if (!lockChat && inputRef.current) {
inputRef.current.focus(); inputRef.current.focus();
@ -46,7 +47,7 @@ export default function ChatInput({
}`, }`,
}} }}
value={lockChat ? "Thinking..." : chatValue} value={lockChat ? "Thinking..." : chatValue}
onChange={(event) => { onChange={(event): void => {
setChatValue(event.target.value); setChatValue(event.target.value);
}} }}
className={classNames( className={classNames(
@ -75,7 +76,7 @@ export default function ChatInput({
: "bg-chat-send text-background" : "bg-chat-send text-background"
)} )}
disabled={lockChat} disabled={lockChat}
onClick={() => sendMessage()} onClick={(): void => sendMessage()}
> >
{lockChat ? ( {lockChat ? (
<IconComponent <IconComponent

View file

@ -3,16 +3,12 @@ import { 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 { programmingLanguages } from "../../../../constants/constants"; import { programmingLanguages } from "../../../../constants/constants";
import { Props } from "../../../../types/components";
interface Props { export function CodeBlock({ language, value }: Props): JSX.Element {
language: string;
value: string;
}
export function CodeBlock({ language, value }) {
const [isCopied, setIsCopied] = useState<Boolean>(false); const [isCopied, setIsCopied] = useState<Boolean>(false);
const copyToClipboard = () => { const copyToClipboard = (): void => {
if (!navigator.clipboard || !navigator.clipboard.writeText) { if (!navigator.clipboard || !navigator.clipboard.writeText) {
return; return;
} }
@ -25,7 +21,7 @@ export function CodeBlock({ language, value }) {
}, 2000); }, 2000);
}); });
}; };
const downloadAsFile = () => { const downloadAsFile = (): void => {
const fileExtension = programmingLanguages[language] || ".file"; const fileExtension = programmingLanguages[language] || ".file";
const suggestedFileName = `${"generated-code"}${fileExtension}`; const suggestedFileName = `${"generated-code"}${fileExtension}`;
const fileName = window.prompt("enter file name", suggestedFileName); const fileName = window.prompt("enter file name", suggestedFileName);

View file

@ -9,18 +9,15 @@ import Robot from "../../../assets/robot.png";
import SanitizedHTMLWrapper from "../../../components/SanitizedHTMLWrapper"; 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 { ChatMessageType } from "../../../types/chat"; import { chatMessagePropsType } from "../../../types/components";
import { classNames } from "../../../utils/utils"; import { classNames } from "../../../utils/utils";
import FileCard from "../fileComponent"; import FileCard from "../fileComponent";
export default function ChatMessage({ export default function ChatMessage({
chat, chat,
lockChat, lockChat,
lastMessage, lastMessage,
}: { }: chatMessagePropsType): JSX.Element {
chat: ChatMessageType;
lockChat: boolean;
lastMessage: boolean;
}) {
const convert = new Convert({ newline: true }); const convert = new Convert({ newline: true });
const [hidden, setHidden] = useState(true); const [hidden, setHidden] = useState(true);
const template = chat.template; const template = chat.template;
@ -57,7 +54,7 @@ export default function ChatMessage({
<div className="form-modal-chat-text"> <div className="form-modal-chat-text">
{hidden && chat.thought && chat.thought !== "" && ( {hidden && chat.thought && chat.thought !== "" && (
<div <div
onClick={() => setHidden((prev) => !prev)} onClick={(): void => setHidden((prev) => !prev)}
className="form-modal-chat-icon-div" className="form-modal-chat-icon-div"
> >
<IconComponent <IconComponent
@ -192,7 +189,7 @@ export default function ChatMessage({
? template?.split("\n")?.map((line, index) => { ? template?.split("\n")?.map((line, index) => {
const regex = /{([^}]+)}/g; const regex = /{([^}]+)}/g;
let match; let match;
let parts = []; let parts: Array<JSX.Element | string> = [];
let lastIndex = 0; let lastIndex = 0;
while ((match = regex.exec(line)) !== null) { while ((match = regex.exec(line)) !== null) {
// Push text up to the match // Push text up to the match

View file

@ -1,9 +1,14 @@
import * as base64js from "base64-js"; import * as base64js from "base64-js";
import { useState } from "react"; import { useState } from "react";
import IconComponent from "../../../components/genericIconComponent"; import IconComponent from "../../../components/genericIconComponent";
import { fileCardPropsType } from "../../../types/components";
export default function FileCard({ fileName, content, fileType }) { export default function FileCard({
const handleDownload = () => { fileName,
content,
fileType,
}: fileCardPropsType): JSX.Element {
const handleDownload = (): void => {
const byteArray = new Uint8Array(base64js.toByteArray(content)); const byteArray = new Uint8Array(base64js.toByteArray(content));
const blob = new Blob([byteArray], { type: "application/octet-stream" }); const blob = new Blob([byteArray], { type: "application/octet-stream" });
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
@ -16,10 +21,10 @@ export default function FileCard({ fileName, content, fileType }) {
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
}; };
const [isHovered, setIsHovered] = useState(false); const [isHovered, setIsHovered] = useState(false);
function handleMouseEnter() { function handleMouseEnter(): void {
setIsHovered(true); setIsHovered(true);
} }
function handleMouseLeave() { function handleMouseLeave(): void {
setIsHovered(false); setIsHovered(false);
} }
@ -55,7 +60,7 @@ export default function FileCard({ fileName, content, fileType }) {
return ( return (
<button onClick={handleDownload} className="file-card-modal-button"> <button onClick={handleDownload} className="file-card-modal-button">
<div className="file-card-modal-div"> <div className="file-card-modal-div">
{" "} ooooooooooooooo{" "}
{fileType === "image" ? ( {fileType === "image" ? (
<img <img
src={`data:image/png;base64,${content}`} src={`data:image/png;base64,${content}`}

View file

@ -24,6 +24,7 @@ import {
import { Textarea } from "../../components/ui/textarea"; import { Textarea } from "../../components/ui/textarea";
import { CHAT_FORM_DIALOG_SUBTITLE } from "../../constants/constants"; import { CHAT_FORM_DIALOG_SUBTITLE } from "../../constants/constants";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { TabsState } from "../../types/tabs";
import { validateNodes } from "../../utils/reactflowUtils"; import { validateNodes } from "../../utils/reactflowUtils";
export default function FormModal({ export default function FormModal({
@ -34,7 +35,7 @@ export default function FormModal({
open: boolean; open: boolean;
setOpen: (open: boolean) => void; setOpen: (open: boolean) => void;
flow: FlowType; flow: FlowType;
}) { }): JSX.Element {
const { tabsState, setTabsState } = useContext(TabsContext); const { tabsState, setTabsState } = useContext(TabsContext);
const [chatValue, setChatValue] = useState(() => { const [chatValue, setChatValue] = useState(() => {
try { try {
@ -45,11 +46,11 @@ export default function FormModal({
const inputKeys = formKeysData.input_keys; const inputKeys = formKeysData.input_keys;
const handleKeys = formKeysData.handle_keys; const handleKeys = formKeysData.handle_keys;
const keyToUse = Object.keys(inputKeys).find( const keyToUse = Object.keys(inputKeys!).find(
(key) => !handleKeys.some((j) => j === key) && inputKeys[key] === "" (key) => !handleKeys?.some((j) => j === key) && inputKeys![key] === ""
); );
return inputKeys[keyToUse]; return inputKeys![keyToUse!];
} catch (error) { } catch (error) {
console.error(error); console.error(error);
// return a sensible default or `undefined` if no default is possible // return a sensible default or `undefined` if no default is possible
@ -63,16 +64,17 @@ export default function FormModal({
const ws = useRef<WebSocket | null>(null); const ws = useRef<WebSocket | null>(null);
const [lockChat, setLockChat] = useState(false); const [lockChat, setLockChat] = useState(false);
const isOpen = useRef(open); const isOpen = useRef(open);
const messagesRef = useRef(null); const messagesRef = useRef<HTMLDivElement | null>(null);
const id = useRef(flow.id); const id = useRef(flow.id);
const tabsStateFlowId = tabsState[flow.id]; const tabsStateFlowId = tabsState[flow.id];
const tabsStateFlowIdFormKeysData = tabsStateFlowId.formKeysData; const tabsStateFlowIdFormKeysData = tabsStateFlowId.formKeysData;
const [chatKey, setChatKey] = useState(() => { const [chatKey, setChatKey] = useState(() => {
if (tabsState[flow.id]?.formKeysData?.input_keys) { if (tabsState[flow.id]?.formKeysData?.input_keys) {
return Object.keys(tabsState[flow.id].formKeysData.input_keys).find( return Object.keys(tabsState[flow.id].formKeysData.input_keys!).find(
(key) => (key) =>
!tabsState[flow.id].formKeysData.handle_keys.some((j) => j === key) && !tabsState[flow.id].formKeysData.handle_keys!.some(
tabsState[flow.id].formKeysData.input_keys[key] === "" (j) => j === key
) && tabsState[flow.id].formKeysData.input_keys![key] === ""
); );
} }
// TODO: return a sensible default // TODO: return a sensible default
@ -149,7 +151,7 @@ export default function FormModal({
}); });
} }
function handleOnClose(event: CloseEvent) { function handleOnClose(event: CloseEvent): void {
if (isOpen.current) { if (isOpen.current) {
setErrorData({ title: event.reason }); setErrorData({ title: event.reason });
setTimeout(() => { setTimeout(() => {
@ -159,7 +161,10 @@ export default function FormModal({
} }
} }
function getWebSocketUrl(chatId, isDevelopment = false) { function getWebSocketUrl(
chatId: string,
isDevelopment: boolean = false
): string {
const isSecureProtocol = const isSecureProtocol =
window.location.protocol === "https:" || window.location.port === "443"; window.location.protocol === "https:" || window.location.port === "443";
const webSocketProtocol = isSecureProtocol ? "wss" : "ws"; const webSocketProtocol = isSecureProtocol ? "wss" : "ws";
@ -212,7 +217,7 @@ export default function FormModal({
}); });
} }
if (data.type === "start") { if (data.type === "start") {
addChatHistory("", false, chatKey); addChatHistory("", false, chatKey!);
isStream = true; isStream = true;
} }
if (data.type === "end") { if (data.type === "end") {
@ -241,7 +246,7 @@ export default function FormModal({
} }
} }
function connectWS() { function connectWS(): void {
try { try {
const urlWs = getWebSocketUrl( const urlWs = getWebSocketUrl(
id.current, id.current,
@ -308,15 +313,15 @@ export default function FormModal({
// do not add connectWS on dependencies array // do not add connectWS on dependencies array
}, [lockChat]); }, [lockChat]);
async function sendAll(data: sendAllProps) { async function sendAll(data: sendAllProps): Promise<void> {
try { try {
if (ws) { if (ws) {
ws.current.send(JSON.stringify(data)); ws.current?.send(JSON.stringify(data));
} }
} catch (error) { } catch (error) {
setErrorData({ setErrorData({
title: "There was an error sending the message", title: "There was an error sending the message",
list: [error.message], list: [(error as { message: string }).message],
}); });
setChatValue(data.inputs); setChatValue(data.inputs);
connectWS(); connectWS();
@ -327,7 +332,7 @@ export default function FormModal({
if (ref.current) ref.current.scrollIntoView({ behavior: "smooth" }); if (ref.current) ref.current.scrollIntoView({ behavior: "smooth" });
}, [chatHistory]); }, [chatHistory]);
const ref = useRef(null); const ref = useRef<HTMLDivElement | null>(null);
useEffect(() => { useEffect(() => {
if (open && ref.current) { if (open && ref.current) {
@ -335,30 +340,31 @@ export default function FormModal({
} }
}, [open]); }, [open]);
function sendMessage() { function sendMessage(): void {
let nodeValidationErrors = validateNodes(reactFlowInstance); let nodeValidationErrors = validateNodes(reactFlowInstance!);
if (nodeValidationErrors.length === 0) { if (nodeValidationErrors.length === 0) {
setLockChat(true); setLockChat(true);
let inputs = tabsState[id.current].formKeysData.input_keys; let inputs = tabsState[id.current].formKeysData.input_keys;
setChatValue(""); setChatValue("");
const message = inputs; const message = inputs;
addChatHistory( addChatHistory(
message, message!,
true, true,
chatKey, chatKey!,
tabsState[flow.id].formKeysData.template tabsState[flow.id].formKeysData.template
); );
sendAll({ sendAll({
...reactFlowInstance.toObject(), ...reactFlowInstance?.toObject()!,
inputs: inputs, inputs: inputs!,
chatHistory, chatHistory,
name: flow.name, name: flow.name,
description: flow.description, description: flow.description,
}); });
setTabsState((old) => { //@ts-ignore
setTabsState((old: TabsState) => {
if (!chatKey) return old; if (!chatKey) return old;
let newTabsState = _.cloneDeep(old); let newTabsState = _.cloneDeep(old);
newTabsState[id.current].formKeysData.input_keys[chatKey] = ""; newTabsState[id.current].formKeysData.input_keys![chatKey] = "";
return newTabsState; return newTabsState;
}); });
} else { } else {
@ -368,18 +374,18 @@ export default function FormModal({
}); });
} }
} }
function clearChat() { function clearChat(): void {
setChatHistory([]); setChatHistory([]);
ws.current.send(JSON.stringify({ clear_history: true })); ws.current?.send(JSON.stringify({ clear_history: true }));
if (lockChat) setLockChat(false); if (lockChat) setLockChat(false);
} }
function handleOnCheckedChange(checked: boolean, i: string) { function handleOnCheckedChange(checked: boolean, i: string) {
if (checked === true) { if (checked === true) {
setChatKey(i); setChatKey(i);
setChatValue(tabsState[flow.id].formKeysData.input_keys[i]); setChatValue(tabsState[flow.id].formKeysData.input_keys![i]);
} else { } else {
setChatKey(null); setChatKey(null!);
setChatValue(""); setChatValue("");
} }
} }
@ -425,7 +431,7 @@ export default function FormModal({
{tabsState[id.current]?.formKeysData?.input_keys {tabsState[id.current]?.formKeysData?.input_keys
? Object.keys( ? Object.keys(
tabsState[id.current].formKeysData.input_keys tabsState[id.current].formKeysData.input_keys!
).map((key, index) => ( ).map((key, index) => (
<div className="file-component-accordion-div" key={index}> <div className="file-component-accordion-div" key={index}>
<AccordionComponent <AccordionComponent
@ -449,7 +455,7 @@ export default function FormModal({
size="small" size="small"
disabled={tabsState[ disabled={tabsState[
id.current id.current
].formKeysData.handle_keys.some( ].formKeysData.handle_keys!.some(
(t) => t === key (t) => t === key
)} )}
/> />
@ -460,7 +466,7 @@ export default function FormModal({
keyValue={key} keyValue={key}
> >
<div className="file-component-tab-column"> <div className="file-component-tab-column">
{tabsState[id.current].formKeysData.handle_keys.some( {tabsState[id.current].formKeysData.handle_keys!.some(
(t) => t === key (t) => t === key
) && ( ) && (
<div className="font-normal text-muted-foreground "> <div className="font-normal text-muted-foreground ">
@ -470,14 +476,18 @@ export default function FormModal({
<Textarea <Textarea
className="custom-scroll" className="custom-scroll"
value={ value={
tabsState[id.current].formKeysData.input_keys[key] tabsState[id.current].formKeysData.input_keys![
key
]
} }
onChange={(e) => { onChange={(e) => {
setTabsState((old) => { //@ts-ignore
setTabsState((old: TabsState) => {
let newTabsState = _.cloneDeep(old); let newTabsState = _.cloneDeep(old);
newTabsState[ newTabsState[
id.current id.current
].formKeysData.input_keys[key] = e.target.value; ].formKeysData.input_keys![key] =
e.target.value;
return newTabsState; return newTabsState;
}); });
}} }}
@ -489,7 +499,7 @@ export default function FormModal({
</div> </div>
)) ))
: null} : null}
{tabsState[id.current].formKeysData.memory_keys.map( {tabsState[id.current].formKeysData.memory_keys!.map(
(key, index) => ( (key, index) => (
<div className="file-component-accordion-div" key={index}> <div className="file-component-accordion-div" key={index}>
<AccordionComponent <AccordionComponent
@ -583,10 +593,11 @@ export default function FormModal({
sendMessage={sendMessage} sendMessage={sendMessage}
setChatValue={(value) => { setChatValue={(value) => {
setChatValue(value); setChatValue(value);
setTabsState((old) => { //@ts-ignore
setTabsState((old: TabsState) => {
let newTabsState = _.cloneDeep(old); let newTabsState = _.cloneDeep(old);
newTabsState[id.current].formKeysData.input_keys[ newTabsState[id.current].formKeysData.input_keys![
chatKey chatKey!
] = value; ] = value;
return newTabsState; return newTabsState;
}); });

View file

@ -1,4 +1,4 @@
import { ReactNode, useContext, useEffect, useRef, useState } from "react"; import { useContext, useEffect, useRef, useState } from "react";
import SanitizedHTMLWrapper from "../../components/SanitizedHTMLWrapper"; import SanitizedHTMLWrapper from "../../components/SanitizedHTMLWrapper";
import ShadTooltip from "../../components/ShadTooltipComponent"; import ShadTooltip from "../../components/ShadTooltipComponent";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
@ -15,7 +15,8 @@ import {
import { TypeModal } from "../../constants/enums"; import { TypeModal } from "../../constants/enums";
import { alertContext } from "../../contexts/alertContext"; import { alertContext } from "../../contexts/alertContext";
import { postValidatePrompt } from "../../controllers/API"; import { postValidatePrompt } from "../../controllers/API";
import { APIClassType } from "../../types/api"; import { genericModalPropsType } from "../../types/components";
import { handleKeyDown } from "../../utils/reactflowUtils";
import { import {
classNames, classNames,
getRandomKeyByssmm, getRandomKeyByssmm,
@ -33,39 +34,29 @@ export default function GenericModal({
nodeClass, nodeClass,
setNodeClass, setNodeClass,
children, children,
}: { }: genericModalPropsType): JSX.Element {
field_name?: string;
setValue: (value: string) => void;
value: string;
buttonText: string;
modalTitle: string;
type: number;
children: ReactNode;
nodeClass?: APIClassType;
setNodeClass?: (Class: APIClassType) => void;
}) {
const [myButtonText] = useState(buttonText); const [myButtonText] = useState(buttonText);
const [myModalTitle] = useState(modalTitle); const [myModalTitle] = useState(modalTitle);
const [myModalType] = useState(type); const [myModalType] = useState(type);
const [inputValue, setInputValue] = useState(value); const [inputValue, setInputValue] = useState(value);
const [isEdit, setIsEdit] = useState(true); const [isEdit, setIsEdit] = useState(true);
const [wordsHighlight, setWordsHighlight] = useState([]); const [wordsHighlight, setWordsHighlight] = useState<string[]>([]);
const { setErrorData, setSuccessData, setNoticeData } = const { setErrorData, setSuccessData, setNoticeData } =
useContext(alertContext); useContext(alertContext);
const ref = useRef(); const ref = useRef();
const divRef = useRef(null); const divRef = useRef(null);
const divRefPrompt = useRef(null); const divRefPrompt = useRef(null);
function checkVariables(valueToCheck) { function checkVariables(valueToCheck: string): void {
const regex = /\{([^{}]+)\}/g; const regex = /\{([^{}]+)\}/g;
const matches = []; const matches: string[] = [];
let match; let match;
while ((match = regex.exec(valueToCheck))) { while ((match = regex.exec(valueToCheck))) {
matches.push(`{${match[1]}}`); matches.push(`{${match[1]}}`);
} }
let invalid_chars = []; let invalid_chars: string[] = [];
let fixed_variables = []; let fixed_variables: string[] = [];
let input_variables = matches; let input_variables = matches;
for (let variable of input_variables) { for (let variable of input_variables) {
let new_var = variable; let new_var = variable;
@ -106,7 +97,7 @@ export default function GenericModal({
.replace(regexHighlight, varHighlightHTML({ name: "$1" })) .replace(regexHighlight, varHighlightHTML({ name: "$1" }))
.replace(/\n/g, "<br />"); .replace(/\n/g, "<br />");
const TextAreaContentView = () => { const TextAreaContentView = (): JSX.Element => {
return ( return (
<SanitizedHTMLWrapper <SanitizedHTMLWrapper
className={getClassByNumberLength()} className={getClassByNumberLength()}
@ -119,7 +110,7 @@ export default function GenericModal({
); );
}; };
function getClassByNumberLength() { function getClassByNumberLength(): string {
let sumOfCaracteres: number = 0; let sumOfCaracteres: number = 0;
wordsHighlight.forEach((element) => { wordsHighlight.forEach((element) => {
sumOfCaracteres = sumOfCaracteres + element.replace(/[{}]/g, "").length; sumOfCaracteres = sumOfCaracteres + element.replace(/[{}]/g, "").length;
@ -129,8 +120,8 @@ export default function GenericModal({
: "code-nohighlight"; : "code-nohighlight";
} }
function validatePrompt(closeModal: boolean) { function validatePrompt(closeModal: boolean): void {
postValidatePrompt(field_name, inputValue, nodeClass) postValidatePrompt(field_name, inputValue, nodeClass!)
.then((apiReturn) => { .then((apiReturn) => {
if (apiReturn.data) { if (apiReturn.data) {
let inputVariables = apiReturn.data.input_variables ?? []; let inputVariables = apiReturn.data.input_variables ?? [];
@ -144,7 +135,7 @@ export default function GenericModal({
setSuccessData({ setSuccessData({
title: "Prompt is ready", title: "Prompt is ready",
}); });
setNodeClass(apiReturn.data?.frontend_node); setNodeClass!(apiReturn.data?.frontend_node);
setModalOpen(closeModal); setModalOpen(closeModal);
setValue(inputValue); setValue(inputValue);
} }
@ -213,11 +204,15 @@ export default function GenericModal({
checkVariables(event.target.value); checkVariables(event.target.value);
}} }}
placeholder="Type message here." placeholder="Type message here."
onKeyDown={(e) => {
handleKeyDown(e, inputValue, "");
}}
/> />
) : type === TypeModal.PROMPT && !isEdit ? ( ) : type === TypeModal.PROMPT && !isEdit ? (
<TextAreaContentView /> <TextAreaContentView />
) : type !== TypeModal.PROMPT ? ( ) : type !== TypeModal.PROMPT ? (
<Textarea <Textarea
//@ts-ignore
ref={ref} ref={ref}
className="form-input h-full w-full rounded-lg focus-visible:ring-1" className="form-input h-full w-full rounded-lg focus-visible:ring-1"
value={inputValue} value={inputValue}
@ -225,6 +220,9 @@ export default function GenericModal({
setInputValue(event.target.value); setInputValue(event.target.value);
}} }}
placeholder="Type message here." placeholder="Type message here."
onKeyDown={(e) => {
handleKeyDown(e, value, "");
}}
/> />
) : ( ) : (
<></> <></>

View file

@ -1,4 +1,4 @@
import { ReactNode } from "react"; import { buttonBoxPropsType } from "../../../types/components";
import { classNames } from "../../../utils/utils"; import { classNames } from "../../../utils/utils";
export default function ButtonBox({ export default function ButtonBox({
@ -10,16 +10,7 @@ export default function ButtonBox({
textColor, textColor,
deactivate, deactivate,
size, size,
}: { }: buttonBoxPropsType): JSX.Element {
onClick: () => void;
title: string;
description: string;
icon: ReactNode;
bgColor: string;
textColor: string;
deactivate?: boolean;
size: "small" | "medium" | "big";
}) {
let bigCircle: string; let bigCircle: string;
let smallCircle: string; let smallCircle: string;
let titleFontSize: string; let titleFontSize: string;

View file

@ -23,7 +23,7 @@ import { FlowType } from "../../types/flow";
import { classNames } from "../../utils/utils"; import { classNames } from "../../utils/utils";
import ButtonBox from "./buttonBox"; import ButtonBox from "./buttonBox";
export default function ImportModal() { export default function ImportModal(): JSX.Element {
const [open, setOpen] = useState(true); const [open, setOpen] = useState(true);
const { setErrorData } = useContext(alertContext); const { setErrorData } = useContext(alertContext);
const ref = useRef(); const ref = useRef();
@ -32,7 +32,7 @@ export default function ImportModal() {
const [examples, setExamples] = useState<FlowType[]>([]); const [examples, setExamples] = useState<FlowType[]>([]);
const { uploadFlow, addFlow } = useContext(TabsContext); const { uploadFlow, addFlow } = useContext(TabsContext);
function handleExamples() { function handleExamples(): void {
setLoadingExamples(true); setLoadingExamples(true);
getExamples() getExamples()
.then((result) => { .then((result) => {

View file

@ -6,9 +6,10 @@ import { TabsContext } from "../../contexts/tabsContext";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { CardComponent } from "../../components/cardComponent"; import { CardComponent } from "../../components/cardComponent";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import Header from "../../components/headerComponent";
import { getExamples } from "../../controllers/API"; import { getExamples } from "../../controllers/API";
import { FlowType } from "../../types/flow"; import { FlowType } from "../../types/flow";
export default function CommunityPage() { export default function CommunityPage(): JSX.Element {
const { flows, setTabId, downloadFlows, uploadFlows, addFlow } = const { flows, setTabId, downloadFlows, uploadFlows, addFlow } =
useContext(TabsContext); useContext(TabsContext);
@ -21,7 +22,7 @@ export default function CommunityPage() {
const [examples, setExamples] = useState<FlowType[]>([]); const [examples, setExamples] = useState<FlowType[]>([]);
// Show community examples on screen // Show community examples on screen
function handleExamples() { function handleExamples(): void {
setLoadingExamples(true); setLoadingExamples(true);
getExamples() getExamples()
.then((result) => { .then((result) => {
@ -42,6 +43,9 @@ export default function CommunityPage() {
handleExamples(); handleExamples();
}, []); }, []);
return ( return (
<>
<Header />
<div className="community-page-arrangement"> <div className="community-page-arrangement">
<div className="community-page-nav-arrangement"> <div className="community-page-nav-arrangement">
<span className="community-page-nav-title"> <span className="community-page-nav-title">
@ -98,5 +102,6 @@ export default function CommunityPage() {
))} ))}
</div> </div>
</div> </div>
</>
); );
} }

View file

@ -6,7 +6,7 @@ const ConnectionLineComponent = ({
toX, toX,
toY, toY,
connectionLineStyle = {}, // provide a default value for connectionLineStyle connectionLineStyle = {}, // provide a default value for connectionLineStyle
}: ConnectionLineComponentProps) => { }: ConnectionLineComponentProps): JSX.Element => {
return ( return (
<g> <g>
<path <path

View file

@ -6,7 +6,7 @@ export default function DisclosureComponent({
button: { title, Icon, buttons = [] }, button: { title, Icon, buttons = [] },
children, children,
openDisc, openDisc,
}: DisclosureComponentType) { }: DisclosureComponentType): JSX.Element {
return ( return (
<Disclosure as="div" key={title}> <Disclosure as="div" key={title}>
{({ open }) => ( {({ open }) => (

View file

@ -1,11 +1,19 @@
import _ from "lodash"; import _ from "lodash";
import { useCallback, useContext, useEffect, useRef, useState } from "react"; import {
MouseEvent,
useCallback,
useContext,
useEffect,
useRef,
useState,
} from "react";
import ReactFlow, { import ReactFlow, {
Background, Background,
Connection, Connection,
Controls, Controls,
Edge, Edge,
EdgeChange, EdgeChange,
Node,
NodeChange, NodeChange,
NodeDragHandler, NodeDragHandler,
OnEdgesDelete, OnEdgesDelete,
@ -26,6 +34,7 @@ import { typesContext } from "../../../../contexts/typesContext";
import { undoRedoContext } from "../../../../contexts/undoRedoContext"; import { undoRedoContext } from "../../../../contexts/undoRedoContext";
import { APIClassType } from "../../../../types/api"; import { APIClassType } from "../../../../types/api";
import { FlowType, NodeType } from "../../../../types/flow"; import { FlowType, NodeType } from "../../../../types/flow";
import { TabsState } from "../../../../types/tabs";
import { isValidConnection } from "../../../../utils/reactflowUtils"; import { isValidConnection } from "../../../../utils/reactflowUtils";
import { isWrappedWithClass } from "../../../../utils/utils"; import { isWrappedWithClass } from "../../../../utils/utils";
import ConnectionLineComponent from "../ConnectionLineComponent"; import ConnectionLineComponent from "../ConnectionLineComponent";
@ -35,7 +44,13 @@ const nodeTypes = {
genericNode: GenericNode, genericNode: GenericNode,
}; };
export default function Page({ flow }: { flow: FlowType }) { export default function Page({
flow,
view,
}: {
flow: FlowType;
view?: boolean;
}): JSX.Element {
let { let {
updateFlow, updateFlow,
uploadFlow, uploadFlow,
@ -51,13 +66,13 @@ export default function Page({ flow }: { flow: FlowType }) {
} = useContext(TabsContext); } = useContext(TabsContext);
const { types, reactFlowInstance, setReactFlowInstance, templates } = const { types, reactFlowInstance, setReactFlowInstance, templates } =
useContext(typesContext); useContext(typesContext);
const reactFlowWrapper = useRef(null); const reactFlowWrapper = useRef<HTMLDivElement>(null);
const { takeSnapshot } = useContext(undoRedoContext); const { takeSnapshot } = useContext(undoRedoContext);
const [position, setPosition] = useState({ x: 0, y: 0 }); const [position, setPosition] = useState({ x: 0, y: 0 });
const [lastSelection, setLastSelection] = const [lastSelection, setLastSelection] =
useState<OnSelectionChangeParams>(null); useState<OnSelectionChangeParams | null>(null);
useEffect(() => { useEffect(() => {
// this effect is used to attach the global event handlers // this effect is used to attach the global event handlers
@ -78,10 +93,10 @@ export default function Page({ flow }: { flow: FlowType }) {
lastCopiedSelection lastCopiedSelection
) { ) {
event.preventDefault(); event.preventDefault();
let bounds = reactFlowWrapper.current.getBoundingClientRect(); let bounds = reactFlowWrapper.current?.getBoundingClientRect();
paste(lastCopiedSelection, { paste(lastCopiedSelection, {
x: position.x - bounds.left, x: position.x - bounds!.left,
y: position.y - bounds.top, y: position.y - bounds!.top,
}); });
} }
if ( if (
@ -147,7 +162,8 @@ export default function Page({ flow }: { flow: FlowType }) {
let newX = _.cloneDeep(node); let newX = _.cloneDeep(node);
return newX; return newX;
}); });
setTabsState((prev) => { //@ts-ignore
setTabsState((prev: TabsState) => {
return { return {
...prev, ...prev,
[tabId]: { [tabId]: {
@ -163,7 +179,8 @@ export default function Page({ flow }: { flow: FlowType }) {
const onNodesChangeMod = useCallback( const onNodesChangeMod = useCallback(
(change: NodeChange[]) => { (change: NodeChange[]) => {
onNodesChange(change); onNodesChange(change);
setTabsState((prev) => { //@ts-ignore
setTabsState((prev: TabsState) => {
return { return {
...prev, ...prev,
[tabId]: { [tabId]: {
@ -185,10 +202,10 @@ export default function Page({ flow }: { flow: FlowType }) {
...params, ...params,
style: { stroke: "#555" }, style: { stroke: "#555" },
className: className:
(params.targetHandle.split("|")[0] === "Text" (params.targetHandle?.split("|")[0] === "Text"
? "stroke-foreground " ? "stroke-foreground "
: "stroke-foreground ") + " stroke-connection", : "stroke-foreground ") + " stroke-connection",
animated: params.targetHandle.split("|")[0] === "Text", animated: params.targetHandle?.split("|")[0] === "Text",
}, },
eds eds
) )
@ -234,7 +251,7 @@ export default function Page({ flow }: { flow: FlowType }) {
// Get the current bounds of the ReactFlow wrapper element // Get the current bounds of the ReactFlow wrapper element
const reactflowBounds = const reactflowBounds =
reactFlowWrapper.current.getBoundingClientRect(); reactFlowWrapper.current?.getBoundingClientRect();
// Extract the data from the drag event and parse it as a JSON object // Extract the data from the drag event and parse it as a JSON object
let data: { type: string; node?: APIClassType } = JSON.parse( let data: { type: string; node?: APIClassType } = JSON.parse(
@ -243,9 +260,9 @@ export default function Page({ flow }: { flow: FlowType }) {
// If data type is not "chatInput" or if there are no "chatInputNode" nodes present in the ReactFlow instance, create a new node // If data type is not "chatInput" or if there are no "chatInputNode" nodes present in the ReactFlow instance, create a new node
// Calculate the position where the node should be created // Calculate the position where the node should be created
const position = reactFlowInstance.project({ const position = reactFlowInstance!.project({
x: event.clientX - reactflowBounds.left, x: event.clientX - reactflowBounds!.left,
y: event.clientY - reactflowBounds.top, y: event.clientY - reactflowBounds!.top,
}); });
// Generate a unique node ID // Generate a unique node ID
@ -262,7 +279,6 @@ export default function Page({ flow }: { flow: FlowType }) {
data: { data: {
...data, ...data,
id: newId, id: newId,
value: null,
}, },
}; };
} else { } else {
@ -274,7 +290,6 @@ export default function Page({ flow }: { flow: FlowType }) {
data: { data: {
...data, ...data,
id: newId, id: newId,
value: null,
}, },
}; };
@ -283,7 +298,7 @@ export default function Page({ flow }: { flow: FlowType }) {
setNodes((nds) => nds.concat(newNode)); setNodes((nds) => nds.concat(newNode));
} else if (event.dataTransfer.types.some((types) => types === "Files")) { } else if (event.dataTransfer.types.some((types) => types === "Files")) {
takeSnapshot(); takeSnapshot();
uploadFlow(false, event.dataTransfer.files.item(0)); uploadFlow(false, event.dataTransfer.files.item(0)!);
} }
}, },
// Specify dependencies for useCallback // Specify dependencies for useCallback
@ -299,7 +314,7 @@ export default function Page({ flow }: { flow: FlowType }) {
}, []); }, []);
const onDelete = useCallback( const onDelete = useCallback(
(mynodes) => { (mynodes: Node[]) => {
takeSnapshot(); takeSnapshot();
setEdges( setEdges(
edges.filter( edges.filter(
@ -319,7 +334,7 @@ export default function Page({ flow }: { flow: FlowType }) {
const onEdgeUpdate = useCallback( const onEdgeUpdate = useCallback(
(oldEdge: Edge, newConnection: Connection) => { (oldEdge: Edge, newConnection: Connection) => {
if (isValidConnection(newConnection, reactFlowInstance)) { if (isValidConnection(newConnection, reactFlowInstance!)) {
edgeUpdateSuccessful.current = true; edgeUpdateSuccessful.current = true;
setEdges((els) => updateEdge(oldEdge, newConnection, els)); setEdges((els) => updateEdge(oldEdge, newConnection, els));
} }
@ -327,11 +342,10 @@ export default function Page({ flow }: { flow: FlowType }) {
[reactFlowInstance, setEdges] [reactFlowInstance, setEdges]
); );
const onEdgeUpdateEnd = useCallback((_, edge) => { const onEdgeUpdateEnd = useCallback((_, edge: Edge): void => {
if (!edgeUpdateSuccessful.current) { if (!edgeUpdateSuccessful.current) {
setEdges((eds) => eds.filter((edg) => edg.id !== edge.id)); setEdges((eds) => eds.filter((edg) => edg.id !== edge.id));
} }
edgeUpdateSuccessful.current = true; edgeUpdateSuccessful.current = true;
}, []); }, []);
@ -340,7 +354,7 @@ export default function Page({ flow }: { flow: FlowType }) {
const onSelectionEnd = useCallback(() => { const onSelectionEnd = useCallback(() => {
setSelectionEnded(true); setSelectionEnded(true);
}, []); }, []);
const onSelectionStart = useCallback((event) => { const onSelectionStart = useCallback((event: MouseEvent) => {
event.preventDefault(); event.preventDefault();
setSelectionEnded(false); setSelectionEnded(false);
}, []); }, []);
@ -354,13 +368,16 @@ export default function Page({ flow }: { flow: FlowType }) {
} }
}, [selectionEnded, lastSelection]); }, [selectionEnded, lastSelection]);
const onSelectionChange = useCallback((flow) => { const onSelectionChange = useCallback(
(flow: OnSelectionChangeParams): void => {
setLastSelection(flow); setLastSelection(flow);
}, []); },
[]
);
return ( return (
<div className="flex h-full overflow-hidden"> <div className="flex h-full overflow-hidden">
<ExtraSidebar /> {!view && <ExtraSidebar />}
{/* Main area */} {/* Main area */}
<main className="flex flex-1"> <main className="flex flex-1">
{/* Primary column */} {/* Primary column */}
@ -383,7 +400,6 @@ export default function Page({ flow }: { flow: FlowType }) {
onEdgesChange={onEdgesChangeMod} onEdgesChange={onEdgesChangeMod}
onConnect={onConnect} onConnect={onConnect}
disableKeyboardA11y={true} disableKeyboardA11y={true}
onLoad={setReactFlowInstance}
onInit={setReactFlowInstance} onInit={setReactFlowInstance}
nodeTypes={nodeTypes} nodeTypes={nodeTypes}
onEdgeUpdate={onEdgeUpdate} onEdgeUpdate={onEdgeUpdate}
@ -402,14 +418,21 @@ export default function Page({ flow }: { flow: FlowType }) {
className="theme-attribution" className="theme-attribution"
minZoom={0.01} minZoom={0.01}
maxZoom={8} maxZoom={8}
zoomOnScroll={!view}
zoomOnPinch={!view}
panOnDrag={!view}
> >
<Background className="" /> <Background className="" />
{!view && (
<Controls <Controls
className="bg-muted fill-foreground stroke-foreground text-primary className="bg-muted fill-foreground stroke-foreground text-primary
[&>button]:border-b-border hover:[&>button]:bg-border" [&>button]:border-b-border hover:[&>button]:bg-border"
></Controls> ></Controls>
)}
</ReactFlow> </ReactFlow>
<Chat flow={flow} reactFlowInstance={reactFlowInstance} /> {!view && (
<Chat flow={flow} reactFlowInstance={reactFlowInstance!} />
)}
</div> </div>
) : ( ) : (
<></> <></>

View file

@ -17,7 +17,7 @@ import {
import { classNames } from "../../../../utils/utils"; import { classNames } from "../../../../utils/utils";
import DisclosureComponent from "../DisclosureComponent"; import DisclosureComponent from "../DisclosureComponent";
export default function ExtraSidebar() { export default function ExtraSidebar(): JSX.Element {
const { data, templates } = useContext(typesContext); const { data, templates } = useContext(typesContext);
const { flows, tabId, uploadFlow, tabsState, saveFlow, isBuilt } = const { flows, tabId, uploadFlow, tabsState, saveFlow, isBuilt } =
useContext(TabsContext); useContext(TabsContext);
@ -28,7 +28,7 @@ export default function ExtraSidebar() {
function onDragStart( function onDragStart(
event: React.DragEvent<any>, event: React.DragEvent<any>,
data: { type: string; node?: APIClassType } data: { type: string; node?: APIClassType }
) { ): void {
//start drag event //start drag event
var crt = event.currentTarget.cloneNode(true); var crt = event.currentTarget.cloneNode(true);
crt.style.position = "absolute"; crt.style.position = "absolute";
@ -59,7 +59,7 @@ export default function ExtraSidebar() {
const flow = flows.find((flow) => flow.id === tabId); const flow = flows.find((flow) => flow.id === tabId);
useEffect(() => { useEffect(() => {
// show components with error on load // show components with error on load
let errors = []; let errors: string[] = [];
Object.keys(templates).forEach((component) => { Object.keys(templates).forEach((component) => {
if (templates[component].error) { if (templates[component].error) {
errors.push(component); errors.push(component);
@ -120,7 +120,7 @@ export default function ExtraSidebar() {
"extra-side-bar-buttons " + (isPending ? "" : "button-disable") "extra-side-bar-buttons " + (isPending ? "" : "button-disable")
} }
onClick={(event) => { onClick={(event) => {
saveFlow(flow); saveFlow(flow!);
setSuccessData({ title: "Changes saved successfully" }); setSuccessData({ title: "Changes saved successfully" });
}} }}
> >

View file

@ -4,14 +4,19 @@ import ShadTooltip from "../../../../components/ShadTooltipComponent";
import IconComponent from "../../../../components/genericIconComponent"; import IconComponent from "../../../../components/genericIconComponent";
import { TabsContext } from "../../../../contexts/tabsContext"; import { TabsContext } from "../../../../contexts/tabsContext";
import EditNodeModal from "../../../../modals/EditNodeModal"; import EditNodeModal from "../../../../modals/EditNodeModal";
import { nodeToolbarPropsType } from "../../../../types/components";
import { classNames } from "../../../../utils/utils"; import { classNames } from "../../../../utils/utils";
export default function NodeToolbarComponent({ data, setData, deleteNode }) { export default function NodeToolbarComponent({
data,
setData,
deleteNode,
}: nodeToolbarPropsType): JSX.Element {
const [nodeLength, setNodeLength] = useState( const [nodeLength, setNodeLength] = useState(
Object.keys(data.node.template).filter( Object.keys(data.node!.template).filter(
(templateField) => (templateField) =>
templateField.charAt(0) !== "_" && templateField.charAt(0) !== "_" &&
data.node.template[templateField].show && data.node?.template[templateField].show &&
(data.node.template[templateField].type === "str" || (data.node.template[templateField].type === "str" ||
data.node.template[templateField].type === "bool" || data.node.template[templateField].type === "bool" ||
data.node.template[templateField].type === "float" || data.node.template[templateField].type === "float" ||
@ -55,8 +60,8 @@ export default function NodeToolbarComponent({ data, setData, deleteNode }) {
{ {
x: 50, x: 50,
y: 10, y: 10,
paneX: reactFlowInstance.getNode(data.id).position.x, paneX: reactFlowInstance.getNode(data.id)?.position.x,
paneY: reactFlowInstance.getNode(data.id).position.y, paneY: reactFlowInstance.getNode(data.id)?.position.y,
} }
); );
}} }}
@ -67,23 +72,23 @@ export default function NodeToolbarComponent({ data, setData, deleteNode }) {
<ShadTooltip <ShadTooltip
content={ content={
data.node.documentation === "" ? "Coming Soon" : "Documentation" data.node?.documentation === "" ? "Coming Soon" : "Documentation"
} }
side="top" side="top"
> >
<a <a
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" +
(data.node.documentation === "" (data.node?.documentation === ""
? " text-muted-foreground" ? " text-muted-foreground"
: " text-foreground") : " text-foreground")
)} )}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
href={data.node.documentation} href={data.node?.documentation}
// deactivate link if no documentation is provided // deactivate link if no documentation is provided
onClick={(event) => { onClick={(event) => {
if (data.node.documentation === "") { if (data.node?.documentation === "") {
event.preventDefault(); event.preventDefault();
} }
}} }}

View file

@ -1,16 +1,17 @@
import { useContext, useEffect, useState } from "react"; import { useContext, useEffect, useState } from "react";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import Header from "../../components/headerComponent";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { getVersion } from "../../controllers/API"; import { getVersion } from "../../controllers/API";
import Page from "./components/PageComponent"; import Page from "./components/PageComponent";
export default function FlowPage() { export default function FlowPage(): JSX.Element {
const { flows, tabId, setTabId } = useContext(TabsContext); const { flows, tabId, setTabId } = useContext(TabsContext);
const { id } = useParams(); const { id } = useParams();
// Set flow tab id // Set flow tab id
useEffect(() => { useEffect(() => {
setTabId(id); setTabId(id!);
}, [id]); }, [id]);
// Initialize state variable for the version // Initialize state variable for the version
@ -22,11 +23,13 @@ export default function FlowPage() {
}, []); }, []);
return ( return (
<>
<Header />
<div className="flow-page-positioning"> <div className="flow-page-positioning">
{flows.length > 0 && {flows.length > 0 &&
tabId !== "" && tabId !== "" &&
flows.findIndex((flow) => flow.id === tabId) !== -1 && ( flows.findIndex((flow) => flow.id === tabId) !== -1 && (
<Page flow={flows.find((flow) => flow.id === tabId)} /> <Page flow={flows.find((flow) => flow.id === tabId)!} />
)} )}
<a <a
target={"_blank"} target={"_blank"}
@ -37,5 +40,6 @@ export default function FlowPage() {
<div className={version ? "mt-2" : "mt-1"}>Created by Logspace</div> <div className={version ? "mt-2" : "mt-1"}>Created by Logspace</div>
</a> </a>
</div> </div>
</>
); );
} }

View file

@ -2,10 +2,11 @@ import { useContext, useEffect } from "react";
import { Link, useNavigate } from "react-router-dom"; import { Link, useNavigate } from "react-router-dom";
import { CardComponent } from "../../components/cardComponent"; import { CardComponent } from "../../components/cardComponent";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import Header from "../../components/headerComponent";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
import { USER_PROJECTS_HEADER } from "../../constants/constants"; import { USER_PROJECTS_HEADER } from "../../constants/constants";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
export default function HomePage() { export default function HomePage(): JSX.Element {
const { flows, setTabId, downloadFlows, uploadFlows, addFlow, removeFlow } = const { flows, setTabId, downloadFlows, uploadFlows, addFlow, removeFlow } =
useContext(TabsContext); useContext(TabsContext);
@ -17,6 +18,8 @@ export default function HomePage() {
// Personal flows display // Personal flows display
return ( return (
<>
<Header />
<div className="main-page-panel"> <div className="main-page-panel">
<div className="main-page-nav-arrangement"> <div className="main-page-nav-arrangement">
<span className="main-page-nav-title"> <span className="main-page-nav-title">
@ -45,7 +48,7 @@ export default function HomePage() {
<Button <Button
variant="primary" variant="primary"
onClick={() => { onClick={() => {
addFlow(null, true).then((id) => { addFlow(null!, true).then((id) => {
navigate("/flow/" + id); navigate("/flow/" + id);
}); });
}} }}
@ -86,5 +89,6 @@ export default function HomePage() {
))} ))}
</div> </div>
</div> </div>
</>
); );
} }

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