Merge branch 'dev' into docs-explain-io-playground
This commit is contained in:
commit
25307e7509
62 changed files with 1276 additions and 962 deletions
3
.github/workflows/docker-build.yml
vendored
3
.github/workflows/docker-build.yml
vendored
|
|
@ -80,7 +80,10 @@ jobs:
|
||||||
langflowai/langflow-frontend:1.0-alpha
|
langflowai/langflow-frontend:1.0-alpha
|
||||||
|
|
||||||
restart-space:
|
restart-space:
|
||||||
|
name: Restart HuggingFace Spaces
|
||||||
|
if: ${{ inputs.release_type == 'main' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
needs: docker_build
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version:
|
python-version:
|
||||||
|
|
|
||||||
4
.github/workflows/pre-release.yml
vendored
4
.github/workflows/pre-release.yml
vendored
|
|
@ -35,6 +35,10 @@ jobs:
|
||||||
with:
|
with:
|
||||||
python-version: "3.10"
|
python-version: "3.10"
|
||||||
cache: "poetry"
|
cache: "poetry"
|
||||||
|
- name: Set up Nodejs 20
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "20"
|
||||||
- name: Check Version
|
- name: Check Version
|
||||||
id: check-version
|
id: check-version
|
||||||
run: |
|
run: |
|
||||||
|
|
|
||||||
1
Makefile
1
Makefile
|
|
@ -168,6 +168,7 @@ build_and_install:
|
||||||
|
|
||||||
build_frontend:
|
build_frontend:
|
||||||
cd src/frontend && CI='' npm run build
|
cd src/frontend && CI='' npm run build
|
||||||
|
rm -rf src/backend/base/langflow/frontend
|
||||||
cp -r src/frontend/build src/backend/base/langflow/frontend
|
cp -r src/frontend/build src/backend/base/langflow/frontend
|
||||||
|
|
||||||
build:
|
build:
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
# Keep this syntax directive! It's used to enable Docker BuildKit
|
# Keep this syntax directive! It's used to enable Docker BuildKit
|
||||||
|
|
||||||
|
|
||||||
################################
|
################################
|
||||||
# BUILDER-BASE
|
# BUILDER-BASE
|
||||||
# Used to build deps + create our virtual environment
|
# Used to build deps + create our virtual environment
|
||||||
|
|
@ -47,12 +48,10 @@ WORKDIR /app
|
||||||
COPY pyproject.toml poetry.lock README.md ./
|
COPY pyproject.toml poetry.lock README.md ./
|
||||||
COPY src/ ./src
|
COPY src/ ./src
|
||||||
COPY scripts/ ./scripts
|
COPY scripts/ ./scripts
|
||||||
|
|
||||||
RUN python -m pip install requests --user && cd ./scripts && python update_dependencies.py
|
RUN python -m pip install requests --user && cd ./scripts && python update_dependencies.py
|
||||||
RUN $POETRY_HOME/bin/poetry lock --no-update \
|
RUN $POETRY_HOME/bin/poetry lock --no-update \
|
||||||
&& $POETRY_HOME/bin/poetry install --no-interaction --no-ansi -E deploy \
|
|
||||||
&& $POETRY_HOME/bin/poetry build -f wheel \
|
&& $POETRY_HOME/bin/poetry build -f wheel \
|
||||||
&& $POETRY_HOME/bin/poetry run pip install dist/*.whl
|
&& $POETRY_HOME/bin/poetry run pip install dist/*.whl --force-reinstall
|
||||||
|
|
||||||
################################
|
################################
|
||||||
# RUNTIME
|
# RUNTIME
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,7 @@ Langflow provides an API key functionality that allows users to access their ind
|
||||||
The default user and password are set using the LANGFLOW_SUPERUSER and
|
The default user and password are set using the LANGFLOW_SUPERUSER and
|
||||||
LANGFLOW_SUPERUSER_PASSWORD environment variables.
|
LANGFLOW_SUPERUSER_PASSWORD environment variables.
|
||||||
|
|
||||||
The default values are
|
The default values are `langflow` and `langflow`, respectively.
|
||||||
langflow and langflow, respectively.
|
|
||||||
|
|
||||||
</Admonition>
|
</Admonition>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,62 +1,51 @@
|
||||||
# Command Line Interface (CLI)
|
# Command Line Interface (CLI)
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Langflow's Command Line Interface (CLI) is a powerful tool that allows you to interact with the Langflow server from the command line. The CLI provides a wide range of commands to help you shape Langflow to your needs.
|
Langflow's Command Line Interface (CLI) is a powerful tool that allows you to interact with the Langflow server from the command line. The CLI provides a wide range of commands to help you shape Langflow to your needs.
|
||||||
|
|
||||||
Running the CLI without any arguments will display a list of available commands and options.
|
The available commands are below. Navigate to their individual sections of this page to see the parameters.
|
||||||
|
|
||||||
|
* [langflow](#overview)
|
||||||
|
* [langflow api-key](#langflow-api-key)
|
||||||
|
* [langflow copy-db](#langflow-copy-db)
|
||||||
|
* [langflow migration](#langflow-migration)
|
||||||
|
* [langflow run](#langflow-run)
|
||||||
|
* [langflow superuser](#langflow-superuser)
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Running the CLI without any arguments displays a list of available options and commands.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python -m langflow run --help
|
langflow
|
||||||
# or
|
# or
|
||||||
python -m langflow run
|
langflow --help
|
||||||
|
# or
|
||||||
|
python -m langflow
|
||||||
```
|
```
|
||||||
|
|
||||||
Each option for `run` command are detailed below:
|
| Command | Description |
|
||||||
|
| ------- | ----------- |
|
||||||
|
| `api-key` | Creates an API key for the default superuser if AUTO_LOGIN is enabled. |
|
||||||
|
| `copy-db` | Copy the database files to the current directory (`which langflow`). |
|
||||||
|
| `migration` | Run or test migrations. |
|
||||||
|
| `run` | Run the Langflow. |
|
||||||
|
| `superuser` | Create a superuser. |
|
||||||
|
|
||||||
- `--help`: Displays all available options.
|
### Options
|
||||||
- `--host`: Defines the host to bind the server to. Can be set using the `LANGFLOW_HOST` environment variable. The default is `127.0.0.1`.
|
|
||||||
- `--workers`: Sets the number of worker processes. Can be set using the `LANGFLOW_WORKERS` environment variable. The default is `1`.
|
|
||||||
- `--timeout`: Sets the worker timeout in seconds. The default is `60`.
|
|
||||||
- `--port`: Sets the port to listen on. Can be set using the `LANGFLOW_PORT` environment variable. The default is `7860`.
|
|
||||||
- `--env-file`: Specifies the path to the .env file containing environment variables. The default is `.env`.
|
|
||||||
- `--log-level`: Defines the logging level. Can be set using the `LANGFLOW_LOG_LEVEL` environment variable. The default is `critical`.
|
|
||||||
- `--components-path`: Specifies the path to the directory containing custom components. Can be set using the `LANGFLOW_COMPONENTS_PATH` environment variable. The default is `langflow/components`.
|
|
||||||
- `--log-file`: Specifies the path to the log file. Can be set using the `LANGFLOW_LOG_FILE` environment variable. The default is `logs/langflow.log`.
|
|
||||||
- `--cache`: Select the type of cache to use. Options are `InMemoryCache` and `SQLiteCache`. Can be set using the `LANGFLOW_LANGCHAIN_CACHE` environment variable. The default is `SQLiteCache`.
|
|
||||||
- `--dev/--no-dev`: Toggles the development mode. The default is `no-dev`.
|
|
||||||
- `--path`: Specifies the path to the frontend directory containing build files. This option is for development purposes only. Can be set using the `LANGFLOW_FRONTEND_PATH` environment variable.
|
|
||||||
- `--open-browser/--no-open-browser`: Toggles the option to open the browser after starting the server. Can be set using the `LANGFLOW_OPEN_BROWSER` environment variable. The default is `open-browser`.
|
|
||||||
- `--remove-api-keys/--no-remove-api-keys`: Toggles the option to remove API keys from the projects saved in the database. Can be set using the `LANGFLOW_REMOVE_API_KEYS` environment variable. The default is `no-remove-api-keys`.
|
|
||||||
- `--install-completion [bash|zsh|fish|powershell|pwsh]`: Installs completion for the specified shell.
|
|
||||||
- `--show-completion [bash|zsh|fish|powershell|pwsh]`: Shows completion for the specified shell, allowing you to copy it or customize the installation.
|
|
||||||
- `--backend-only`: This parameter, with a default value of `False`, allows running only the backend server without the frontend. It can also be set using the `LANGFLOW_BACKEND_ONLY` environment variable.
|
|
||||||
- `--store`: This parameter, with a default value of `True`, enables the store features, use `--no-store` to deactivate it. It can be configured using the `LANGFLOW_STORE` environment variable.
|
|
||||||
|
|
||||||
These parameters are important for users who need to customize the behavior of Langflow, especially in development or specialized deployment scenarios.
|
| Option | Description |
|
||||||
|
| ------ | ----------- |
|
||||||
|
| `--install-completion` | Install completion for the current shell. |
|
||||||
|
| `--show-completion` | Show completion for the current shell, to copy it or customize the installation. |
|
||||||
|
| `--help` | Show this message and exit. |
|
||||||
|
|
||||||
### API Key Command
|
## langflow api-key
|
||||||
|
|
||||||
The `api-key` command allows you to create an API key for accessing Langflow's API when `LANGFLOW_AUTO_LOGIN` is set to `True`.
|
Run the `api-key` command to create an API key for the default superuser if `LANGFLOW_AUTO_LOGIN` is set to `True`.
|
||||||
|
|
||||||
```bash
|
|
||||||
python -m langflow api-key --help
|
|
||||||
|
|
||||||
Usage: langflow api-key [OPTIONS]
|
|
||||||
|
|
||||||
Creates an API key for the default superuser if AUTO_LOGIN is enabled.
|
|
||||||
Args: log_level (str, optional): Logging level. Defaults to "error".
|
|
||||||
Returns: None
|
|
||||||
|
|
||||||
╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
|
||||||
│ --log-level TEXT Logging level. [env var: LANGFLOW_LOG_LEVEL] [default: error] │
|
|
||||||
│ --help Show this message and exit. │
|
|
||||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
|
||||||
```
|
|
||||||
|
|
||||||
Once you run the `api-key` command, it will create an API key for the default superuser if `LANGFLOW_AUTO_LOGIN` is set to `True`.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
langflow api-key
|
||||||
|
# or
|
||||||
python -m langflow api-key
|
python -m langflow api-key
|
||||||
╭─────────────────────────────────────────────────────────────────────╮
|
╭─────────────────────────────────────────────────────────────────────╮
|
||||||
│ API Key Created Successfully: │
|
│ API Key Created Successfully: │
|
||||||
|
|
@ -67,11 +56,99 @@ python -m langflow api-key
|
||||||
│ Make sure to store it in a secure location. │
|
│ Make sure to store it in a secure location. │
|
||||||
│ │
|
│ │
|
||||||
│ The API key has been copied to your clipboard. Cmd + V to paste it. │
|
│ The API key has been copied to your clipboard. Cmd + V to paste it. │
|
||||||
╰─────────────────────────────────────────────────────────────────────╯
|
╰──────────────────────────────
|
||||||
```
|
```
|
||||||
|
|
||||||
### Environment Variables
|
### Options
|
||||||
|
|
||||||
|
| Option | Type | Description |
|
||||||
|
|------------------|------|-------------------------------------------------------------|
|
||||||
|
| --log-level | TEXT | Logging level. [env var: LANGFLOW_LOG_LEVEL] [default: error] |
|
||||||
|
| --help | | Show this message and exit. |
|
||||||
|
|
||||||
|
## langflow copy-db
|
||||||
|
|
||||||
|
Run the `copy-db` command to copy the cached `langflow.db` and `langflow-pre.db` database files to the current directory.
|
||||||
|
|
||||||
|
If the files exist in the cache directory, they will be copied to the same directory as `__main__.py`, which can be found with `which langflow`.
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## langflow migration
|
||||||
|
|
||||||
|
Run or test migrations with the [Alembic](https://pypi.org/project/alembic/) database tool.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
langflow migration
|
||||||
|
# or
|
||||||
|
python -m langflow migration
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
| Option | Description |
|
||||||
|
|-----------------|-------------------------------------------------------------|
|
||||||
|
| `--test, --no-test` | Run migrations in test mode. [default: test] |
|
||||||
|
| `--fix, --no-fix` | Fix migrations. This is a destructive operation, and should only be used if you know what you are doing. [default: no-fix] |
|
||||||
|
| `--help` | Show this message and exit. |
|
||||||
|
|
||||||
|
|
||||||
|
## langflow run
|
||||||
|
|
||||||
|
Run Langflow.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
langflow run
|
||||||
|
# or
|
||||||
|
python -m langflow run
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
| Option | Description |
|
||||||
|
|-------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| `--help` | Displays all available options. |
|
||||||
|
| `--host` | Defines the host to bind the server to. Can be set using the `LANGFLOW_HOST` environment variable. The default is `127.0.0.1`. |
|
||||||
|
| `--workers` | Sets the number of worker processes. Can be set using the `LANGFLOW_WORKERS` environment variable. The default is `1`. |
|
||||||
|
| `--timeout` | Sets the worker timeout in seconds. The default is `60`. |
|
||||||
|
| `--port` | Sets the port to listen on. Can be set using the `LANGFLOW_PORT` environment variable. The default is `7860`. |
|
||||||
|
| `--env-file` | Specifies the path to the .env file containing environment variables. The default is `.env`. |
|
||||||
|
| `--log-level` | Defines the logging level. Can be set using the `LANGFLOW_LOG_LEVEL` environment variable. The default is `critical`. |
|
||||||
|
| `--components-path` | Specifies the path to the directory containing custom components. Can be set using the `LANGFLOW_COMPONENTS_PATH` environment variable. The default is `langflow/components`. |
|
||||||
|
| `--log-file` | Specifies the path to the log file. Can be set using the `LANGFLOW_LOG_FILE` environment variable. The default is `logs/langflow.log`. |
|
||||||
|
| `--cache` | Select the type of cache to use. Options are `InMemoryCache` and `SQLiteCache`. Can be set using the `LANGFLOW_LANGCHAIN_CACHE` environment variable. The default is `SQLiteCache`. |
|
||||||
|
| `--dev`/`--no-dev` | Toggles the development mode. The default is `no-dev`. |
|
||||||
|
| `--path` | Specifies the path to the frontend directory containing build files. This option is for development purposes only. Can be set using the `LANGFLOW_FRONTEND_PATH` environment variable. |
|
||||||
|
| `--open-browser`/`--no-open-browser`| Toggles the option to open the browser after starting the server. Can be set using the `LANGFLOW_OPEN_BROWSER` environment variable. The default is `open-browser`. |
|
||||||
|
| `--remove-api-keys`/`--no-remove-api-keys`| Toggles the option to remove API keys from the projects saved in the database. Can be set using the `LANGFLOW_REMOVE_API_KEYS` environment variable. The default is `no-remove-api-keys`. |
|
||||||
|
| `--install-completion [bash\|zsh\|fish\|powershell\|pwsh]`| Installs completion for the specified shell. |
|
||||||
|
| `--show-completion [bash\|zsh\|fish\|powershell\|pwsh]` | Shows completion for the specified shell, allowing you to copy it or customize the installation. |
|
||||||
|
| `--backend-only` | This parameter, with a default value of `False`, allows running only the backend server without the frontend. It can also be set using the `LANGFLOW_BACKEND_ONLY` environment variable. For more, see [Backend-only](../deployment/backend-only.md).|
|
||||||
|
| `--store` | This parameter, with a default value of `True`, enables the store features, use `--no-store` to deactivate it. It can be configured using the `LANGFLOW_STORE` environment variable. |
|
||||||
|
|
||||||
|
#### Environment Variables
|
||||||
|
|
||||||
You can configure many of the CLI options using environment variables. These can be exported in your operating system or added to a `.env` file and loaded using the `--env-file` option.
|
You can configure many of the CLI options using environment variables. These can be exported in your operating system or added to a `.env` file and loaded using the `--env-file` option.
|
||||||
|
|
||||||
A sample `.env` file named `.env.example` is included with the project. Copy this file to a new file named `.env` and replace the example values with your actual settings. If you're setting values in both your OS and the `.env` file, the `.env` settings will take precedence.
|
A sample `.env` file named `.env.example` is included with the project. Copy this file to a new file named `.env` and replace the example values with your actual settings. If you're setting values in both your OS and the `.env` file, the `.env` settings will take precedence.
|
||||||
|
|
||||||
|
## langflow superuser
|
||||||
|
|
||||||
|
Create a superuser for Langflow.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
langflow superuser
|
||||||
|
# or
|
||||||
|
python -m langflow superuser
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
| Option | Type | Description |
|
||||||
|
|----------------|-------|-------------------------------------------------------------|
|
||||||
|
| `--username` | TEXT | Username for the superuser. [default: None] [required] |
|
||||||
|
| `--password` | TEXT | Password for the superuser. [default: None] [required] |
|
||||||
|
| `--log-level` | TEXT | Logging level. [env var: LANGFLOW_LOG_LEVEL] [default: error] |
|
||||||
|
| `--help` | | Show this message and exit. |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ Langflow [Discord](https://discord.gg/EqksyE2EX9) server.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🐦 Stay tunned for **Langflow** on Twitter
|
## 🐦 Stay tuned for **Langflow** on Twitter
|
||||||
|
|
||||||
Follow [@langflow_ai](https://twitter.com/langflow_ai) on **Twitter** to get the latest news about **Langflow**.
|
Follow [@langflow_ai](https://twitter.com/langflow_ai) on **Twitter** to get the latest news about **Langflow**.
|
||||||
|
|
||||||
|
|
|
||||||
113
docs/docs/deployment/backend-only.md
Normal file
113
docs/docs/deployment/backend-only.md
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
# Backend-only
|
||||||
|
You can run Langflow in `--backend-only` mode to expose your Langflow app as an API, without running the frontend UI.
|
||||||
|
|
||||||
|
Start langflow in backend-only mode with `python3 -m langflow run --backend-only`.
|
||||||
|
|
||||||
|
The terminal prints ` Welcome to ⛓ Langflow `, and a blank window opens at `http://127.0.0.1:7864/all`.
|
||||||
|
Langflow will now serve requests to its API without the frontend running.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
* [Langflow installed](../getting-started/install-langflow.mdx)
|
||||||
|
|
||||||
|
* [OpenAI API key](https://platform.openai.com)
|
||||||
|
|
||||||
|
* [A Langflow flow created](../starter-projects/basic-prompting.mdx)
|
||||||
|
|
||||||
|
## Download your flow's curl call
|
||||||
|
|
||||||
|
1. Click API.
|
||||||
|
2. Click **curl** > **Copy code** and save the code to your local machine.
|
||||||
|
It will look something like this:
|
||||||
|
```curl
|
||||||
|
curl -X POST \
|
||||||
|
"http://127.0.0.1:7864/api/v1/run/ef7e0554-69e5-4e3e-ab29-ee83bcd8d9ef?stream=false" \
|
||||||
|
-H 'Content-Type: application/json'\
|
||||||
|
-d '{"input_value": "message",
|
||||||
|
"output_type": "chat",
|
||||||
|
"input_type": "chat",
|
||||||
|
"tweaks": {
|
||||||
|
"Prompt-kvo86": {},
|
||||||
|
"OpenAIModel-MilkD": {},
|
||||||
|
"ChatOutput-ktwdw": {},
|
||||||
|
"ChatInput-xXC4F": {}
|
||||||
|
}}'
|
||||||
|
```
|
||||||
|
Note the flow ID of `ef7e0554-69e5-4e3e-ab29-ee83bcd8d9ef`. You can find this ID in the UI as well to ensure you're querying the right flow.
|
||||||
|
|
||||||
|
## Start Langflow in backend-only mode
|
||||||
|
|
||||||
|
1. Stop Langflow with Ctrl+C.
|
||||||
|
2. Start langflow in backend-only mode with `python3 -m langflow run --backend-only`.
|
||||||
|
The terminal prints ` Welcome to ⛓ Langflow `, and a blank window opens at `http://127.0.0.1:7864/all`.
|
||||||
|
Langflow will now serve requests to its API.
|
||||||
|
3. Run the curl code you copied from the UI.
|
||||||
|
You should get a result like this:
|
||||||
|
```bash
|
||||||
|
{"session_id":"ef7e0554-69e5-4e3e-ab29-ee83bcd8d9ef:bf81d898868ac87e1b4edbd96c131c5dee801ea2971122cc91352d144a45b880","outputs":[{"inputs":{"input_value":"hi, are you there?"},"outputs":[{"results":{"result":"Arrr, ahoy matey! Aye, I be here. What be ye needin', me hearty?"},"artifacts":{"message":"Arrr, ahoy matey! Aye, I be here. What be ye needin', me hearty?","sender":"Machine","sender_name":"AI"},"messages":[{"message":"Arrr, ahoy matey! Aye, I be here. What be ye needin', me hearty?","sender":"Machine","sender_name":"AI","component_id":"ChatOutput-ktwdw"}],"component_display_name":"Chat Output","component_id":"ChatOutput-ktwdw","used_frozen_result":false}]}]}%
|
||||||
|
```
|
||||||
|
Again, note that the flow ID matches.
|
||||||
|
Langflow is receiving your POST request, running the flow, and returning the result, all without running the frontend. Cool!
|
||||||
|
|
||||||
|
## Download your flow's Python API call
|
||||||
|
|
||||||
|
Instead of using curl, you can download your flow as a Python API call instead.
|
||||||
|
|
||||||
|
1. Click API.
|
||||||
|
2. Click **Python API** > **Copy code** and save the code to your local machine.
|
||||||
|
The code will look something like this:
|
||||||
|
```python
|
||||||
|
import requests
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
BASE_API_URL = "http://127.0.0.1:7864/api/v1/run"
|
||||||
|
FLOW_ID = "ef7e0554-69e5-4e3e-ab29-ee83bcd8d9ef"
|
||||||
|
# You can tweak the flow by adding a tweaks dictionary
|
||||||
|
# e.g {"OpenAI-XXXXX": {"model_name": "gpt-4"}}
|
||||||
|
|
||||||
|
def run_flow(message: str,
|
||||||
|
flow_id: str,
|
||||||
|
output_type: str = "chat",
|
||||||
|
input_type: str = "chat",
|
||||||
|
tweaks: Optional[dict] = None,
|
||||||
|
api_key: Optional[str] = None) -> dict:
|
||||||
|
"""
|
||||||
|
Run a flow with a given message and optional tweaks.
|
||||||
|
|
||||||
|
:param message: The message to send to the flow
|
||||||
|
:param flow_id: The ID of the flow to run
|
||||||
|
:param tweaks: Optional tweaks to customize the flow
|
||||||
|
:return: The JSON response from the flow
|
||||||
|
"""
|
||||||
|
api_url = f"{BASE_API_URL}/{flow_id}"
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"input_value": message,
|
||||||
|
"output_type": output_type,
|
||||||
|
"input_type": input_type,
|
||||||
|
}
|
||||||
|
headers = None
|
||||||
|
if tweaks:
|
||||||
|
payload["tweaks"] = tweaks
|
||||||
|
if api_key:
|
||||||
|
headers = {"x-api-key": api_key}
|
||||||
|
response = requests.post(api_url, json=payload, headers=headers)
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
# Setup any tweaks you want to apply to the flow
|
||||||
|
message = "message"
|
||||||
|
|
||||||
|
print(run_flow(message=message, flow_id=FLOW_ID))
|
||||||
|
```
|
||||||
|
3. Run your Python app:
|
||||||
|
```python
|
||||||
|
python3 app.py
|
||||||
|
```
|
||||||
|
|
||||||
|
The result is similar to the curl call:
|
||||||
|
```bash
|
||||||
|
{'session_id': 'ef7e0554-69e5-4e3e-ab29-ee83bcd8d9ef:bf81d898868ac87e1b4edbd96c131c5dee801ea2971122cc91352d144a45b880', 'outputs': [{'inputs': {'input_value': 'message'}, 'outputs': [{'results': {'result': "Arrr matey! What be yer message for this ol' pirate? Speak up or walk the plank!"}, 'artifacts': {'message': "Arrr matey! What be yer message for this ol' pirate? Speak up or walk the plank!", 'sender': 'Machine', 'sender_name': 'AI'}, 'messages': [{'message': "Arrr matey! What be yer message for this ol' pirate? Speak up or walk the plank!", 'sender': 'Machine', 'sender_name': 'AI', 'component_id': 'ChatOutput-ktwdw'}], 'component_display_name': 'Chat Output', 'component_id': 'ChatOutput-ktwdw', 'used_frozen_result': False}]}]}
|
||||||
|
```
|
||||||
|
Your Python app POSTs to your Langflow server, and the server runs the flow and returns the result.
|
||||||
|
|
||||||
|
See [API](../administration/api.mdx) for more ways to interact with your headless Langflow server.
|
||||||
65
docs/docs/deployment/docker.md
Normal file
65
docs/docs/deployment/docker.md
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
# Docker
|
||||||
|
|
||||||
|
This guide will help you get LangFlow up and running using Docker and Docker Compose.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Docker
|
||||||
|
- Docker Compose
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
1. Clone the LangFlow repository:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/langflow-ai/langflow.git
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Navigate to the `docker_example` directory:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd langflow/docker_example
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Run the Docker Compose file:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker compose up
|
||||||
|
```
|
||||||
|
|
||||||
|
LangFlow will now be accessible at [http://localhost:7860/](http://localhost:7860/).
|
||||||
|
|
||||||
|
## Docker Compose Configuration
|
||||||
|
|
||||||
|
The Docker Compose configuration spins up two services: `langflow` and `postgres`.
|
||||||
|
|
||||||
|
### LangFlow Service
|
||||||
|
|
||||||
|
The `langflow` service uses the `langflowai/langflow:latest` Docker image and exposes port 7860. It depends on the `postgres` service.
|
||||||
|
|
||||||
|
Environment variables:
|
||||||
|
|
||||||
|
- `LANGFLOW_DATABASE_URL`: The connection string for the PostgreSQL database.
|
||||||
|
- `LANGFLOW_CONFIG_DIR`: The directory where LangFlow stores logs, file storage, monitor data, and secret keys.
|
||||||
|
|
||||||
|
Volumes:
|
||||||
|
|
||||||
|
- `langflow-data`: This volume is mapped to `/var/lib/langflow` in the container.
|
||||||
|
|
||||||
|
### PostgreSQL Service
|
||||||
|
|
||||||
|
The `postgres` service uses the `postgres:16` Docker image and exposes port 5432.
|
||||||
|
|
||||||
|
Environment variables:
|
||||||
|
|
||||||
|
- `POSTGRES_USER`: The username for the PostgreSQL database.
|
||||||
|
- `POSTGRES_PASSWORD`: The password for the PostgreSQL database.
|
||||||
|
- `POSTGRES_DB`: The name of the PostgreSQL database.
|
||||||
|
|
||||||
|
Volumes:
|
||||||
|
|
||||||
|
- `langflow-postgres`: This volume is mapped to `/var/lib/postgresql/data` in the container.
|
||||||
|
|
||||||
|
## Switching to a Specific LangFlow Version
|
||||||
|
|
||||||
|
If you want to use a specific version of LangFlow, you can modify the `image` field under the `langflow` service in the Docker Compose file. For example, to use version 1.0-alpha, change `langflowai/langflow:latest` to `langflowai/langflow:1.0-alpha`.
|
||||||
|
|
@ -113,7 +113,10 @@ module.exports = {
|
||||||
type: "category",
|
type: "category",
|
||||||
label: "Deployment",
|
label: "Deployment",
|
||||||
collapsed: true,
|
collapsed: true,
|
||||||
items: ["deployment/gcp-deployment"],
|
items: ["deployment/docker",
|
||||||
|
"deployment/backend-only",
|
||||||
|
"deployment/gcp-deployment",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "category",
|
type: "category",
|
||||||
|
|
|
||||||
4
docs/static/logos/twitter.svg
vendored
4
docs/static/logos/twitter.svg
vendored
|
|
@ -1,3 +1,3 @@
|
||||||
<svg width="18" height="18" viewBox="0 0 24 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="1200" height="1227" viewBox="0 0 1200 1227" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24 2.36764C23.1181 2.76923 22.1687 3.04081 21.1728 3.16215C22.1898 2.5381 22.9703 1.54857 23.338 0.369812C22.3856 0.947636 21.3334 1.368 20.2092 1.59334C19.3133 0.612492 18.0328 0 16.6156 0C13.8983 0 11.6936 2.26074 11.6936 5.04874C11.6936 5.44456 11.7359 5.82881 11.8204 6.19862C7.72812 5.98771 4.10072 3.97977 1.67071 0.921629C1.24669 1.66992 1.00439 2.5381 1.00439 3.46262C1.00439 5.21343 1.87357 6.75911 3.19493 7.66485C2.38915 7.64029 1.62845 7.41061 0.963547 7.03502V7.09713C0.963547 9.54422 2.66102 11.5854 4.91495 12.0476C4.5022 12.1661 4.06691 12.2253 3.61754 12.2253C3.30058 12.2253 2.99066 12.195 2.69062 12.1358C3.31748 14.1408 5.1347 15.6013 7.29001 15.6403C5.6052 16.9953 3.48089 17.8028 1.17485 17.8028C0.777598 17.8028 0.384575 17.7796 0 17.7334C2.17926 19.1636 4.76844 20 7.54781 20C16.6057 20 21.5573 12.3077 21.5573 5.63524C21.5573 5.41566 21.5531 5.19609 21.5447 4.98084C22.5067 4.26868 23.3422 3.38027 24 2.36764Z" fill="#00AAEC"/>
|
<path d="M714.163 519.284L1160.89 0H1055.03L667.137 450.887L357.328 0H0L468.492 681.821L0 1226.37H105.866L515.491 750.218L842.672 1226.37H1200L714.137 519.284H714.163ZM569.165 687.828L521.697 619.934L144.011 79.6944H306.615L611.412 515.685L658.88 583.579L1055.08 1150.3H892.476L569.165 687.854V687.828Z" fill="white"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 430 B |
1100
poetry.lock
generated
1100
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "langflow"
|
name = "langflow"
|
||||||
version = "1.0.0a43"
|
version = "1.0.0a49"
|
||||||
description = "A Python package with a built-in web application"
|
description = "A Python package with a built-in web application"
|
||||||
authors = ["Langflow <contact@langflow.org>"]
|
authors = ["Langflow <contact@langflow.org>"]
|
||||||
maintainers = [
|
maintainers = [
|
||||||
|
|
@ -66,7 +66,7 @@ qianfan = "0.3.5"
|
||||||
pgvector = "^0.2.3"
|
pgvector = "^0.2.3"
|
||||||
pyautogen = "^0.2.0"
|
pyautogen = "^0.2.0"
|
||||||
langchain-google-genai = "^1.0.1"
|
langchain-google-genai = "^1.0.1"
|
||||||
langchain-cohere = "^0.1.0rc1"
|
langchain-cohere = "^0.1.5"
|
||||||
elasticsearch = "^8.12.0"
|
elasticsearch = "^8.12.0"
|
||||||
pytube = "^15.0.0"
|
pytube = "^15.0.0"
|
||||||
dspy-ai = "^2.4.0"
|
dspy-ai = "^2.4.0"
|
||||||
|
|
@ -81,7 +81,7 @@ langchain-google-vertexai = "^1.0.3"
|
||||||
langchain-groq = "^0.1.3"
|
langchain-groq = "^0.1.3"
|
||||||
langchain-pinecone = "^0.1.0"
|
langchain-pinecone = "^0.1.0"
|
||||||
langchain-mistralai = "^0.1.6"
|
langchain-mistralai = "^0.1.6"
|
||||||
couchbase = "^4.2.1"
|
couchbase = { extras = ["couchbase"], version = "^4.2.1", optional = true }
|
||||||
youtube-transcript-api = "^0.6.2"
|
youtube-transcript-api = "^0.6.2"
|
||||||
markdown = "^3.6"
|
markdown = "^3.6"
|
||||||
langchain-chroma = "^0.1.1"
|
langchain-chroma = "^0.1.1"
|
||||||
|
|
@ -118,6 +118,7 @@ vulture = "^2.11"
|
||||||
|
|
||||||
[tool.poetry.extras]
|
[tool.poetry.extras]
|
||||||
deploy = ["celery", "redis", "flower"]
|
deploy = ["celery", "redis", "flower"]
|
||||||
|
couchbase = ["couchbase"]
|
||||||
local = ["llama-cpp-python", "sentence-transformers", "ctransformers"]
|
local = ["llama-cpp-python", "sentence-transformers", "ctransformers"]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -140,7 +141,7 @@ testpaths = ["tests", "integration"]
|
||||||
console_output_style = "progress"
|
console_output_style = "progress"
|
||||||
filterwarnings = ["ignore::DeprecationWarning"]
|
filterwarnings = ["ignore::DeprecationWarning"]
|
||||||
log_cli = true
|
log_cli = true
|
||||||
markers = ["async_test"]
|
markers = ["async_test", "api_key_required"]
|
||||||
|
|
||||||
|
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
from .AstraDBSearch import AstraDBSearchComponent
|
|
||||||
from .ChromaSearch import ChromaSearchComponent
|
|
||||||
from .FAISSSearch import FAISSSearchComponent
|
|
||||||
from .MongoDBAtlasVectorSearch import MongoDBAtlasSearchComponent
|
|
||||||
from .PineconeSearch import PineconeSearchComponent
|
|
||||||
from .QdrantSearch import QdrantSearchComponent
|
|
||||||
from .RedisSearch import RedisSearchComponent
|
|
||||||
from .SupabaseVectorStoreSearch import SupabaseSearchComponent
|
|
||||||
from .VectaraSearch import VectaraSearchComponent
|
|
||||||
from .WeaviateSearch import WeaviateSearchVectorStore
|
|
||||||
from .pgvectorSearch import PGVectorSearchComponent
|
|
||||||
from .Couchbase import CouchbaseSearchComponent # type: ignore
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
"AstraDBSearchComponent",
|
|
||||||
"ChromaSearchComponent",
|
|
||||||
"CouchbaseSearchComponent",
|
|
||||||
"FAISSSearchComponent",
|
|
||||||
"MongoDBAtlasSearchComponent",
|
|
||||||
"PineconeSearchComponent",
|
|
||||||
"QdrantSearchComponent",
|
|
||||||
"RedisSearchComponent",
|
|
||||||
"SupabaseSearchComponent",
|
|
||||||
"VectaraSearchComponent",
|
|
||||||
"WeaviateSearchVectorStore",
|
|
||||||
"PGVectorSearchComponent",
|
|
||||||
]
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
from .AstraDB import AstraDBVectorStoreComponent
|
|
||||||
from .Chroma import ChromaComponent
|
|
||||||
from .FAISS import FAISSComponent
|
|
||||||
from .MongoDBAtlasVector import MongoDBAtlasComponent
|
|
||||||
from .Pinecone import PineconeComponent
|
|
||||||
from .Qdrant import QdrantComponent
|
|
||||||
from .Redis import RedisComponent
|
|
||||||
from .SupabaseVectorStore import SupabaseComponent
|
|
||||||
from .Vectara import VectaraComponent
|
|
||||||
from .Weaviate import WeaviateVectorStoreComponent
|
|
||||||
from .pgvector import PGVectorComponent
|
|
||||||
from .Couchbase import CouchbaseComponent
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
"AstraDBVectorStoreComponent",
|
|
||||||
"ChromaComponent",
|
|
||||||
"CouchbaseComponent",
|
|
||||||
"FAISSComponent",
|
|
||||||
"MongoDBAtlasComponent",
|
|
||||||
"PineconeComponent",
|
|
||||||
"QdrantComponent",
|
|
||||||
"RedisComponent",
|
|
||||||
"SupabaseComponent",
|
|
||||||
"VectaraComponent",
|
|
||||||
"WeaviateVectorStoreComponent",
|
|
||||||
"base",
|
|
||||||
"PGVectorComponent",
|
|
||||||
]
|
|
||||||
|
|
@ -297,7 +297,7 @@ class CodeParser:
|
||||||
bases = self.execute_and_inspect_classes(self.code)
|
bases = self.execute_and_inspect_classes(self.code)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# If the code cannot be executed, return an empty list
|
# If the code cannot be executed, return an empty list
|
||||||
logger.exception(e)
|
logger.debug(e)
|
||||||
bases = []
|
bases = []
|
||||||
raise e
|
raise e
|
||||||
return bases
|
return bases
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,8 @@ class DirectoryReader:
|
||||||
component_tuple = (*build_component(component), component)
|
component_tuple = (*build_component(component), component)
|
||||||
components.append(component_tuple)
|
components.append(component_tuple)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error while loading component { component['name']}: {e}")
|
logger.debug(f"Error while loading component { component['name']}")
|
||||||
|
logger.debug(e)
|
||||||
continue
|
continue
|
||||||
items.append({"name": menu["name"], "path": menu["path"], "components": components})
|
items.append({"name": menu["name"], "path": menu["path"], "components": components})
|
||||||
filtered = [menu for menu in items if menu["components"]]
|
filtered = [menu for menu in items if menu["components"]]
|
||||||
|
|
@ -266,8 +267,7 @@ class DirectoryReader:
|
||||||
if validation_result:
|
if validation_result:
|
||||||
try:
|
try:
|
||||||
output_types = self.get_output_types_from_code(result_content)
|
output_types = self.get_output_types_from_code(result_content)
|
||||||
except Exception as exc:
|
except Exception:
|
||||||
logger.exception(f"Error while getting output types from code: {str(exc)}")
|
|
||||||
output_types = [component_name_camelcase]
|
output_types = [component_name_camelcase]
|
||||||
else:
|
else:
|
||||||
output_types = [component_name_camelcase]
|
output_types = [component_name_camelcase]
|
||||||
|
|
|
||||||
|
|
@ -251,7 +251,7 @@ def get_flow_by_id_or_endpoint_name(
|
||||||
flow = db.get(Flow, flow_id)
|
flow = db.get(Flow, flow_id)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
endpoint_name = flow_id_or_name
|
endpoint_name = flow_id_or_name
|
||||||
stmt = select(Flow).where(Flow.name == endpoint_name)
|
stmt = select(Flow).where(Flow.endpoint_name == endpoint_name)
|
||||||
if user_id:
|
if user_id:
|
||||||
stmt = stmt.where(Flow.user_id == user_id)
|
stmt = stmt.where(Flow.user_id == user_id)
|
||||||
flow = db.exec(stmt).first()
|
flow = db.exec(stmt).first()
|
||||||
|
|
|
||||||
51
src/backend/base/poetry.lock
generated
51
src/backend/base/poetry.lock
generated
|
|
@ -1159,13 +1159,13 @@ files = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langchain"
|
name = "langchain"
|
||||||
version = "0.2.2"
|
version = "0.2.3"
|
||||||
description = "Building applications with LLMs through composability"
|
description = "Building applications with LLMs through composability"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "<4.0,>=3.8.1"
|
python-versions = "<4.0,>=3.8.1"
|
||||||
files = [
|
files = [
|
||||||
{file = "langchain-0.2.2-py3-none-any.whl", hash = "sha256:58ca0c47bcdd156da66f50a0a4fcedc49bf6950827f4a6b06c8c4842d55805f3"},
|
{file = "langchain-0.2.3-py3-none-any.whl", hash = "sha256:5dc33cd9c8008693d328b7cb698df69073acecc89ad9c2a95f243b3314f8d834"},
|
||||||
{file = "langchain-0.2.2.tar.gz", hash = "sha256:9d61e50e9cdc2bea659bc5e6c03650ba048fda63a307490ae368e539f61a0d3a"},
|
{file = "langchain-0.2.3.tar.gz", hash = "sha256:81962cc72cce6515f7bd71e01542727870789bf8b666c6913d85559080c1a201"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
|
@ -1181,29 +1181,15 @@ requests = ">=2,<3"
|
||||||
SQLAlchemy = ">=1.4,<3"
|
SQLAlchemy = ">=1.4,<3"
|
||||||
tenacity = ">=8.1.0,<9.0.0"
|
tenacity = ">=8.1.0,<9.0.0"
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
azure = ["azure-ai-formrecognizer (>=3.2.1,<4.0.0)", "azure-ai-textanalytics (>=5.3.0,<6.0.0)", "azure-cognitiveservices-speech (>=1.28.0,<2.0.0)", "azure-core (>=1.26.4,<2.0.0)", "azure-cosmos (>=4.4.0b1,<5.0.0)", "azure-identity (>=1.12.0,<2.0.0)", "azure-search-documents (==11.4.0b8)", "openai (<2)"]
|
|
||||||
clarifai = ["clarifai (>=9.1.0)"]
|
|
||||||
cli = ["typer (>=0.9.0,<0.10.0)"]
|
|
||||||
cohere = ["cohere (>=4,<6)"]
|
|
||||||
docarray = ["docarray[hnswlib] (>=0.32.0,<0.33.0)"]
|
|
||||||
embeddings = ["sentence-transformers (>=2,<3)"]
|
|
||||||
extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.0,<3.0.0)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.0,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "cohere (>=4,<6)", "couchbase (>=4.1.9,<5.0.0)", "dashvector (>=1.0.1,<2.0.0)", "databricks-vectorsearch (>=0.21,<0.22)", "datasets (>=2.15.0,<3.0.0)", "dgml-utils (>=0.3.0,<0.4.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "fireworks-ai (>=0.9.0,<0.10.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "google-cloud-documentai (>=2.20.1,<3.0.0)", "gql (>=3.4.1,<4.0.0)", "hologres-vector (>=0.0.6,<0.0.7)", "html2text (>=2020.1.16,<2021.0.0)", "javelin-sdk (>=0.1.8,<0.2.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "jsonschema (>1)", "langchain-openai (>=0.1,<0.2)", "lxml (>=4.9.3,<6.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "msal (>=1.25.0,<2.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "openai (<2)", "openapi-pydantic (>=0.3.2,<0.4.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "praw (>=7.7.1,<8.0.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "rdflib (==7.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "rspace_client (>=2.5.0,<3.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "upstash-redis (>=0.15.0,<0.16.0)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)"]
|
|
||||||
javascript = ["esprima (>=4.0.1,<5.0.0)"]
|
|
||||||
llms = ["clarifai (>=9.1.0)", "cohere (>=4,<6)", "huggingface_hub (>=0,<1)", "manifest-ml (>=0.0.1,<0.0.2)", "nlpcloud (>=1,<2)", "openai (<2)", "openlm (>=0.0.5,<0.0.6)", "torch (>=1,<3)", "transformers (>=4,<5)"]
|
|
||||||
openai = ["openai (<2)", "tiktoken (>=0.7,<1.0)"]
|
|
||||||
qdrant = ["qdrant-client (>=1.3.1,<2.0.0)"]
|
|
||||||
text-helpers = ["chardet (>=5.1.0,<6.0.0)"]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langchain-community"
|
name = "langchain-community"
|
||||||
version = "0.2.3"
|
version = "0.2.4"
|
||||||
description = "Community contributed LangChain integrations."
|
description = "Community contributed LangChain integrations."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "<4.0,>=3.8.1"
|
python-versions = "<4.0,>=3.8.1"
|
||||||
files = [
|
files = [
|
||||||
{file = "langchain_community-0.2.3-py3-none-any.whl", hash = "sha256:aa895545be2f3f4aa2fea36f6da2e3b4ec50ce61ec986e8f146901a1e9138138"},
|
{file = "langchain_community-0.2.4-py3-none-any.whl", hash = "sha256:8582e9800f4837660dc297cccd2ee1ddc1d8c440d0fe8b64edb07620f0373b0e"},
|
||||||
{file = "langchain_community-0.2.3.tar.gz", hash = "sha256:a3c35af215e47b700e7cb4e548fa8b45c6d46d52b5a5a65af2577c5a0104fc9f"},
|
{file = "langchain_community-0.2.4.tar.gz", hash = "sha256:2bb6a1a36b8500a564d25d76469c02457b1a7c3afea6d4a609a47c06b993e3e4"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
|
@ -1218,19 +1204,15 @@ requests = ">=2,<3"
|
||||||
SQLAlchemy = ">=1.4,<3"
|
SQLAlchemy = ">=1.4,<3"
|
||||||
tenacity = ">=8.1.0,<9.0.0"
|
tenacity = ">=8.1.0,<9.0.0"
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
cli = ["typer (>=0.9.0,<0.10.0)"]
|
|
||||||
extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.0,<3.0.0)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "azure-ai-documentintelligence (>=1.0.0b1,<2.0.0)", "azure-identity (>=1.15.0,<2.0.0)", "azure-search-documents (==11.4.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.6,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "cloudpathlib (>=0.18,<0.19)", "cloudpickle (>=2.0.0)", "cohere (>=4,<5)", "databricks-vectorsearch (>=0.21,<0.22)", "datasets (>=2.15.0,<3.0.0)", "dgml-utils (>=0.3.0,<0.4.0)", "elasticsearch (>=8.12.0,<9.0.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "fireworks-ai (>=0.9.0,<0.10.0)", "friendli-client (>=1.2.4,<2.0.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "google-cloud-documentai (>=2.20.1,<3.0.0)", "gql (>=3.4.1,<4.0.0)", "gradientai (>=1.4.0,<2.0.0)", "hdbcli (>=2.19.21,<3.0.0)", "hologres-vector (>=0.0.6,<0.0.7)", "html2text (>=2020.1.16,<2021.0.0)", "httpx (>=0.24.1,<0.25.0)", "httpx-sse (>=0.4.0,<0.5.0)", "javelin-sdk (>=0.1.8,<0.2.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "jsonschema (>1)", "lxml (>=4.9.3,<6.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "msal (>=1.25.0,<2.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "nvidia-riva-client (>=2.14.0,<3.0.0)", "oci (>=2.119.1,<3.0.0)", "openai (<2)", "openapi-pydantic (>=0.3.2,<0.4.0)", "oracle-ads (>=2.9.1,<3.0.0)", "oracledb (>=2.2.0,<3.0.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "praw (>=7.7.1,<8.0.0)", "premai (>=0.3.25,<0.4.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pyjwt (>=2.8.0,<3.0.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "rdflib (==7.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "rspace_client (>=2.5.0,<3.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "simsimd (>=4.3.1,<5.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "tidb-vector (>=0.0.3,<1.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "tree-sitter (>=0.20.2,<0.21.0)", "tree-sitter-languages (>=1.8.0,<2.0.0)", "upstash-redis (>=0.15.0,<0.16.0)", "vdms (>=0.0.20,<0.0.21)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)"]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langchain-core"
|
name = "langchain-core"
|
||||||
version = "0.2.4"
|
version = "0.2.5"
|
||||||
description = "Building applications with LLMs through composability"
|
description = "Building applications with LLMs through composability"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "<4.0,>=3.8.1"
|
python-versions = "<4.0,>=3.8.1"
|
||||||
files = [
|
files = [
|
||||||
{file = "langchain_core-0.2.4-py3-none-any.whl", hash = "sha256:5212f7ec78a525e88a178ed3aefe2fd7134b03fb92573dfbab9914f1d92d6ec5"},
|
{file = "langchain_core-0.2.5-py3-none-any.whl", hash = "sha256:abe5138f22acff23a079ec538be5268bbf97cf023d51987a0dd474d2a16cae3e"},
|
||||||
{file = "langchain_core-0.2.4.tar.gz", hash = "sha256:82bdcc546eb0341cefcf1f4ecb3e49836fff003903afddda2d1312bb8491ef81"},
|
{file = "langchain_core-0.2.5.tar.gz", hash = "sha256:4a5c2f56b22396a63ef4790043660e393adbfa6832b978f023ca996a04b8e752"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
|
@ -1241,9 +1223,6 @@ pydantic = ">=1,<3"
|
||||||
PyYAML = ">=5.3"
|
PyYAML = ">=5.3"
|
||||||
tenacity = ">=8.1.0,<9.0.0"
|
tenacity = ">=8.1.0,<9.0.0"
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
extended-testing = ["jinja2 (>=3,<4)"]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langchain-experimental"
|
name = "langchain-experimental"
|
||||||
version = "0.0.60"
|
version = "0.0.60"
|
||||||
|
|
@ -1296,13 +1275,13 @@ types-requests = ">=2.31.0.2,<3.0.0.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langsmith"
|
name = "langsmith"
|
||||||
version = "0.1.72"
|
version = "0.1.75"
|
||||||
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 = "<4.0,>=3.8.1"
|
python-versions = "<4.0,>=3.8.1"
|
||||||
files = [
|
files = [
|
||||||
{file = "langsmith-0.1.72-py3-none-any.whl", hash = "sha256:a4456707669521bd75b7431b9205a6b99579fb9ff01bd338f52d29df11a7662d"},
|
{file = "langsmith-0.1.75-py3-none-any.whl", hash = "sha256:d08b08dd6b3fa4da170377f95123d77122ef4c52999d10fff4ae08ff70d07aed"},
|
||||||
{file = "langsmith-0.1.72.tar.gz", hash = "sha256:262ae9e8aceaba50f3a0f5b6eb559d6110886f0afc6b0ed5270e7d3d3f1fd8d6"},
|
{file = "langsmith-0.1.75.tar.gz", hash = "sha256:61274e144ea94c297dd78ce03e6dfae18459fe9bd8ab5094d61a0c4816561279"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
|
@ -1600,13 +1579,13 @@ files = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "marshmallow"
|
name = "marshmallow"
|
||||||
version = "3.21.2"
|
version = "3.21.3"
|
||||||
description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
|
description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "marshmallow-3.21.2-py3-none-any.whl", hash = "sha256:70b54a6282f4704d12c0a41599682c5c5450e843b9ec406308653b47c59648a1"},
|
{file = "marshmallow-3.21.3-py3-none-any.whl", hash = "sha256:86ce7fb914aa865001a4b2092c4c2872d13bc347f3d42673272cabfdbad386f1"},
|
||||||
{file = "marshmallow-3.21.2.tar.gz", hash = "sha256:82408deadd8b33d56338d2182d455db632c6313aa2af61916672146bb32edc56"},
|
{file = "marshmallow-3.21.3.tar.gz", hash = "sha256:4f57c5e050a54d66361e826f94fba213eb10b67b2fdb02c3e0343ce207ba1662"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "langflow-base"
|
name = "langflow-base"
|
||||||
version = "0.0.57"
|
version = "0.0.60"
|
||||||
description = "A Python package with a built-in web application"
|
description = "A Python package with a built-in web application"
|
||||||
authors = ["Langflow <contact@langflow.org>"]
|
authors = ["Langflow <contact@langflow.org>"]
|
||||||
maintainers = [
|
maintainers = [
|
||||||
|
|
|
||||||
17
src/frontend/package-lock.json
generated
17
src/frontend/package-lock.json
generated
|
|
@ -41,7 +41,6 @@
|
||||||
"class-variance-authority": "^0.6.1",
|
"class-variance-authority": "^0.6.1",
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
"cmdk": "^1.0.0",
|
"cmdk": "^1.0.0",
|
||||||
"debounce-promise": "^3.1.2",
|
|
||||||
"dompurify": "^3.0.5",
|
"dompurify": "^3.0.5",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"emoji-regex": "^10.3.0",
|
"emoji-regex": "^10.3.0",
|
||||||
|
|
@ -53,6 +52,7 @@
|
||||||
"million": "^3.0.6",
|
"million": "^3.0.6",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"openseadragon": "^4.1.1",
|
"openseadragon": "^4.1.1",
|
||||||
|
"p-debounce": "^4.0.0",
|
||||||
"playwright": "^1.42.0",
|
"playwright": "^1.42.0",
|
||||||
"react": "^18.2.21",
|
"react": "^18.2.21",
|
||||||
"react-ace": "^10.1.0",
|
"react-ace": "^10.1.0",
|
||||||
|
|
@ -480,7 +480,6 @@
|
||||||
},
|
},
|
||||||
"node_modules/@clack/prompts/node_modules/is-unicode-supported": {
|
"node_modules/@clack/prompts/node_modules/is-unicode-supported": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"extraneous": true,
|
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -5699,11 +5698,6 @@
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/debounce-promise": {
|
|
||||||
"version": "3.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/debounce-promise/-/debounce-promise-3.1.2.tgz",
|
|
||||||
"integrity": "sha512-rZHcgBkbYavBeD9ej6sP56XfG53d51CD4dnaw989YX/nZ/ZJfgRx/9ePKmTNiUiyQvh4mtrMoS3OAWW+yoYtpg=="
|
|
||||||
},
|
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.3.4",
|
"version": "4.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
|
|
@ -10051,6 +10045,15 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/p-debounce": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-debounce/-/p-debounce-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-4Ispi9I9qYGO4lueiLDhe4q4iK5ERK8reLsuzH6BPaXn53EGaua8H66PXIFGrW897hwjXp+pVLrm/DLxN0RF0A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/p-finally": {
|
"node_modules/p-finally": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@
|
||||||
"class-variance-authority": "^0.6.1",
|
"class-variance-authority": "^0.6.1",
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
"cmdk": "^1.0.0",
|
"cmdk": "^1.0.0",
|
||||||
"debounce-promise": "^3.1.2",
|
|
||||||
"dompurify": "^3.0.5",
|
"dompurify": "^3.0.5",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"emoji-regex": "^10.3.0",
|
"emoji-regex": "^10.3.0",
|
||||||
|
|
@ -48,6 +47,7 @@
|
||||||
"million": "^3.0.6",
|
"million": "^3.0.6",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"openseadragon": "^4.1.1",
|
"openseadragon": "^4.1.1",
|
||||||
|
"p-debounce": "^4.0.0",
|
||||||
"playwright": "^1.42.0",
|
"playwright": "^1.42.0",
|
||||||
"react": "^18.2.21",
|
"react": "^18.2.21",
|
||||||
"react-ace": "^10.1.0",
|
"react-ace": "^10.1.0",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { cloneDeep } from "lodash";
|
|
||||||
import emojiRegex from "emoji-regex";
|
import emojiRegex from "emoji-regex";
|
||||||
|
import { cloneDeep } from "lodash";
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { NodeToolbar, useUpdateNodeInternals } from "reactflow";
|
import { NodeToolbar, useUpdateNodeInternals } from "reactflow";
|
||||||
import IconComponent from "../../components/genericIconComponent";
|
import IconComponent from "../../components/genericIconComponent";
|
||||||
|
|
@ -56,14 +56,14 @@ export default function GenericNode({
|
||||||
const [nodeName, setNodeName] = useState(data.node!.display_name);
|
const [nodeName, setNodeName] = useState(data.node!.display_name);
|
||||||
const [inputDescription, setInputDescription] = useState(false);
|
const [inputDescription, setInputDescription] = useState(false);
|
||||||
const [nodeDescription, setNodeDescription] = useState(
|
const [nodeDescription, setNodeDescription] = useState(
|
||||||
data.node?.description!
|
data.node?.description!,
|
||||||
);
|
);
|
||||||
const [isOutdated, setIsOutdated] = useState(false);
|
const [isOutdated, setIsOutdated] = useState(false);
|
||||||
const buildStatus = useFlowStore(
|
const buildStatus = useFlowStore(
|
||||||
(state) => state.flowBuildStatus[data.id]?.status
|
(state) => state.flowBuildStatus[data.id]?.status,
|
||||||
);
|
);
|
||||||
const lastRunTime = useFlowStore(
|
const lastRunTime = useFlowStore(
|
||||||
(state) => state.flowBuildStatus[data.id]?.timestamp
|
(state) => state.flowBuildStatus[data.id]?.timestamp,
|
||||||
);
|
);
|
||||||
const [validationStatus, setValidationStatus] =
|
const [validationStatus, setValidationStatus] =
|
||||||
useState<validationStatusType | null>(null);
|
useState<validationStatusType | null>(null);
|
||||||
|
|
@ -116,7 +116,7 @@ export default function GenericNode({
|
||||||
|
|
||||||
updateNodeInternals(data.id);
|
updateNodeInternals(data.id);
|
||||||
},
|
},
|
||||||
[data.id, data.node, setNode, setIsOutdated]
|
[data.id, data.node, setNode, setIsOutdated],
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!data.node!.template) {
|
if (!data.node!.template) {
|
||||||
|
|
@ -255,8 +255,17 @@ export default function GenericNode({
|
||||||
const isDark = useDarkStore((state) => state.dark);
|
const isDark = useDarkStore((state) => state.dark);
|
||||||
const renderIconStatus = (
|
const renderIconStatus = (
|
||||||
buildStatus: BuildStatus | undefined,
|
buildStatus: BuildStatus | undefined,
|
||||||
validationStatus: validationStatusType | null
|
validationStatus: validationStatusType | null,
|
||||||
) => {
|
) => {
|
||||||
|
const conditionSuccess = validationStatus && validationStatus.valid;
|
||||||
|
const conditionInactive =
|
||||||
|
validationStatus &&
|
||||||
|
!validationStatus.valid &&
|
||||||
|
buildStatus === BuildStatus.INACTIVE;
|
||||||
|
const conditionError =
|
||||||
|
buildStatus === BuildStatus.ERROR ||
|
||||||
|
(validationStatus && !validationStatus.valid);
|
||||||
|
|
||||||
if (buildStatus === BuildStatus.BUILDING) {
|
if (buildStatus === BuildStatus.BUILDING) {
|
||||||
return <Loading className="text-medium-indigo" />;
|
return <Loading className="text-medium-indigo" />;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -264,31 +273,30 @@ export default function GenericNode({
|
||||||
<>
|
<>
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="Play"
|
name="Play"
|
||||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-medium-indigo opacity-0 transition-all group-hover:opacity-100"
|
className={cn(
|
||||||
|
!conditionSuccess && !conditionInactive && !conditionError
|
||||||
|
? "opacity-100"
|
||||||
|
: "opacity-0",
|
||||||
|
"absolute ml-0.5 h-5 fill-current stroke-2 text-muted-foreground transition-all group-hover:text-medium-indigo group-hover/node:opacity-100",
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
{validationStatus && validationStatus.valid ? (
|
{conditionSuccess ? (
|
||||||
<Checkmark
|
<Checkmark
|
||||||
className="absolute ml-0.5 h-5 stroke-2 text-status-green opacity-100 transition-all group-hover:opacity-0"
|
className="absolute ml-0.5 h-5 stroke-2 text-status-green opacity-100 transition-all group-hover/node:opacity-0"
|
||||||
isVisible={true}
|
isVisible={true}
|
||||||
/>
|
/>
|
||||||
) : validationStatus &&
|
) : conditionInactive ? (
|
||||||
!validationStatus.valid &&
|
|
||||||
buildStatus === BuildStatus.INACTIVE ? (
|
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="Play"
|
name="Play"
|
||||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-status-green opacity-30 transition-all group-hover:opacity-0"
|
className="absolute ml-0.5 h-5 fill-current stroke-2 text-status-gray opacity-30 transition-all group-hover/node:opacity-0"
|
||||||
/>
|
/>
|
||||||
) : buildStatus === BuildStatus.ERROR ||
|
) : conditionError ? (
|
||||||
(validationStatus && !validationStatus.valid) ? (
|
|
||||||
<Xmark
|
<Xmark
|
||||||
isVisible={true}
|
isVisible={true}
|
||||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-status-red opacity-100 transition-all group-hover:opacity-0"
|
className="absolute ml-0.5 h-5 fill-current stroke-2 text-status-red opacity-100 transition-all group-hover/node:opacity-0"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<IconComponent
|
<></>
|
||||||
name="Play"
|
|
||||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-muted-foreground opacity-100 transition-all group-hover:opacity-0"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
@ -296,7 +304,7 @@ export default function GenericNode({
|
||||||
};
|
};
|
||||||
const getSpecificClassFromBuildStatus = (
|
const getSpecificClassFromBuildStatus = (
|
||||||
buildStatus: BuildStatus | undefined,
|
buildStatus: BuildStatus | undefined,
|
||||||
validationStatus: validationStatusType | null
|
validationStatus: validationStatusType | null,
|
||||||
) => {
|
) => {
|
||||||
let isInvalid = validationStatus && !validationStatus.valid;
|
let isInvalid = validationStatus && !validationStatus.valid;
|
||||||
|
|
||||||
|
|
@ -320,11 +328,11 @@ export default function GenericNode({
|
||||||
selected: boolean,
|
selected: boolean,
|
||||||
showNode: boolean,
|
showNode: boolean,
|
||||||
buildStatus: BuildStatus | undefined,
|
buildStatus: BuildStatus | undefined,
|
||||||
validationStatus: validationStatusType | null
|
validationStatus: validationStatusType | null,
|
||||||
) => {
|
) => {
|
||||||
const specificClassFromBuildStatus = getSpecificClassFromBuildStatus(
|
const specificClassFromBuildStatus = getSpecificClassFromBuildStatus(
|
||||||
buildStatus,
|
buildStatus,
|
||||||
validationStatus
|
validationStatus,
|
||||||
);
|
);
|
||||||
|
|
||||||
const baseBorderClass = getBaseBorderClass(selected);
|
const baseBorderClass = getBaseBorderClass(selected);
|
||||||
|
|
@ -332,8 +340,8 @@ export default function GenericNode({
|
||||||
const names = classNames(
|
const names = classNames(
|
||||||
baseBorderClass,
|
baseBorderClass,
|
||||||
nodeSizeClass,
|
nodeSizeClass,
|
||||||
"generic-node-div",
|
"generic-node-div group/node",
|
||||||
specificClassFromBuildStatus
|
specificClassFromBuildStatus,
|
||||||
);
|
);
|
||||||
return names;
|
return names;
|
||||||
};
|
};
|
||||||
|
|
@ -393,7 +401,7 @@ export default function GenericNode({
|
||||||
selected,
|
selected,
|
||||||
showNode,
|
showNode,
|
||||||
buildStatus,
|
buildStatus,
|
||||||
validationStatus
|
validationStatus,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{data.node?.beta && showNode && (
|
{data.node?.beta && showNode && (
|
||||||
|
|
@ -524,7 +532,7 @@ export default function GenericNode({
|
||||||
}
|
}
|
||||||
title={getFieldTitle(
|
title={getFieldTitle(
|
||||||
data.node?.template!,
|
data.node?.template!,
|
||||||
templateField
|
templateField,
|
||||||
)}
|
)}
|
||||||
info={data.node?.template[templateField].info}
|
info={data.node?.template[templateField].info}
|
||||||
name={templateField}
|
name={templateField}
|
||||||
|
|
@ -552,7 +560,7 @@ export default function GenericNode({
|
||||||
proxy={data.node?.template[templateField].proxy}
|
proxy={data.node?.template[templateField].proxy}
|
||||||
showNode={showNode}
|
showNode={showNode}
|
||||||
/>
|
/>
|
||||||
)
|
),
|
||||||
)}
|
)}
|
||||||
<ParameterComponent
|
<ParameterComponent
|
||||||
key={scapedJSONStringfy({
|
key={scapedJSONStringfy({
|
||||||
|
|
@ -709,7 +717,7 @@ export default function GenericNode({
|
||||||
!data.node?.description) &&
|
!data.node?.description) &&
|
||||||
nameEditable
|
nameEditable
|
||||||
? "font-light italic"
|
? "font-light italic"
|
||||||
: ""
|
: "",
|
||||||
)}
|
)}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
setInputDescription(true);
|
setInputDescription(true);
|
||||||
|
|
@ -771,13 +779,13 @@ export default function GenericNode({
|
||||||
}
|
}
|
||||||
title={getFieldTitle(
|
title={getFieldTitle(
|
||||||
data.node?.template!,
|
data.node?.template!,
|
||||||
templateField
|
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}
|
||||||
|
|
@ -804,7 +812,7 @@ 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",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{" "}
|
{" "}
|
||||||
|
|
@ -51,13 +51,15 @@ export default function ErrorAlert({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-3">
|
<div className="ml-3">
|
||||||
<h3 className="error-build-foreground">{title}</h3>
|
<h3 className="error-build-foreground line-clamp-2">{title}</h3>
|
||||||
{list?.length !== 0 &&
|
{list?.length !== 0 &&
|
||||||
list?.some((item) => item !== null && item !== undefined) ? (
|
list?.some((item) => item !== null && item !== undefined) ? (
|
||||||
<div className="error-build-message-div">
|
<div className="error-build-message-div">
|
||||||
<ul className="error-build-message-list">
|
<ul className="error-build-message-list">
|
||||||
{list.map((item, index) => (
|
{list.map((item, index) => (
|
||||||
<li key={index}>{item}</li>
|
<li key={index} className="line-clamp-5">
|
||||||
|
{item}
|
||||||
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ export default function NoticeAlert({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-3 flex-1 md:flex md:justify-between">
|
<div className="ml-3 flex-1 md:flex md:justify-between">
|
||||||
<p className="text-sm text-info-foreground word-break-break-word">
|
<p className="line-clamp-2 text-sm text-info-foreground word-break-break-word">
|
||||||
{title}
|
{title}
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-3 text-sm md:ml-6 md:mt-0">
|
<p className="mt-3 text-sm md:ml-6 md:mt-0">
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ export default function SuccessAlert({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-3">
|
<div className="ml-3">
|
||||||
<p className="success-alert-message">{title}</p>
|
<p className="success-alert-message line-clamp-2">{title}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ export default function Header(): JSX.Element {
|
||||||
const lastFlowVisitedIndex = routeHistory
|
const lastFlowVisitedIndex = routeHistory
|
||||||
.reverse()
|
.reverse()
|
||||||
.findIndex(
|
.findIndex(
|
||||||
(path) => path.includes("/flow/") && path !== location.pathname
|
(path) => path.includes("/flow/") && path !== location.pathname,
|
||||||
);
|
);
|
||||||
|
|
||||||
const lastFlowVisited = routeHistory[lastFlowVisitedIndex];
|
const lastFlowVisited = routeHistory[lastFlowVisitedIndex];
|
||||||
|
|
@ -81,14 +81,16 @@ export default function Header(): JSX.Element {
|
||||||
<span className="ml-4 text-2xl">⛓️</span>
|
<span className="ml-4 text-2xl">⛓️</span>
|
||||||
</Link>
|
</Link>
|
||||||
{showArrowReturnIcon && (
|
{showArrowReturnIcon && (
|
||||||
<button
|
<Button
|
||||||
|
variant="none"
|
||||||
|
size="none"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
checkForChanges();
|
checkForChanges();
|
||||||
redirectToLastLocation();
|
redirectToLastLocation();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<IconComponent name="ChevronLeft" className="w-4" />
|
<IconComponent name="ChevronLeft" className="w-4" />
|
||||||
</button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<MenuBar />
|
<MenuBar />
|
||||||
|
|
@ -186,7 +188,9 @@ export default function Header(): JSX.Element {
|
||||||
<Separator orientation="vertical" />
|
<Separator orientation="vertical" />
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<button
|
<Button
|
||||||
|
variant="none"
|
||||||
|
size="none"
|
||||||
data-testid="user-profile-settings"
|
data-testid="user-profile-settings"
|
||||||
className={
|
className={
|
||||||
"h-7 w-7 rounded-full focus-visible:outline-0 " +
|
"h-7 w-7 rounded-full focus-visible:outline-0 " +
|
||||||
|
|
@ -200,6 +204,28 @@ export default function Header(): JSX.Element {
|
||||||
/>
|
/>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent>
|
<DropdownMenuContent>
|
||||||
|
{!autoLogin && (
|
||||||
|
<>
|
||||||
|
<DropdownMenuLabel>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
"h-5 w-5 rounded-full focus-visible:outline-0 " +
|
||||||
|
(userData?.profile_image ??
|
||||||
|
(userData?.id
|
||||||
|
? gradients[
|
||||||
|
parseInt(userData?.id ?? "", 30) %
|
||||||
|
gradients.length
|
||||||
|
]
|
||||||
|
: "bg-gray-500"))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{userData?.username ?? "User"}
|
||||||
|
</div>
|
||||||
|
</DropdownMenuLabel>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<DropdownMenuLabel>General</DropdownMenuLabel>
|
<DropdownMenuLabel>General</DropdownMenuLabel>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import IconComponent, {
|
||||||
import { Button, buttonVariants } from "../../../ui/button";
|
import { Button, buttonVariants } from "../../../ui/button";
|
||||||
import { Input } from "../../../ui/input";
|
import { Input } from "../../../ui/input";
|
||||||
import useFileDrop from "../../hooks/use-on-file-drop";
|
import useFileDrop from "../../hooks/use-on-file-drop";
|
||||||
|
import useAlertStore from "../../../../stores/alertStore";
|
||||||
|
|
||||||
type SideBarFoldersButtonsComponentProps = {
|
type SideBarFoldersButtonsComponentProps = {
|
||||||
folders: FolderType[];
|
folders: FolderType[];
|
||||||
|
|
@ -33,7 +34,7 @@ const SideBarFoldersButtonsComponent = ({
|
||||||
const [foldersNames, setFoldersNames] = useState({});
|
const [foldersNames, setFoldersNames] = useState({});
|
||||||
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
|
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
|
||||||
const [editFolders, setEditFolderName] = useState(
|
const [editFolders, setEditFolderName] = useState(
|
||||||
folders.map((obj) => ({ name: obj.name, edit: false }))
|
folders.map((obj) => ({ name: obj.name, edit: false })),
|
||||||
);
|
);
|
||||||
const uploadFolder = useFolderStore((state) => state.uploadFolder);
|
const uploadFolder = useFolderStore((state) => state.uploadFolder);
|
||||||
const currentFolder = pathname.split("/");
|
const currentFolder = pathname.split("/");
|
||||||
|
|
@ -51,6 +52,7 @@ const SideBarFoldersButtonsComponent = ({
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const folderId = location?.state?.folderId ?? myCollectionId;
|
const folderId = location?.state?.folderId ?? myCollectionId;
|
||||||
const getFolderById = useFolderStore((state) => state.getFolderById);
|
const getFolderById = useFolderStore((state) => state.getFolderById);
|
||||||
|
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||||
|
|
||||||
const handleFolderChange = (folderId: string) => {
|
const handleFolderChange = (folderId: string) => {
|
||||||
getFolderById(folderId);
|
getFolderById(folderId);
|
||||||
|
|
@ -58,11 +60,21 @@ const SideBarFoldersButtonsComponent = ({
|
||||||
|
|
||||||
const { dragOver, dragEnter, dragLeave, onDrop } = useFileDrop(
|
const { dragOver, dragEnter, dragLeave, onDrop } = useFileDrop(
|
||||||
folderId,
|
folderId,
|
||||||
handleFolderChange
|
handleFolderChange,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleUploadFlowsToFolder = () => {
|
const handleUploadFlowsToFolder = () => {
|
||||||
uploadFolder(folderId);
|
uploadFolder(folderId)
|
||||||
|
.then(() => {
|
||||||
|
getFolderById(folderId);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
setErrorData({
|
||||||
|
title: `Error on upload`,
|
||||||
|
list: [err["response"]["data"]],
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDownloadFolder = (id: string) => {
|
const handleDownloadFolder = (id: string) => {
|
||||||
|
|
@ -73,7 +85,7 @@ const SideBarFoldersButtonsComponent = ({
|
||||||
addFolder({ name: "New Folder", parent_id: null, description: "" }).then(
|
addFolder({ name: "New Folder", parent_id: null, description: "" }).then(
|
||||||
(res) => {
|
(res) => {
|
||||||
getFoldersApi(true);
|
getFoldersApi(true);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,7 +131,7 @@ const SideBarFoldersButtonsComponent = ({
|
||||||
<>
|
<>
|
||||||
{folders.map((item, index) => {
|
{folders.map((item, index) => {
|
||||||
const editFolderName = editFolders?.filter(
|
const editFolderName = editFolders?.filter(
|
||||||
(folder) => folder.name === item.name
|
(folder) => folder.name === item.name,
|
||||||
)[0];
|
)[0];
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
@ -135,7 +147,7 @@ const SideBarFoldersButtonsComponent = ({
|
||||||
? "border border-border bg-muted hover:bg-muted"
|
? "border border-border bg-muted hover:bg-muted"
|
||||||
: "border hover:bg-transparent lg:border-transparent lg:hover:border-border",
|
: "border hover:bg-transparent lg:border-transparent lg:hover:border-border",
|
||||||
"group flex w-full shrink-0 cursor-pointer gap-2 opacity-100 lg:min-w-full",
|
"group flex w-full shrink-0 cursor-pointer gap-2 opacity-100 lg:min-w-full",
|
||||||
folderIdDragging === item.id! ? "bg-border" : ""
|
folderIdDragging === item.id! ? "bg-border" : "",
|
||||||
)}
|
)}
|
||||||
onClick={() => handleChangeFolder!(item.id!)}
|
onClick={() => handleChangeFolder!(item.id!)}
|
||||||
>
|
>
|
||||||
|
|
@ -205,7 +217,7 @@ const SideBarFoldersButtonsComponent = ({
|
||||||
folders.map((obj) => ({
|
folders.map((obj) => ({
|
||||||
name: obj.name,
|
name: obj.name,
|
||||||
edit: false,
|
edit: false,
|
||||||
}))
|
})),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (e.key === "Enter") {
|
if (e.key === "Enter") {
|
||||||
|
|
@ -238,10 +250,10 @@ const SideBarFoldersButtonsComponent = ({
|
||||||
};
|
};
|
||||||
const updatedFolder = await updateFolder(
|
const updatedFolder = await updateFolder(
|
||||||
body,
|
body,
|
||||||
item.id!
|
item.id!,
|
||||||
);
|
);
|
||||||
const updateFolders = folders.filter(
|
const updateFolders = folders.filter(
|
||||||
(f) => f.name !== item.name
|
(f) => f.name !== item.name,
|
||||||
);
|
);
|
||||||
setFolders([...updateFolders, updatedFolder]);
|
setFolders([...updateFolders, updatedFolder]);
|
||||||
setFoldersNames({});
|
setFoldersNames({});
|
||||||
|
|
@ -249,7 +261,7 @@ const SideBarFoldersButtonsComponent = ({
|
||||||
folders.map((obj) => ({
|
folders.map((obj) => ({
|
||||||
name: obj.name,
|
name: obj.name,
|
||||||
edit: false,
|
edit: false,
|
||||||
}))
|
})),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
setFoldersNames((old) => ({
|
setFoldersNames((old) => ({
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ type SidebarNavProps = {
|
||||||
title: string;
|
title: string;
|
||||||
icon: React.ReactNode;
|
icon: React.ReactNode;
|
||||||
}[];
|
}[];
|
||||||
handleOpenNewFolderModal?: () => void;
|
|
||||||
handleChangeFolder?: (id: string) => void;
|
handleChangeFolder?: (id: string) => void;
|
||||||
handleEditFolder?: (item: FolderType) => void;
|
handleEditFolder?: (item: FolderType) => void;
|
||||||
handleDeleteFolder?: (item: FolderType) => void;
|
handleDeleteFolder?: (item: FolderType) => void;
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import ForwardedIconComponent from "../genericIconComponent";
|
||||||
import { Alert, AlertDescription, AlertTitle } from "../ui/alert";
|
import { Alert, AlertDescription, AlertTitle } from "../ui/alert";
|
||||||
import ResetColumns from "./components/ResetColumns";
|
import ResetColumns from "./components/ResetColumns";
|
||||||
import resetGrid from "./utils/reset-grid-columns";
|
import resetGrid from "./utils/reset-grid-columns";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
|
||||||
interface TableComponentProps extends AgGridReactProps {
|
interface TableComponentProps extends AgGridReactProps {
|
||||||
columnDefs: NonNullable<AgGridReactProps["columnDefs"]>;
|
columnDefs: NonNullable<AgGridReactProps["columnDefs"]>;
|
||||||
|
|
@ -32,7 +33,7 @@ const TableComponent = forwardRef<
|
||||||
alertDescription = DEFAULT_TABLE_ALERT_MSG,
|
alertDescription = DEFAULT_TABLE_ALERT_MSG,
|
||||||
...props
|
...props
|
||||||
},
|
},
|
||||||
ref
|
ref,
|
||||||
) => {
|
) => {
|
||||||
let colDef = props.columnDefs.map((col, index) => {
|
let colDef = props.columnDefs.map((col, index) => {
|
||||||
let newCol = {
|
let newCol = {
|
||||||
|
|
@ -82,16 +83,16 @@ const TableComponent = forwardRef<
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
realRef.current = params;
|
realRef.current = params;
|
||||||
const updatedColumnDefs = makeLastColumnNonResizable([...colDef]);
|
const updatedColumnDefs = makeLastColumnNonResizable([...colDef]);
|
||||||
params.api.setColumnDefs(updatedColumnDefs);
|
params.api.setGridOption("columnDefs", updatedColumnDefs);
|
||||||
initialColumnDefs.current = params.api.getColumnDefs();
|
initialColumnDefs.current = params.api.getColumnDefs();
|
||||||
if (props.onGridReady) props.onGridReady(params);
|
if (props.onGridReady) props.onGridReady(params);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onColumnMoved = (params) => {
|
const onColumnMoved = (params) => {
|
||||||
const updatedColumnDefs = makeLastColumnNonResizable(
|
const updatedColumnDefs = makeLastColumnNonResizable(
|
||||||
params.columnApi.getAllGridColumns().map((col) => col.getColDef())
|
params.columnApi.getAllGridColumns().map((col) => col.getColDef()),
|
||||||
);
|
);
|
||||||
params.api.setColumnDefs(updatedColumnDefs);
|
params.api.setGridOption("columnDefs", updatedColumnDefs);
|
||||||
if (props.onColumnMoved) props.onColumnMoved(params);
|
if (props.onColumnMoved) props.onColumnMoved(params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -114,7 +115,7 @@ const TableComponent = forwardRef<
|
||||||
className={cn(
|
className={cn(
|
||||||
dark ? "ag-theme-quartz-dark" : "ag-theme-quartz",
|
dark ? "ag-theme-quartz-dark" : "ag-theme-quartz",
|
||||||
"ag-theme-shadcn flex h-full flex-col",
|
"ag-theme-shadcn flex h-full flex-col",
|
||||||
"relative"
|
"relative",
|
||||||
)} // applying the grid theme
|
)} // applying the grid theme
|
||||||
>
|
>
|
||||||
<AgGridReact
|
<AgGridReact
|
||||||
|
|
@ -125,9 +126,6 @@ const TableComponent = forwardRef<
|
||||||
}}
|
}}
|
||||||
columnDefs={colDef}
|
columnDefs={colDef}
|
||||||
ref={realRef}
|
ref={realRef}
|
||||||
getRowId={(params) => {
|
|
||||||
return params.data.id;
|
|
||||||
}}
|
|
||||||
pagination={true}
|
pagination={true}
|
||||||
onGridReady={onGridReady}
|
onGridReady={onGridReady}
|
||||||
onColumnMoved={onColumnMoved}
|
onColumnMoved={onColumnMoved}
|
||||||
|
|
@ -135,7 +133,7 @@ const TableComponent = forwardRef<
|
||||||
<ResetColumns resetGrid={() => resetGrid(realRef, initialColumnDefs)} />
|
<ResetColumns resetGrid={() => resetGrid(realRef, initialColumnDefs)} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export default TableComponent;
|
export default TableComponent;
|
||||||
|
|
|
||||||
|
|
@ -668,7 +668,7 @@ export const ZERO_NOTIFICATIONS = "No new notifications";
|
||||||
export const SUCCESS_BUILD = "Built sucessfully ✨";
|
export const SUCCESS_BUILD = "Built sucessfully ✨";
|
||||||
|
|
||||||
export const ALERT_SAVE_WITH_API =
|
export const ALERT_SAVE_WITH_API =
|
||||||
"Caution: Uncheck this box only removes API keys from fields specifically designated for API keys.";
|
"Caution: Unchecking this box only removes API keys from fields specifically designated for API keys.";
|
||||||
|
|
||||||
export const SAVE_WITH_API_CHECKBOX = "Save with my API keys";
|
export const SAVE_WITH_API_CHECKBOX = "Save with my API keys";
|
||||||
export const EDIT_TEXT_MODAL_TITLE = "Edit Text";
|
export const EDIT_TEXT_MODAL_TITLE = "Edit Text";
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@ const SvgBotMessageSquare = (props) => (
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
stroke-width="2"
|
strokeWidth="2"
|
||||||
stroke-linecap="round"
|
strokeLinecap="round"
|
||||||
stroke-linejoin="round"
|
strokeLinejoin="round"
|
||||||
className="lucide lucide-bot-message-square"
|
className="lucide lucide-bot-message-square"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ export default function ChatView({
|
||||||
function updateChat(
|
function updateChat(
|
||||||
chat: ChatMessageType,
|
chat: ChatMessageType,
|
||||||
message: string,
|
message: string,
|
||||||
stream_url?: string
|
stream_url?: string,
|
||||||
) {
|
) {
|
||||||
// if (message === "") return;
|
// if (message === "") return;
|
||||||
chat.message = message;
|
chat.message = message;
|
||||||
|
|
@ -165,14 +165,12 @@ export default function ChatView({
|
||||||
className="flex gap-1"
|
className="flex gap-1"
|
||||||
size="none"
|
size="none"
|
||||||
variant="none"
|
variant="none"
|
||||||
|
disabled={lockChat}
|
||||||
onClick={() => handleSelectChange("builds")}
|
onClick={() => handleSelectChange("builds")}
|
||||||
>
|
>
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="Eraser"
|
name="Eraser"
|
||||||
className={classNames(
|
className={classNames("h-5 w-5 text-primary")}
|
||||||
"h-5 w-5 transition-all duration-100",
|
|
||||||
lockChat ? "animate-pulse text-primary" : "text-primary"
|
|
||||||
)}
|
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -44,17 +44,17 @@ export const switchCaseModalSize = (size: string) => {
|
||||||
break;
|
break;
|
||||||
case "large-thin":
|
case "large-thin":
|
||||||
minWidth = "min-w-[65vw]";
|
minWidth = "min-w-[65vw]";
|
||||||
height = "h-[80vh]";
|
height = "h-[90vh]";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "md-thin":
|
case "md-thin":
|
||||||
minWidth = "min-w-[85vw]";
|
minWidth = "min-w-[85vw]";
|
||||||
height = "h-[70vh]";
|
height = "h-[90vh]";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "sm-thin":
|
case "sm-thin":
|
||||||
minWidth = "min-w-[65vw]";
|
minWidth = "min-w-[65vw]";
|
||||||
height = "h-[70vh]";
|
height = "h-[90vh]";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "large-h-full":
|
case "large-h-full":
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ interface BaseModalProps {
|
||||||
React.ReactElement<ContentProps>,
|
React.ReactElement<ContentProps>,
|
||||||
React.ReactElement<HeaderProps>,
|
React.ReactElement<HeaderProps>,
|
||||||
React.ReactElement<TriggerProps>?,
|
React.ReactElement<TriggerProps>?,
|
||||||
React.ReactElement<FooterProps>?
|
React.ReactElement<FooterProps>?,
|
||||||
];
|
];
|
||||||
open?: boolean;
|
open?: boolean;
|
||||||
setOpen?: (open: boolean) => void;
|
setOpen?: (open: boolean) => void;
|
||||||
|
|
@ -138,16 +138,16 @@ function BaseModal({
|
||||||
onSubmit,
|
onSubmit,
|
||||||
}: BaseModalProps) {
|
}: BaseModalProps) {
|
||||||
const headerChild = React.Children.toArray(children).find(
|
const headerChild = React.Children.toArray(children).find(
|
||||||
(child) => (child as React.ReactElement).type === Header
|
(child) => (child as React.ReactElement).type === Header,
|
||||||
);
|
);
|
||||||
const triggerChild = React.Children.toArray(children).find(
|
const triggerChild = React.Children.toArray(children).find(
|
||||||
(child) => (child as React.ReactElement).type === Trigger
|
(child) => (child as React.ReactElement).type === Trigger,
|
||||||
);
|
);
|
||||||
const ContentChild = React.Children.toArray(children).find(
|
const ContentChild = React.Children.toArray(children).find(
|
||||||
(child) => (child as React.ReactElement).type === Content
|
(child) => (child as React.ReactElement).type === Content,
|
||||||
);
|
);
|
||||||
const ContentFooter = React.Children.toArray(children).find(
|
const ContentFooter = React.Children.toArray(children).find(
|
||||||
(child) => (child as React.ReactElement).type === Footer
|
(child) => (child as React.ReactElement).type === Footer,
|
||||||
);
|
);
|
||||||
|
|
||||||
let { minWidth, height } = switchCaseModalSize(size);
|
let { minWidth, height } = switchCaseModalSize(size);
|
||||||
|
|
@ -164,17 +164,21 @@ function BaseModal({
|
||||||
{type === "modal" ? (
|
{type === "modal" ? (
|
||||||
<Modal open={open} onOpenChange={setOpen}>
|
<Modal open={open} onOpenChange={setOpen}>
|
||||||
{triggerChild}
|
{triggerChild}
|
||||||
<ModalContent className={cn(minWidth, "duration-300")}>
|
<ModalContent
|
||||||
<div className="truncate-doubleline word-break-break-word">
|
className={cn(minWidth, height, "flex flex-col duration-300")}
|
||||||
|
>
|
||||||
|
<div className="flex-shrink-0 truncate-doubleline word-break-break-word">
|
||||||
{headerChild}
|
{headerChild}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`flex flex-col ${height} w-full transition-all duration-300`}
|
className={`flex w-full flex-1 flex-col transition-all duration-300`}
|
||||||
>
|
>
|
||||||
{ContentChild}
|
{ContentChild}
|
||||||
</div>
|
</div>
|
||||||
{ContentFooter && (
|
{ContentFooter && (
|
||||||
<div className="flex flex-row-reverse">{ContentFooter}</div>
|
<div className="flex flex-shrink-0 flex-row-reverse">
|
||||||
|
{ContentFooter}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
@ -184,7 +188,7 @@ function BaseModal({
|
||||||
<DialogContent
|
<DialogContent
|
||||||
className={cn(minWidth, height, "flex flex-col duration-300")}
|
className={cn(minWidth, height, "flex flex-col duration-300")}
|
||||||
>
|
>
|
||||||
<div className="truncate-doubleline word-break-break-word">
|
<div className="flex-shrink-0 truncate-doubleline word-break-break-word">
|
||||||
{headerChild}
|
{headerChild}
|
||||||
</div>
|
</div>
|
||||||
{onSubmit ? (
|
{onSubmit ? (
|
||||||
|
|
@ -193,26 +197,30 @@ function BaseModal({
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
onSubmit();
|
onSubmit();
|
||||||
}}
|
}}
|
||||||
className="flex flex-1 flex-col gap-6"
|
className="flex min-h-0 flex-1 flex-col gap-6"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`flex w-full flex-1 flex-col transition-all duration-300`}
|
className={`flex w-full flex-1 flex-col overflow-hidden transition-all duration-300`}
|
||||||
>
|
>
|
||||||
{ContentChild}
|
{ContentChild}
|
||||||
</div>
|
</div>
|
||||||
{ContentFooter && (
|
{ContentFooter && (
|
||||||
<div className="flex flex-row-reverse">{ContentFooter}</div>
|
<div className="flex flex-shrink-0 flex-row-reverse">
|
||||||
|
{ContentFooter}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</Form.Root>
|
</Form.Root>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
className={`flex w-full flex-1 flex-col transition-all duration-300`}
|
className={`flex min-h-0 w-full flex-1 flex-col transition-all duration-300`}
|
||||||
>
|
>
|
||||||
{ContentChild}
|
{ContentChild}
|
||||||
</div>
|
</div>
|
||||||
{ContentFooter && (
|
{ContentFooter && (
|
||||||
<div className="flex flex-row-reverse">{ContentFooter}</div>
|
<div className="flex flex-shrink-0 flex-row-reverse">
|
||||||
|
{ContentFooter}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ const ExportModal = forwardRef(
|
||||||
is_component: false,
|
is_component: false,
|
||||||
},
|
},
|
||||||
name!,
|
name!,
|
||||||
description
|
description,
|
||||||
);
|
);
|
||||||
setNoticeData({
|
setNoticeData({
|
||||||
title: API_WARNING_NOTICE_ALERT,
|
title: API_WARNING_NOTICE_ALERT,
|
||||||
|
|
@ -61,7 +61,7 @@ const ExportModal = forwardRef(
|
||||||
is_component: false,
|
is_component: false,
|
||||||
}),
|
}),
|
||||||
name!,
|
name!,
|
||||||
description
|
description,
|
||||||
);
|
);
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
}}
|
}}
|
||||||
|
|
@ -94,7 +94,7 @@ const ExportModal = forwardRef(
|
||||||
{SAVE_WITH_API_CHECKBOX}
|
{SAVE_WITH_API_CHECKBOX}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<span className=" text-xs text-destructive ">
|
<span className="mt-1 text-xs text-destructive ">
|
||||||
{ALERT_SAVE_WITH_API}
|
{ALERT_SAVE_WITH_API}
|
||||||
</span>
|
</span>
|
||||||
</BaseModal.Content>
|
</BaseModal.Content>
|
||||||
|
|
@ -102,6 +102,6 @@ const ExportModal = forwardRef(
|
||||||
<BaseModal.Footer submit={{ label: "Download Flow" }} />
|
<BaseModal.Footer submit={{ label: "Download Flow" }} />
|
||||||
</BaseModal>
|
</BaseModal>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
export default ExportModal;
|
export default ExportModal;
|
||||||
|
|
|
||||||
|
|
@ -11,13 +11,13 @@ import ReactFlow, {
|
||||||
SelectionDragHandler,
|
SelectionDragHandler,
|
||||||
updateEdge,
|
updateEdge,
|
||||||
} from "reactflow";
|
} from "reactflow";
|
||||||
|
import GenericNode from "../../../../CustomNodes/GenericNode";
|
||||||
import {
|
import {
|
||||||
INVALID_SELECTION_ERROR_ALERT,
|
INVALID_SELECTION_ERROR_ALERT,
|
||||||
UPLOAD_ALERT_LIST,
|
UPLOAD_ALERT_LIST,
|
||||||
UPLOAD_ERROR_ALERT,
|
UPLOAD_ERROR_ALERT,
|
||||||
WRONG_FILE_ERROR_ALERT,
|
WRONG_FILE_ERROR_ALERT,
|
||||||
} from "../../../../constants/alerts_constants";
|
} from "../../../../constants/alerts_constants";
|
||||||
import GenericNode from "../../../../customNodes/genericNode";
|
|
||||||
import useAlertStore from "../../../../stores/alertStore";
|
import useAlertStore from "../../../../stores/alertStore";
|
||||||
import useFlowStore from "../../../../stores/flowStore";
|
import useFlowStore from "../../../../stores/flowStore";
|
||||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ import { FormProvider, useForm, useWatch } from "react-hook-form";
|
||||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||||
import CollectionCardComponent from "../../../../components/cardComponent";
|
import CollectionCardComponent from "../../../../components/cardComponent";
|
||||||
import CardsWrapComponent from "../../../../components/cardsWrapComponent";
|
import CardsWrapComponent from "../../../../components/cardsWrapComponent";
|
||||||
import IconComponent from "../../../../components/genericIconComponent";
|
import IconComponent, {
|
||||||
|
ForwardedIconComponent,
|
||||||
|
} from "../../../../components/genericIconComponent";
|
||||||
import PaginatorComponent from "../../../../components/paginatorComponent";
|
import PaginatorComponent from "../../../../components/paginatorComponent";
|
||||||
import { SkeletonCardComponent } from "../../../../components/skeletonCardComponent";
|
import { SkeletonCardComponent } from "../../../../components/skeletonCardComponent";
|
||||||
import { Button } from "../../../../components/ui/button";
|
import { Button } from "../../../../components/ui/button";
|
||||||
|
|
@ -18,6 +20,9 @@ import { getNameByType } from "../../utils/get-name-by-type";
|
||||||
import { sortFlows } from "../../utils/sort-flows";
|
import { sortFlows } from "../../utils/sort-flows";
|
||||||
import EmptyComponent from "../emptyComponent";
|
import EmptyComponent from "../emptyComponent";
|
||||||
import HeaderComponent from "../headerComponent";
|
import HeaderComponent from "../headerComponent";
|
||||||
|
import { downloadFlow, removeApiKeys } from "../../../../utils/reactflowUtils";
|
||||||
|
import { useDarkStore } from "../../../../stores/darkStore";
|
||||||
|
import { UPLOAD_ERROR_ALERT } from "../../../../constants/alerts_constants";
|
||||||
|
|
||||||
export default function ComponentsComponent({
|
export default function ComponentsComponent({
|
||||||
type = "all",
|
type = "all",
|
||||||
|
|
@ -31,22 +36,22 @@ export default function ComponentsComponent({
|
||||||
const allFlows = useFlowsManagerStore((state) => state.allFlows);
|
const allFlows = useFlowsManagerStore((state) => state.allFlows);
|
||||||
|
|
||||||
const flowsFromFolder = useFolderStore(
|
const flowsFromFolder = useFolderStore(
|
||||||
(state) => state.selectedFolder?.flows
|
(state) => state.selectedFolder?.flows,
|
||||||
);
|
);
|
||||||
|
|
||||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||||
const [openDelete, setOpenDelete] = useState(false);
|
const [openDelete, setOpenDelete] = useState(false);
|
||||||
const searchFlowsComponents = useFlowsManagerStore(
|
const searchFlowsComponents = useFlowsManagerStore(
|
||||||
(state) => state.searchFlowsComponents
|
(state) => state.searchFlowsComponents,
|
||||||
);
|
);
|
||||||
|
|
||||||
const setSelectedFlowsComponentsCards = useFlowsManagerStore(
|
const setSelectedFlowsComponentsCards = useFlowsManagerStore(
|
||||||
(state) => state.setSelectedFlowsComponentsCards
|
(state) => state.setSelectedFlowsComponentsCards,
|
||||||
);
|
);
|
||||||
|
|
||||||
const selectedFlowsComponentsCards = useFlowsManagerStore(
|
const selectedFlowsComponentsCards = useFlowsManagerStore(
|
||||||
(state) => state.selectedFlowsComponentsCards
|
(state) => state.selectedFlowsComponentsCards,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [handleFileDrop] = useFileDrop(uploadFlow, type)!;
|
const [handleFileDrop] = useFileDrop(uploadFlow, type)!;
|
||||||
|
|
@ -82,7 +87,7 @@ export default function ComponentsComponent({
|
||||||
f.name.toLowerCase().includes(searchFlowsComponents.toLowerCase()) ||
|
f.name.toLowerCase().includes(searchFlowsComponents.toLowerCase()) ||
|
||||||
f.description
|
f.description
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.includes(searchFlowsComponents.toLowerCase())
|
.includes(searchFlowsComponents.toLowerCase()),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (searchFlowsComponents === "") {
|
if (searchFlowsComponents === "") {
|
||||||
|
|
@ -129,6 +134,8 @@ export default function ComponentsComponent({
|
||||||
setOpenDelete(true);
|
setOpenDelete(true);
|
||||||
} else if (action === "duplicate") {
|
} else if (action === "duplicate") {
|
||||||
handleDuplicate();
|
handleDuplicate();
|
||||||
|
} else if (action === "export") {
|
||||||
|
handleExport();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -137,9 +144,9 @@ export default function ComponentsComponent({
|
||||||
selectedFlowsComponentsCards.map((selectedFlow) =>
|
selectedFlowsComponentsCards.map((selectedFlow) =>
|
||||||
addFlow(
|
addFlow(
|
||||||
true,
|
true,
|
||||||
allFlows.find((flow) => flow.id === selectedFlow)
|
allFlows.find((flow) => flow.id === selectedFlow),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
).then(() => {
|
).then(() => {
|
||||||
resetFilter();
|
resetFilter();
|
||||||
getFoldersApi(true);
|
getFoldersApi(true);
|
||||||
|
|
@ -152,6 +159,47 @@ export default function ComponentsComponent({
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleImport = () => {
|
||||||
|
uploadFlow({ newProject: true, isComponent: false })
|
||||||
|
.then(() => {
|
||||||
|
resetFilter();
|
||||||
|
getFoldersApi(true);
|
||||||
|
if (!folderId || folderId === myCollectionId) {
|
||||||
|
getFolderById(folderId ? folderId : myCollectionId);
|
||||||
|
}
|
||||||
|
setSelectedFlowsComponentsCards([]);
|
||||||
|
|
||||||
|
setSuccessData({ title: "Flows imported successfully" });
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
setErrorData({
|
||||||
|
title: UPLOAD_ERROR_ALERT,
|
||||||
|
list: [error],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const version = useDarkStore((state) => state.version);
|
||||||
|
|
||||||
|
const handleExport = () => {
|
||||||
|
selectedFlowsComponentsCards.map((selectedFlowId) => {
|
||||||
|
const selectedFlow = allFlows.find((flow) => flow.id === selectedFlowId);
|
||||||
|
downloadFlow(
|
||||||
|
removeApiKeys({
|
||||||
|
id: selectedFlow!.id,
|
||||||
|
data: selectedFlow!.data!,
|
||||||
|
description: selectedFlow!.description,
|
||||||
|
name: selectedFlow!.name,
|
||||||
|
last_tested_version: version,
|
||||||
|
is_component: false,
|
||||||
|
}),
|
||||||
|
selectedFlow!.name,
|
||||||
|
selectedFlow!.description,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
setSuccessData({ title: "Flows exported successfully" });
|
||||||
|
};
|
||||||
|
|
||||||
const handleDeleteMultiple = () => {
|
const handleDeleteMultiple = () => {
|
||||||
removeFlow(selectedFlowsComponentsCards)
|
removeFlow(selectedFlowsComponentsCards)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
@ -161,7 +209,7 @@ export default function ComponentsComponent({
|
||||||
getFolderById(folderId ? folderId : myCollectionId);
|
getFolderById(folderId ? folderId : myCollectionId);
|
||||||
}
|
}
|
||||||
setSuccessData({
|
setSuccessData({
|
||||||
title: "Selected items deleted successfully!",
|
title: "Selected items deleted successfully",
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
|
|
@ -180,7 +228,7 @@ export default function ComponentsComponent({
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
setSelectedFlowsComponentsCards(selectedFlows);
|
setSelectedFlowsComponentsCards(selectedFlows);
|
||||||
|
|
@ -215,20 +263,23 @@ export default function ComponentsComponent({
|
||||||
if (type === "all") return allFlows?.length;
|
if (type === "all") return allFlows?.length;
|
||||||
|
|
||||||
return allFlows?.filter(
|
return allFlows?.filter(
|
||||||
(f) => (f.is_component ?? false) === (type === "component")
|
(f) => (f.is_component ?? false) === (type === "component"),
|
||||||
)?.length;
|
)?.length;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{allFlows?.length > 0 && (
|
<div className="flex w-full gap-4 pb-5">
|
||||||
<HeaderComponent
|
{allFlows?.length > 0 && (
|
||||||
handleDelete={() => handleSelectOptionsChange("delete")}
|
<HeaderComponent
|
||||||
handleSelectAll={handleSelectAll}
|
handleDelete={() => handleSelectOptionsChange("delete")}
|
||||||
handleDuplicate={() => handleSelectOptionsChange("duplicate")}
|
handleSelectAll={handleSelectAll}
|
||||||
disableFunctions={!(selectedFlowsComponentsCards?.length > 0)}
|
handleDuplicate={() => handleSelectOptionsChange("duplicate")}
|
||||||
/>
|
handleExport={() => handleSelectOptionsChange("export")}
|
||||||
)}
|
disableFunctions={!(selectedFlowsComponentsCards?.length > 0)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<CardsWrapComponent
|
<CardsWrapComponent
|
||||||
onFileDrop={handleFileDrop}
|
onFileDrop={handleFileDrop}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ const EmptyComponent = ({}: EmptyComponentProps) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="mt-6 flex w-full items-center justify-center text-center">
|
<div className="mt-2 flex w-full items-center justify-center text-center">
|
||||||
<div className="flex-max-width h-full flex-col">
|
<div className="flex-max-width h-full flex-col">
|
||||||
<div className="align-center flex w-full justify-center gap-1 ">
|
<div className="align-center flex w-full justify-center gap-1 ">
|
||||||
<span className="text-muted-foreground">
|
<span className="text-muted-foreground">
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import IconComponent from "../../../../components/genericIconComponent";
|
import IconComponent, {
|
||||||
|
ForwardedIconComponent,
|
||||||
|
} from "../../../../components/genericIconComponent";
|
||||||
import ShadTooltip from "../../../../components/shadTooltipComponent";
|
import ShadTooltip from "../../../../components/shadTooltipComponent";
|
||||||
import { Checkbox } from "../../../../components/ui/checkbox";
|
import { Checkbox } from "../../../../components/ui/checkbox";
|
||||||
import { cn } from "../../../../utils/utils";
|
import { cn } from "../../../../utils/utils";
|
||||||
|
import { Button } from "../../../../components/ui/button";
|
||||||
|
|
||||||
type HeaderComponentProps = {
|
type HeaderComponentProps = {
|
||||||
handleSelectAll: (select) => void;
|
handleSelectAll: (select) => void;
|
||||||
handleDelete: () => void;
|
handleDelete: () => void;
|
||||||
handleDuplicate: () => void;
|
handleDuplicate: () => void;
|
||||||
|
handleExport: () => void;
|
||||||
disableFunctions: boolean;
|
disableFunctions: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -15,6 +19,7 @@ const HeaderComponent = ({
|
||||||
handleSelectAll,
|
handleSelectAll,
|
||||||
handleDelete,
|
handleDelete,
|
||||||
handleDuplicate,
|
handleDuplicate,
|
||||||
|
handleExport,
|
||||||
disableFunctions,
|
disableFunctions,
|
||||||
}: HeaderComponentProps) => {
|
}: HeaderComponentProps) => {
|
||||||
const [shouldSelectAll, setShouldSelectAll] = useState(true);
|
const [shouldSelectAll, setShouldSelectAll] = useState(true);
|
||||||
|
|
@ -26,29 +31,41 @@ const HeaderComponent = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="grid grid-cols-3 pb-5">
|
<div className="flex w-full items-center justify-between gap-4">
|
||||||
<div className="col-auto grid-cols-1 self-center justify-self-start">
|
<div className="flex items-center justify-self-start">
|
||||||
<a onClick={handleClick} className="text-sm">
|
<a onClick={handleClick} className="cursor-pointer text-sm">
|
||||||
<div className="header-menu-bar-display ">
|
<div className="flex items-center space-x-2">
|
||||||
<div
|
<Checkbox checked={!shouldSelectAll} id="terms" />
|
||||||
className="header-menu-flow-name"
|
<span className="label text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
|
||||||
data-testid="select_all_collection"
|
{shouldSelectAll ? "Select All" : "Unselect All"}
|
||||||
>
|
</span>
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<Checkbox checked={!shouldSelectAll} id="terms" />
|
|
||||||
<label
|
|
||||||
onClick={handleClick}
|
|
||||||
htmlFor="terms"
|
|
||||||
className="label cursor-pointer text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
||||||
>
|
|
||||||
{shouldSelectAll ? "Select All" : "Unselect All"}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-2 flex grid-cols-1 gap-2 justify-self-end">
|
<div className="flex items-center gap-2">
|
||||||
|
<div>
|
||||||
|
<ShadTooltip
|
||||||
|
content={
|
||||||
|
disableFunctions ? (
|
||||||
|
<span>Select items to export</span>
|
||||||
|
) : (
|
||||||
|
<span>Export selected items</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant="none"
|
||||||
|
size="none"
|
||||||
|
onClick={handleExport}
|
||||||
|
disabled={disableFunctions}
|
||||||
|
>
|
||||||
|
<IconComponent
|
||||||
|
name="FileDown"
|
||||||
|
className={cn("h-5 w-5 text-primary transition-all")}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</ShadTooltip>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<ShadTooltip
|
<ShadTooltip
|
||||||
content={
|
content={
|
||||||
|
|
@ -59,12 +76,17 @@ const HeaderComponent = ({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<button onClick={handleDuplicate} disabled={disableFunctions}>
|
<Button
|
||||||
|
variant="none"
|
||||||
|
size="none"
|
||||||
|
onClick={handleDuplicate}
|
||||||
|
disabled={disableFunctions}
|
||||||
|
>
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="Copy"
|
name="Copy"
|
||||||
className={cn("h-5 w-5 text-primary transition-all")}
|
className={cn("h-5 w-5 text-primary transition-all")}
|
||||||
/>
|
/>
|
||||||
</button>
|
</Button>
|
||||||
</ShadTooltip>
|
</ShadTooltip>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -77,15 +99,20 @@ const HeaderComponent = ({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<button onClick={handleDelete} disabled={disableFunctions}>
|
<Button
|
||||||
|
variant="none"
|
||||||
|
size="none"
|
||||||
|
onClick={handleDelete}
|
||||||
|
disabled={disableFunctions}
|
||||||
|
>
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="Trash2"
|
name="Trash2"
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-5 w-5 text-primary transition-all",
|
"h-5 w-5 text-primary transition-all",
|
||||||
disableFunctions ? "" : "hover:text-destructive"
|
disableFunctions ? "" : "hover:text-destructive",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</button>
|
</Button>
|
||||||
</ShadTooltip>
|
</ShadTooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,15 @@ import ComponentsComponent from "../componentsComponent";
|
||||||
import HeaderTabsSearchComponent from "./components/headerTabsSearchComponent";
|
import HeaderTabsSearchComponent from "./components/headerTabsSearchComponent";
|
||||||
|
|
||||||
type MyCollectionComponentProps = {
|
type MyCollectionComponentProps = {
|
||||||
key: string;
|
|
||||||
type: string;
|
type: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MyCollectionComponent = ({ key, type }: MyCollectionComponentProps) => {
|
const MyCollectionComponent = ({ type }: MyCollectionComponentProps) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<HeaderTabsSearchComponent />
|
<HeaderTabsSearchComponent />
|
||||||
<div className="mt-5 flex h-full flex-col">
|
<div className="mt-5 flex h-full flex-col">
|
||||||
<ComponentsComponent key={key} type={type} />
|
<ComponentsComponent key={type} type={type} />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import useDropdownOptions from "../../hooks/use-dropdown-options";
|
||||||
export default function HomePage(): JSX.Element {
|
export default function HomePage(): JSX.Element {
|
||||||
const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow);
|
const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow);
|
||||||
const setCurrentFlowId = useFlowsManagerStore(
|
const setCurrentFlowId = useFlowsManagerStore(
|
||||||
(state) => state.setCurrentFlowId
|
(state) => state.setCurrentFlowId,
|
||||||
);
|
);
|
||||||
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
@ -67,10 +67,6 @@ export default function HomePage(): JSX.Element {
|
||||||
<div className="flex h-full w-full space-y-8 md:flex-col lg:flex-row lg:space-x-8 lg:space-y-0">
|
<div className="flex h-full w-full space-y-8 md:flex-col lg:flex-row lg:space-x-8 lg:space-y-0">
|
||||||
<aside className="flex h-fit w-fit flex-col space-y-6">
|
<aside className="flex h-fit w-fit flex-col space-y-6">
|
||||||
<SidebarNav
|
<SidebarNav
|
||||||
handleOpenNewFolderModal={() => {
|
|
||||||
setFolderToEdit(null);
|
|
||||||
setOpenFolderModal(true);
|
|
||||||
}}
|
|
||||||
items={[]}
|
items={[]}
|
||||||
handleChangeFolder={(id: string) => {
|
handleChangeFolder={(id: string) => {
|
||||||
navigate(`all/folder/${id}`, { state: { folderId: id } });
|
navigate(`all/folder/${id}`, { state: { folderId: id } });
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||||
export default function SettingsPage(): JSX.Element {
|
export default function SettingsPage(): JSX.Element {
|
||||||
const pathname = location.pathname;
|
const pathname = location.pathname;
|
||||||
const setCurrentFlowId = useFlowsManagerStore(
|
const setCurrentFlowId = useFlowsManagerStore(
|
||||||
(state) => state.setCurrentFlowId
|
(state) => state.setCurrentFlowId,
|
||||||
);
|
);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCurrentFlowId("");
|
setCurrentFlowId("");
|
||||||
|
|
@ -59,7 +59,10 @@ export default function SettingsPage(): JSX.Element {
|
||||||
title: "Messages",
|
title: "Messages",
|
||||||
href: "/settings/messages",
|
href: "/settings/messages",
|
||||||
icon: (
|
icon: (
|
||||||
<ForwardedIconComponent name="Keyboard" className="w-5 stroke-[1.5]" />
|
<ForwardedIconComponent
|
||||||
|
name="MessagesSquare"
|
||||||
|
className="w-4 flex-shrink-0 justify-start stroke-[1.5]"
|
||||||
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
@ -72,8 +75,10 @@ export default function SettingsPage(): JSX.Element {
|
||||||
<aside className="flex h-full shrink-0 flex-col space-y-6 lg:w-[20vw]">
|
<aside className="flex h-full shrink-0 flex-col space-y-6 lg:w-[20vw]">
|
||||||
<SidebarNav items={sidebarNavItems} />
|
<SidebarNav items={sidebarNavItems} />
|
||||||
</aside>
|
</aside>
|
||||||
<div className="h-full w-full flex-1 pb-8">
|
<div className="flex h-full w-full flex-1 flex-col">
|
||||||
<Outlet />
|
<div className="flex-1 pb-8">
|
||||||
|
<Outlet />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
|
|
|
||||||
|
|
@ -52,23 +52,17 @@ export default function ApiKeysPage() {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex h-full w-full flex-col justify-between">
|
<div className="flex h-full w-full flex-col justify-between">
|
||||||
<Card x-chunk="dashboard-04-chunk-2" className="h-full pt-4">
|
<TableComponent
|
||||||
<CardContent className="h-full">
|
overlayNoRowsTemplate="No data available"
|
||||||
<TableComponent
|
onSelectionChanged={(event: SelectionChangedEvent) => {
|
||||||
overlayNoRowsTemplate="No data available"
|
setSelectedRows(event.api.getSelectedRows().map((row) => row.id));
|
||||||
onSelectionChanged={(event: SelectionChangedEvent) => {
|
}}
|
||||||
setSelectedRows(
|
rowSelection="multiple"
|
||||||
event.api.getSelectedRows().map((row) => row.id),
|
suppressRowClickSelection={true}
|
||||||
);
|
pagination={true}
|
||||||
}}
|
columnDefs={columnDefs}
|
||||||
rowSelection="multiple"
|
rowData={keysList.current}
|
||||||
suppressRowClickSelection={true}
|
/>
|
||||||
pagination={true}
|
|
||||||
columnDefs={columnDefs}
|
|
||||||
rowData={keysList.current}
|
|
||||||
/>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,13 @@ import { cn } from "../../../../utils/utils";
|
||||||
|
|
||||||
export default function GlobalVariablesPage() {
|
export default function GlobalVariablesPage() {
|
||||||
const globalVariablesEntries = useGlobalVariablesStore(
|
const globalVariablesEntries = useGlobalVariablesStore(
|
||||||
(state) => state.globalVariablesEntries
|
(state) => state.globalVariablesEntries,
|
||||||
);
|
);
|
||||||
const removeGlobalVariable = useGlobalVariablesStore(
|
const removeGlobalVariable = useGlobalVariablesStore(
|
||||||
(state) => state.removeGlobalVariable
|
(state) => state.removeGlobalVariable,
|
||||||
);
|
);
|
||||||
const globalVariables = useGlobalVariablesStore(
|
const globalVariables = useGlobalVariablesStore(
|
||||||
(state) => state.globalVariables
|
(state) => state.globalVariables,
|
||||||
);
|
);
|
||||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||||
const getVariableId = useGlobalVariablesStore((state) => state.getVariableId);
|
const getVariableId = useGlobalVariablesStore((state) => state.getVariableId);
|
||||||
|
|
@ -154,7 +154,7 @@ export default function GlobalVariablesPage() {
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="Trash2"
|
name="Trash2"
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-5 w-5 text-destructive group-disabled:text-primary"
|
"h-5 w-5 text-destructive group-disabled:text-primary",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -168,23 +168,17 @@ export default function GlobalVariablesPage() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex h-full w-full flex-col justify-between">
|
<div className="flex h-full w-full flex-col justify-between">
|
||||||
<Card x-chunk="dashboard-04-chunk-2" className="h-full pt-4">
|
<TableComponent
|
||||||
<CardContent className="h-full">
|
overlayNoRowsTemplate="No data available"
|
||||||
<TableComponent
|
onSelectionChanged={(event: SelectionChangedEvent) => {
|
||||||
overlayNoRowsTemplate="No data available"
|
setSelectedRows(event.api.getSelectedRows().map((row) => row.name));
|
||||||
onSelectionChanged={(event: SelectionChangedEvent) => {
|
}}
|
||||||
setSelectedRows(
|
rowSelection="multiple"
|
||||||
event.api.getSelectedRows().map((row) => row.name)
|
suppressRowClickSelection={true}
|
||||||
);
|
pagination={true}
|
||||||
}}
|
columnDefs={colDefs}
|
||||||
rowSelection="multiple"
|
rowData={rowData}
|
||||||
suppressRowClickSelection={true}
|
/>
|
||||||
pagination={true}
|
|
||||||
columnDefs={colDefs}
|
|
||||||
rowData={rowData}
|
|
||||||
/>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export default function ShortcutsPage() {
|
||||||
const redoShortcut = `${isMac ? "Cmd" : "Ctrl"} + Y`;
|
const redoShortcut = `${isMac ? "Cmd" : "Ctrl"} + Y`;
|
||||||
|
|
||||||
// Column Definitions: Defines the columns to be displayed.
|
// Column Definitions: Defines the columns to be displayed.
|
||||||
const [colDefs, setColDefs] = useState<(ColDef<any> | ColGroupDef<any>)[]>([
|
const colDefs = [
|
||||||
{
|
{
|
||||||
headerName: "Functionality",
|
headerName: "Functionality",
|
||||||
field: "name",
|
field: "name",
|
||||||
|
|
@ -33,9 +33,9 @@ export default function ShortcutsPage() {
|
||||||
editable: false,
|
editable: false,
|
||||||
resizable: false,
|
resizable: false,
|
||||||
},
|
},
|
||||||
]);
|
];
|
||||||
|
|
||||||
const [nodesRowData, setNodesRowData] = useState([
|
const nodesRowData = [
|
||||||
{
|
{
|
||||||
name: "Advanced Settings Component",
|
name: "Advanced Settings Component",
|
||||||
shortcut: advancedShortcut,
|
shortcut: advancedShortcut,
|
||||||
|
|
@ -96,7 +96,7 @@ export default function ShortcutsPage() {
|
||||||
shortcut: redoShortcut,
|
shortcut: redoShortcut,
|
||||||
resizable: false,
|
resizable: false,
|
||||||
},
|
},
|
||||||
]);
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full flex-col gap-6">
|
<div className="flex h-full w-full flex-col gap-6">
|
||||||
|
|
@ -114,17 +114,12 @@ export default function ShortcutsPage() {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid gap-6">
|
<div className="flex h-full w-full flex-col justify-between">
|
||||||
<Card x-chunk="dashboard-04-chunk-2" className="h-full pt-4">
|
<TableComponent
|
||||||
<CardContent className="h-full">
|
pagination={false}
|
||||||
<TableComponent
|
columnDefs={colDefs}
|
||||||
domLayout="autoHeight"
|
rowData={nodesRowData}
|
||||||
pagination={false}
|
/>
|
||||||
columnDefs={colDefs}
|
|
||||||
rowData={nodesRowData}
|
|
||||||
/>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ export default function MessagesPage() {
|
||||||
setSelectedRows,
|
setSelectedRows,
|
||||||
setSuccessData,
|
setSuccessData,
|
||||||
setErrorData,
|
setErrorData,
|
||||||
selectedRows
|
selectedRows,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { handleUpdate } = useUpdateMessage(setSuccessData, setErrorData);
|
const { handleUpdate } = useUpdateMessage(setSuccessData, setErrorData);
|
||||||
|
|
@ -52,29 +52,25 @@ export default function MessagesPage() {
|
||||||
handleRemoveMessages={handleRemoveMessages}
|
handleRemoveMessages={handleRemoveMessages}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex h-full w-full flex-col justify-between pb-8">
|
<div className="flex h-full w-full flex-col justify-between">
|
||||||
<Card x-chunk="dashboard-04-chunk-2" className="h-full pt-4">
|
<TableComponent
|
||||||
<CardContent className="h-full">
|
readOnlyEdit
|
||||||
<TableComponent
|
onCellEditRequest={(event) => {
|
||||||
readOnlyEdit
|
handleUpdateMessage(event);
|
||||||
onCellEditRequest={(event) => {
|
}}
|
||||||
handleUpdateMessage(event);
|
editable={["Sender Name", "Message"]}
|
||||||
}}
|
overlayNoRowsTemplate="No data available"
|
||||||
editable={["Sender Name", "Message"]}
|
onSelectionChanged={(event: SelectionChangedEvent) => {
|
||||||
overlayNoRowsTemplate="No data available"
|
setSelectedRows(
|
||||||
onSelectionChanged={(event: SelectionChangedEvent) => {
|
event.api.getSelectedRows().map((row) => row.index),
|
||||||
setSelectedRows(
|
);
|
||||||
event.api.getSelectedRows().map((row) => row.index)
|
}}
|
||||||
);
|
rowSelection="multiple"
|
||||||
}}
|
suppressRowClickSelection={true}
|
||||||
rowSelection="multiple"
|
pagination={true}
|
||||||
suppressRowClickSelection={true}
|
columnDefs={columns}
|
||||||
pagination={true}
|
rowData={messages}
|
||||||
columnDefs={columns}
|
/>
|
||||||
rowData={messages}
|
|
||||||
/>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import * as debounce from "debounce-promise";
|
|
||||||
import { cloneDeep } from "lodash";
|
import { cloneDeep } from "lodash";
|
||||||
|
import pDebounce from "p-debounce";
|
||||||
import { Edge, Node, Viewport, XYPosition } from "reactflow";
|
import { Edge, Node, Viewport, XYPosition } from "reactflow";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { SAVE_DEBOUNCE_TIME } from "../constants/constants";
|
import { SAVE_DEBOUNCE_TIME } from "../constants/constants";
|
||||||
|
|
@ -128,7 +128,7 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
||||||
set({ saveLoading: true }); // set saveLoading true immediately
|
set({ saveLoading: true }); // set saveLoading true immediately
|
||||||
return get().saveFlowDebounce(flow, silent); // call the debounced function directly
|
return get().saveFlowDebounce(flow, silent); // call the debounced function directly
|
||||||
},
|
},
|
||||||
saveFlowDebounce: debounce((flow: FlowType, silent?: boolean) => {
|
saveFlowDebounce: pDebounce((flow: FlowType, silent?: boolean) => {
|
||||||
set({ saveLoading: true });
|
set({ saveLoading: true });
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
updateFlowInDatabase(flow)
|
updateFlowInDatabase(flow)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import {
|
||||||
} from "../pages/MainPage/services";
|
} from "../pages/MainPage/services";
|
||||||
import { FoldersStoreType } from "../types/zustand/folders";
|
import { FoldersStoreType } from "../types/zustand/folders";
|
||||||
import useFlowsManagerStore from "./flowsManagerStore";
|
import useFlowsManagerStore from "./flowsManagerStore";
|
||||||
|
import { uploadFlowsToDatabase } from "../controllers/API";
|
||||||
|
|
||||||
export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
||||||
folders: [],
|
folders: [],
|
||||||
|
|
@ -17,18 +18,18 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
||||||
getFolders().then(
|
getFolders().then(
|
||||||
(res) => {
|
(res) => {
|
||||||
const foldersWithoutStarterProjects = res.filter(
|
const foldersWithoutStarterProjects = res.filter(
|
||||||
(folder) => folder.name !== STARTER_FOLDER_NAME
|
(folder) => folder.name !== STARTER_FOLDER_NAME,
|
||||||
);
|
);
|
||||||
|
|
||||||
const starterProjects = res.find(
|
const starterProjects = res.find(
|
||||||
(folder) => folder.name === STARTER_FOLDER_NAME
|
(folder) => folder.name === STARTER_FOLDER_NAME,
|
||||||
);
|
);
|
||||||
|
|
||||||
set({ starterProjectId: starterProjects!.id ?? "" });
|
set({ starterProjectId: starterProjects!.id ?? "" });
|
||||||
set({ folders: foldersWithoutStarterProjects });
|
set({ folders: foldersWithoutStarterProjects });
|
||||||
|
|
||||||
const myCollectionId = res?.find(
|
const myCollectionId = res?.find(
|
||||||
(f) => f.name === DEFAULT_FOLDER
|
(f) => f.name === DEFAULT_FOLDER,
|
||||||
)?.id;
|
)?.id;
|
||||||
|
|
||||||
set({ myCollectionId });
|
set({ myCollectionId });
|
||||||
|
|
@ -41,11 +42,11 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
||||||
get().setLoading(false);
|
get().setLoading(false);
|
||||||
resolve();
|
resolve();
|
||||||
},
|
},
|
||||||
() => {
|
(error) => {
|
||||||
set({ folders: [] });
|
set({ folders: [] });
|
||||||
get().setLoading(false);
|
get().setLoading(false);
|
||||||
reject();
|
reject(error);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -65,7 +66,7 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
get().setLoadingById(false);
|
get().setLoadingById(false);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -100,7 +101,7 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
||||||
folderIdDragging: "",
|
folderIdDragging: "",
|
||||||
setFolderIdDragging: (id) => set(() => ({ folderIdDragging: id })),
|
setFolderIdDragging: (id) => set(() => ({ folderIdDragging: id })),
|
||||||
uploadFolder: () => {
|
uploadFolder: () => {
|
||||||
return new Promise<void>(() => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const input = document.createElement("input");
|
const input = document.createElement("input");
|
||||||
input.type = "file";
|
input.type = "file";
|
||||||
input.onchange = (event: Event) => {
|
input.onchange = (event: Event) => {
|
||||||
|
|
@ -111,8 +112,31 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
||||||
const file = (event.target as HTMLInputElement).files![0];
|
const file = (event.target as HTMLInputElement).files![0];
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("file", file);
|
formData.append("file", file);
|
||||||
uploadFlowsFromFolders(formData).then(() => {
|
file.text().then((text) => {
|
||||||
get().getFoldersApi(true);
|
const data = JSON.parse(text);
|
||||||
|
if (data.data?.nodes) {
|
||||||
|
useFlowsManagerStore
|
||||||
|
.getState()
|
||||||
|
.addFlow(true, data)
|
||||||
|
.then(() => {
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
uploadFlowsFromFolders(formData)
|
||||||
|
.then(() => {
|
||||||
|
get()
|
||||||
|
.getFoldersApi(true)
|
||||||
|
.then(() => {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export type FoldersStoreType = {
|
||||||
setFolderUrl: (folderUrl: string) => void;
|
setFolderUrl: (folderUrl: string) => void;
|
||||||
folderDragging: boolean;
|
folderDragging: boolean;
|
||||||
setFolderDragging: (set: boolean) => void;
|
setFolderDragging: (set: boolean) => void;
|
||||||
uploadFolder: (folderId: string) => void;
|
uploadFolder: (folderId: string) => Promise<void>;
|
||||||
folderIdDragging: string;
|
folderIdDragging: string;
|
||||||
setFolderIdDragging: (id: string) => void;
|
setFolderIdDragging: (id: string) => void;
|
||||||
starterProjectId: string;
|
starterProjectId: string;
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import {
|
||||||
XYPosition,
|
XYPosition,
|
||||||
} from "reactflow";
|
} from "reactflow";
|
||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from "short-unique-id";
|
||||||
|
import getFieldTitle from "../CustomNodes/utils/get-field-title";
|
||||||
import {
|
import {
|
||||||
INPUT_TYPES,
|
INPUT_TYPES,
|
||||||
LANGFLOW_SUPPORTED_TYPES,
|
LANGFLOW_SUPPORTED_TYPES,
|
||||||
|
|
@ -16,7 +17,6 @@ import {
|
||||||
specialCharsRegex,
|
specialCharsRegex,
|
||||||
} from "../constants/constants";
|
} from "../constants/constants";
|
||||||
import { downloadFlowsFromDatabase } from "../controllers/API";
|
import { downloadFlowsFromDatabase } from "../controllers/API";
|
||||||
import getFieldTitle from "../customNodes/utils/get-field-title";
|
|
||||||
import { DESCRIPTIONS } from "../flow_constants";
|
import { DESCRIPTIONS } from "../flow_constants";
|
||||||
import {
|
import {
|
||||||
APIClassType,
|
APIClassType,
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ import {
|
||||||
FolderIcon,
|
FolderIcon,
|
||||||
FolderPlus,
|
FolderPlus,
|
||||||
FolderPlusIcon,
|
FolderPlusIcon,
|
||||||
|
FolderUp,
|
||||||
FormInput,
|
FormInput,
|
||||||
Forward,
|
Forward,
|
||||||
Gift,
|
Gift,
|
||||||
|
|
@ -431,6 +432,7 @@ export const nodeIconsLucide: iconsType = {
|
||||||
ChevronLeft,
|
ChevronLeft,
|
||||||
SlidersHorizontal,
|
SlidersHorizontal,
|
||||||
Palette,
|
Palette,
|
||||||
|
FolderUp,
|
||||||
Blocks,
|
Blocks,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue