Compare commits
181 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80a4a094af | ||
|
|
70e1560cb3 | ||
|
|
725033659a | ||
|
|
059111fb57 | ||
|
|
d4a5eadf13 | ||
|
|
79cf487ac5 | ||
|
|
52ecbab859 | ||
|
|
adfc79bf92 | ||
|
|
2447bab924 | ||
|
|
1057ca78a6 | ||
|
|
7e7f98fd92 | ||
|
|
64552ce2de | ||
|
|
7506256f42 | ||
|
|
db75230521 | ||
|
|
f8955d5607 | ||
|
|
0bad217b93 | ||
|
|
4da400a136 | ||
|
|
24740bd341 | ||
|
|
3b6a15de84 | ||
|
|
ac1f525a6c | ||
|
|
e3999bdb0c | ||
|
|
ad3d5a30ec | ||
|
|
e4b5847725 | ||
|
|
1a91a245a3 | ||
|
|
229f62d071 | ||
|
|
b96fe16770 | ||
|
|
97750cb5e2 | ||
|
|
e1a2bd11a9 | ||
|
|
229b408252 | ||
|
|
ae929438a5 | ||
|
|
5daaf84e05 | ||
|
|
19b09515a1 | ||
|
|
9ce6078c8b | ||
|
|
51f588f4b1 | ||
|
|
5ee6605703 | ||
|
|
7ef97cfd81 | ||
|
|
f4288f0bd4 | ||
|
|
4a701cb993 | ||
|
|
00dfb07b15 | ||
|
|
5fffa8e9db | ||
|
|
54d187a0ad | ||
|
|
192ce468b7 | ||
|
|
75c0cadb50 | ||
|
|
5d578d4b3b | ||
|
|
325a8889ab | ||
|
|
9cdd78e68c | ||
|
|
3a6770a1ae | ||
|
|
8073924056 | ||
|
|
7b53e1c54b | ||
|
|
c4c0516820 | ||
|
|
8d36f8850e | ||
|
|
abe5f43f3d | ||
|
|
c8d8a8d0b5 | ||
|
|
f60e88573a | ||
|
|
4216671ea2 | ||
|
|
ee3ea7a970 | ||
|
|
2b644dbb01 | ||
|
|
63878e7ffd | ||
|
|
007cd6cff1 | ||
|
|
4375215baa | ||
|
|
8cc5e9db13 | ||
|
|
5685f831a7 | ||
|
|
0cb3d12d94 | ||
|
|
0e38c6751b | ||
|
|
70ad1fb3d8 | ||
|
|
44f27d91a0 | ||
|
|
1bb559c285 | ||
|
|
7a005ef126 | ||
|
|
030c2a740f | ||
|
|
5dcde67ae9 | ||
|
|
ee06fa85f1 | ||
|
|
5b9352a946 | ||
|
|
b7927d8d75 | ||
|
|
c144f30606 | ||
|
|
d2dba3a0db | ||
|
|
2c991583ff | ||
|
|
2e14dec12d | ||
|
|
8826f0ff3c | ||
|
|
9129f7fb33 | ||
|
|
c0ed54406f | ||
|
|
18be257e10 | ||
|
|
615d549494 | ||
|
|
0ce39e7f52 | ||
|
|
3c68cbc955 | ||
|
|
300430e2d5 | ||
|
|
166a07732a | ||
|
|
510b517270 | ||
|
|
dea385384a | ||
|
|
7a1c9101b2 | ||
|
|
2be523cf77 | ||
|
|
c01e334487 | ||
|
|
a2418d1373 | ||
|
|
a697248b26 | ||
|
|
6058939c00 | ||
|
|
318de530e3 | ||
|
|
9e04b7796a | ||
|
|
e8099c4db5 | ||
|
|
bf808811cc | ||
|
|
f0293de1b9 | ||
|
|
810dcb90ce | ||
|
|
a2f2b8fabc | ||
|
|
cbc5f47786 | ||
|
|
3e3886ced7 | ||
|
|
9ce39fd2ba | ||
|
|
5b08cdedf0 | ||
|
|
67e4d40c49 | ||
|
|
537a733157 | ||
|
|
5136e7726d | ||
|
|
6e236ba74d | ||
|
|
374b665089 | ||
|
|
ffecc9a0c7 | ||
|
|
0b997418d3 | ||
|
|
eaad8a4cf5 | ||
|
|
396b4595f4 | ||
|
|
0752aae9ef | ||
|
|
ad2221a677 | ||
|
|
1713d693b1 | ||
|
|
f4f056449f | ||
|
|
6a70e3e45b | ||
|
|
a04cdee33f | ||
|
|
157769eeb4 | ||
|
|
667b66b926 | ||
|
|
c0f7b344d9 | ||
|
|
060c59e97d | ||
|
|
b3461b7134 | ||
|
|
001c450abb | ||
|
|
ceaa5763d4 | ||
|
|
b45fd58944 | ||
|
|
b3149def82 | ||
|
|
378d498402 | ||
|
|
98f52b32a3 | ||
|
|
0ab32a6f84 | ||
|
|
71cc22325d | ||
|
|
e1b2991aa6 | ||
|
|
033bcf80d0 | ||
|
|
103118d558 | ||
|
|
f91b5fa004 | ||
|
|
7179bf7b67 | ||
|
|
a3e6239e6e | ||
|
|
1fa12e56c6 | ||
|
|
4ff834de76 | ||
|
|
6db38ad769 | ||
|
|
293b7b09a9 | ||
|
|
d5945f9ee7 | ||
|
|
d1f5a6fc31 | ||
|
|
e7b9f5e4c3 | ||
|
|
7870749077 | ||
|
|
c5352f443a | ||
|
|
fd8b7aa0f2 | ||
|
|
458ea266ec | ||
|
|
9748eaba25 | ||
|
|
887a3740b2 | ||
|
|
2e7cfe9cd7 | ||
|
|
6dbe156a02 | ||
|
|
2a9ef6d48e | ||
|
|
6717ddbd0b | ||
|
|
47c1aab064 | ||
|
|
eda41658b9 | ||
|
|
7f79363944 | ||
|
|
25967f2a09 | ||
|
|
4d3963ad67 | ||
|
|
f78c5257dc | ||
|
|
ccc6234ac8 | ||
|
|
c81b0200eb | ||
|
|
f039d37c8a | ||
|
|
237975bfef | ||
|
|
015bc7c8c3 | ||
|
|
3da2a00ee9 | ||
|
|
16eca5bebf | ||
|
|
f8ac5e0af3 | ||
|
|
eb48a153d9 | ||
|
|
381a2740ee | ||
|
|
8b3b16bce4 | ||
|
|
024674eef3 | ||
|
|
b7d88b4c0f | ||
|
|
719ca63ec1 | ||
|
|
2cfb416fd0 | ||
|
|
50f07f9ef5 | ||
|
|
c517bdd2e1 | ||
|
|
658867cb46 | ||
|
|
8f2ad38503 |
@@ -1,4 +1,5 @@
|
||||
API_KEY=<LLM api key (for example, open ai key)>
|
||||
LLM_NAME=docsgpt
|
||||
VITE_API_STREAMING=true
|
||||
|
||||
#For Azure (you can delete it if you don't use Azure)
|
||||
|
||||
6
.github/holopin.yml
vendored
@@ -1,5 +1,5 @@
|
||||
organization: arc53
|
||||
defaultSticker: cln9dm7qz164460gk5ksrgr034
|
||||
defaultSticker: clqmdf0ed34290glbvqh0kzxd
|
||||
stickers:
|
||||
- id: cln9dm7qz164460gk5ksrgr034
|
||||
alias: hacktober
|
||||
- id: clqmdf0ed34290glbvqh0kzxd
|
||||
alias: festive
|
||||
|
||||
1
.gitignore
vendored
@@ -172,3 +172,4 @@ application/vectors/
|
||||
node_modules/
|
||||
.vscode/settings.json
|
||||
models/
|
||||
model/
|
||||
|
||||
30
README.md
@@ -18,7 +18,7 @@ Say goodbye to time-consuming manual searches, and let <strong><a href="https://
|
||||
<a href="https://github.com/arc53/DocsGPT"></a>
|
||||
<a href="https://github.com/arc53/DocsGPT/blob/main/LICENSE"></a>
|
||||
<a href="https://discord.gg/n5BX8dh8rU"></a>
|
||||
<a href="https://twitter.com/ATushynski"></a>
|
||||
<a href="https://twitter.com/docsgptai"></a>
|
||||
|
||||
|
||||
</div>
|
||||
@@ -40,7 +40,7 @@ You can find our roadmap [here](https://github.com/orgs/arc53/projects/2). Pleas
|
||||
|
||||
| Name | Base Model | Requirements (or similar) |
|
||||
| --------------------------------------------------------------------- | ----------- | ------------------------- |
|
||||
| [Docsgpt-7b-falcon](https://huggingface.co/Arc53/docsgpt-7b-falcon) | Falcon-7b | 1xA10G gpu |
|
||||
| [Docsgpt-7b-mistral](https://huggingface.co/Arc53/docsgpt-7b-mistral) | Mistral-7b | 1xA10G gpu |
|
||||
| [Docsgpt-14b](https://huggingface.co/Arc53/docsgpt-14b) | llama-2-14b | 2xA10 gpu's |
|
||||
| [Docsgpt-40b-falcon](https://huggingface.co/Arc53/docsgpt-40b-falcon) | falcon-40b | 8xA10G gpu's |
|
||||
|
||||
@@ -83,17 +83,18 @@ On Mac OS or Linux, write:
|
||||
|
||||
`./setup.sh`
|
||||
|
||||
It will install all the dependencies and allow you to download the local model or use OpenAI.
|
||||
It will install all the dependencies and allow you to download the local model, use OpenAI or use our LLM API.
|
||||
|
||||
Otherwise, refer to this Guide:
|
||||
|
||||
1. Download and open this repository with `git clone https://github.com/arc53/DocsGPT.git`
|
||||
2. Create a `.env` file in your root directory and set the env variable `API_KEY` with your [OpenAI API key](https://platform.openai.com/account/api-keys) and `VITE_API_STREAMING` to true or false, depending on whether you want streaming answers or not.
|
||||
2. Create a `.env` file in your root directory and set the env variables and `VITE_API_STREAMING` to true or false, depending on whether you want streaming answers or not.
|
||||
It should look like this inside:
|
||||
|
||||
```
|
||||
API_KEY=Yourkey
|
||||
LLM_NAME=[docsgpt or openai or others]
|
||||
VITE_API_STREAMING=true
|
||||
API_KEY=[if LLM_NAME is openai]
|
||||
```
|
||||
|
||||
See optional environment variables in the [/.env-template](https://github.com/arc53/DocsGPT/blob/main/.env-template) and [/application/.env_sample](https://github.com/arc53/DocsGPT/blob/main/application/.env_sample) files.
|
||||
@@ -122,8 +123,8 @@ docker compose -f docker-compose-dev.yaml up -d
|
||||
> [!Note]
|
||||
> Make sure you have Python 3.10 or 3.11 installed.
|
||||
|
||||
1. Export required environment variables or prepare a `.env` file in the `/application` folder:
|
||||
- Copy [.env_sample](https://github.com/arc53/DocsGPT/blob/main/application/.env_sample) and create `.env` with your OpenAI API token for the `API_KEY` and `EMBEDDINGS_KEY` fields.
|
||||
1. Export required environment variables or prepare a `.env` file in the project folder:
|
||||
- Copy [.env_sample](https://github.com/arc53/DocsGPT/blob/main/application/.env_sample) and create `.env`.
|
||||
|
||||
(check out [`application/core/settings.py`](application/core/settings.py) if you want to see more config options.)
|
||||
|
||||
@@ -144,14 +145,23 @@ python -m venv venv
|
||||
venv/Scripts/activate
|
||||
```
|
||||
|
||||
3. Change to the `application/` subdir by the command `cd application/` and install dependencies for the backend:
|
||||
3. Download embedding model and save it in the `model/` folder:
|
||||
You can use the script below, or download it manually from [here](https://d3dg1063dc54p9.cloudfront.net/models/embeddings/mpnet-base-v2.zip), unzip it and save it in the `model/` folder.
|
||||
|
||||
```commandline
|
||||
wget https://d3dg1063dc54p9.cloudfront.net/models/embeddings/mpnet-base-v2.zip
|
||||
unzip mpnet-base-v2.zip -d model
|
||||
rm mpnet-base-v2.zip
|
||||
```
|
||||
|
||||
4. Install dependencies for the backend:
|
||||
|
||||
```commandline
|
||||
pip install -r application/requirements.txt
|
||||
```
|
||||
|
||||
4. Run the app using `flask --app application/app.py run --host=0.0.0.0 --port=7091`.
|
||||
5. Start worker with `celery -A application.app.celery worker -l INFO`.
|
||||
5. Run the app using `flask --app application/app.py run --host=0.0.0.0 --port=7091`.
|
||||
6. Start worker with `celery -A application.app.celery worker -l INFO`.
|
||||
|
||||
### Start Frontend
|
||||
|
||||
|
||||
@@ -1,19 +1,27 @@
|
||||
FROM python:3.10-slim-bullseye as builder
|
||||
FROM python:3.11-slim-bullseye as builder
|
||||
|
||||
# Tiktoken requires Rust toolchain, so build it in a separate stage
|
||||
RUN apt-get update && apt-get install -y gcc curl
|
||||
RUN apt-get install -y wget unzip
|
||||
RUN wget https://d3dg1063dc54p9.cloudfront.net/models/embeddings/mpnet-base-v2.zip
|
||||
RUN unzip mpnet-base-v2.zip -d model
|
||||
RUN rm mpnet-base-v2.zip
|
||||
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y && apt-get install --reinstall libc6-dev -y
|
||||
ENV PATH="/root/.cargo/bin:${PATH}"
|
||||
RUN pip install --upgrade pip && pip install tiktoken==0.3.3
|
||||
RUN pip install --upgrade pip && pip install tiktoken==0.5.2
|
||||
COPY requirements.txt .
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
FROM python:3.10-slim-bullseye
|
||||
|
||||
|
||||
FROM python:3.11-slim-bullseye
|
||||
|
||||
# Copy pre-built packages and binaries from builder stage
|
||||
COPY --from=builder /usr/local/ /usr/local/
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=builder /model /app/model
|
||||
|
||||
COPY . /app/application
|
||||
ENV FLASK_APP=app.py
|
||||
ENV FLASK_DEBUG=true
|
||||
|
||||
@@ -117,12 +117,9 @@ def complete_stream(question, docsearch, chat_history, api_key, prompt_id, conve
|
||||
source_log_docs = []
|
||||
for doc in docs:
|
||||
if doc.metadata:
|
||||
data = json.dumps({"type": "source", "doc": doc.page_content, "metadata": doc.metadata})
|
||||
source_log_docs.append({"title": doc.metadata['title'].split('/')[-1], "text": doc.page_content})
|
||||
else:
|
||||
data = json.dumps({"type": "source", "doc": doc.page_content})
|
||||
source_log_docs.append({"title": doc.page_content, "text": doc.page_content})
|
||||
yield f"data:{data}\n\n"
|
||||
|
||||
if len(chat_history) > 1:
|
||||
tokens_current_history = 0
|
||||
@@ -343,3 +340,35 @@ def api_answer():
|
||||
traceback.print_exc()
|
||||
print(str(e))
|
||||
return bad_request(500, str(e))
|
||||
|
||||
|
||||
@answer.route("/api/search", methods=["POST"])
|
||||
def api_search():
|
||||
data = request.get_json()
|
||||
# get parameter from url question
|
||||
question = data["question"]
|
||||
|
||||
if not embeddings_key_set:
|
||||
if "embeddings_key" in data:
|
||||
embeddings_key = data["embeddings_key"]
|
||||
else:
|
||||
embeddings_key = settings.EMBEDDINGS_KEY
|
||||
else:
|
||||
embeddings_key = settings.EMBEDDINGS_KEY
|
||||
if "active_docs" in data:
|
||||
vectorstore = get_vectorstore({"active_docs": data["active_docs"]})
|
||||
else:
|
||||
vectorstore = ""
|
||||
docsearch = VectorCreator.create_vectorstore(settings.VECTOR_STORE, vectorstore, embeddings_key)
|
||||
|
||||
docs = docsearch.search(question, k=2)
|
||||
|
||||
source_log_docs = []
|
||||
for doc in docs:
|
||||
if doc.metadata:
|
||||
source_log_docs.append({"title": doc.metadata['title'].split('/')[-1], "text": doc.page_content})
|
||||
else:
|
||||
source_log_docs.append({"title": doc.page_content, "text": doc.page_content})
|
||||
#yield f"data:{data}\n\n"
|
||||
return source_log_docs
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ from pymongo import MongoClient
|
||||
from bson.objectid import ObjectId
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
from application.api.user.tasks import ingest
|
||||
from application.api.user.tasks import ingest, ingest_remote
|
||||
|
||||
from application.core.settings import settings
|
||||
from application.vectorstore.vector_creator import VectorCreator
|
||||
@@ -36,7 +36,7 @@ def delete_conversation():
|
||||
@user.route("/api/get_conversations", methods=["get"])
|
||||
def get_conversations():
|
||||
# provides a list of conversations
|
||||
conversations = conversations_collection.find().sort("date", -1)
|
||||
conversations = conversations_collection.find().sort("date", -1).limit(30)
|
||||
list_conversations = []
|
||||
for conversation in conversations:
|
||||
list_conversations.append({"id": str(conversation["_id"]), "name": conversation["name"]})
|
||||
@@ -157,6 +157,32 @@ def upload_file():
|
||||
return {"status": "ok", "task_id": task_id}
|
||||
else:
|
||||
return {"status": "error"}
|
||||
|
||||
@user.route("/api/remote", methods=["POST"])
|
||||
def upload_remote():
|
||||
"""Upload a remote source to get vectorized and indexed."""
|
||||
if "user" not in request.form:
|
||||
return {"status": "no user"}
|
||||
user = secure_filename(request.form["user"])
|
||||
if "source" not in request.form:
|
||||
return {"status": "no source"}
|
||||
source = secure_filename(request.form["source"])
|
||||
if "name" not in request.form:
|
||||
return {"status": "no name"}
|
||||
job_name = secure_filename(request.form["name"])
|
||||
# check if the post request has the file part
|
||||
if "data" not in request.form:
|
||||
print("No data")
|
||||
return {"status": "no data"}
|
||||
source_data = request.form["data"]
|
||||
|
||||
if source_data:
|
||||
task = ingest_remote.delay(source_data=source_data, job_name=job_name, user=user, loader=source)
|
||||
# task id
|
||||
task_id = task.id
|
||||
return {"status": "ok", "task_id": task_id}
|
||||
else:
|
||||
return {"status": "error"}
|
||||
|
||||
@user.route("/api/task_status", methods=["GET"])
|
||||
def task_status():
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
from application.worker import ingest_worker
|
||||
from application.worker import ingest_worker, remote_worker
|
||||
from application.celery import celery
|
||||
|
||||
@celery.task(bind=True)
|
||||
def ingest(self, directory, formats, name_job, filename, user):
|
||||
resp = ingest_worker(self, directory, formats, name_job, filename, user)
|
||||
return resp
|
||||
|
||||
@celery.task(bind=True)
|
||||
def ingest_remote(self, source_data, job_name, user, loader):
|
||||
resp = remote_worker(self, source_data, job_name, user, loader)
|
||||
return resp
|
||||
|
||||
@@ -1,42 +1,62 @@
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
import os
|
||||
|
||||
from pydantic import BaseSettings
|
||||
from pydantic_settings import BaseSettings
|
||||
|
||||
current_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
LLM_NAME: str = "openai"
|
||||
EMBEDDINGS_NAME: str = "openai_text-embedding-ada-002"
|
||||
LLM_NAME: str = "docsgpt"
|
||||
EMBEDDINGS_NAME: str = "huggingface_sentence-transformers/all-mpnet-base-v2"
|
||||
CELERY_BROKER_URL: str = "redis://localhost:6379/0"
|
||||
CELERY_RESULT_BACKEND: str = "redis://localhost:6379/1"
|
||||
MONGO_URI: str = "mongodb://localhost:27017/docsgpt"
|
||||
MODEL_PATH: str = os.path.join(current_dir, "models/docsgpt-7b-f16.gguf")
|
||||
TOKENS_MAX_HISTORY: int = 150
|
||||
UPLOAD_FOLDER: str = "inputs"
|
||||
VECTOR_STORE: str = "faiss" # "faiss" or "elasticsearch"
|
||||
VECTOR_STORE: str = "faiss" # "faiss" or "elasticsearch" or "qdrant"
|
||||
|
||||
API_URL: str = "http://localhost:7091" # backend url for celery worker
|
||||
|
||||
API_KEY: str = None # LLM api key
|
||||
EMBEDDINGS_KEY: str = None # api key for embeddings (if using openai, just copy API_KEY)
|
||||
OPENAI_API_BASE: str = None # azure openai api base url
|
||||
OPENAI_API_VERSION: str = None # azure openai api version
|
||||
AZURE_DEPLOYMENT_NAME: str = None # azure deployment name for answering
|
||||
AZURE_EMBEDDINGS_DEPLOYMENT_NAME: str = None # azure deployment name for embeddings
|
||||
API_KEY: Optional[str] = None # LLM api key
|
||||
EMBEDDINGS_KEY: Optional[str] = None # api key for embeddings (if using openai, just copy API_KEY)
|
||||
OPENAI_API_BASE: Optional[str] = None # azure openai api base url
|
||||
OPENAI_API_VERSION: Optional[str] = None # azure openai api version
|
||||
AZURE_DEPLOYMENT_NAME: Optional[str] = None # azure deployment name for answering
|
||||
AZURE_EMBEDDINGS_DEPLOYMENT_NAME: Optional[str] = None # azure deployment name for embeddings
|
||||
|
||||
# elasticsearch
|
||||
ELASTIC_CLOUD_ID: str = None # cloud id for elasticsearch
|
||||
ELASTIC_USERNAME: str = None # username for elasticsearch
|
||||
ELASTIC_PASSWORD: str = None # password for elasticsearch
|
||||
ELASTIC_URL: str = None # url for elasticsearch
|
||||
ELASTIC_INDEX: str = "docsgpt" # index name for elasticsearch
|
||||
ELASTIC_CLOUD_ID: Optional[str] = None # cloud id for elasticsearch
|
||||
ELASTIC_USERNAME: Optional[str] = None # username for elasticsearch
|
||||
ELASTIC_PASSWORD: Optional[str] = None # password for elasticsearch
|
||||
ELASTIC_URL: Optional[str] = None # url for elasticsearch
|
||||
ELASTIC_INDEX: Optional[str] = "docsgpt" # index name for elasticsearch
|
||||
|
||||
# SageMaker config
|
||||
SAGEMAKER_ENDPOINT: str = None # SageMaker endpoint name
|
||||
SAGEMAKER_REGION: str = None # SageMaker region name
|
||||
SAGEMAKER_ACCESS_KEY: str = None # SageMaker access key
|
||||
SAGEMAKER_SECRET_KEY: str = None # SageMaker secret key
|
||||
SAGEMAKER_ENDPOINT: Optional[str] = None # SageMaker endpoint name
|
||||
SAGEMAKER_REGION: Optional[str] = None # SageMaker region name
|
||||
SAGEMAKER_ACCESS_KEY: Optional[str] = None # SageMaker access key
|
||||
SAGEMAKER_SECRET_KEY: Optional[str] = None # SageMaker secret key
|
||||
|
||||
# prem ai project id
|
||||
PREMAI_PROJECT_ID: Optional[str] = None
|
||||
|
||||
# Qdrant vectorstore config
|
||||
QDRANT_COLLECTION_NAME: Optional[str] = "docsgpt"
|
||||
QDRANT_LOCATION: Optional[str] = None
|
||||
QDRANT_URL: Optional[str] = None
|
||||
QDRANT_PORT: Optional[int] = 6333
|
||||
QDRANT_GRPC_PORT: int = 6334
|
||||
QDRANT_PREFER_GRPC: bool = False
|
||||
QDRANT_HTTPS: Optional[bool] = None
|
||||
QDRANT_API_KEY: Optional[str] = None
|
||||
QDRANT_PREFIX: Optional[str] = None
|
||||
QDRANT_TIMEOUT: Optional[float] = None
|
||||
QDRANT_HOST: Optional[str] = None
|
||||
QDRANT_PATH: Optional[str] = None
|
||||
QDRANT_DISTANCE_FUNC: str = "Cosine"
|
||||
|
||||
|
||||
path = Path(__file__).parent.parent.absolute()
|
||||
|
||||
49
application/llm/docsgpt_provider.py
Normal file
@@ -0,0 +1,49 @@
|
||||
from application.llm.base import BaseLLM
|
||||
import json
|
||||
import requests
|
||||
|
||||
class DocsGPTAPILLM(BaseLLM):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.endpoint = "https://llm.docsgpt.co.uk"
|
||||
|
||||
|
||||
def gen(self, model, engine, messages, stream=False, **kwargs):
|
||||
context = messages[0]['content']
|
||||
user_question = messages[-1]['content']
|
||||
prompt = f"### Instruction \n {user_question} \n ### Context \n {context} \n ### Answer \n"
|
||||
|
||||
response = requests.post(
|
||||
f"{self.endpoint}/answer",
|
||||
json={
|
||||
"prompt": prompt,
|
||||
"max_new_tokens": 30
|
||||
}
|
||||
)
|
||||
response_clean = response.json()['a'].replace("###", "")
|
||||
|
||||
return response_clean
|
||||
|
||||
def gen_stream(self, model, engine, messages, stream=True, **kwargs):
|
||||
context = messages[0]['content']
|
||||
user_question = messages[-1]['content']
|
||||
prompt = f"### Instruction \n {user_question} \n ### Context \n {context} \n ### Answer \n"
|
||||
|
||||
# send prompt to endpoint /stream
|
||||
response = requests.post(
|
||||
f"{self.endpoint}/stream",
|
||||
json={
|
||||
"prompt": prompt,
|
||||
"max_new_tokens": 256
|
||||
},
|
||||
stream=True
|
||||
)
|
||||
|
||||
for line in response.iter_lines():
|
||||
if line:
|
||||
#data = json.loads(line)
|
||||
data_str = line.decode('utf-8')
|
||||
if data_str.startswith("data: "):
|
||||
data = json.loads(data_str[6:])
|
||||
yield data['a']
|
||||
|
||||
@@ -3,6 +3,8 @@ from application.llm.sagemaker import SagemakerAPILLM
|
||||
from application.llm.huggingface import HuggingFaceLLM
|
||||
from application.llm.llama_cpp import LlamaCpp
|
||||
from application.llm.anthropic import AnthropicLLM
|
||||
from application.llm.docsgpt_provider import DocsGPTAPILLM
|
||||
from application.llm.premai import PremAILLM
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +15,9 @@ class LLMCreator:
|
||||
'sagemaker': SagemakerAPILLM,
|
||||
'huggingface': HuggingFaceLLM,
|
||||
'llama.cpp': LlamaCpp,
|
||||
'anthropic': AnthropicLLM
|
||||
'anthropic': AnthropicLLM,
|
||||
'docsgpt': DocsGPTAPILLM,
|
||||
'premai': PremAILLM,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -5,40 +5,38 @@ class OpenAILLM(BaseLLM):
|
||||
|
||||
def __init__(self, api_key):
|
||||
global openai
|
||||
import openai
|
||||
openai.api_key = api_key
|
||||
self.api_key = api_key # Save the API key to be used later
|
||||
from openai import OpenAI
|
||||
|
||||
self.client = OpenAI(
|
||||
api_key=api_key,
|
||||
)
|
||||
self.api_key = api_key
|
||||
|
||||
def _get_openai(self):
|
||||
# Import openai when needed
|
||||
import openai
|
||||
# Set the API key every time you import openai
|
||||
openai.api_key = self.api_key
|
||||
|
||||
return openai
|
||||
|
||||
def gen(self, model, engine, messages, stream=False, **kwargs):
|
||||
response = openai.ChatCompletion.create(
|
||||
model=model,
|
||||
engine=engine,
|
||||
response = self.client.chat.completions.create(model=model,
|
||||
messages=messages,
|
||||
stream=stream,
|
||||
**kwargs
|
||||
)
|
||||
**kwargs)
|
||||
|
||||
return response["choices"][0]["message"]["content"]
|
||||
return response.choices[0].message.content
|
||||
|
||||
def gen_stream(self, model, engine, messages, stream=True, **kwargs):
|
||||
response = openai.ChatCompletion.create(
|
||||
model=model,
|
||||
engine=engine,
|
||||
response = self.client.chat.completions.create(model=model,
|
||||
messages=messages,
|
||||
stream=stream,
|
||||
**kwargs
|
||||
)
|
||||
**kwargs)
|
||||
|
||||
for line in response:
|
||||
if "content" in line["choices"][0]["delta"]:
|
||||
yield line["choices"][0]["delta"]["content"]
|
||||
# import sys
|
||||
# print(line.choices[0].delta.content, file=sys.stderr)
|
||||
if line.choices[0].delta.content is not None:
|
||||
yield line.choices[0].delta.content
|
||||
|
||||
|
||||
class AzureOpenAILLM(OpenAILLM):
|
||||
@@ -48,10 +46,15 @@ class AzureOpenAILLM(OpenAILLM):
|
||||
self.api_base = settings.OPENAI_API_BASE,
|
||||
self.api_version = settings.OPENAI_API_VERSION,
|
||||
self.deployment_name = settings.AZURE_DEPLOYMENT_NAME,
|
||||
from openai import AzureOpenAI
|
||||
self.client = AzureOpenAI(
|
||||
api_key=openai_api_key,
|
||||
api_version=settings.OPENAI_API_VERSION,
|
||||
api_base=settings.OPENAI_API_BASE,
|
||||
deployment_name=settings.AZURE_DEPLOYMENT_NAME,
|
||||
)
|
||||
|
||||
def _get_openai(self):
|
||||
openai = super()._get_openai()
|
||||
openai.api_base = self.api_base
|
||||
openai.api_version = self.api_version
|
||||
openai.api_type = "azure"
|
||||
|
||||
return openai
|
||||
|
||||
33
application/llm/premai.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from application.llm.base import BaseLLM
|
||||
from application.core.settings import settings
|
||||
|
||||
class PremAILLM(BaseLLM):
|
||||
|
||||
def __init__(self, api_key):
|
||||
from premai import Prem
|
||||
|
||||
self.client = Prem(
|
||||
api_key=api_key
|
||||
)
|
||||
self.api_key = api_key
|
||||
self.project_id = settings.PREMAI_PROJECT_ID
|
||||
|
||||
def gen(self, model, engine, messages, stream=False, **kwargs):
|
||||
response = self.client.chat.completions.create(model=model,
|
||||
project_id=self.project_id,
|
||||
messages=messages,
|
||||
stream=stream,
|
||||
**kwargs)
|
||||
|
||||
return response.choices[0].message["content"]
|
||||
|
||||
def gen_stream(self, model, engine, messages, stream=True, **kwargs):
|
||||
response = self.client.chat.completions.create(model=model,
|
||||
project_id=self.project_id,
|
||||
messages=messages,
|
||||
stream=stream,
|
||||
**kwargs)
|
||||
|
||||
for line in response:
|
||||
if line.choices[0].delta["content"] is not None:
|
||||
yield line.choices[0].delta["content"]
|
||||
@@ -62,7 +62,6 @@ class SimpleDirectoryReader(BaseReader):
|
||||
file_extractor: Optional[Dict[str, BaseParser]] = None,
|
||||
num_files_limit: Optional[int] = None,
|
||||
file_metadata: Optional[Callable[[str], Dict]] = None,
|
||||
chunk_size_max: int = 2048,
|
||||
) -> None:
|
||||
"""Initialize with parameters."""
|
||||
super().__init__()
|
||||
@@ -148,12 +147,24 @@ class SimpleDirectoryReader(BaseReader):
|
||||
# do standard read
|
||||
with open(input_file, "r", errors=self.errors) as f:
|
||||
data = f.read()
|
||||
if isinstance(data, List):
|
||||
data_list.extend(data)
|
||||
else:
|
||||
data_list.append(str(data))
|
||||
# Prepare metadata for this file
|
||||
if self.file_metadata is not None:
|
||||
metadata_list.append(self.file_metadata(str(input_file)))
|
||||
file_metadata = self.file_metadata(str(input_file))
|
||||
else:
|
||||
# Provide a default empty metadata
|
||||
file_metadata = {'title': '', 'store': ''}
|
||||
# TODO: Find a case with no metadata and check if breaks anything
|
||||
|
||||
if isinstance(data, List):
|
||||
# Extend data_list with each item in the data list
|
||||
data_list.extend([str(d) for d in data])
|
||||
# For each item in the data list, add the file's metadata to metadata_list
|
||||
metadata_list.extend([file_metadata for _ in data])
|
||||
else:
|
||||
# Add the single piece of data to data_list
|
||||
data_list.append(str(data))
|
||||
# Add the file's metadata to metadata_list
|
||||
metadata_list.append(file_metadata)
|
||||
|
||||
if concatenate:
|
||||
return [Document("\n".join(data_list))]
|
||||
|
||||
@@ -6,9 +6,9 @@ from application.core.settings import settings
|
||||
from retry import retry
|
||||
|
||||
|
||||
# from langchain.embeddings import HuggingFaceEmbeddings
|
||||
# from langchain.embeddings import HuggingFaceInstructEmbeddings
|
||||
# from langchain.embeddings import CohereEmbeddings
|
||||
# from langchain_community.embeddings import HuggingFaceEmbeddings
|
||||
# from langchain_community.embeddings import HuggingFaceInstructEmbeddings
|
||||
# from langchain_community.embeddings import CohereEmbeddings
|
||||
|
||||
|
||||
def num_tokens_from_string(string: str, encoding_name: str) -> int:
|
||||
|
||||
19
application/parser/remote/base.py
Normal file
@@ -0,0 +1,19 @@
|
||||
"""Base reader class."""
|
||||
from abc import abstractmethod
|
||||
from typing import Any, List
|
||||
|
||||
from langchain.docstore.document import Document as LCDocument
|
||||
from application.parser.schema.base import Document
|
||||
|
||||
|
||||
class BaseRemote:
|
||||
"""Utilities for loading data from a directory."""
|
||||
|
||||
@abstractmethod
|
||||
def load_data(self, *args: Any, **load_kwargs: Any) -> List[Document]:
|
||||
"""Load data from the input directory."""
|
||||
|
||||
def load_langchain_documents(self, **load_kwargs: Any) -> List[LCDocument]:
|
||||
"""Load data in LangChain document format."""
|
||||
docs = self.load_data(**load_kwargs)
|
||||
return [d.to_langchain_format() for d in docs]
|
||||
59
application/parser/remote/crawler_loader.py
Normal file
@@ -0,0 +1,59 @@
|
||||
import requests
|
||||
from urllib.parse import urlparse, urljoin
|
||||
from bs4 import BeautifulSoup
|
||||
from application.parser.remote.base import BaseRemote
|
||||
|
||||
class CrawlerLoader(BaseRemote):
|
||||
def __init__(self, limit=10):
|
||||
from langchain.document_loaders import WebBaseLoader
|
||||
self.loader = WebBaseLoader # Initialize the document loader
|
||||
self.limit = limit # Set the limit for the number of pages to scrape
|
||||
|
||||
def load_data(self, inputs):
|
||||
url = inputs
|
||||
# Check if the input is a list and if it is, use the first element
|
||||
if isinstance(url, list) and url:
|
||||
url = url[0]
|
||||
|
||||
# Check if the URL scheme is provided, if not, assume http
|
||||
if not urlparse(url).scheme:
|
||||
url = "http://" + url
|
||||
|
||||
visited_urls = set() # Keep track of URLs that have been visited
|
||||
base_url = urlparse(url).scheme + "://" + urlparse(url).hostname # Extract the base URL
|
||||
urls_to_visit = [url] # List of URLs to be visited, starting with the initial URL
|
||||
loaded_content = [] # Store the loaded content from each URL
|
||||
|
||||
# Continue crawling until there are no more URLs to visit
|
||||
while urls_to_visit:
|
||||
current_url = urls_to_visit.pop(0) # Get the next URL to visit
|
||||
visited_urls.add(current_url) # Mark the URL as visited
|
||||
|
||||
# Try to load and process the content from the current URL
|
||||
try:
|
||||
response = requests.get(current_url) # Fetch the content of the current URL
|
||||
response.raise_for_status() # Raise an exception for HTTP errors
|
||||
loader = self.loader([current_url]) # Initialize the document loader for the current URL
|
||||
loaded_content.extend(loader.load()) # Load the content and add it to the loaded_content list
|
||||
except Exception as e:
|
||||
# Print an error message if loading or processing fails and continue with the next URL
|
||||
print(f"Error processing URL {current_url}: {e}")
|
||||
continue
|
||||
|
||||
# Parse the HTML content to extract all links
|
||||
soup = BeautifulSoup(response.text, 'html.parser')
|
||||
all_links = [
|
||||
urljoin(current_url, a['href'])
|
||||
for a in soup.find_all('a', href=True)
|
||||
if base_url in urljoin(current_url, a['href']) # Ensure links are from the same domain
|
||||
]
|
||||
|
||||
# Add new links to the list of URLs to visit if they haven't been visited yet
|
||||
urls_to_visit.extend([link for link in all_links if link not in visited_urls])
|
||||
urls_to_visit = list(set(urls_to_visit)) # Remove duplicate URLs
|
||||
|
||||
# Stop crawling if the limit of pages to scrape is reached
|
||||
if self.limit is not None and len(visited_urls) >= self.limit:
|
||||
break
|
||||
|
||||
return loaded_content # Return the loaded content from all visited URLs
|
||||
0
application/parser/remote/github_loader.py
Normal file
18
application/parser/remote/remote_creator.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from application.parser.remote.sitemap_loader import SitemapLoader
|
||||
from application.parser.remote.crawler_loader import CrawlerLoader
|
||||
from application.parser.remote.web_loader import WebLoader
|
||||
|
||||
|
||||
class RemoteCreator:
|
||||
loaders = {
|
||||
'url': WebLoader,
|
||||
'sitemap': SitemapLoader,
|
||||
'crawler': CrawlerLoader
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def create_loader(cls, type, *args, **kwargs):
|
||||
loader_class = cls.loaders.get(type.lower())
|
||||
if not loader_class:
|
||||
raise ValueError(f"No LLM class found for type {type}")
|
||||
return loader_class(*args, **kwargs)
|
||||
81
application/parser/remote/sitemap_loader.py
Normal file
@@ -0,0 +1,81 @@
|
||||
import requests
|
||||
import re # Import regular expression library
|
||||
import xml.etree.ElementTree as ET
|
||||
from application.parser.remote.base import BaseRemote
|
||||
|
||||
class SitemapLoader(BaseRemote):
|
||||
def __init__(self, limit=20):
|
||||
from langchain.document_loaders import WebBaseLoader
|
||||
self.loader = WebBaseLoader
|
||||
self.limit = limit # Adding limit to control the number of URLs to process
|
||||
|
||||
def load_data(self, inputs):
|
||||
sitemap_url= inputs
|
||||
# Check if the input is a list and if it is, use the first element
|
||||
if isinstance(sitemap_url, list) and sitemap_url:
|
||||
url = sitemap_url[0]
|
||||
|
||||
urls = self._extract_urls(sitemap_url)
|
||||
if not urls:
|
||||
print(f"No URLs found in the sitemap: {sitemap_url}")
|
||||
return []
|
||||
|
||||
# Load content of extracted URLs
|
||||
documents = []
|
||||
processed_urls = 0 # Counter for processed URLs
|
||||
for url in urls:
|
||||
if self.limit is not None and processed_urls >= self.limit:
|
||||
break # Stop processing if the limit is reached
|
||||
|
||||
try:
|
||||
loader = self.loader([url])
|
||||
documents.extend(loader.load())
|
||||
processed_urls += 1 # Increment the counter after processing each URL
|
||||
except Exception as e:
|
||||
print(f"Error processing URL {url}: {e}")
|
||||
continue
|
||||
|
||||
return documents
|
||||
|
||||
def _extract_urls(self, sitemap_url):
|
||||
try:
|
||||
response = requests.get(sitemap_url)
|
||||
response.raise_for_status() # Raise an exception for HTTP errors
|
||||
except (requests.exceptions.HTTPError, requests.exceptions.ConnectionError) as e:
|
||||
print(f"Failed to fetch sitemap: {sitemap_url}. Error: {e}")
|
||||
return []
|
||||
|
||||
# Determine if this is a sitemap or a URL
|
||||
if self._is_sitemap(response):
|
||||
# It's a sitemap, so parse it and extract URLs
|
||||
return self._parse_sitemap(response.content)
|
||||
else:
|
||||
# It's not a sitemap, return the URL itself
|
||||
return [sitemap_url]
|
||||
|
||||
def _is_sitemap(self, response):
|
||||
content_type = response.headers.get('Content-Type', '')
|
||||
if 'xml' in content_type or response.url.endswith('.xml'):
|
||||
return True
|
||||
|
||||
if '<sitemapindex' in response.text or '<urlset' in response.text:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _parse_sitemap(self, sitemap_content):
|
||||
# Remove namespaces
|
||||
sitemap_content = re.sub(' xmlns="[^"]+"', '', sitemap_content.decode('utf-8'), count=1)
|
||||
|
||||
root = ET.fromstring(sitemap_content)
|
||||
|
||||
urls = []
|
||||
for loc in root.findall('.//url/loc'):
|
||||
urls.append(loc.text)
|
||||
|
||||
# Check for nested sitemaps
|
||||
for sitemap in root.findall('.//sitemap/loc'):
|
||||
nested_sitemap_url = sitemap.text
|
||||
urls.extend(self._extract_urls(nested_sitemap_url))
|
||||
|
||||
return urls
|
||||
11
application/parser/remote/telegram.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from langchain.document_loader import TelegramChatApiLoader
|
||||
from application.parser.remote.base import BaseRemote
|
||||
|
||||
class TelegramChatApiRemote(BaseRemote):
|
||||
def _init_parser(self, *args, **load_kwargs):
|
||||
self.loader = TelegramChatApiLoader(**load_kwargs)
|
||||
return {}
|
||||
|
||||
def parse_file(self, *args, **load_kwargs):
|
||||
|
||||
return
|
||||
22
application/parser/remote/web_loader.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from application.parser.remote.base import BaseRemote
|
||||
|
||||
class WebLoader(BaseRemote):
|
||||
def __init__(self):
|
||||
from langchain.document_loaders import WebBaseLoader
|
||||
self.loader = WebBaseLoader
|
||||
|
||||
def load_data(self, inputs):
|
||||
urls = inputs
|
||||
|
||||
if isinstance(urls, str):
|
||||
urls = [urls] # Convert string to list if a single URL is passed
|
||||
|
||||
documents = []
|
||||
for url in urls:
|
||||
try:
|
||||
loader = self.loader([url]) # Process URLs one by one
|
||||
documents.extend(loader.load())
|
||||
except Exception as e:
|
||||
print(f"Error processing URL {url}: {e}")
|
||||
continue # Continue with the next URL if an error occurs
|
||||
return documents
|
||||
@@ -21,16 +21,15 @@ def group_documents(documents: List[Document], min_tokens: int, max_tokens: int)
|
||||
for doc in documents:
|
||||
doc_len = len(tiktoken.get_encoding("cl100k_base").encode(doc.text))
|
||||
|
||||
if current_group is None:
|
||||
current_group = Document(text=doc.text, doc_id=doc.doc_id, embedding=doc.embedding,
|
||||
extra_info=doc.extra_info)
|
||||
elif len(tiktoken.get_encoding("cl100k_base").encode(
|
||||
current_group.text)) + doc_len < max_tokens and doc_len < min_tokens:
|
||||
current_group.text += " " + doc.text
|
||||
# Check if current group is empty or if the document can be added based on token count and matching metadata
|
||||
if current_group is None or (len(tiktoken.get_encoding("cl100k_base").encode(current_group.text)) + doc_len < max_tokens and doc_len < min_tokens and current_group.extra_info == doc.extra_info):
|
||||
if current_group is None:
|
||||
current_group = doc # Use the document directly to retain its metadata
|
||||
else:
|
||||
current_group.text += " " + doc.text # Append text to the current group
|
||||
else:
|
||||
docs.append(current_group)
|
||||
current_group = Document(text=doc.text, doc_id=doc.doc_id, embedding=doc.embedding,
|
||||
extra_info=doc.extra_info)
|
||||
current_group = doc # Start a new group with the current document
|
||||
|
||||
if current_group is not None:
|
||||
docs.append(current_group)
|
||||
|
||||
@@ -1,110 +1,34 @@
|
||||
aiodns==3.0.0
|
||||
aiohttp==3.8.6
|
||||
aiohttp-retry==2.8.3
|
||||
aiosignal==1.3.1
|
||||
aleph-alpha-client==2.16.1
|
||||
amqp==5.1.1
|
||||
anthropic==0.5.0
|
||||
async-timeout==4.0.2
|
||||
attrs==22.2.0
|
||||
billiard==3.6.4.0
|
||||
blobfile==2.0.1
|
||||
boto3==1.28.20
|
||||
celery==5.2.7
|
||||
cffi==1.15.1
|
||||
charset-normalizer==3.1.0
|
||||
click==8.1.3
|
||||
click-didyoumean==0.3.0
|
||||
click-plugins==1.1.1
|
||||
click-repl==0.2.0
|
||||
cryptography==41.0.4
|
||||
dataclasses-json==0.5.7
|
||||
decorator==5.1.1
|
||||
anthropic==0.12.0
|
||||
boto3==1.34.6
|
||||
celery==5.3.6
|
||||
dataclasses_json==0.6.3
|
||||
docx2txt==0.8
|
||||
dill==0.3.6
|
||||
dnspython==2.3.0
|
||||
ecdsa==0.18.0
|
||||
elasticsearch==8.9.0
|
||||
entrypoints==0.4
|
||||
faiss-cpu==1.7.3
|
||||
filelock==3.9.0
|
||||
Flask==2.2.5
|
||||
Flask-Cors==3.0.10
|
||||
frozenlist==1.3.3
|
||||
geojson==2.5.0
|
||||
gunicorn==20.1.0
|
||||
greenlet==2.0.2
|
||||
gpt4all==0.1.7
|
||||
huggingface-hub==0.15.1
|
||||
humbug==0.3.2
|
||||
idna==3.4
|
||||
itsdangerous==2.1.2
|
||||
Jinja2==3.1.2
|
||||
jmespath==1.0.1
|
||||
joblib==1.2.0
|
||||
kombu==5.2.4
|
||||
langchain==0.0.312
|
||||
loguru==0.6.0
|
||||
lxml==4.9.2
|
||||
MarkupSafe==2.1.2
|
||||
marshmallow==3.19.0
|
||||
marshmallow-enum==1.5.1
|
||||
mpmath==1.3.0
|
||||
multidict==6.0.4
|
||||
multiprocess==0.70.14
|
||||
mypy-extensions==1.0.0
|
||||
networkx==3.0
|
||||
npx
|
||||
EbookLib==0.18
|
||||
elasticsearch==8.12.0
|
||||
escodegen==1.0.11
|
||||
esprima==4.0.1
|
||||
faiss-cpu==1.7.4
|
||||
Flask==3.0.1
|
||||
gunicorn==21.2.0
|
||||
html2text==2020.1.16
|
||||
javalang==0.13.0
|
||||
langchain==0.1.4
|
||||
langchain-openai==0.0.5
|
||||
nltk==3.8.1
|
||||
numcodecs==0.11.0
|
||||
numpy==1.24.2
|
||||
openai==0.27.8
|
||||
openapi3-parser==1.1.14
|
||||
packaging==23.0
|
||||
pathos==0.3.0
|
||||
Pillow==10.0.1
|
||||
pox==0.3.2
|
||||
ppft==1.7.6.6
|
||||
prompt-toolkit==3.0.38
|
||||
py==1.11.0
|
||||
pyasn1==0.4.8
|
||||
pycares==4.3.0
|
||||
pycparser==2.21
|
||||
pycryptodomex==3.17
|
||||
pycryptodome==3.19.0
|
||||
pydantic==1.10.5
|
||||
PyJWT==2.6.0
|
||||
pymongo==4.3.3
|
||||
pyowm==3.3.0
|
||||
openapi3_parser==1.1.16
|
||||
pandas==2.2.0
|
||||
pydantic_settings==2.1.0
|
||||
pymongo==4.6.1
|
||||
PyPDF2==3.0.1
|
||||
PySocks==1.7.1
|
||||
pytest
|
||||
python-dateutil==2.8.2
|
||||
python-dotenv==1.0.0
|
||||
python-jose==3.3.0
|
||||
pytz==2022.7.1
|
||||
PyYAML==6.0
|
||||
redis==4.5.4
|
||||
regex==2022.10.31
|
||||
requests==2.31.0
|
||||
python-dotenv==1.0.1
|
||||
qdrant-client==1.7.3
|
||||
redis==5.0.1
|
||||
Requests==2.31.0
|
||||
retry==0.9.2
|
||||
rsa==4.9
|
||||
scikit-learn==1.2.2
|
||||
scipy==1.10.1
|
||||
sentencepiece
|
||||
six==1.16.0
|
||||
SQLAlchemy==1.4.46
|
||||
sympy==1.11.1
|
||||
tenacity==8.2.2
|
||||
threadpoolctl==3.1.0
|
||||
tiktoken
|
||||
tqdm==4.65.0
|
||||
transformers==4.30.0
|
||||
typer==0.7.0
|
||||
typing-inspect==0.8.0
|
||||
typing_extensions==4.5.0
|
||||
urllib3==1.26.18
|
||||
vine==5.0.0
|
||||
wcwidth==0.2.6
|
||||
yarl==1.8.2
|
||||
sentence-transformers==2.2.2
|
||||
sentence-transformers
|
||||
tiktoken==0.5.2
|
||||
torch==2.1.2
|
||||
tqdm==4.66.1
|
||||
transformers==4.36.2
|
||||
unstructured==0.12.2
|
||||
Werkzeug==3.0.1
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
from abc import ABC, abstractmethod
|
||||
import os
|
||||
from langchain.embeddings import (
|
||||
OpenAIEmbeddings,
|
||||
from langchain_community.embeddings import (
|
||||
HuggingFaceEmbeddings,
|
||||
CohereEmbeddings,
|
||||
HuggingFaceInstructEmbeddings,
|
||||
)
|
||||
from langchain_openai import OpenAIEmbeddings
|
||||
from application.core.settings import settings
|
||||
|
||||
class BaseVectorStore(ABC):
|
||||
@@ -44,6 +44,11 @@ class BaseVectorStore(ABC):
|
||||
embedding_instance = embeddings_factory[embeddings_name](
|
||||
cohere_api_key=embeddings_key
|
||||
)
|
||||
elif embeddings_name == "huggingface_sentence-transformers/all-mpnet-base-v2":
|
||||
embedding_instance = embeddings_factory[embeddings_name](
|
||||
#model_name="./model/all-mpnet-base-v2",
|
||||
model_kwargs={"device": "cpu"},
|
||||
)
|
||||
else:
|
||||
embedding_instance = embeddings_factory[embeddings_name]()
|
||||
|
||||
|
||||
8
application/vectorstore/document_class.py
Normal file
@@ -0,0 +1,8 @@
|
||||
class Document(str):
|
||||
"""Class for storing a piece of text and associated metadata."""
|
||||
|
||||
def __new__(cls, page_content: str, metadata: dict):
|
||||
instance = super().__new__(cls, page_content)
|
||||
instance.page_content = page_content
|
||||
instance.metadata = metadata
|
||||
return instance
|
||||
@@ -1,16 +1,8 @@
|
||||
from application.vectorstore.base import BaseVectorStore
|
||||
from application.core.settings import settings
|
||||
from application.vectorstore.document_class import Document
|
||||
import elasticsearch
|
||||
|
||||
class Document(str):
|
||||
"""Class for storing a piece of text and associated metadata."""
|
||||
|
||||
def __new__(cls, page_content: str, metadata: dict):
|
||||
instance = super().__new__(cls, page_content)
|
||||
instance.page_content = page_content
|
||||
instance.metadata = metadata
|
||||
return instance
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from langchain.vectorstores import FAISS
|
||||
from langchain_community.vectorstores import FAISS
|
||||
from application.vectorstore.base import BaseVectorStore
|
||||
from application.core.settings import settings
|
||||
|
||||
|
||||
126
application/vectorstore/mongodb.py
Normal file
@@ -0,0 +1,126 @@
|
||||
from application.vectorstore.base import BaseVectorStore
|
||||
from application.core.settings import settings
|
||||
from application.vectorstore.document_class import Document
|
||||
|
||||
class MongoDBVectorStore(BaseVectorStore):
|
||||
def __init__(
|
||||
self,
|
||||
path: str = "",
|
||||
embeddings_key: str = "embeddings",
|
||||
collection: str = "documents",
|
||||
index_name: str = "vector_search_index",
|
||||
text_key: str = "text",
|
||||
embedding_key: str = "embedding",
|
||||
database: str = "docsgpt",
|
||||
):
|
||||
self._index_name = index_name
|
||||
self._text_key = text_key
|
||||
self._embedding_key = embedding_key
|
||||
self._embeddings_key = embeddings_key
|
||||
self._mongo_uri = settings.MONGO_URI
|
||||
self._path = path.replace("application/indexes/", "").rstrip("/")
|
||||
self._embedding = self._get_embeddings(settings.EMBEDDINGS_NAME, embeddings_key)
|
||||
|
||||
try:
|
||||
import pymongo
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Could not import pymongo python package. "
|
||||
"Please install it with `pip install pymongo`."
|
||||
)
|
||||
|
||||
self._client = pymongo.MongoClient(self._mongo_uri)
|
||||
self._database = self._client[database]
|
||||
self._collection = self._database[collection]
|
||||
|
||||
|
||||
def search(self, question, k=2, *args, **kwargs):
|
||||
query_vector = self._embedding.embed_query(question)
|
||||
|
||||
pipeline = [
|
||||
{
|
||||
"$vectorSearch": {
|
||||
"queryVector": query_vector,
|
||||
"path": self._embedding_key,
|
||||
"limit": k,
|
||||
"numCandidates": k * 10,
|
||||
"index": self._index_name,
|
||||
"filter": {
|
||||
"store": {"$eq": self._path}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
cursor = self._collection.aggregate(pipeline)
|
||||
|
||||
results = []
|
||||
for doc in cursor:
|
||||
text = doc[self._text_key]
|
||||
doc.pop("_id")
|
||||
doc.pop(self._text_key)
|
||||
doc.pop(self._embedding_key)
|
||||
metadata = doc
|
||||
results.append(Document(text, metadata))
|
||||
return results
|
||||
|
||||
def _insert_texts(self, texts, metadatas):
|
||||
if not texts:
|
||||
return []
|
||||
embeddings = self._embedding.embed_documents(texts)
|
||||
to_insert = [
|
||||
{self._text_key: t, self._embedding_key: embedding, **m}
|
||||
for t, m, embedding in zip(texts, metadatas, embeddings)
|
||||
]
|
||||
# insert the documents in MongoDB Atlas
|
||||
insert_result = self._collection.insert_many(to_insert)
|
||||
return insert_result.inserted_ids
|
||||
|
||||
def add_texts(self,
|
||||
texts,
|
||||
metadatas = None,
|
||||
ids = None,
|
||||
refresh_indices = True,
|
||||
create_index_if_not_exists = True,
|
||||
bulk_kwargs = None,
|
||||
**kwargs,):
|
||||
|
||||
|
||||
#dims = self._embedding.client[1].word_embedding_dimension
|
||||
# # check if index exists
|
||||
# if create_index_if_not_exists:
|
||||
# # check if index exists
|
||||
# info = self._collection.index_information()
|
||||
# if self._index_name not in info:
|
||||
# index_mongo = {
|
||||
# "fields": [{
|
||||
# "type": "vector",
|
||||
# "path": self._embedding_key,
|
||||
# "numDimensions": dims,
|
||||
# "similarity": "cosine",
|
||||
# },
|
||||
# {
|
||||
# "type": "filter",
|
||||
# "path": "store"
|
||||
# }]
|
||||
# }
|
||||
# self._collection.create_index(self._index_name, index_mongo)
|
||||
|
||||
batch_size = 100
|
||||
_metadatas = metadatas or ({} for _ in texts)
|
||||
texts_batch = []
|
||||
metadatas_batch = []
|
||||
result_ids = []
|
||||
for i, (text, metadata) in enumerate(zip(texts, _metadatas)):
|
||||
texts_batch.append(text)
|
||||
metadatas_batch.append(metadata)
|
||||
if (i + 1) % batch_size == 0:
|
||||
result_ids.extend(self._insert_texts(texts_batch, metadatas_batch))
|
||||
texts_batch = []
|
||||
metadatas_batch = []
|
||||
if texts_batch:
|
||||
result_ids.extend(self._insert_texts(texts_batch, metadatas_batch))
|
||||
return result_ids
|
||||
|
||||
def delete_index(self, *args, **kwargs):
|
||||
self._collection.delete_many({"store": self._path})
|
||||
47
application/vectorstore/qdrant.py
Normal file
@@ -0,0 +1,47 @@
|
||||
from langchain_community.vectorstores.qdrant import Qdrant
|
||||
from application.vectorstore.base import BaseVectorStore
|
||||
from application.core.settings import settings
|
||||
from qdrant_client import models
|
||||
|
||||
|
||||
class QdrantStore(BaseVectorStore):
|
||||
def __init__(self, path: str = "", embeddings_key: str = "embeddings"):
|
||||
self._filter = models.Filter(
|
||||
must=[
|
||||
models.FieldCondition(
|
||||
key="metadata.store",
|
||||
match=models.MatchValue(value=path.replace("application/indexes/", "").rstrip("/")),
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
self._docsearch = Qdrant.construct_instance(
|
||||
["TEXT_TO_OBTAIN_EMBEDDINGS_DIMENSION"],
|
||||
embedding=self._get_embeddings(settings.EMBEDDINGS_NAME, embeddings_key),
|
||||
collection_name=settings.QDRANT_COLLECTION_NAME,
|
||||
location=settings.QDRANT_LOCATION,
|
||||
url=settings.QDRANT_URL,
|
||||
port=settings.QDRANT_PORT,
|
||||
grpc_port=settings.QDRANT_GRPC_PORT,
|
||||
https=settings.QDRANT_HTTPS,
|
||||
prefer_grpc=settings.QDRANT_PREFER_GRPC,
|
||||
api_key=settings.QDRANT_API_KEY,
|
||||
prefix=settings.QDRANT_PREFIX,
|
||||
timeout=settings.QDRANT_TIMEOUT,
|
||||
path=settings.QDRANT_PATH,
|
||||
distance_func=settings.QDRANT_DISTANCE_FUNC,
|
||||
)
|
||||
|
||||
def search(self, *args, **kwargs):
|
||||
return self._docsearch.similarity_search(filter=self._filter, *args, **kwargs)
|
||||
|
||||
def add_texts(self, *args, **kwargs):
|
||||
return self._docsearch.add_texts(*args, **kwargs)
|
||||
|
||||
def save_local(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def delete_index(self, *args, **kwargs):
|
||||
return self._docsearch.client.delete(
|
||||
collection_name=settings.QDRANT_COLLECTION_NAME, points_selector=self._filter
|
||||
)
|
||||
@@ -1,11 +1,15 @@
|
||||
from application.vectorstore.faiss import FaissStore
|
||||
from application.vectorstore.elasticsearch import ElasticsearchStore
|
||||
from application.vectorstore.mongodb import MongoDBVectorStore
|
||||
from application.vectorstore.qdrant import QdrantStore
|
||||
|
||||
|
||||
class VectorCreator:
|
||||
vectorstores = {
|
||||
'faiss': FaissStore,
|
||||
'elasticsearch':ElasticsearchStore
|
||||
"faiss": FaissStore,
|
||||
"elasticsearch": ElasticsearchStore,
|
||||
"mongodb": MongoDBVectorStore,
|
||||
"qdrant": QdrantStore,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
@@ -13,4 +17,4 @@ class VectorCreator:
|
||||
vectorstore_class = cls.vectorstores.get(type.lower())
|
||||
if not vectorstore_class:
|
||||
raise ValueError(f"No vectorstore class found for type {type}")
|
||||
return vectorstore_class(*args, **kwargs)
|
||||
return vectorstore_class(*args, **kwargs)
|
||||
|
||||
@@ -9,6 +9,7 @@ import requests
|
||||
|
||||
from application.core.settings import settings
|
||||
from application.parser.file.bulk import SimpleDirectoryReader
|
||||
from application.parser.remote.remote_creator import RemoteCreator
|
||||
from application.parser.open_ai_func import call_openai_api
|
||||
from application.parser.schema.base import Document
|
||||
from application.parser.token_func import group_split
|
||||
@@ -121,3 +122,49 @@ def ingest_worker(self, directory, formats, name_job, filename, user):
|
||||
'user': user,
|
||||
'limited': False
|
||||
}
|
||||
|
||||
def remote_worker(self, source_data, name_job, user, directory = 'temp', loader = 'url'):
|
||||
# sample = False
|
||||
token_check = True
|
||||
min_tokens = 150
|
||||
max_tokens = 1250
|
||||
full_path = directory + '/' + user + '/' + name_job
|
||||
|
||||
if not os.path.exists(full_path):
|
||||
os.makedirs(full_path)
|
||||
|
||||
self.update_state(state='PROGRESS', meta={'current': 1})
|
||||
|
||||
# source_data {"data": [url]} for url type task just urls
|
||||
|
||||
# Use RemoteCreator to load data from URL
|
||||
remote_loader = RemoteCreator.create_loader(loader)
|
||||
raw_docs = remote_loader.load_data(source_data)
|
||||
|
||||
docs = group_split(documents=raw_docs, min_tokens=min_tokens, max_tokens=max_tokens, token_check=token_check)
|
||||
|
||||
#docs = [Document.to_langchain_format(raw_doc) for raw_doc in raw_docs]
|
||||
|
||||
call_openai_api(docs, full_path, self)
|
||||
self.update_state(state='PROGRESS', meta={'current': 100})
|
||||
|
||||
|
||||
|
||||
# Proceed with uploading and cleaning as in the original function
|
||||
file_data = {'name': name_job, 'user': user}
|
||||
if settings.VECTOR_STORE == "faiss":
|
||||
files = {'file_faiss': open(full_path + '/index.faiss', 'rb'),
|
||||
'file_pkl': open(full_path + '/index.pkl', 'rb')}
|
||||
requests.post(urljoin(settings.API_URL, "/api/upload_index"), files=files, data=file_data)
|
||||
requests.get(urljoin(settings.API_URL, "/api/delete_old?path=" + full_path))
|
||||
else:
|
||||
requests.post(urljoin(settings.API_URL, "/api/upload_index"), data=file_data)
|
||||
|
||||
shutil.rmtree(full_path)
|
||||
|
||||
return {
|
||||
'urls': source_data,
|
||||
'name_job': name_job,
|
||||
'user': user,
|
||||
'limited': False
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
const withNextra = require('nextra')({
|
||||
theme: 'nextra-theme-docs',
|
||||
themeConfig: './theme.config.jsx'
|
||||
})
|
||||
theme: 'nextra-theme-docs',
|
||||
themeConfig: './theme.config.jsx'
|
||||
})
|
||||
|
||||
module.exports = withNextra()
|
||||
|
||||
module.exports = withNextra()
|
||||
|
||||
// If you have other Next.js configurations, you can pass them as the parameter:
|
||||
// module.exports = withNextra({ /* other next.js config */ })
|
||||
// If you have other Next.js configurations, you can pass them as the parameter:
|
||||
// module.exports = withNextra({ /* other next.js config */ })
|
||||
|
||||
9383
docs/package-lock.json
generated
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"scripts":{
|
||||
"dev": "next dev",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
},
|
||||
"license": "MIT",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vercel/analytics": "^1.0.2",
|
||||
"docsgpt": "^0.2.4",
|
||||
"next": "^13.4.19",
|
||||
"nextra": "^2.12.3",
|
||||
"nextra-theme-docs": "^2.12.3",
|
||||
"@vercel/analytics": "^1.1.1",
|
||||
"docsgpt": "^0.3.7",
|
||||
"next": "^14.0.4",
|
||||
"nextra": "^2.13.2",
|
||||
"nextra-theme-docs": "^2.13.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,3 +107,4 @@ Your instance is now available at your Public IP Address on port 5173. Enjoy usi
|
||||
|
||||
- [Deploy DocsGPT on Civo Compute Cloud](https://dev.to/rutamhere/deploying-docsgpt-on-civo-compute-c)
|
||||
- [Deploy DocsGPT on DigitalOcean Droplet](https://dev.to/rutamhere/deploying-docsgpt-on-digitalocean-droplet-50ea)
|
||||
- [Deploy DocsGPT on Kamatera Performance Cloud](https://dev.to/rutamhere/deploying-docsgpt-on-kamatera-performance-cloud-1bj)
|
||||
|
||||
@@ -8,7 +8,7 @@ Just run the following command:
|
||||
./setup.sh
|
||||
```
|
||||
|
||||
This command will install all the necessary dependencies and provide you with an option to download the local model or use OpenAI.
|
||||
This command will install all the necessary dependencies and provide you with an option to use our LLM API, download the local model or use OpenAI.
|
||||
|
||||
If you prefer to follow manual steps, refer to this guide:
|
||||
|
||||
@@ -16,7 +16,7 @@ If you prefer to follow manual steps, refer to this guide:
|
||||
```bash
|
||||
git clone https://github.com/arc53/DocsGPT.git
|
||||
```
|
||||
2. Create a `.env` file in your root directory and set your `API_KEY` with your [OpenAI API key](https://platform.openai.com/account/api-keys).
|
||||
2. Create a `.env` file in your root directory and set your `API_KEY` with your [OpenAI API key](https://platform.openai.com/account/api-keys). (optional in case you want to use OpenAI)
|
||||
3. Run the following commands:
|
||||
```bash
|
||||
docker-compose build && docker-compose up
|
||||
|
||||
@@ -10,7 +10,6 @@ First, make sure you have Node.js and npm installed in your project. Then go to
|
||||
In the file where you want to use the widget, import it and include the CSS file:
|
||||
```js
|
||||
import { DocsGPTWidget } from "docsgpt";
|
||||
import "docsgpt/dist/style.css";
|
||||
```
|
||||
|
||||
|
||||
@@ -20,18 +19,28 @@ Now, you can use the widget in your component like this :
|
||||
apiHost="https://your-docsgpt-api.com"
|
||||
selectDocs="local/docs.zip"
|
||||
apiKey=""
|
||||
avatar = "https://d3dg1063dc54p9.cloudfront.net/cute-docsgpt.png",
|
||||
title = "Get AI assistance",
|
||||
description = "DocsGPT's AI Chatbot is here to help",
|
||||
heroTitle = "Welcome to DocsGPT !",
|
||||
heroDescription="This chatbot is built with DocsGPT and utilises GenAI,
|
||||
please review important information using sources."
|
||||
/>
|
||||
```
|
||||
DocsGPTWidget takes 3 **props**:
|
||||
DocsGPTWidget takes 8 **props** with default fallback values:
|
||||
1. `apiHost` — The URL of your DocsGPT API.
|
||||
2. `selectDocs` — The documentation source that you want to use for your widget (e.g. `default` or `local/docs1.zip`).
|
||||
3. `apiKey` — Usually, it's empty.
|
||||
4. `avatar`: Specifies the URL of the avatar or image representing the chatbot.
|
||||
5. `title`: Sets the title text displayed in the chatbot interface.
|
||||
6. `description`: Provides a brief description of the chatbot's purpose or functionality.
|
||||
7. `heroTitle`: Displays a welcome title when users interact with the chatbot.
|
||||
8. `heroDescription`: Provide additional introductory text or information about the chatbot's capabilities.
|
||||
|
||||
### How to use DocsGPTWidget with [Nextra](https://nextra.site/) (Next.js + MDX)
|
||||
Install your widget as described above and then go to your `pages/` folder and create a new file `_app.js` with the following content:
|
||||
```js
|
||||
import { DocsGPTWidget } from "docsgpt";
|
||||
import "docsgpt/dist/style.css";
|
||||
|
||||
export default function MyApp({ Component, pageProps }) {
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { DocsGPTWidget } from "docsgpt";
|
||||
import "docsgpt/dist/style.css";
|
||||
|
||||
export default function MyApp({ Component, pageProps }) {
|
||||
return (
|
||||
3
extensions/react-widget/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
dist
|
||||
.parcel-cache
|
||||
10
extensions/react-widget/.parcelrc
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "@parcel/config-default",
|
||||
"resolvers": ["@parcel/resolver-glob","..."],
|
||||
"transformers": {
|
||||
"*.svg": ["...", "@parcel/transformer-svg-react", "@parcel/transformer-typescript-tsc"]
|
||||
},
|
||||
"validators": {
|
||||
"*.{ts,tsx}": ["@parcel/validator-typescript"]
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@ npm install docsgpt
|
||||
|
||||
```javascript
|
||||
import { DocsGPTWidget } from "docsgpt";
|
||||
import "docsgpt/dist/style.css";
|
||||
|
||||
const App = () => {
|
||||
return <DocsGPTWidget />;
|
||||
@@ -24,10 +23,18 @@ To link the widget to your api and your documents you can pass parameters to the
|
||||
|
||||
```javascript
|
||||
import { DocsGPTWidget } from "docsgpt";
|
||||
import "docsgpt/dist/style.css";
|
||||
|
||||
const App = () => {
|
||||
return <DocsGPTWidget apiHost="http://localhost:7001" selectDocs='default' apiKey=''/>;
|
||||
return <DocsGPTWidget
|
||||
apiHost = 'http://localhost:7001',
|
||||
selectDocs = 'default',
|
||||
apiKey = '',
|
||||
avatar = 'https://d3dg1063dc54p9.cloudfront.net/cute-docsgpt.png',
|
||||
title = 'Get AI assistance',
|
||||
description = 'DocsGPT\'s AI Chatbot is here to help',
|
||||
heroTitle = 'Welcome to DocsGPT !',
|
||||
heroDescription='This chatbot is built with DocsGPT and utilises GenAI, please review important information using sources.'
|
||||
/>;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
9
extensions/react-widget/custom.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
declare module "*.svg" {
|
||||
import * as React from "react";
|
||||
|
||||
const ReactComponent: React.FunctionComponent<
|
||||
React.SVGProps<SVGSVGElement> & { title?: string }
|
||||
>;
|
||||
|
||||
export default ReactComponent;
|
||||
}
|
||||
1
extensions/react-widget/dist/index.d.ts
vendored
@@ -1 +0,0 @@
|
||||
export { DocsGPTWidget } from "./src/components/DocsGPTWidget";
|
||||
832
extensions/react-widget/dist/index.es.js
vendored
@@ -1,832 +0,0 @@
|
||||
import Ne, { useState as ke, useRef as ur, useEffect as Pe } from "react";
|
||||
var ne = { exports: {} }, Y = {};
|
||||
/**
|
||||
* @license React
|
||||
* react-jsx-runtime.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
var Ce;
|
||||
function cr() {
|
||||
if (Ce)
|
||||
return Y;
|
||||
Ce = 1;
|
||||
var N = Ne, w = Symbol.for("react.element"), C = Symbol.for("react.fragment"), u = Object.prototype.hasOwnProperty, E = N.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner, S = { key: !0, ref: !0, __self: !0, __source: !0 };
|
||||
function T(b, d, v) {
|
||||
var m, h = {}, x = null, p = null;
|
||||
v !== void 0 && (x = "" + v), d.key !== void 0 && (x = "" + d.key), d.ref !== void 0 && (p = d.ref);
|
||||
for (m in d)
|
||||
u.call(d, m) && !S.hasOwnProperty(m) && (h[m] = d[m]);
|
||||
if (b && b.defaultProps)
|
||||
for (m in d = b.defaultProps, d)
|
||||
h[m] === void 0 && (h[m] = d[m]);
|
||||
return { $$typeof: w, type: b, key: x, ref: p, props: h, _owner: E.current };
|
||||
}
|
||||
return Y.Fragment = C, Y.jsx = T, Y.jsxs = T, Y;
|
||||
}
|
||||
var L = {};
|
||||
/**
|
||||
* @license React
|
||||
* react-jsx-runtime.development.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
var Oe;
|
||||
function fr() {
|
||||
return Oe || (Oe = 1, process.env.NODE_ENV !== "production" && function() {
|
||||
var N = Ne, w = Symbol.for("react.element"), C = Symbol.for("react.portal"), u = Symbol.for("react.fragment"), E = Symbol.for("react.strict_mode"), S = Symbol.for("react.profiler"), T = Symbol.for("react.provider"), b = Symbol.for("react.context"), d = Symbol.for("react.forward_ref"), v = Symbol.for("react.suspense"), m = Symbol.for("react.suspense_list"), h = Symbol.for("react.memo"), x = Symbol.for("react.lazy"), p = Symbol.for("react.offscreen"), R = Symbol.iterator, j = "@@iterator";
|
||||
function J(e) {
|
||||
if (e === null || typeof e != "object")
|
||||
return null;
|
||||
var r = R && e[R] || e[j];
|
||||
return typeof r == "function" ? r : null;
|
||||
}
|
||||
var O = N.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
||||
function g(e) {
|
||||
{
|
||||
for (var r = arguments.length, t = new Array(r > 1 ? r - 1 : 0), n = 1; n < r; n++)
|
||||
t[n - 1] = arguments[n];
|
||||
B("error", e, t);
|
||||
}
|
||||
}
|
||||
function B(e, r, t) {
|
||||
{
|
||||
var n = O.ReactDebugCurrentFrame, o = n.getStackAddendum();
|
||||
o !== "" && (r += "%s", t = t.concat([o]));
|
||||
var s = t.map(function(i) {
|
||||
return String(i);
|
||||
});
|
||||
s.unshift("Warning: " + r), Function.prototype.apply.call(console[e], console, s);
|
||||
}
|
||||
}
|
||||
var D = !1, z = !1, De = !1, Ae = !1, Fe = !1, ae;
|
||||
ae = Symbol.for("react.module.reference");
|
||||
function Ie(e) {
|
||||
return !!(typeof e == "string" || typeof e == "function" || e === u || e === S || Fe || e === E || e === v || e === m || Ae || e === p || D || z || De || typeof e == "object" && e !== null && (e.$$typeof === x || e.$$typeof === h || e.$$typeof === T || e.$$typeof === b || e.$$typeof === d || // This needs to include all possible module reference object
|
||||
// types supported by any Flight configuration anywhere since
|
||||
// we don't know which Flight build this will end up being used
|
||||
// with.
|
||||
e.$$typeof === ae || e.getModuleId !== void 0));
|
||||
}
|
||||
function $e(e, r, t) {
|
||||
var n = e.displayName;
|
||||
if (n)
|
||||
return n;
|
||||
var o = r.displayName || r.name || "";
|
||||
return o !== "" ? t + "(" + o + ")" : t;
|
||||
}
|
||||
function ie(e) {
|
||||
return e.displayName || "Context";
|
||||
}
|
||||
function k(e) {
|
||||
if (e == null)
|
||||
return null;
|
||||
if (typeof e.tag == "number" && g("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."), typeof e == "function")
|
||||
return e.displayName || e.name || null;
|
||||
if (typeof e == "string")
|
||||
return e;
|
||||
switch (e) {
|
||||
case u:
|
||||
return "Fragment";
|
||||
case C:
|
||||
return "Portal";
|
||||
case S:
|
||||
return "Profiler";
|
||||
case E:
|
||||
return "StrictMode";
|
||||
case v:
|
||||
return "Suspense";
|
||||
case m:
|
||||
return "SuspenseList";
|
||||
}
|
||||
if (typeof e == "object")
|
||||
switch (e.$$typeof) {
|
||||
case b:
|
||||
var r = e;
|
||||
return ie(r) + ".Consumer";
|
||||
case T:
|
||||
var t = e;
|
||||
return ie(t._context) + ".Provider";
|
||||
case d:
|
||||
return $e(e, e.render, "ForwardRef");
|
||||
case h:
|
||||
var n = e.displayName || null;
|
||||
return n !== null ? n : k(e.type) || "Memo";
|
||||
case x: {
|
||||
var o = e, s = o._payload, i = o._init;
|
||||
try {
|
||||
return k(i(s));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var A = Object.assign, $ = 0, oe, se, le, ue, ce, fe, de;
|
||||
function ve() {
|
||||
}
|
||||
ve.__reactDisabledLog = !0;
|
||||
function We() {
|
||||
{
|
||||
if ($ === 0) {
|
||||
oe = console.log, se = console.info, le = console.warn, ue = console.error, ce = console.group, fe = console.groupCollapsed, de = console.groupEnd;
|
||||
var e = {
|
||||
configurable: !0,
|
||||
enumerable: !0,
|
||||
value: ve,
|
||||
writable: !0
|
||||
};
|
||||
Object.defineProperties(console, {
|
||||
info: e,
|
||||
log: e,
|
||||
warn: e,
|
||||
error: e,
|
||||
group: e,
|
||||
groupCollapsed: e,
|
||||
groupEnd: e
|
||||
});
|
||||
}
|
||||
$++;
|
||||
}
|
||||
}
|
||||
function Ye() {
|
||||
{
|
||||
if ($--, $ === 0) {
|
||||
var e = {
|
||||
configurable: !0,
|
||||
enumerable: !0,
|
||||
writable: !0
|
||||
};
|
||||
Object.defineProperties(console, {
|
||||
log: A({}, e, {
|
||||
value: oe
|
||||
}),
|
||||
info: A({}, e, {
|
||||
value: se
|
||||
}),
|
||||
warn: A({}, e, {
|
||||
value: le
|
||||
}),
|
||||
error: A({}, e, {
|
||||
value: ue
|
||||
}),
|
||||
group: A({}, e, {
|
||||
value: ce
|
||||
}),
|
||||
groupCollapsed: A({}, e, {
|
||||
value: fe
|
||||
}),
|
||||
groupEnd: A({}, e, {
|
||||
value: de
|
||||
})
|
||||
});
|
||||
}
|
||||
$ < 0 && g("disabledDepth fell below zero. This is a bug in React. Please file an issue.");
|
||||
}
|
||||
}
|
||||
var H = O.ReactCurrentDispatcher, K;
|
||||
function V(e, r, t) {
|
||||
{
|
||||
if (K === void 0)
|
||||
try {
|
||||
throw Error();
|
||||
} catch (o) {
|
||||
var n = o.stack.trim().match(/\n( *(at )?)/);
|
||||
K = n && n[1] || "";
|
||||
}
|
||||
return `
|
||||
` + K + e;
|
||||
}
|
||||
}
|
||||
var X = !1, M;
|
||||
{
|
||||
var Le = typeof WeakMap == "function" ? WeakMap : Map;
|
||||
M = new Le();
|
||||
}
|
||||
function pe(e, r) {
|
||||
if (!e || X)
|
||||
return "";
|
||||
{
|
||||
var t = M.get(e);
|
||||
if (t !== void 0)
|
||||
return t;
|
||||
}
|
||||
var n;
|
||||
X = !0;
|
||||
var o = Error.prepareStackTrace;
|
||||
Error.prepareStackTrace = void 0;
|
||||
var s;
|
||||
s = H.current, H.current = null, We();
|
||||
try {
|
||||
if (r) {
|
||||
var i = function() {
|
||||
throw Error();
|
||||
};
|
||||
if (Object.defineProperty(i.prototype, "props", {
|
||||
set: function() {
|
||||
throw Error();
|
||||
}
|
||||
}), typeof Reflect == "object" && Reflect.construct) {
|
||||
try {
|
||||
Reflect.construct(i, []);
|
||||
} catch (P) {
|
||||
n = P;
|
||||
}
|
||||
Reflect.construct(e, [], i);
|
||||
} else {
|
||||
try {
|
||||
i.call();
|
||||
} catch (P) {
|
||||
n = P;
|
||||
}
|
||||
e.call(i.prototype);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
throw Error();
|
||||
} catch (P) {
|
||||
n = P;
|
||||
}
|
||||
e();
|
||||
}
|
||||
} catch (P) {
|
||||
if (P && n && typeof P.stack == "string") {
|
||||
for (var a = P.stack.split(`
|
||||
`), y = n.stack.split(`
|
||||
`), c = a.length - 1, f = y.length - 1; c >= 1 && f >= 0 && a[c] !== y[f]; )
|
||||
f--;
|
||||
for (; c >= 1 && f >= 0; c--, f--)
|
||||
if (a[c] !== y[f]) {
|
||||
if (c !== 1 || f !== 1)
|
||||
do
|
||||
if (c--, f--, f < 0 || a[c] !== y[f]) {
|
||||
var _ = `
|
||||
` + a[c].replace(" at new ", " at ");
|
||||
return e.displayName && _.includes("<anonymous>") && (_ = _.replace("<anonymous>", e.displayName)), typeof e == "function" && M.set(e, _), _;
|
||||
}
|
||||
while (c >= 1 && f >= 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
X = !1, H.current = s, Ye(), Error.prepareStackTrace = o;
|
||||
}
|
||||
var I = e ? e.displayName || e.name : "", je = I ? V(I) : "";
|
||||
return typeof e == "function" && M.set(e, je), je;
|
||||
}
|
||||
function Ve(e, r, t) {
|
||||
return pe(e, !1);
|
||||
}
|
||||
function Me(e) {
|
||||
var r = e.prototype;
|
||||
return !!(r && r.isReactComponent);
|
||||
}
|
||||
function U(e, r, t) {
|
||||
if (e == null)
|
||||
return "";
|
||||
if (typeof e == "function")
|
||||
return pe(e, Me(e));
|
||||
if (typeof e == "string")
|
||||
return V(e);
|
||||
switch (e) {
|
||||
case v:
|
||||
return V("Suspense");
|
||||
case m:
|
||||
return V("SuspenseList");
|
||||
}
|
||||
if (typeof e == "object")
|
||||
switch (e.$$typeof) {
|
||||
case d:
|
||||
return Ve(e.render);
|
||||
case h:
|
||||
return U(e.type, r, t);
|
||||
case x: {
|
||||
var n = e, o = n._payload, s = n._init;
|
||||
try {
|
||||
return U(s(o), r, t);
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
var G = Object.prototype.hasOwnProperty, he = {}, me = O.ReactDebugCurrentFrame;
|
||||
function q(e) {
|
||||
if (e) {
|
||||
var r = e._owner, t = U(e.type, e._source, r ? r.type : null);
|
||||
me.setExtraStackFrame(t);
|
||||
} else
|
||||
me.setExtraStackFrame(null);
|
||||
}
|
||||
function Ue(e, r, t, n, o) {
|
||||
{
|
||||
var s = Function.call.bind(G);
|
||||
for (var i in e)
|
||||
if (s(e, i)) {
|
||||
var a = void 0;
|
||||
try {
|
||||
if (typeof e[i] != "function") {
|
||||
var y = Error((n || "React class") + ": " + t + " type `" + i + "` is invalid; it must be a function, usually from the `prop-types` package, but received `" + typeof e[i] + "`.This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.");
|
||||
throw y.name = "Invariant Violation", y;
|
||||
}
|
||||
a = e[i](r, i, n, t, null, "SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED");
|
||||
} catch (c) {
|
||||
a = c;
|
||||
}
|
||||
a && !(a instanceof Error) && (q(o), g("%s: type specification of %s `%s` is invalid; the type checker function must return `null` or an `Error` but returned a %s. You may have forgotten to pass an argument to the type checker creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and shape all require an argument).", n || "React class", t, i, typeof a), q(null)), a instanceof Error && !(a.message in he) && (he[a.message] = !0, q(o), g("Failed %s type: %s", t, a.message), q(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
var Ge = Array.isArray;
|
||||
function Z(e) {
|
||||
return Ge(e);
|
||||
}
|
||||
function qe(e) {
|
||||
{
|
||||
var r = typeof Symbol == "function" && Symbol.toStringTag, t = r && e[Symbol.toStringTag] || e.constructor.name || "Object";
|
||||
return t;
|
||||
}
|
||||
}
|
||||
function Je(e) {
|
||||
try {
|
||||
return ge(e), !1;
|
||||
} catch {
|
||||
return !0;
|
||||
}
|
||||
}
|
||||
function ge(e) {
|
||||
return "" + e;
|
||||
}
|
||||
function ye(e) {
|
||||
if (Je(e))
|
||||
return g("The provided key is an unsupported type %s. This value must be coerced to a string before using it here.", qe(e)), ge(e);
|
||||
}
|
||||
var W = O.ReactCurrentOwner, Be = {
|
||||
key: !0,
|
||||
ref: !0,
|
||||
__self: !0,
|
||||
__source: !0
|
||||
}, be, Ee, Q;
|
||||
Q = {};
|
||||
function ze(e) {
|
||||
if (G.call(e, "ref")) {
|
||||
var r = Object.getOwnPropertyDescriptor(e, "ref").get;
|
||||
if (r && r.isReactWarning)
|
||||
return !1;
|
||||
}
|
||||
return e.ref !== void 0;
|
||||
}
|
||||
function He(e) {
|
||||
if (G.call(e, "key")) {
|
||||
var r = Object.getOwnPropertyDescriptor(e, "key").get;
|
||||
if (r && r.isReactWarning)
|
||||
return !1;
|
||||
}
|
||||
return e.key !== void 0;
|
||||
}
|
||||
function Ke(e, r) {
|
||||
if (typeof e.ref == "string" && W.current && r && W.current.stateNode !== r) {
|
||||
var t = k(W.current.type);
|
||||
Q[t] || (g('Component "%s" contains the string ref "%s". Support for string refs will be removed in a future major release. This case cannot be automatically converted to an arrow function. We ask you to manually fix this case by using useRef() or createRef() instead. Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref', k(W.current.type), e.ref), Q[t] = !0);
|
||||
}
|
||||
}
|
||||
function Xe(e, r) {
|
||||
{
|
||||
var t = function() {
|
||||
be || (be = !0, g("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://reactjs.org/link/special-props)", r));
|
||||
};
|
||||
t.isReactWarning = !0, Object.defineProperty(e, "key", {
|
||||
get: t,
|
||||
configurable: !0
|
||||
});
|
||||
}
|
||||
}
|
||||
function Ze(e, r) {
|
||||
{
|
||||
var t = function() {
|
||||
Ee || (Ee = !0, g("%s: `ref` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://reactjs.org/link/special-props)", r));
|
||||
};
|
||||
t.isReactWarning = !0, Object.defineProperty(e, "ref", {
|
||||
get: t,
|
||||
configurable: !0
|
||||
});
|
||||
}
|
||||
}
|
||||
var Qe = function(e, r, t, n, o, s, i) {
|
||||
var a = {
|
||||
// This tag allows us to uniquely identify this as a React Element
|
||||
$$typeof: w,
|
||||
// Built-in properties that belong on the element
|
||||
type: e,
|
||||
key: r,
|
||||
ref: t,
|
||||
props: i,
|
||||
// Record the component responsible for creating this element.
|
||||
_owner: s
|
||||
};
|
||||
return a._store = {}, Object.defineProperty(a._store, "validated", {
|
||||
configurable: !1,
|
||||
enumerable: !1,
|
||||
writable: !0,
|
||||
value: !1
|
||||
}), Object.defineProperty(a, "_self", {
|
||||
configurable: !1,
|
||||
enumerable: !1,
|
||||
writable: !1,
|
||||
value: n
|
||||
}), Object.defineProperty(a, "_source", {
|
||||
configurable: !1,
|
||||
enumerable: !1,
|
||||
writable: !1,
|
||||
value: o
|
||||
}), Object.freeze && (Object.freeze(a.props), Object.freeze(a)), a;
|
||||
};
|
||||
function er(e, r, t, n, o) {
|
||||
{
|
||||
var s, i = {}, a = null, y = null;
|
||||
t !== void 0 && (ye(t), a = "" + t), He(r) && (ye(r.key), a = "" + r.key), ze(r) && (y = r.ref, Ke(r, o));
|
||||
for (s in r)
|
||||
G.call(r, s) && !Be.hasOwnProperty(s) && (i[s] = r[s]);
|
||||
if (e && e.defaultProps) {
|
||||
var c = e.defaultProps;
|
||||
for (s in c)
|
||||
i[s] === void 0 && (i[s] = c[s]);
|
||||
}
|
||||
if (a || y) {
|
||||
var f = typeof e == "function" ? e.displayName || e.name || "Unknown" : e;
|
||||
a && Xe(i, f), y && Ze(i, f);
|
||||
}
|
||||
return Qe(e, a, y, o, n, W.current, i);
|
||||
}
|
||||
}
|
||||
var ee = O.ReactCurrentOwner, xe = O.ReactDebugCurrentFrame;
|
||||
function F(e) {
|
||||
if (e) {
|
||||
var r = e._owner, t = U(e.type, e._source, r ? r.type : null);
|
||||
xe.setExtraStackFrame(t);
|
||||
} else
|
||||
xe.setExtraStackFrame(null);
|
||||
}
|
||||
var re;
|
||||
re = !1;
|
||||
function te(e) {
|
||||
return typeof e == "object" && e !== null && e.$$typeof === w;
|
||||
}
|
||||
function _e() {
|
||||
{
|
||||
if (ee.current) {
|
||||
var e = k(ee.current.type);
|
||||
if (e)
|
||||
return `
|
||||
|
||||
Check the render method of \`` + e + "`.";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
function rr(e) {
|
||||
{
|
||||
if (e !== void 0) {
|
||||
var r = e.fileName.replace(/^.*[\\\/]/, ""), t = e.lineNumber;
|
||||
return `
|
||||
|
||||
Check your code at ` + r + ":" + t + ".";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
var Re = {};
|
||||
function tr(e) {
|
||||
{
|
||||
var r = _e();
|
||||
if (!r) {
|
||||
var t = typeof e == "string" ? e : e.displayName || e.name;
|
||||
t && (r = `
|
||||
|
||||
Check the top-level render call using <` + t + ">.");
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
function we(e, r) {
|
||||
{
|
||||
if (!e._store || e._store.validated || e.key != null)
|
||||
return;
|
||||
e._store.validated = !0;
|
||||
var t = tr(r);
|
||||
if (Re[t])
|
||||
return;
|
||||
Re[t] = !0;
|
||||
var n = "";
|
||||
e && e._owner && e._owner !== ee.current && (n = " It was passed a child from " + k(e._owner.type) + "."), F(e), g('Each child in a list should have a unique "key" prop.%s%s See https://reactjs.org/link/warning-keys for more information.', t, n), F(null);
|
||||
}
|
||||
}
|
||||
function Te(e, r) {
|
||||
{
|
||||
if (typeof e != "object")
|
||||
return;
|
||||
if (Z(e))
|
||||
for (var t = 0; t < e.length; t++) {
|
||||
var n = e[t];
|
||||
te(n) && we(n, r);
|
||||
}
|
||||
else if (te(e))
|
||||
e._store && (e._store.validated = !0);
|
||||
else if (e) {
|
||||
var o = J(e);
|
||||
if (typeof o == "function" && o !== e.entries)
|
||||
for (var s = o.call(e), i; !(i = s.next()).done; )
|
||||
te(i.value) && we(i.value, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
function nr(e) {
|
||||
{
|
||||
var r = e.type;
|
||||
if (r == null || typeof r == "string")
|
||||
return;
|
||||
var t;
|
||||
if (typeof r == "function")
|
||||
t = r.propTypes;
|
||||
else if (typeof r == "object" && (r.$$typeof === d || // Note: Memo only checks outer props here.
|
||||
// Inner props are checked in the reconciler.
|
||||
r.$$typeof === h))
|
||||
t = r.propTypes;
|
||||
else
|
||||
return;
|
||||
if (t) {
|
||||
var n = k(r);
|
||||
Ue(t, e.props, "prop", n, e);
|
||||
} else if (r.PropTypes !== void 0 && !re) {
|
||||
re = !0;
|
||||
var o = k(r);
|
||||
g("Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?", o || "Unknown");
|
||||
}
|
||||
typeof r.getDefaultProps == "function" && !r.getDefaultProps.isReactClassApproved && g("getDefaultProps is only used on classic React.createClass definitions. Use a static property named `defaultProps` instead.");
|
||||
}
|
||||
}
|
||||
function ar(e) {
|
||||
{
|
||||
for (var r = Object.keys(e.props), t = 0; t < r.length; t++) {
|
||||
var n = r[t];
|
||||
if (n !== "children" && n !== "key") {
|
||||
F(e), g("Invalid prop `%s` supplied to `React.Fragment`. React.Fragment can only have `key` and `children` props.", n), F(null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
e.ref !== null && (F(e), g("Invalid attribute `ref` supplied to `React.Fragment`."), F(null));
|
||||
}
|
||||
}
|
||||
function Se(e, r, t, n, o, s) {
|
||||
{
|
||||
var i = Ie(e);
|
||||
if (!i) {
|
||||
var a = "";
|
||||
(e === void 0 || typeof e == "object" && e !== null && Object.keys(e).length === 0) && (a += " You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.");
|
||||
var y = rr(o);
|
||||
y ? a += y : a += _e();
|
||||
var c;
|
||||
e === null ? c = "null" : Z(e) ? c = "array" : e !== void 0 && e.$$typeof === w ? (c = "<" + (k(e.type) || "Unknown") + " />", a = " Did you accidentally export a JSX literal instead of a component?") : c = typeof e, g("React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s", c, a);
|
||||
}
|
||||
var f = er(e, r, t, o, s);
|
||||
if (f == null)
|
||||
return f;
|
||||
if (i) {
|
||||
var _ = r.children;
|
||||
if (_ !== void 0)
|
||||
if (n)
|
||||
if (Z(_)) {
|
||||
for (var I = 0; I < _.length; I++)
|
||||
Te(_[I], e);
|
||||
Object.freeze && Object.freeze(_);
|
||||
} else
|
||||
g("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");
|
||||
else
|
||||
Te(_, e);
|
||||
}
|
||||
return e === u ? ar(f) : nr(f), f;
|
||||
}
|
||||
}
|
||||
function ir(e, r, t) {
|
||||
return Se(e, r, t, !0);
|
||||
}
|
||||
function or(e, r, t) {
|
||||
return Se(e, r, t, !1);
|
||||
}
|
||||
var sr = or, lr = ir;
|
||||
L.Fragment = u, L.jsx = sr, L.jsxs = lr;
|
||||
}()), L;
|
||||
}
|
||||
process.env.NODE_ENV === "production" ? ne.exports = cr() : ne.exports = fr();
|
||||
var l = ne.exports;
|
||||
function dr({
|
||||
question: N = "",
|
||||
apiKey: w = "",
|
||||
selectedDocs: C = "",
|
||||
history: u = [],
|
||||
conversationId: E = null,
|
||||
apiHost: S = "",
|
||||
onEvent: T = () => {
|
||||
console.log("Event triggered, but no handler provided.");
|
||||
}
|
||||
}) {
|
||||
let b = "default";
|
||||
return C && (b = C), new Promise((d, v) => {
|
||||
const m = {
|
||||
question: N,
|
||||
api_key: w,
|
||||
embeddings_key: w,
|
||||
active_docs: b,
|
||||
history: JSON.stringify(u),
|
||||
conversation_id: E,
|
||||
model: "default"
|
||||
};
|
||||
fetch(S + "/stream", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify(m)
|
||||
}).then((h) => {
|
||||
if (!h.body)
|
||||
throw Error("No response body");
|
||||
const x = h.body.getReader(), p = new TextDecoder("utf-8");
|
||||
let R = 0;
|
||||
const j = ({
|
||||
done: J,
|
||||
value: O
|
||||
}) => {
|
||||
if (J) {
|
||||
console.log(R), d();
|
||||
return;
|
||||
}
|
||||
R += 1;
|
||||
const B = p.decode(O).split(`
|
||||
`);
|
||||
for (let D of B) {
|
||||
if (D.trim() == "")
|
||||
continue;
|
||||
D.startsWith("data:") && (D = D.substring(5));
|
||||
const z = new MessageEvent("message", {
|
||||
data: D
|
||||
});
|
||||
T(z);
|
||||
}
|
||||
x.read().then(j).catch(v);
|
||||
};
|
||||
x.read().then(j).catch(v);
|
||||
}).catch((h) => {
|
||||
console.error("Connection failed:", h), v(h);
|
||||
});
|
||||
});
|
||||
}
|
||||
const pr = ({ apiHost: N = "https://gptcloud.arc53.com", selectDocs: w = "default", apiKey: C = "docsgpt-public" }) => {
|
||||
const [u, E] = ke(() => typeof window < "u" && localStorage.getItem("docsGPTChatState") || "init"), [S, T] = ke(""), b = ur(null);
|
||||
Pe(() => {
|
||||
if (b.current) {
|
||||
const v = b.current;
|
||||
v.scrollTop = v.scrollHeight;
|
||||
}
|
||||
}, [S]), Pe(() => {
|
||||
localStorage.setItem("docsGPTChatState", u);
|
||||
}, [u]);
|
||||
const d = (v) => {
|
||||
T(""), v.preventDefault(), E(
|
||||
"processing"
|
||||
/* Processing */
|
||||
), setTimeout(() => {
|
||||
E(
|
||||
"answer"
|
||||
/* Answer */
|
||||
);
|
||||
}, 800);
|
||||
const h = v.currentTarget[0].value;
|
||||
dr({
|
||||
question: h,
|
||||
apiKey: C,
|
||||
selectedDocs: w,
|
||||
history: [],
|
||||
conversationId: null,
|
||||
apiHost: N,
|
||||
onEvent: (x) => {
|
||||
const p = JSON.parse(x.data);
|
||||
if (p.type === "end")
|
||||
E(
|
||||
"answer"
|
||||
/* Answer */
|
||||
);
|
||||
else if (p.type === "source") {
|
||||
let R;
|
||||
if (p.metadata && p.metadata.title) {
|
||||
const j = p.metadata.title.split("/");
|
||||
R = {
|
||||
title: j[j.length - 1],
|
||||
text: p.doc
|
||||
};
|
||||
} else
|
||||
R = { title: p.doc, text: p.doc };
|
||||
console.log(R);
|
||||
} else if (p.type === "id")
|
||||
console.log(p.id);
|
||||
else {
|
||||
const R = p.answer;
|
||||
T((j) => j + R);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
return /* @__PURE__ */ l.jsx(l.Fragment, { children: /* @__PURE__ */ l.jsxs("div", { className: "dark widget-container", children: [
|
||||
/* @__PURE__ */ l.jsx(
|
||||
"div",
|
||||
{
|
||||
onClick: () => E(
|
||||
"init"
|
||||
/* Init */
|
||||
),
|
||||
className: `${u !== "minimized" ? "hidden" : ""} cursor-pointer`,
|
||||
children: /* @__PURE__ */ l.jsx("div", { className: "mr-2 mb-2 w-20 h-20 rounded-full overflow-hidden dark:divide-gray-700 border dark:border-gray-700 bg-gradient-to-br from-gray-100/80 via-white to-white dark:from-gray-900/80 dark:via-gray-900 dark:to-gray-900 font-sans shadow backdrop-blur-sm flex items-center justify-center", children: /* @__PURE__ */ l.jsx(
|
||||
"img",
|
||||
{
|
||||
src: "https://d3dg1063dc54p9.cloudfront.net/cute-docsgpt.png",
|
||||
alt: "DocsGPT",
|
||||
className: "cursor-pointer hover:opacity-50 h-14"
|
||||
}
|
||||
) })
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ l.jsxs("div", { className: ` ${u !== "minimized" ? "" : "hidden"} divide-y dark:divide-gray-700 rounded-md border dark:border-gray-700 bg-gradient-to-br from-gray-100/80 via-white to-white dark:from-gray-900/80 dark:via-gray-900 dark:to-gray-900 font-sans shadow backdrop-blur-sm`, style: { width: "18rem", transform: "translateY(0%) translateZ(0px)" }, children: [
|
||||
/* @__PURE__ */ l.jsxs("div", { children: [
|
||||
/* @__PURE__ */ l.jsx(
|
||||
"img",
|
||||
{
|
||||
src: "https://d3dg1063dc54p9.cloudfront.net/exit.svg",
|
||||
alt: "Exit",
|
||||
className: "cursor-pointer hover:opacity-50 h-2 absolute top-0 right-0 m-2 white-filter",
|
||||
onClick: (v) => {
|
||||
v.stopPropagation(), E(
|
||||
"minimized"
|
||||
/* Minimized */
|
||||
);
|
||||
}
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ l.jsxs("div", { className: "flex items-center gap-2 p-3", children: [
|
||||
/* @__PURE__ */ l.jsxs("div", { className: `${u === "init" || u === "processing" || u === "typing" ? "" : "hidden"} flex-1`, children: [
|
||||
/* @__PURE__ */ l.jsx("h3", { className: "text-sm font-bold text-gray-700 dark:text-gray-200", children: "Need help with documentation?" }),
|
||||
/* @__PURE__ */ l.jsx("p", { className: "mt-1 text-xs text-gray-400 dark:text-gray-500", children: "DocsGPT AI assistant will help you with docs" })
|
||||
] }),
|
||||
/* @__PURE__ */ l.jsx("div", { id: "docsgpt-answer", ref: b, className: `${u !== "answer" ? "hidden" : ""}`, children: /* @__PURE__ */ l.jsx("p", { className: "mt-1 text-sm text-gray-600 dark:text-white text-left", children: S }) })
|
||||
] })
|
||||
] }),
|
||||
/* @__PURE__ */ l.jsxs("div", { className: "w-full", children: [
|
||||
/* @__PURE__ */ l.jsx(
|
||||
"button",
|
||||
{
|
||||
onClick: () => E(
|
||||
"typing"
|
||||
/* Typing */
|
||||
),
|
||||
className: `flex w-full justify-center px-5 py-3 text-sm text-gray-800 font-bold dark:text-white transition duration-300 hover:bg-gray-100 rounded-b dark:hover:bg-gray-800/70 ${u !== "init" ? "hidden" : ""}`,
|
||||
children: "Ask DocsGPT"
|
||||
}
|
||||
),
|
||||
(u === "typing" || u === "answer") && /* @__PURE__ */ l.jsxs(
|
||||
"form",
|
||||
{
|
||||
onSubmit: d,
|
||||
className: "relative w-full m-0",
|
||||
style: { opacity: 1 },
|
||||
children: [
|
||||
/* @__PURE__ */ l.jsx(
|
||||
"input",
|
||||
{
|
||||
type: "text",
|
||||
className: "w-full bg-transparent px-5 py-3 pr-8 text-sm text-gray-700 dark:text-white focus:outline-none",
|
||||
placeholder: "What do you want to do?"
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ l.jsx("button", { className: "absolute text-gray-400 dark:text-gray-500 text-sm inset-y-0 right-2 -mx-2 px-2", type: "submit", children: "Submit" })
|
||||
]
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ l.jsxs("p", { className: `${u !== "processing" ? "hidden" : ""} flex w-full justify-center px-5 py-3 text-sm text-gray-800 font-bold dark:text-white transition duration-300 rounded-b`, children: [
|
||||
"Processing",
|
||||
/* @__PURE__ */ l.jsx("span", { className: "dot-animation", children: "." }),
|
||||
/* @__PURE__ */ l.jsx("span", { className: "dot-animation delay-200", children: "." }),
|
||||
/* @__PURE__ */ l.jsx("span", { className: "dot-animation delay-400", children: "." })
|
||||
] })
|
||||
] })
|
||||
] })
|
||||
] }) });
|
||||
};
|
||||
export {
|
||||
pr as DocsGPTWidget
|
||||
};
|
||||
//# sourceMappingURL=index.es.js.map
|
||||
1
extensions/react-widget/dist/index.es.js.map
vendored
29
extensions/react-widget/dist/index.umd.js
vendored
@@ -1,5 +0,0 @@
|
||||
export declare const DocsGPTWidget: ({ apiHost, selectDocs, apiKey }: {
|
||||
apiHost?: string | undefined;
|
||||
selectDocs?: string | undefined;
|
||||
apiKey?: string | undefined;
|
||||
}) => JSX.Element;
|
||||
@@ -1 +0,0 @@
|
||||
/// <reference types="vite/client" />
|
||||
762
extensions/react-widget/dist/style.css
vendored
@@ -1,762 +0,0 @@
|
||||
/*
|
||||
! tailwindcss v3.2.4 | MIT License | https://tailwindcss.com
|
||||
*//*
|
||||
1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
|
||||
2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
|
||||
*/
|
||||
|
||||
*,
|
||||
::before,
|
||||
::after {
|
||||
box-sizing: border-box; /* 1 */
|
||||
border-width: 0; /* 2 */
|
||||
border-style: solid; /* 2 */
|
||||
border-color: #e5e7eb; /* 2 */
|
||||
}
|
||||
|
||||
::before,
|
||||
::after {
|
||||
--tw-content: '';
|
||||
}
|
||||
|
||||
/*
|
||||
1. Use a consistent sensible line-height in all browsers.
|
||||
2. Prevent adjustments of font size after orientation changes in iOS.
|
||||
3. Use a more readable tab size.
|
||||
4. Use the user's configured `sans` font-family by default.
|
||||
5. Use the user's configured `sans` font-feature-settings by default.
|
||||
*/
|
||||
|
||||
html {
|
||||
line-height: 1.5; /* 1 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
-moz-tab-size: 4; /* 3 */
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4; /* 3 */
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 4 */
|
||||
font-feature-settings: normal; /* 5 */
|
||||
}
|
||||
|
||||
/*
|
||||
1. Remove the margin in all browsers.
|
||||
2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0; /* 1 */
|
||||
line-height: inherit; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
1. Add the correct height in Firefox.
|
||||
2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
|
||||
3. Ensure horizontal rules are visible by default.
|
||||
*/
|
||||
|
||||
hr {
|
||||
height: 0; /* 1 */
|
||||
color: inherit; /* 2 */
|
||||
border-top-width: 1px; /* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct text decoration in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
abbr:where([title]) {
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the default font size and weight for headings.
|
||||
*/
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
/*
|
||||
Reset links to optimize for opt-in styling instead of opt-out.
|
||||
*/
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct font weight in Edge and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Use the user's configured `mono` font family by default.
|
||||
2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp,
|
||||
pre {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/*
|
||||
Prevent `sub` and `sup` elements from affecting the line height in all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
|
||||
2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
|
||||
3. Remove gaps between table borders by default.
|
||||
*/
|
||||
|
||||
table {
|
||||
text-indent: 0; /* 1 */
|
||||
border-color: inherit; /* 2 */
|
||||
border-collapse: collapse; /* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
1. Change the font styles in all browsers.
|
||||
2. Remove the margin in Firefox and Safari.
|
||||
3. Remove default padding in all browsers.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit; /* 1 */
|
||||
font-size: 100%; /* 1 */
|
||||
font-weight: inherit; /* 1 */
|
||||
line-height: inherit; /* 1 */
|
||||
color: inherit; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
padding: 0; /* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the inheritance of text transform in Edge and Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Correct the inability to style clickable types in iOS and Safari.
|
||||
2. Remove default button styles.
|
||||
*/
|
||||
|
||||
button,
|
||||
[type='button'],
|
||||
[type='reset'],
|
||||
[type='submit'] {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
background-color: transparent; /* 2 */
|
||||
background-image: none; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Use the modern Firefox focus style for all focusable elements.
|
||||
*/
|
||||
|
||||
:-moz-focusring {
|
||||
outline: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
|
||||
*/
|
||||
|
||||
:-moz-ui-invalid {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct vertical alignment in Chrome and Firefox.
|
||||
*/
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/*
|
||||
Correct the cursor style of increment and decrement buttons in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-inner-spin-button,
|
||||
::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Correct the odd appearance in Chrome and Safari.
|
||||
2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type='search'] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
outline-offset: -2px; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the inner padding in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Correct the inability to style clickable types in iOS and Safari.
|
||||
2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct display in Chrome and Safari.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/*
|
||||
Removes the default spacing and border for appropriate elements.
|
||||
*/
|
||||
|
||||
blockquote,
|
||||
dl,
|
||||
dd,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
hr,
|
||||
figure,
|
||||
p,
|
||||
pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
menu {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Prevent resizing textareas horizontally by default.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
|
||||
2. Set the default placeholder color to the user's configured gray 400 color.
|
||||
*/
|
||||
|
||||
input::-moz-placeholder, textarea::-moz-placeholder {
|
||||
opacity: 1; /* 1 */
|
||||
color: #9ca3af; /* 2 */
|
||||
}
|
||||
|
||||
input::placeholder,
|
||||
textarea::placeholder {
|
||||
opacity: 1; /* 1 */
|
||||
color: #9ca3af; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Set the default cursor for buttons.
|
||||
*/
|
||||
|
||||
button,
|
||||
[role="button"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*
|
||||
Make sure disabled buttons don't get the pointer cursor.
|
||||
*/
|
||||
:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
|
||||
2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
|
||||
This can trigger a poorly considered lint error in some tools but is included by design.
|
||||
*/
|
||||
|
||||
img,
|
||||
svg,
|
||||
video,
|
||||
canvas,
|
||||
audio,
|
||||
iframe,
|
||||
embed,
|
||||
object {
|
||||
display: block; /* 1 */
|
||||
vertical-align: middle; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
|
||||
*/
|
||||
|
||||
img,
|
||||
video {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Make elements with the HTML hidden attribute stay hidden by default */
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
*, ::before, ::after {
|
||||
--tw-border-spacing-x: 0;
|
||||
--tw-border-spacing-y: 0;
|
||||
--tw-translate-x: 0;
|
||||
--tw-translate-y: 0;
|
||||
--tw-rotate: 0;
|
||||
--tw-skew-x: 0;
|
||||
--tw-skew-y: 0;
|
||||
--tw-scale-x: 1;
|
||||
--tw-scale-y: 1;
|
||||
--tw-pan-x: ;
|
||||
--tw-pan-y: ;
|
||||
--tw-pinch-zoom: ;
|
||||
--tw-scroll-snap-strictness: proximity;
|
||||
--tw-ordinal: ;
|
||||
--tw-slashed-zero: ;
|
||||
--tw-numeric-figure: ;
|
||||
--tw-numeric-spacing: ;
|
||||
--tw-numeric-fraction: ;
|
||||
--tw-ring-inset: ;
|
||||
--tw-ring-offset-width: 0px;
|
||||
--tw-ring-offset-color: #fff;
|
||||
--tw-ring-color: rgb(59 130 246 / 0.5);
|
||||
--tw-ring-offset-shadow: 0 0 #0000;
|
||||
--tw-ring-shadow: 0 0 #0000;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
--tw-shadow-colored: 0 0 #0000;
|
||||
--tw-blur: ;
|
||||
--tw-brightness: ;
|
||||
--tw-contrast: ;
|
||||
--tw-grayscale: ;
|
||||
--tw-hue-rotate: ;
|
||||
--tw-invert: ;
|
||||
--tw-saturate: ;
|
||||
--tw-sepia: ;
|
||||
--tw-drop-shadow: ;
|
||||
--tw-backdrop-blur: ;
|
||||
--tw-backdrop-brightness: ;
|
||||
--tw-backdrop-contrast: ;
|
||||
--tw-backdrop-grayscale: ;
|
||||
--tw-backdrop-hue-rotate: ;
|
||||
--tw-backdrop-invert: ;
|
||||
--tw-backdrop-opacity: ;
|
||||
--tw-backdrop-saturate: ;
|
||||
--tw-backdrop-sepia: ;
|
||||
}
|
||||
|
||||
::backdrop {
|
||||
--tw-border-spacing-x: 0;
|
||||
--tw-border-spacing-y: 0;
|
||||
--tw-translate-x: 0;
|
||||
--tw-translate-y: 0;
|
||||
--tw-rotate: 0;
|
||||
--tw-skew-x: 0;
|
||||
--tw-skew-y: 0;
|
||||
--tw-scale-x: 1;
|
||||
--tw-scale-y: 1;
|
||||
--tw-pan-x: ;
|
||||
--tw-pan-y: ;
|
||||
--tw-pinch-zoom: ;
|
||||
--tw-scroll-snap-strictness: proximity;
|
||||
--tw-ordinal: ;
|
||||
--tw-slashed-zero: ;
|
||||
--tw-numeric-figure: ;
|
||||
--tw-numeric-spacing: ;
|
||||
--tw-numeric-fraction: ;
|
||||
--tw-ring-inset: ;
|
||||
--tw-ring-offset-width: 0px;
|
||||
--tw-ring-offset-color: #fff;
|
||||
--tw-ring-color: rgb(59 130 246 / 0.5);
|
||||
--tw-ring-offset-shadow: 0 0 #0000;
|
||||
--tw-ring-shadow: 0 0 #0000;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
--tw-shadow-colored: 0 0 #0000;
|
||||
--tw-blur: ;
|
||||
--tw-brightness: ;
|
||||
--tw-contrast: ;
|
||||
--tw-grayscale: ;
|
||||
--tw-hue-rotate: ;
|
||||
--tw-invert: ;
|
||||
--tw-saturate: ;
|
||||
--tw-sepia: ;
|
||||
--tw-drop-shadow: ;
|
||||
--tw-backdrop-blur: ;
|
||||
--tw-backdrop-brightness: ;
|
||||
--tw-backdrop-contrast: ;
|
||||
--tw-backdrop-grayscale: ;
|
||||
--tw-backdrop-hue-rotate: ;
|
||||
--tw-backdrop-invert: ;
|
||||
--tw-backdrop-opacity: ;
|
||||
--tw-backdrop-saturate: ;
|
||||
--tw-backdrop-sepia: ;
|
||||
}
|
||||
.absolute {
|
||||
position: absolute;
|
||||
}
|
||||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
.inset-y-0 {
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
}
|
||||
.top-0 {
|
||||
top: 0px;
|
||||
}
|
||||
.right-0 {
|
||||
right: 0px;
|
||||
}
|
||||
.right-2 {
|
||||
right: 0.5rem;
|
||||
}
|
||||
.m-2 {
|
||||
margin: 0.5rem;
|
||||
}
|
||||
.m-0 {
|
||||
margin: 0px;
|
||||
}
|
||||
.-mx-2 {
|
||||
margin-left: -0.5rem;
|
||||
margin-right: -0.5rem;
|
||||
}
|
||||
.mr-2 {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
.mb-2 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.mt-1 {
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
.h-20 {
|
||||
height: 5rem;
|
||||
}
|
||||
.h-14 {
|
||||
height: 3.5rem;
|
||||
}
|
||||
.h-2 {
|
||||
height: 0.5rem;
|
||||
}
|
||||
.w-20 {
|
||||
width: 5rem;
|
||||
}
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
.flex-1 {
|
||||
flex: 1 1 0%;
|
||||
}
|
||||
.transform {
|
||||
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
||||
}
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
.gap-2 {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.divide-y > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-divide-y-reverse: 0;
|
||||
border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
|
||||
border-bottom-width: calc(1px * var(--tw-divide-y-reverse));
|
||||
}
|
||||
.overflow-hidden {
|
||||
overflow: hidden;
|
||||
}
|
||||
.rounded-full {
|
||||
border-radius: 9999px;
|
||||
}
|
||||
.rounded-md {
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
.rounded-b {
|
||||
border-bottom-right-radius: 0.25rem;
|
||||
border-bottom-left-radius: 0.25rem;
|
||||
}
|
||||
.border {
|
||||
border-width: 1px;
|
||||
}
|
||||
.bg-transparent {
|
||||
background-color: transparent;
|
||||
}
|
||||
.bg-gradient-to-br {
|
||||
background-image: linear-gradient(to bottom right, var(--tw-gradient-stops));
|
||||
}
|
||||
.from-gray-100\/80 {
|
||||
--tw-gradient-from: rgb(243 244 246 / 0.8);
|
||||
--tw-gradient-to: rgb(243 244 246 / 0);
|
||||
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
|
||||
}
|
||||
.via-white {
|
||||
--tw-gradient-to: rgb(255 255 255 / 0);
|
||||
--tw-gradient-stops: var(--tw-gradient-from), #fff, var(--tw-gradient-to);
|
||||
}
|
||||
.to-white {
|
||||
--tw-gradient-to: #fff;
|
||||
}
|
||||
.p-3 {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
.px-5 {
|
||||
padding-left: 1.25rem;
|
||||
padding-right: 1.25rem;
|
||||
}
|
||||
.py-3 {
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
}
|
||||
.px-2 {
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
.pr-8 {
|
||||
padding-right: 2rem;
|
||||
}
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
.font-sans {
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
}
|
||||
.text-sm {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
.text-xs {
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.text-gray-700 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(55 65 81 / var(--tw-text-opacity));
|
||||
}
|
||||
.text-gray-400 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(156 163 175 / var(--tw-text-opacity));
|
||||
}
|
||||
.text-gray-600 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(75 85 99 / var(--tw-text-opacity));
|
||||
}
|
||||
.text-gray-800 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(31 41 55 / var(--tw-text-opacity));
|
||||
}
|
||||
.shadow {
|
||||
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
}
|
||||
.backdrop-blur-sm {
|
||||
--tw-backdrop-blur: blur(4px);
|
||||
-webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
|
||||
backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
|
||||
}
|
||||
.transition {
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter;
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
.delay-200 {
|
||||
transition-delay: 200ms;
|
||||
}
|
||||
.duration-300 {
|
||||
transition-duration: 300ms;
|
||||
}
|
||||
|
||||
#docsgpt-answer {
|
||||
max-height: 50vh; /* 50% of the viewport height */
|
||||
overflow-y: auto; /* Adds a vertical scrollbar if the content exceeds the container height */
|
||||
}
|
||||
|
||||
.widget-container {
|
||||
position: fixed; /* fixed positioning */
|
||||
right: 10px; /* from the right edge */
|
||||
bottom: 10px; /* from the bottom edge */
|
||||
z-index: 1000; /* to ensure it appears on top of other content, if any */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@keyframes dotBounce {
|
||||
0%, 80%, 100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
40% {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
}
|
||||
|
||||
.dot-animation {
|
||||
display: inline-block;
|
||||
animation: dotBounce 1s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.delay-200 {
|
||||
animation-delay: 200ms;
|
||||
}
|
||||
|
||||
.delay-400 {
|
||||
animation-delay: 400ms;
|
||||
}
|
||||
|
||||
.white-filter {
|
||||
filter: invert(1) brightness(2);
|
||||
}
|
||||
|
||||
.hover\:bg-gray-100:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(243 244 246 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:opacity-50:hover {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.focus\:outline-none:focus {
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
.dark\:divide-gray-700 > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-divide-opacity: 1;
|
||||
border-color: rgb(55 65 81 / var(--tw-divide-opacity));
|
||||
}
|
||||
|
||||
.dark\:border-gray-700 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(55 65 81 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.dark\:from-gray-900\/80 {
|
||||
--tw-gradient-from: rgb(17 24 39 / 0.8);
|
||||
--tw-gradient-to: rgb(17 24 39 / 0);
|
||||
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
|
||||
}
|
||||
|
||||
.dark\:via-gray-900 {
|
||||
--tw-gradient-to: rgb(17 24 39 / 0);
|
||||
--tw-gradient-stops: var(--tw-gradient-from), #111827, var(--tw-gradient-to);
|
||||
}
|
||||
|
||||
.dark\:to-gray-900 {
|
||||
--tw-gradient-to: #111827;
|
||||
}
|
||||
|
||||
.dark\:text-gray-200 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(229 231 235 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:text-gray-500 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(107 114 128 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:text-white {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(255 255 255 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:hover\:bg-gray-800\/70:hover {
|
||||
background-color: rgb(31 41 55 / 0.7);
|
||||
}
|
||||
}
|
||||
1
extensions/react-widget/dist/vite.svg
vendored
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1 +0,0 @@
|
||||
export { DocsGPTWidget } from "./src/components/DocsGPTWidget";
|
||||
13025
extensions/react-widget/package-lock.json
generated
@@ -1,47 +1,65 @@
|
||||
{
|
||||
"name": "docsgpt",
|
||||
"version": "0.3.7",
|
||||
"private": false,
|
||||
"version": "0.2.4",
|
||||
"type": "module",
|
||||
"main": "dist/index.umd.js",
|
||||
"module": "dist/index.es.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.es.js",
|
||||
"require": "./dist/index.umd.js",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"./dist/style.css": "./dist/style.css"
|
||||
},
|
||||
"description": "DocsGPT 🦖 is an innovative open-source tool designed to simplify the retrieval of information from project documentation using advanced GPT models 🤖.",
|
||||
"source": "./src/index.html",
|
||||
"main": "dist/main.js",
|
||||
"module": "dist/module.js",
|
||||
"types": "dist/types.d.ts",
|
||||
"files": [
|
||||
"/dist"
|
||||
"dist",
|
||||
"package.json"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
"@parcel/resolver-default": {
|
||||
"packageExports": true
|
||||
},
|
||||
"resolution": {
|
||||
"styled-components": "^5"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"prepare": "npm run build && npm run build-css",
|
||||
"build-css": "postcss src/index.css -o dist/style.css",
|
||||
"preview": "vite preview"
|
||||
"build": "parcel build src/index.ts",
|
||||
"dev": "parcel",
|
||||
"test": "jest",
|
||||
"lint": "eslint",
|
||||
"check": "tsc --noEmit",
|
||||
"ci": "yarn build && yarn test && yarn lint && yarn check"
|
||||
},
|
||||
"dependencies": {
|
||||
"postcss-cli": "^10.1.0",
|
||||
"@babel/plugin-transform-flow-strip-types": "^7.23.3",
|
||||
"@bpmn-io/snarkdown": "^2.2.0",
|
||||
"@parcel/resolver-glob": "^2.12.0",
|
||||
"@parcel/transformer-svg-react": "^2.12.0",
|
||||
"@parcel/transformer-typescript-tsc": "^2.12.0",
|
||||
"@parcel/validator-typescript": "^2.12.0",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@types/react": "^18.2.61",
|
||||
"@types/react-dom": "^18.2.19",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.0",
|
||||
"dompurify": "^3.0.9",
|
||||
"flow-bin": "^0.229.2",
|
||||
"i": "^0.3.7",
|
||||
"install": "^0.13.0",
|
||||
"npm": "^10.5.0",
|
||||
"parcel": "^2.12.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"tailwindcss": "^3.2.4"
|
||||
"styled-components": "^6.1.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.0.26",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
"@vitejs/plugin-react-swc": "^3.0.0",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"postcss": "^8.4.31",
|
||||
"typescript": "^4.9.3",
|
||||
"vite": "^4.0.0",
|
||||
"vite-plugin-dts": "^1.7.1"
|
||||
"@babel/core": "^7.24.0",
|
||||
"@babel/preset-env": "^7.24.0",
|
||||
"@babel/preset-react": "^7.23.3",
|
||||
"@parcel/packager-ts": "^2.12.0",
|
||||
"@parcel/transformer-typescript-types": "^2.12.0",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"babel-loader": "^8.0.4",
|
||||
"process": "^0.11.10",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,5 +0,0 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import { useState } from "react";
|
||||
//import "./App.css";
|
||||
import {DocsGPTWidget} from "./components/DocsGPTWidget";
|
||||
|
||||
function App() {
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
import React from "react"
|
||||
import {DocsGPTWidget} from "./components/DocsGPTWidget"
|
||||
const App = () => {
|
||||
return (
|
||||
<div className="App">
|
||||
<DocsGPTWidget />
|
||||
<div>
|
||||
<DocsGPTWidget/>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export default App;
|
||||
export default App
|
||||
7
extensions/react-widget/src/assets/message.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg width="36" height="36" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.37891 9.44824H7.75821" stroke="white" stroke-width="1.68965" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M11.1377 9.44824H12.8273" stroke="white" stroke-width="1.68965" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4.37891 6.06934H6.06856" stroke="white" stroke-width="1.68965" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M9.44824 6.06934H12.8276" stroke="white" stroke-width="1.68965" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M16.2069 11.1379C16.2069 11.5861 16.0289 12.0158 15.712 12.3327C15.3951 12.6496 14.9654 12.8276 14.5172 12.8276H4.37931L1 16.2069V2.68965C1 2.24153 1.17802 1.81176 1.49489 1.49489C1.81176 1.17802 2.24153 1 2.68965 1H14.5172C14.9654 1 15.3951 1.17802 15.712 1.49489C16.0289 1.81176 16.2069 2.24153 16.2069 2.68965V11.1379Z" stroke="white" stroke-width="1.68965" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1009 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 4.0 KiB |
@@ -1,247 +1,460 @@
|
||||
"use client";
|
||||
import {useEffect, useRef, useState} from 'react'
|
||||
//import './style.css'
|
||||
|
||||
interface HistoryItem {
|
||||
prompt: string;
|
||||
response: string;
|
||||
import { Fragment, useEffect, useRef, useState } from 'react'
|
||||
import { PaperPlaneIcon, RocketIcon, ExclamationTriangleIcon, Cross2Icon } from '@radix-ui/react-icons';
|
||||
import { MESSAGE_TYPE } from '../models/types';
|
||||
import { Query, Status } from '../models/types';
|
||||
import MessageIcon from '../assets/message.svg'
|
||||
import { fetchAnswerStreaming } from '../requests/streamingApi';
|
||||
import styled, { keyframes, createGlobalStyle } from 'styled-components';
|
||||
import snarkdown from '@bpmn-io/snarkdown';
|
||||
import { sanitize } from 'dompurify';
|
||||
const GlobalStyles = createGlobalStyle`
|
||||
.response pre {
|
||||
padding: 8px;
|
||||
width: 90%;
|
||||
font-size: 12px;
|
||||
border-radius: 6px;
|
||||
overflow-x: auto;
|
||||
background-color: #1B1C1F;
|
||||
}
|
||||
|
||||
interface FetchAnswerStreamingProps {
|
||||
question?: string;
|
||||
apiKey?: string;
|
||||
selectedDocs?: string;
|
||||
history?: HistoryItem[];
|
||||
conversationId?: string | null;
|
||||
apiHost?: string;
|
||||
onEvent?: (event: MessageEvent) => void;
|
||||
.response h1{
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
|
||||
enum ChatStates {
|
||||
Init = 'init',
|
||||
Processing = 'processing',
|
||||
Typing = 'typing',
|
||||
Answer = 'answer',
|
||||
Minimized = 'minimized',
|
||||
.response h2{
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
function fetchAnswerStreaming({
|
||||
question = '',
|
||||
apiKey = '',
|
||||
selectedDocs = '',
|
||||
history = [],
|
||||
conversationId = null,
|
||||
apiHost = '',
|
||||
onEvent = () => {console.log("Event triggered, but no handler provided.");}
|
||||
}: FetchAnswerStreamingProps): Promise<void> {
|
||||
let docPath = 'default';
|
||||
if (selectedDocs) {
|
||||
docPath = selectedDocs;
|
||||
}
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const body = {
|
||||
question: question,
|
||||
api_key: apiKey,
|
||||
embeddings_key: apiKey,
|
||||
active_docs: docPath,
|
||||
history: JSON.stringify(history),
|
||||
conversation_id: conversationId,
|
||||
model: 'default'
|
||||
};
|
||||
|
||||
fetch(apiHost + '/stream', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.body) throw Error('No response body');
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
let counterrr = 0;
|
||||
const processStream = ({
|
||||
done,
|
||||
value,
|
||||
}: ReadableStreamReadResult<Uint8Array>) => {
|
||||
if (done) {
|
||||
console.log(counterrr);
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
counterrr += 1;
|
||||
|
||||
const chunk = decoder.decode(value);
|
||||
|
||||
const lines = chunk.split('\n');
|
||||
|
||||
for (let line of lines) {
|
||||
if (line.trim() == '') {
|
||||
continue;
|
||||
}
|
||||
if (line.startsWith('data:')) {
|
||||
line = line.substring(5);
|
||||
}
|
||||
|
||||
const messageEvent = new MessageEvent('message', {
|
||||
data: line,
|
||||
});
|
||||
|
||||
onEvent(messageEvent); // handle each message
|
||||
}
|
||||
|
||||
reader.read().then(processStream).catch(reject);
|
||||
};
|
||||
|
||||
reader.read().then(processStream).catch(reject);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Connection failed:', error);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
.response h3{
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
export const DocsGPTWidget = ({ apiHost = 'https://gptcloud.arc53.com', selectDocs = 'default', apiKey = 'docsgpt-public'}) => {
|
||||
// processing states
|
||||
const [chatState, setChatState] = useState<ChatStates>(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
return localStorage.getItem('docsGPTChatState') as ChatStates || ChatStates.Init;
|
||||
}
|
||||
return ChatStates.Init;
|
||||
});
|
||||
|
||||
const [answer, setAnswer] = useState<string>('');
|
||||
|
||||
//const selectDocs = 'local/1706.03762.pdf/'
|
||||
const answerRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (answerRef.current) {
|
||||
const element = answerRef.current;
|
||||
element.scrollTop = element.scrollHeight;
|
||||
}
|
||||
}, [answer]);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem('docsGPTChatState', chatState);
|
||||
}, [chatState]);
|
||||
|
||||
|
||||
|
||||
// submit handler
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
setAnswer('')
|
||||
e.preventDefault()
|
||||
// get question
|
||||
setChatState(ChatStates.Processing)
|
||||
setTimeout(() => {
|
||||
setChatState(ChatStates.Answer)
|
||||
}, 800)
|
||||
const inputElement = e.currentTarget[0] as HTMLInputElement;
|
||||
const questionValue = inputElement.value;
|
||||
|
||||
fetchAnswerStreaming({
|
||||
question: questionValue,
|
||||
apiKey: apiKey,
|
||||
selectedDocs: selectDocs,
|
||||
history: [],
|
||||
conversationId: null,
|
||||
apiHost: apiHost,
|
||||
onEvent: (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
|
||||
// check if the 'end' event has been received
|
||||
if (data.type === 'end') {
|
||||
setChatState(ChatStates.Answer)
|
||||
} else if (data.type === 'source') {
|
||||
// check if data.metadata exists
|
||||
let result;
|
||||
if (data.metadata && data.metadata.title) {
|
||||
const titleParts = data.metadata.title.split('/');
|
||||
result = {
|
||||
title: titleParts[titleParts.length - 1],
|
||||
text: data.doc,
|
||||
};
|
||||
} else {
|
||||
result = { title: data.doc, text: data.doc };
|
||||
}
|
||||
console.log(result)
|
||||
|
||||
} else if (data.type === 'id') {
|
||||
console.log(data.id);
|
||||
} else {
|
||||
const result = data.answer;
|
||||
// set answer by appending answer
|
||||
setAnswer(prevAnswer => prevAnswer + result);
|
||||
}
|
||||
},
|
||||
});
|
||||
.response code:not(pre code){
|
||||
border-radius: 6px;
|
||||
padding: 1px 3px 1px 3px;
|
||||
font-size: 12px;
|
||||
display: inline-block;
|
||||
background-color: #646464;
|
||||
}
|
||||
`;
|
||||
const WidgetContainer = styled.div`
|
||||
display: block;
|
||||
position: fixed;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: left;
|
||||
`;
|
||||
const StyledContainer = styled.div`
|
||||
display: block;
|
||||
position: relative;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 352px;
|
||||
height: 407px;
|
||||
max-height: 407px;
|
||||
border-radius: 0.75rem;
|
||||
background-color: #222327;
|
||||
font-family: sans-serif;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05), 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
transition: visibility 0.3s, opacity 0.3s;
|
||||
`;
|
||||
const FloatingButton = styled.div`
|
||||
position: fixed;
|
||||
display: flex;
|
||||
z-index: 500;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
bottom: 1rem;
|
||||
right: 1rem;
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
border-radius: 9999px;
|
||||
background-image: linear-gradient(to bottom right, #5AF0EC, #E80D9D);
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
transition: transform 0.2s ease-in-out;
|
||||
}
|
||||
`;
|
||||
const CancelButton = styled.button`
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
margin: 0.5rem;
|
||||
width: 30px;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
color: inherit;
|
||||
transition: opacity 0.3s ease;
|
||||
opacity: 0.6;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
.white-filter {
|
||||
filter: invert(100%);
|
||||
}
|
||||
`;
|
||||
|
||||
const Header = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-inline: 0.75rem;
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 0.5rem;
|
||||
`;
|
||||
|
||||
const IconWrapper = styled.div`
|
||||
padding: 0.5rem;
|
||||
`;
|
||||
|
||||
const ContentWrapper = styled.div`
|
||||
flex: 1;
|
||||
margin-left: 0.5rem;
|
||||
`;
|
||||
|
||||
const Title = styled.h3`
|
||||
font-size: 1rem;
|
||||
font-weight: normal;
|
||||
color: #FAFAFA;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.25rem;
|
||||
`;
|
||||
|
||||
const Description = styled.p`
|
||||
font-size: 0.85rem;
|
||||
color: #A1A1AA;
|
||||
margin-top: 0;
|
||||
`;
|
||||
const Conversation = styled.div`
|
||||
height: 16rem;
|
||||
padding-inline: 0.5rem;
|
||||
border-radius: 0.375rem;
|
||||
text-align: left;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #4a4a4a transparent; /* thumb color track color */
|
||||
`;
|
||||
|
||||
const MessageBubble = styled.div<{ type: MESSAGE_TYPE }>`
|
||||
display: flex;
|
||||
font-size: 16px;
|
||||
justify-content: ${props => props.type === 'QUESTION' ? 'flex-end' : 'flex-start'};
|
||||
margin: 0.5rem;
|
||||
`;
|
||||
const Message = styled.p<{ type: MESSAGE_TYPE }>`
|
||||
background: ${props => props.type === 'QUESTION' ?
|
||||
'linear-gradient(to bottom right, #8860DB, #6D42C5)' :
|
||||
'#38383b'};
|
||||
color: #ffff;
|
||||
border: none;
|
||||
max-width: 80%;
|
||||
overflow: auto;
|
||||
margin: 4px;
|
||||
display: block;
|
||||
line-height: 1.5;
|
||||
padding: 0.75rem;
|
||||
border-radius: 0.375rem;
|
||||
`;
|
||||
const ErrorAlert = styled.div`
|
||||
color: #b91c1c;
|
||||
border:0.1px solid #b91c1c;
|
||||
display: flex;
|
||||
padding:4px;
|
||||
margin:0.7rem;
|
||||
opacity: 90%;
|
||||
max-width: 70%;
|
||||
font-weight: 400;
|
||||
border-radius: 0.375rem;
|
||||
justify-content: space-evenly;
|
||||
`
|
||||
//dot loading animation
|
||||
const dotBounce = keyframes`
|
||||
0%, 80%, 100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
40% {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
`;
|
||||
|
||||
const DotAnimation = styled.div`
|
||||
display: inline-block;
|
||||
animation: ${dotBounce} 1s infinite ease-in-out;
|
||||
`;
|
||||
// delay classes as styled components
|
||||
const Delay = styled(DotAnimation) <{ delay: number }>`
|
||||
animation-delay: ${props => props.delay + 'ms'};
|
||||
`;
|
||||
const PromptContainer = styled.form`
|
||||
background-color: transparent;
|
||||
height: 36px;
|
||||
position: absolute;
|
||||
bottom: 25px;
|
||||
left: 24px;
|
||||
right: 24px;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
`;
|
||||
const StyledInput = styled.input`
|
||||
width: 260px;
|
||||
height: 36px;
|
||||
border: 1px solid #686877;
|
||||
padding-left: 12px;
|
||||
background-color: transparent;
|
||||
font-size: 16px;
|
||||
border-radius: 6px;
|
||||
color: #ffff;
|
||||
outline: none;
|
||||
`;
|
||||
const StyledButton = styled.button`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-image: linear-gradient(to bottom right, #5AF0EC, #E80D9D);
|
||||
border-radius: 6px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
margin-left:8px;
|
||||
padding: 0px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
&:hover{
|
||||
opacity: 90%;
|
||||
}
|
||||
&:disabled {
|
||||
opacity: 60%;
|
||||
}`;
|
||||
const HeroContainer = styled.div`
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: middle;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 80%;
|
||||
background-image: linear-gradient(to bottom right, #5AF0EC, #ff1bf4);
|
||||
border-radius: 10px;
|
||||
margin: 0 auto;
|
||||
padding: 2px;
|
||||
`;
|
||||
const HeroWrapper = styled.div`
|
||||
background-color: #222327;
|
||||
border-radius: 10px;
|
||||
font-weight: normal;
|
||||
padding: 6px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
`
|
||||
const HeroTitle = styled.h3`
|
||||
color: #fff;
|
||||
font-size: 17px;
|
||||
margin-bottom: 5px;
|
||||
padding: 2px;
|
||||
`;
|
||||
const HeroDescription = styled.p`
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
`;
|
||||
const Hero = ({ title, description }: { title: string, description: string }) => {
|
||||
return (
|
||||
<>
|
||||
<div className="dark widget-container">
|
||||
<div onClick={() => setChatState(ChatStates.Init)}
|
||||
className={`${chatState !== 'minimized' ? 'hidden' : ''} cursor-pointer`}>
|
||||
<div className="mr-2 mb-2 w-20 h-20 rounded-full overflow-hidden dark:divide-gray-700 border dark:border-gray-700 bg-gradient-to-br from-gray-100/80 via-white to-white dark:from-gray-900/80 dark:via-gray-900 dark:to-gray-900 font-sans shadow backdrop-blur-sm flex items-center justify-center">
|
||||
<img
|
||||
src="https://d3dg1063dc54p9.cloudfront.net/cute-docsgpt.png"
|
||||
alt="DocsGPT"
|
||||
className="cursor-pointer hover:opacity-50 h-14"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={` ${chatState !== 'minimized' ? '' : 'hidden'} divide-y dark:divide-gray-700 rounded-md border dark:border-gray-700 bg-gradient-to-br from-gray-100/80 via-white to-white dark:from-gray-900/80 dark:via-gray-900 dark:to-gray-900 font-sans shadow backdrop-blur-sm`} style={{ width: '18rem', transform: 'translateY(0%) translateZ(0px)' }}>
|
||||
<div>
|
||||
<img
|
||||
src="https://d3dg1063dc54p9.cloudfront.net/exit.svg"
|
||||
alt="Exit"
|
||||
className="cursor-pointer hover:opacity-50 h-2 absolute top-0 right-0 m-2 white-filter"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
setChatState(ChatStates.Minimized);
|
||||
}}
|
||||
/>
|
||||
<div className="flex items-center gap-2 p-3">
|
||||
<div className={`${chatState === 'init' ? '' :
|
||||
chatState === 'processing' ? '' :
|
||||
chatState === 'typing' ? '' :
|
||||
'hidden'} flex-1`}>
|
||||
<h3 className="text-sm font-bold text-gray-700 dark:text-gray-200">Need help with documentation?</h3>
|
||||
<p className="mt-1 text-xs text-gray-400 dark:text-gray-500">DocsGPT AI assistant will help you with docs</p>
|
||||
</div>
|
||||
<div id="docsgpt-answer" ref={answerRef} className={`${chatState !== 'answer' ? 'hidden' : ''}`}>
|
||||
<p className="mt-1 text-sm text-gray-600 dark:text-white text-left">{answer}</p>
|
||||
</div>
|
||||
<HeroContainer>
|
||||
<HeroWrapper>
|
||||
<IconWrapper style={{ marginTop: '8px' }}>
|
||||
<RocketIcon color='white' width={20} height={20} />
|
||||
</IconWrapper>
|
||||
<div>
|
||||
<HeroTitle>{title}</HeroTitle>
|
||||
<HeroDescription>
|
||||
{description}
|
||||
</HeroDescription>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<button onClick={() => setChatState(ChatStates.Typing)}
|
||||
className={`flex w-full justify-center px-5 py-3 text-sm text-gray-800 font-bold dark:text-white transition duration-300 hover:bg-gray-100 rounded-b dark:hover:bg-gray-800/70 ${chatState !== 'init' ? 'hidden' : ''}`}>
|
||||
Ask DocsGPT
|
||||
</button>
|
||||
{ (chatState === 'typing' || chatState === 'answer') && (
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className="relative w-full m-0" style={{ opacity: 1 }}>
|
||||
<input type="text"
|
||||
className="w-full bg-transparent px-5 py-3 pr-8 text-sm text-gray-700 dark:text-white focus:outline-none" placeholder="What do you want to do?" />
|
||||
<button className="absolute text-gray-400 dark:text-gray-500 text-sm inset-y-0 right-2 -mx-2 px-2" type="submit" >Submit</button>
|
||||
</form>
|
||||
)}
|
||||
<p className={`${chatState !== 'processing' ? 'hidden' : ''} flex w-full justify-center px-5 py-3 text-sm text-gray-800 font-bold dark:text-white transition duration-300 rounded-b`}>
|
||||
Processing<span className="dot-animation">.</span><span className="dot-animation delay-200">.</span><span className="dot-animation delay-400">.</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</HeroWrapper>
|
||||
</HeroContainer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export const DocsGPTWidget = ({
|
||||
apiHost = 'https://gptcloud.arc53.com',
|
||||
selectDocs = 'default',
|
||||
apiKey = 'docsgpt-public',
|
||||
avatar = 'https://d3dg1063dc54p9.cloudfront.net/cute-docsgpt.png',
|
||||
title = 'Get AI assistance',
|
||||
description = 'DocsGPT\'s AI Chatbot is here to help',
|
||||
heroTitle = 'Welcome to DocsGPT !',
|
||||
heroDescription = 'This chatbot is built with DocsGPT and utilises GenAI, please review important information using sources.'
|
||||
}) => {
|
||||
|
||||
const [prompt, setPrompt] = useState('');
|
||||
const [status, setStatus] = useState<Status>('idle');
|
||||
const [queries, setQueries] = useState<Query[]>([])
|
||||
const [conversationId, setConversationId] = useState<string | null>(null)
|
||||
const [open, setOpen] = useState<boolean>(false)
|
||||
const [eventInterrupt, setEventInterrupt] = useState<boolean>(false); //click or scroll by user while autoScrolling
|
||||
const endMessageRef = useRef<HTMLDivElement | null>(null);
|
||||
const handleUserInterrupt = () => {
|
||||
(status === 'loading') && setEventInterrupt(true);
|
||||
}
|
||||
const scrollToBottom = (element: Element | null) => {
|
||||
//recursive function to scroll to the last child of the last child ...
|
||||
// to get to the bottom most element
|
||||
if (!element) return;
|
||||
if (element?.children.length === 0) {
|
||||
element?.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start',
|
||||
});
|
||||
}
|
||||
const lastChild = element?.children?.[element.children.length - 1]
|
||||
lastChild && scrollToBottom(lastChild)
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
!eventInterrupt && scrollToBottom(endMessageRef.current);
|
||||
}, [queries.length, queries[queries.length - 1]?.response]);
|
||||
|
||||
async function stream(question: string) {
|
||||
setStatus('loading')
|
||||
try {
|
||||
await fetchAnswerStreaming(
|
||||
{
|
||||
question: question,
|
||||
apiKey: apiKey,
|
||||
apiHost: apiHost,
|
||||
selectedDocs: selectDocs,
|
||||
history: queries,
|
||||
conversationId: conversationId,
|
||||
onEvent: (event: MessageEvent) => {
|
||||
const data = JSON.parse(event.data);
|
||||
// check if the 'end' event has been received
|
||||
if (data.type === 'end') {
|
||||
// set status to 'idle'
|
||||
setStatus('idle');
|
||||
|
||||
} else if (data.type === 'id') {
|
||||
setConversationId(data.id)
|
||||
} else {
|
||||
const result = data.answer;
|
||||
const streamingResponse = queries[queries.length - 1].response ? queries[queries.length - 1].response : '';
|
||||
const updatedQueries = [...queries];
|
||||
updatedQueries[updatedQueries.length - 1].response = streamingResponse + result;
|
||||
setQueries(updatedQueries);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
const updatedQueries = [...queries];
|
||||
updatedQueries[updatedQueries.length - 1].error = 'error'
|
||||
setQueries(updatedQueries);
|
||||
setStatus('idle')
|
||||
//setEventInterrupt(false)
|
||||
}
|
||||
|
||||
}
|
||||
// submit handler
|
||||
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault()
|
||||
setEventInterrupt(false);
|
||||
queries.push({ prompt })
|
||||
setPrompt('')
|
||||
await stream(prompt)
|
||||
}
|
||||
const handleImageError = (event: React.SyntheticEvent<HTMLImageElement, Event>) => {
|
||||
event.currentTarget.src = "https://d3dg1063dc54p9.cloudfront.net/cute-docsgpt.png";
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<WidgetContainer>
|
||||
<GlobalStyles />
|
||||
{!open && <FloatingButton onClick={() => setOpen(true)} hidden={open}>
|
||||
<MessageIcon style={{ marginTop: '8px' }} />
|
||||
</FloatingButton>}
|
||||
{open && <StyledContainer>
|
||||
<div>
|
||||
<CancelButton onClick={() => setOpen(false)}>
|
||||
<Cross2Icon width={24} height={24} color='white' />
|
||||
</CancelButton>
|
||||
<Header>
|
||||
<IconWrapper>
|
||||
<img style={{ maxWidth: "42px", maxHeight: "42px" }} onError={handleImageError} src={avatar} alt='docs-gpt' />
|
||||
</IconWrapper>
|
||||
<ContentWrapper>
|
||||
<Title>{title}</Title>
|
||||
<Description>{description}</Description>
|
||||
</ContentWrapper>
|
||||
</Header>
|
||||
</div>
|
||||
<Conversation onWheel={handleUserInterrupt} onTouchMove={handleUserInterrupt}>
|
||||
{
|
||||
queries.length > 0 ? queries?.map((query, index) => {
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
{
|
||||
query.prompt && <MessageBubble type='QUESTION'>
|
||||
<Message
|
||||
type='QUESTION'
|
||||
ref={(!(query.response || query.error) && index === queries.length - 1) ? endMessageRef : null}>
|
||||
{query.prompt}
|
||||
</Message>
|
||||
</MessageBubble>
|
||||
}
|
||||
{
|
||||
query.response ? <MessageBubble type='ANSWER'>
|
||||
<Message
|
||||
type='ANSWER'
|
||||
ref={(index === queries.length - 1) ? endMessageRef : null}
|
||||
>
|
||||
<div className="response" dangerouslySetInnerHTML={{ __html: sanitize(snarkdown(query.response)) }} />
|
||||
</Message>
|
||||
</MessageBubble>
|
||||
: <div>
|
||||
{
|
||||
query.error ? <ErrorAlert>
|
||||
<IconWrapper>
|
||||
<ExclamationTriangleIcon style={{ marginTop: '4px' }} width={22} height={22} color='#b91c1c' />
|
||||
</IconWrapper>
|
||||
<div>
|
||||
<h5 style={{ margin: 2 }}>Network Error</h5>
|
||||
<span style={{ margin: 2, fontSize: '13px' }}>Something went wrong !</span>
|
||||
</div>
|
||||
</ErrorAlert>
|
||||
: <MessageBubble type='ANSWER'>
|
||||
<Message type='ANSWER' style={{ fontWeight: 600 }}>
|
||||
<DotAnimation>.</DotAnimation>
|
||||
<Delay delay={200}>.</Delay>
|
||||
<Delay delay={400}>.</Delay>
|
||||
</Message>
|
||||
</MessageBubble>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</Fragment>)
|
||||
})
|
||||
: <Hero title={heroTitle} description={heroDescription} />
|
||||
}
|
||||
</Conversation>
|
||||
|
||||
<PromptContainer
|
||||
onSubmit={handleSubmit}>
|
||||
<StyledInput
|
||||
value={prompt} onChange={(event) => setPrompt(event.target.value)}
|
||||
type='text' placeholder="What do you want to do?" />
|
||||
<StyledButton
|
||||
disabled={prompt.length == 0 || status !== 'idle'}>
|
||||
<PaperPlaneIcon width={15} height={15} color='white' />
|
||||
</StyledButton>
|
||||
</PromptContainer>
|
||||
</StyledContainer>}
|
||||
</WidgetContainer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export { DocsGPTWidget } from "./DocsGPTWidget";
|
||||
@@ -1,44 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
#docsgpt-answer {
|
||||
max-height: 50vh; /* 50% of the viewport height */
|
||||
overflow-y: auto; /* Adds a vertical scrollbar if the content exceeds the container height */
|
||||
}
|
||||
|
||||
.widget-container {
|
||||
position: fixed; /* fixed positioning */
|
||||
right: 10px; /* from the right edge */
|
||||
bottom: 10px; /* from the bottom edge */
|
||||
z-index: 1000; /* to ensure it appears on top of other content, if any */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@keyframes dotBounce {
|
||||
0%, 80%, 100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
40% {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
}
|
||||
|
||||
.dot-animation {
|
||||
display: inline-block;
|
||||
animation: dotBounce 1s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.delay-200 {
|
||||
animation-delay: 200ms;
|
||||
}
|
||||
|
||||
.delay-400 {
|
||||
animation-delay: 400ms;
|
||||
}
|
||||
|
||||
.white-filter {
|
||||
filter: invert(1) brightness(2);
|
||||
}
|
||||
@@ -2,12 +2,12 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React + TS</title>
|
||||
<title>DocsGPT Widget</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
1
extensions/react-widget/src/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { DocsGPTWidget } from "./components/DocsGPTWidget";
|
||||
@@ -1,10 +1,6 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App'
|
||||
import './index.css'
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import App from './App.tsx';
|
||||
import React from 'react';
|
||||
const root = createRoot(document.getElementById('app') as HTMLElement);
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
)
|
||||
root.render(<App />);
|
||||
|
||||
92
extensions/react-widget/src/requests/streamingApi.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
interface HistoryItem {
|
||||
prompt: string;
|
||||
response?: string;
|
||||
}
|
||||
interface FetchAnswerStreamingProps {
|
||||
question?: string;
|
||||
apiKey?: string;
|
||||
selectedDocs?: string;
|
||||
history?: HistoryItem[];
|
||||
conversationId?: string | null;
|
||||
apiHost?: string;
|
||||
onEvent?: (event: MessageEvent) => void;
|
||||
}
|
||||
export function fetchAnswerStreaming({
|
||||
question = '',
|
||||
apiKey = '',
|
||||
selectedDocs = '',
|
||||
history = [],
|
||||
conversationId = null,
|
||||
apiHost = '',
|
||||
onEvent = () => {console.log("Event triggered, but no handler provided.");}
|
||||
}: FetchAnswerStreamingProps): Promise<void> {
|
||||
let docPath = 'default';
|
||||
if (selectedDocs) {
|
||||
docPath = selectedDocs;
|
||||
}
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const body = {
|
||||
question: question,
|
||||
api_key: apiKey,
|
||||
embeddings_key: apiKey,
|
||||
active_docs: docPath,
|
||||
history: JSON.stringify(history),
|
||||
conversation_id: conversationId,
|
||||
model: 'default'
|
||||
};
|
||||
|
||||
fetch(apiHost + '/stream', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.body) throw Error('No response body');
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
let counterrr = 0;
|
||||
const processStream = ({
|
||||
done,
|
||||
value,
|
||||
}: ReadableStreamReadResult<Uint8Array>) => {
|
||||
if (done) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
counterrr += 1;
|
||||
|
||||
const chunk = decoder.decode(value);
|
||||
|
||||
const lines = chunk.split('\n');
|
||||
|
||||
for (let line of lines) {
|
||||
if (line.trim() == '') {
|
||||
continue;
|
||||
}
|
||||
if (line.startsWith('data:')) {
|
||||
line = line.substring(5);
|
||||
}
|
||||
|
||||
const messageEvent = new MessageEvent('message', {
|
||||
data: line,
|
||||
});
|
||||
|
||||
onEvent(messageEvent); // handle each message
|
||||
}
|
||||
|
||||
reader.read().then(processStream).catch(reject);
|
||||
};
|
||||
|
||||
reader.read().then(processStream).catch(reject);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Connection failed:', error);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
1
extensions/react-widget/src/vite-env.d.ts
vendored
@@ -1 +0,0 @@
|
||||
/// <reference types="vite/client" />
|
||||
@@ -1,8 +0,0 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
@@ -1,23 +1,34 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"allowJs": false,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"declaration": true,
|
||||
"typeRoots": ["./dist/index.d.ts", "node_modules/@types"],
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["src", "./index.ts"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*", "@/*"]
|
||||
},
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
/* The "typeRoots" configuration specifies the locations where
|
||||
TypeScript looks for type definitions (.d.ts files) to
|
||||
include in the compilation process.*/
|
||||
"typeRoots": ["./dist/index.d.ts", "node_modules/@types"]
|
||||
},
|
||||
/* include /index.ts*/
|
||||
"include": ["src/index.ts","custom.d.ts"],
|
||||
"exclude": ["node_modules"],
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react-swc";
|
||||
import dts from "vite-plugin-dts";
|
||||
import path from "path";
|
||||
|
||||
export default defineConfig({
|
||||
build: {
|
||||
lib: {
|
||||
entry: path.resolve(__dirname, "index.ts"),
|
||||
name: "ViteButton",
|
||||
fileName: (format) => `index.${format}.js`,
|
||||
},
|
||||
rollupOptions: {
|
||||
external: ["react", "react-dom"],
|
||||
output: {
|
||||
globals: {
|
||||
react: "React",
|
||||
"react-dom": "ReactDOM",
|
||||
},
|
||||
},
|
||||
},
|
||||
sourcemap: true,
|
||||
emptyOutDir: true,
|
||||
},
|
||||
plugins: [react(), dts()],
|
||||
});
|
||||
1014
frontend/package-lock.json
generated
@@ -37,7 +37,7 @@
|
||||
"@types/react-syntax-highlighter": "^15.5.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.51.0",
|
||||
"@typescript-eslint/parser": "^5.51.0",
|
||||
"@vitejs/plugin-react": "^3.1.0",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"eslint": "^8.33.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
@@ -55,7 +55,7 @@
|
||||
"prettier-plugin-tailwindcss": "^0.2.2",
|
||||
"tailwindcss": "^3.2.4",
|
||||
"typescript": "^4.9.5",
|
||||
"vite": "^4.5.0",
|
||||
"vite-plugin-svgr": "^2.4.0"
|
||||
"vite": "^5.0.12",
|
||||
"vite-plugin-svgr": "^4.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
7
frontend/public/lock-dark.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="33" height="33" viewBox="0 0 33 33" fill="none">
|
||||
<path d="M8.25 13.75V11C8.25 6.44875 9.625 2.75 16.5 2.75C23.375 2.75 24.75 6.44875 24.75 11V13.75" stroke="#ECECF1" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M23.375 30.25H9.625C4.125 30.25 2.75 28.875 2.75 23.375V20.625C2.75 15.125 4.125 13.75 9.625 13.75H23.375C28.875 13.75 30.25 15.125 30.25 20.625V23.375C30.25 28.875 28.875 30.25 23.375 30.25Z" stroke="#ECECF1" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M21.9951 22H22.0075" stroke="#ECECF1" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M16.4938 22H16.5061" stroke="#ECECF1" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10.9924 22H11.0048" stroke="#ECECF1" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 919 B |
6
frontend/public/message-programming-dark.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36" fill="none">
|
||||
<path d="M12.75 28.4551H12C6 28.4551 3 26.9551 3 19.4551V11.9551C3 5.95508 6 2.95508 12 2.95508H24C30 2.95508 33 5.95508 33 11.9551V19.4551C33 25.4551 30 28.4551 24 28.4551H23.25C22.785 28.4551 22.335 28.6801 22.05 29.0551L19.8 32.0551C18.81 33.3751 17.19 33.3751 16.2 32.0551L13.95 29.0551C13.71 28.7251 13.17 28.4551 12.75 28.4551Z" stroke="#ECECF1" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 13.05L9 16.05L12 19.05" stroke="#ECECF1" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M24 13.05L27 16.05L24 19.05" stroke="#ECECF1" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M19.5 12.5549L16.5 19.545" stroke="#ECECF1" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 980 B |
@@ -1,6 +1,10 @@
|
||||
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.75 28.455H12C6 28.455 3 26.955 3 19.455V11.955C3 5.95496 6 2.95496 12 2.95496H24C30 2.95496 33 5.95496 33 11.955V19.455C33 25.455 30 28.455 24 28.455H23.25C22.785 28.455 22.335 28.68 22.05 29.055L19.8 32.055C18.81 33.375 17.19 33.375 16.2 32.055L13.95 29.055C13.71 28.725 13.17 28.455 12.75 28.455Z" stroke="#3E3434" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 13.05L9 16.05L12 19.05" stroke="#3E3434" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M24 13.05L27 16.05L24 19.05" stroke="#3E3434" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M19.5 12.5549L16.5 19.545" stroke="#3E3434" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<g id="vuesax/linear/message-programming">
|
||||
<g id="message-programming">
|
||||
<path id="Vector" d="M12.75 28.455H12C6 28.455 3 26.955 3 19.455V11.955C3 5.95496 6 2.95496 12 2.95496H24C30 2.95496 33 5.95496 33 11.955V19.455C33 25.455 30 28.455 24 28.455H23.25C22.785 28.455 22.335 28.68 22.05 29.055L19.8 32.055C18.81 33.375 17.19 33.375 16.2 32.055L13.95 29.055C13.71 28.725 13.17 28.455 12.75 28.455Z" stroke="#3E3434" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Vector_2" d="M12 13.05L9 16.05L12 19.05" stroke="#3E3434" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Vector_3" d="M24 13.05L27 16.05L24 19.05" stroke="#3E3434" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Vector_4" d="M19.5 12.555L16.5 19.545" stroke="#3E3434" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 951 B After Width: | Height: | Size: 1.1 KiB |
5
frontend/public/message-text-dark.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="29" height="29" viewBox="0 0 29 29" fill="none">
|
||||
<path d="M10.2708 22.9584H9.66663C4.83329 22.9584 2.41663 21.7501 2.41663 15.7084V9.66675C2.41663 4.83341 4.83329 2.41675 9.66663 2.41675H19.3333C24.1666 2.41675 26.5833 4.83341 26.5833 9.66675V15.7084C26.5833 20.5417 24.1666 22.9584 19.3333 22.9584H18.7291C18.3545 22.9584 17.992 23.1397 17.7625 23.4417L15.95 25.8584C15.1525 26.9217 13.8475 26.9217 13.05 25.8584L11.2375 23.4417C11.0441 23.1759 10.597 22.9584 10.2708 22.9584Z" stroke="#ECECF1" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M8.45837 9.66675H20.5417" stroke="#ECECF1" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M8.45837 15.7083H15.7084" stroke="#ECECF1" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 880 B |
@@ -5,7 +5,7 @@ import DocsGPT3 from './assets/cute_docsgpt3.svg';
|
||||
export default function About() {
|
||||
return (
|
||||
<div className="mx-5 grid min-h-screen md:mx-36">
|
||||
<article className="place-items-left mx-auto my-auto flex w-full max-w-6xl flex-col gap-4 rounded-3xl bg-gray-100 p-6 text-jet lg:p-6 xl:p-10">
|
||||
<article className="place-items-left mx-auto my-auto flex w-full max-w-6xl flex-col gap-4 rounded-3xl bg-gray-100 p-6 text-jet dark:bg-gun-metal dark:text-bright-gray lg:p-6 xl:p-10">
|
||||
<div className="flex items-center">
|
||||
<p className="mr-2 text-3xl">About DocsGPT</p>
|
||||
<img className="h14 mb-2" src={DocsGPT3} alt="DocsGPT" />
|
||||
@@ -31,23 +31,34 @@ export default function About() {
|
||||
</p>
|
||||
<p className="mt-4 ml-2">
|
||||
1. Navigate to{' '}
|
||||
<span className="bg-gray-200 italic"> /application</span> folder
|
||||
<span className="bg-gray-200 italic dark:bg-outer-space">
|
||||
{' '}
|
||||
/application
|
||||
</span>{' '}
|
||||
folder
|
||||
</p>
|
||||
<p className="mt-4 ml-2">
|
||||
2. Install dependencies from{' '}
|
||||
<span className="bg-gray-200 italic">
|
||||
<span className="bg-gray-200 italic dark:bg-outer-space">
|
||||
pip install -r requirements.txt
|
||||
</span>
|
||||
</p>
|
||||
<p className="mt-4 ml-2">
|
||||
3. Prepare a <span className="bg-gray-200 italic">.env</span> file.
|
||||
Copy <span className="bg-gray-200 italic">.env_sample</span> and
|
||||
create <span className="bg-gray-200 italic">.env</span> with your
|
||||
OpenAI API token
|
||||
3. Prepare a{' '}
|
||||
<span className="bg-gray-200 italic dark:bg-outer-space">.env</span>{' '}
|
||||
file. Copy{' '}
|
||||
<span className="bg-gray-200 italic dark:bg-outer-space">
|
||||
.env_sample
|
||||
</span>{' '}
|
||||
and create{' '}
|
||||
<span className="bg-gray-200 italic dark:bg-outer-space">.env</span>{' '}
|
||||
with your OpenAI API token
|
||||
</p>
|
||||
<p className="mt-4 ml-2">
|
||||
4. Run the app with{' '}
|
||||
<span className="bg-gray-200 italic">python app.py</span>
|
||||
<span className="bg-gray-200 italic dark:bg-outer-space">
|
||||
python app.py
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -13,9 +13,8 @@ inject();
|
||||
export default function App() {
|
||||
const { isMobile } = useMediaQuery();
|
||||
const [navOpen, setNavOpen] = useState(!isMobile);
|
||||
|
||||
return (
|
||||
<div className="min-h-full min-w-full">
|
||||
<div className="min-h-full min-w-full dark:bg-raisin-black">
|
||||
<Navigation navOpen={navOpen} setNavOpen={setNavOpen} />
|
||||
<div
|
||||
className={`transition-all duration-200 ${
|
||||
|
||||
@@ -1,33 +1,38 @@
|
||||
import { useMediaQuery } from './hooks';
|
||||
import { useDarkTheme, useMediaQuery } from './hooks';
|
||||
import DocsGPT3 from './assets/cute_docsgpt3.svg';
|
||||
|
||||
export default function Hero({ className = '' }: { className?: string }) {
|
||||
// const isMobile = window.innerWidth <= 768;
|
||||
const { isMobile } = useMediaQuery();
|
||||
const [isDarkTheme] = useDarkTheme();
|
||||
return (
|
||||
<div className={`mt-14 ${isMobile ? 'mb-2' : 'mb-12'}flex flex-col `}>
|
||||
<div
|
||||
className={`mt-14 ${
|
||||
isMobile ? 'mb-2' : 'mb-12'
|
||||
} flex flex-col text-black-1000 dark:text-bright-gray`}
|
||||
>
|
||||
<div className=" mb-2 flex items-center justify-center sm:mb-10">
|
||||
<p className="mr-2 text-4xl font-semibold">DocsGPT</p>
|
||||
<img className="mb-2 h-14" src={DocsGPT3} alt="DocsGPT" />
|
||||
</div>
|
||||
{isMobile ? (
|
||||
<p className="mb-3 text-center leading-6 text-black-1000">
|
||||
Welcome to <span className="font-bold ">DocsGPT</span>, your technical
|
||||
<p className="mb-3 text-center leading-6">
|
||||
Welcome to <span className="font-bold">DocsGPT</span>, your technical
|
||||
documentation assistant! Start by entering your query in the input
|
||||
field below, and we'll provide you with the most relevant
|
||||
answers.
|
||||
</p>
|
||||
) : (
|
||||
<>
|
||||
<p className="mb-3 text-center leading-6 text-black-1000">
|
||||
<p className="mb-3 text-center leading-6">
|
||||
Welcome to DocsGPT, your technical documentation assistant!
|
||||
</p>
|
||||
<p className="mb-3 text-center leading-6 text-black-1000">
|
||||
<p className="mb-3 text-center leading-6">
|
||||
Enter a query related to the information in the documentation you
|
||||
selected to receive
|
||||
<br /> and we will provide you with the most relevant answers.
|
||||
</p>
|
||||
<p className="mb-3 text-center leading-6 text-black-1000">
|
||||
<p className="mb-3 text-center leading-6">
|
||||
Start by entering your query in the input field below and we will do
|
||||
the rest!
|
||||
</p>
|
||||
@@ -39,9 +44,9 @@ export default function Hero({ className = '' }: { className?: string }) {
|
||||
} flex flex-wrap items-center justify-center gap-2 sm:gap-1 md:gap-0`}
|
||||
>
|
||||
{/* first */}
|
||||
<div className=" rounded-[50px] bg-gradient-to-l from-[#6EE7B7]/70 via-[#3B82F6] to-[#9333EA]/50 p-1 md:rounded-tr-none md:rounded-br-none">
|
||||
<div className="h-auto rounded-[50px] bg-gradient-to-l from-[#6EE7B7]/70 via-[#3B82F6] to-[#9333EA]/50 p-1 dark:from-[#D16FF8] dark:via-[#48E6E0] dark:to-[#C85EF6] md:h-60 md:rounded-tr-none md:rounded-br-none">
|
||||
<div
|
||||
className={`h-full rounded-[45px] bg-white p-${
|
||||
className={`h-full rounded-[45px] bg-white dark:bg-dark-charcoal p-${
|
||||
isMobile ? '3.5' : '6 py-8'
|
||||
} md:rounded-tr-none md:rounded-br-none`}
|
||||
>
|
||||
@@ -49,7 +54,9 @@ export default function Hero({ className = '' }: { className?: string }) {
|
||||
{isMobile ? (
|
||||
<div className="flex justify-center">
|
||||
<img
|
||||
src="/message-text.svg"
|
||||
src={
|
||||
isDarkTheme ? '/message-text-dark.svg' : '/message-text.svg'
|
||||
}
|
||||
alt="lock"
|
||||
className="h-[24px] w-[24px] "
|
||||
/>
|
||||
@@ -60,7 +67,9 @@ export default function Hero({ className = '' }: { className?: string }) {
|
||||
) : (
|
||||
<>
|
||||
<img
|
||||
src="/message-text.svg"
|
||||
src={
|
||||
isDarkTheme ? '/message-text-dark.svg' : '/message-text.svg'
|
||||
}
|
||||
alt="lock"
|
||||
className="h-[24px] w-[24px]"
|
||||
/>
|
||||
@@ -72,8 +81,8 @@ export default function Hero({ className = '' }: { className?: string }) {
|
||||
<p
|
||||
className={
|
||||
isMobile
|
||||
? `w-[250px] text-center text-xs text-gray-500`
|
||||
: `w-[250px] text-xs text-gray-500`
|
||||
? `w-[250px] text-center text-xs text-gray-500 dark:text-bright-gray`
|
||||
: `w-[250px] text-xs text-gray-500 dark:text-bright-gray`
|
||||
}
|
||||
>
|
||||
DocsGPT will use your data to answer questions. Whether its
|
||||
@@ -84,23 +93,31 @@ export default function Hero({ className = '' }: { className?: string }) {
|
||||
</div>
|
||||
</div>
|
||||
{/* second */}
|
||||
<div className=" rounded-[50px] bg-gradient-to-r from-[#6EE7B7]/70 via-[#3B82F6] to-[#9333EA]/50 p-1 md:rounded-none md:py-1 md:px-0">
|
||||
<div className="h-auto rounded-[50px] bg-gradient-to-r from-[#6EE7B7]/70 via-[#3B82F6] to-[#9333EA]/50 p-1 dark:from-[#D16FF8] dark:via-[#48E6E0] dark:to-[#C85EF6] md:h-60 md:rounded-none md:py-1 md:px-0">
|
||||
<div
|
||||
className={`rounded-[45px] bg-white p-${
|
||||
className={`h-full rounded-[45px] bg-white dark:bg-dark-charcoal p-${
|
||||
isMobile ? '3.5' : '6 py-6'
|
||||
} md:rounded-none`}
|
||||
>
|
||||
{/* Add Mobile check here */}
|
||||
{isMobile ? (
|
||||
<div className="flex justify-center">
|
||||
<img src="/lock.svg" alt="lock" className="h-[24px] w-[24px]" />
|
||||
<div className="flex justify-center ">
|
||||
<img
|
||||
src={isDarkTheme ? '/lock-dark.svg' : '/lock.svg'}
|
||||
alt="lock"
|
||||
className="h-[24px] w-[24px]"
|
||||
/>
|
||||
<h2 className="mb-0 pl-1 text-lg font-bold">
|
||||
Secure Data Storage
|
||||
</h2>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<img src="/lock.svg" alt="lock" className="h-[24px] w-[24px]" />
|
||||
<img
|
||||
src={isDarkTheme ? '/lock-dark.svg' : '/lock.svg'}
|
||||
alt="lock"
|
||||
className="h-[24px] w-[24px]"
|
||||
/>
|
||||
<h2 className="mt-2 mb-3 text-lg font-bold">
|
||||
Secure Data Storage
|
||||
</h2>
|
||||
@@ -109,8 +126,8 @@ export default function Hero({ className = '' }: { className?: string }) {
|
||||
<p
|
||||
className={
|
||||
isMobile
|
||||
? `w-[250px] text-center text-xs text-gray-500`
|
||||
: `w-[250px] text-xs text-gray-500`
|
||||
? `w-[250px] text-center text-xs text-gray-500 dark:text-bright-gray`
|
||||
: `w-[250px] text-xs text-gray-500 dark:text-bright-gray`
|
||||
}
|
||||
>
|
||||
The security of your data is our top priority. DocsGPT ensures the
|
||||
@@ -121,9 +138,9 @@ export default function Hero({ className = '' }: { className?: string }) {
|
||||
</div>
|
||||
</div>
|
||||
{/* third */}
|
||||
<div className=" rounded-[50px] bg-gradient-to-l from-[#6EE7B7]/80 via-[#3B82F6] to-[#9333EA]/50 p-1 md:rounded-tl-none md:rounded-bl-none ">
|
||||
<div className="h-auto rounded-[50px] bg-gradient-to-l from-[#6EE7B7]/70 via-[#3B82F6] to-[#9333EA]/50 p-1 dark:from-[#D16FF8] dark:via-[#48E6E0] dark:to-[#C85EF6] md:h-60 md:rounded-tl-none md:rounded-bl-none ">
|
||||
<div
|
||||
className={`firefox rounded-[45px] bg-white p-${
|
||||
className={`firefox h-full rounded-[45px] bg-white dark:bg-dark-charcoal p-${
|
||||
isMobile ? '3.5' : '6 px-6 '
|
||||
} lg:rounded-tl-none lg:rounded-bl-none`}
|
||||
>
|
||||
@@ -131,7 +148,11 @@ export default function Hero({ className = '' }: { className?: string }) {
|
||||
{isMobile ? (
|
||||
<div className="flex justify-center">
|
||||
<img
|
||||
src="/message-programming.svg"
|
||||
src={
|
||||
isDarkTheme
|
||||
? 'message-programming-dark.svg'
|
||||
: '/message-programming.svg'
|
||||
}
|
||||
alt="lock"
|
||||
className="h-[24px] w-[24px]"
|
||||
/>
|
||||
@@ -142,7 +163,11 @@ export default function Hero({ className = '' }: { className?: string }) {
|
||||
) : (
|
||||
<>
|
||||
<img
|
||||
src="/message-programming.svg"
|
||||
src={
|
||||
isDarkTheme
|
||||
? '/message-programming-dark.svg'
|
||||
: '/message-programming.svg'
|
||||
}
|
||||
alt="lock"
|
||||
className="h-[24px] w-[24px]"
|
||||
/>
|
||||
@@ -154,8 +179,8 @@ export default function Hero({ className = '' }: { className?: string }) {
|
||||
<p
|
||||
className={
|
||||
isMobile
|
||||
? `w-[250px] text-center text-xs text-gray-500`
|
||||
: `w-[250px] text-xs text-gray-500`
|
||||
? `w-[250px] text-center text-xs text-gray-500 dark:text-bright-gray`
|
||||
: `w-[250px] text-xs text-gray-500 dark:text-bright-gray`
|
||||
}
|
||||
>
|
||||
DocsGPT is built on open source principles, promoting transparency
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { NavLink, useNavigate } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import DocsGPT3 from './assets/cute_docsgpt3.svg';
|
||||
import Documentation from './assets/documentation.svg';
|
||||
import DocumentationDark from './assets/documentation-dark.svg';
|
||||
import Discord from './assets/discord.svg';
|
||||
import Arrow2 from './assets/dropdown-arrow.svg';
|
||||
import DiscordDark from './assets/discord-dark.svg';
|
||||
import Expand from './assets/expand.svg';
|
||||
import Trash from './assets/trash.svg';
|
||||
import Github from './assets/github.svg';
|
||||
import GithubDark from './assets/github-dark.svg';
|
||||
import Hamburger from './assets/hamburger.svg';
|
||||
import HamburgerDark from './assets/hamburger-dark.svg';
|
||||
import Info from './assets/info.svg';
|
||||
import InfoDark from './assets/info-dark.svg';
|
||||
import SettingGear from './assets/settingGear.svg';
|
||||
import SettingGearDark from './assets/settingGear-dark.svg';
|
||||
import Add from './assets/add.svg';
|
||||
import UploadIcon from './assets/upload.svg';
|
||||
import { ActiveState } from './models/misc';
|
||||
@@ -34,12 +39,28 @@ import Upload from './upload/Upload';
|
||||
import { Doc, getConversations } from './preferences/preferenceApi';
|
||||
import SelectDocsModal from './preferences/SelectDocsModal';
|
||||
import ConversationTile from './conversation/ConversationTile';
|
||||
import { useDarkTheme } from './hooks';
|
||||
import SourceDropdown from './components/SourceDropdown';
|
||||
|
||||
interface NavigationProps {
|
||||
navOpen: boolean;
|
||||
setNavOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
const NavImage: React.FC<{
|
||||
Light: string | undefined;
|
||||
Dark: string | undefined;
|
||||
}> = ({ Light, Dark }) => {
|
||||
return (
|
||||
<>
|
||||
<img src={Dark} alt="icon" className="ml-2 hidden w-5 dark:block " />
|
||||
<img src={Light} alt="icon" className="ml-2 w-5 dark:hidden " />
|
||||
</>
|
||||
);
|
||||
};
|
||||
NavImage.propTypes = {
|
||||
Light: PropTypes.string,
|
||||
Dark: PropTypes.string,
|
||||
};
|
||||
export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
const dispatch = useDispatch();
|
||||
const docs = useSelector(selectSourceDocs);
|
||||
@@ -47,7 +68,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
const conversations = useSelector(selectConversations);
|
||||
const conversationId = useSelector(selectConversationId);
|
||||
const { isMobile } = useMediaQuery();
|
||||
|
||||
const [isDarkTheme] = useDarkTheme();
|
||||
const [isDocsListOpen, setIsDocsListOpen] = useState(false);
|
||||
|
||||
const isApiKeySet = useSelector(selectApiKeyStatus);
|
||||
@@ -63,8 +84,6 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
|
||||
const navRef = useRef(null);
|
||||
const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com';
|
||||
const embeddingsName =
|
||||
import.meta.env.VITE_EMBEDDINGS_NAME || 'openai_text-embedding-ada-002';
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -73,7 +92,6 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
fetchConversations();
|
||||
}
|
||||
}, [conversations, dispatch]);
|
||||
|
||||
async function fetchConversations() {
|
||||
return await getConversations()
|
||||
.then((fetchedConversations) => {
|
||||
@@ -168,12 +186,11 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
useEffect(() => {
|
||||
setNavOpen(!isMobile);
|
||||
}, [isMobile]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{!navOpen && (
|
||||
<button
|
||||
className="duration-25 absolute sticky top-3 left-3 z-20 hidden transition-all md:block"
|
||||
className="duration-25 absolute top-3 left-3 z-20 hidden transition-all md:block"
|
||||
onClick={() => {
|
||||
setNavOpen(!navOpen);
|
||||
}}
|
||||
@@ -191,10 +208,10 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
ref={navRef}
|
||||
className={`${
|
||||
!navOpen && '-ml-96 md:-ml-[18rem]'
|
||||
} duration-20 fixed top-0 z-20 flex h-full w-72 flex-col border-r-2 bg-white transition-all`}
|
||||
} duration-20 fixed top-0 z-20 flex h-full w-72 flex-col border-r-[1px] border-b-0 bg-white transition-all dark:border-r-purple-taupe dark:bg-chinese-black dark:text-white`}
|
||||
>
|
||||
<div
|
||||
className={'visible mt-2 flex h-16 w-full justify-between md:h-12'}
|
||||
className={'visible mt-2 flex h-[6vh] w-full justify-between md:h-12'}
|
||||
>
|
||||
<div className="my-auto mx-4 flex cursor-pointer gap-1.5">
|
||||
<img className="mb-2 h-10" src={DocsGPT3} alt="" />
|
||||
@@ -215,182 +232,142 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div className="mb-auto">
|
||||
<NavLink
|
||||
to={'/'}
|
||||
onClick={() => {
|
||||
dispatch(setConversation([]));
|
||||
dispatch(
|
||||
updateConversationId({
|
||||
query: { conversationId: null },
|
||||
}),
|
||||
);
|
||||
}}
|
||||
className={({ isActive }) =>
|
||||
`${
|
||||
isActive ? 'bg-gray-3000' : ''
|
||||
} group mx-4 mt-4 flex cursor-pointer gap-2.5 rounded-3xl border border-silver p-3 hover:border-rainy-gray hover:bg-gray-3000`
|
||||
}
|
||||
>
|
||||
<img
|
||||
src={Add}
|
||||
alt="new"
|
||||
className="opacity-80 group-hover:opacity-100"
|
||||
/>
|
||||
<p className=" text-sm text-dove-gray group-hover:text-neutral-600">
|
||||
New Chat
|
||||
</p>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
to={'/'}
|
||||
onClick={() => {
|
||||
dispatch(setConversation([]));
|
||||
dispatch(
|
||||
updateConversationId({
|
||||
query: { conversationId: null },
|
||||
}),
|
||||
);
|
||||
}}
|
||||
className={({ isActive }) =>
|
||||
`${
|
||||
isActive ? 'bg-gray-3000 dark:bg-transparent' : ''
|
||||
} group sticky mx-4 mt-4 flex cursor-pointer gap-2.5 rounded-3xl border border-silver p-3 hover:border-rainy-gray hover:bg-gray-3000 dark:border-purple-taupe dark:text-white dark:hover:bg-transparent`
|
||||
}
|
||||
>
|
||||
<img
|
||||
src={Add}
|
||||
alt="new"
|
||||
className="opacity-80 group-hover:opacity-100"
|
||||
/>
|
||||
<p className=" text-sm text-dove-gray group-hover:text-neutral-600 dark:text-chinese-silver dark:group-hover:text-bright-gray">
|
||||
New Chat
|
||||
</p>
|
||||
</NavLink>
|
||||
<div className="mb-auto h-[56vh] overflow-x-hidden overflow-y-scroll dark:text-white">
|
||||
{conversations && (
|
||||
<div className="conversations-container max-h-[25rem] overflow-y-auto">
|
||||
<div>
|
||||
<p className="ml-6 mt-3 text-sm font-semibold">Chats</p>
|
||||
{conversations?.map((conversation) => (
|
||||
<ConversationTile
|
||||
key={conversation.id}
|
||||
conversation={conversation}
|
||||
selectConversation={(id) => handleConversationClick(id)}
|
||||
onDeleteConversation={(id) => handleDeleteConversation(id)}
|
||||
onSave={(conversation) =>
|
||||
updateConversationName(conversation)
|
||||
}
|
||||
/>
|
||||
))}
|
||||
<div className="conversations-container">
|
||||
{conversations?.map((conversation) => (
|
||||
<ConversationTile
|
||||
key={conversation.id}
|
||||
conversation={conversation}
|
||||
selectConversation={(id) => handleConversationClick(id)}
|
||||
onDeleteConversation={(id) => handleDeleteConversation(id)}
|
||||
onSave={(conversation) =>
|
||||
updateConversationName(conversation)
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col-reverse border-b-2">
|
||||
<div className="relative my-4 flex gap-2 px-2">
|
||||
<div
|
||||
className="flex h-12 w-5/6 cursor-pointer justify-between rounded-3xl border-2 bg-white"
|
||||
onClick={() => setIsDocsListOpen(!isDocsListOpen)}
|
||||
>
|
||||
{selectedDocs && (
|
||||
<p className="my-3 mx-4 overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
{selectedDocs.name} {selectedDocs.version}
|
||||
</p>
|
||||
)}
|
||||
<img
|
||||
src={Arrow2}
|
||||
alt="arrow"
|
||||
className={`${
|
||||
!isDocsListOpen ? 'rotate-0' : 'rotate-180'
|
||||
} ml-auto mr-3 w-3 transition-all`}
|
||||
<div className="flex h-auto flex-col justify-end text-eerie-black dark:text-white">
|
||||
<div className="flex flex-col-reverse border-b-[1px] dark:border-b-purple-taupe">
|
||||
<div className="relative my-4 flex gap-2 px-2">
|
||||
<SourceDropdown
|
||||
options={docs}
|
||||
selectedDocs={selectedDocs}
|
||||
setSelectedDocs={setSelectedDocs}
|
||||
isDocsListOpen={isDocsListOpen}
|
||||
setIsDocsListOpen={setIsDocsListOpen}
|
||||
handleDeleteClick={handleDeleteClick}
|
||||
/>
|
||||
<img
|
||||
className="mt-2 h-9 w-9 hover:cursor-pointer"
|
||||
src={UploadIcon}
|
||||
onClick={() => setUploadModalState('ACTIVE')}
|
||||
></img>
|
||||
</div>
|
||||
<img
|
||||
className="mt-2 h-9 w-9 hover:cursor-pointer"
|
||||
src={UploadIcon}
|
||||
onClick={() => setUploadModalState('ACTIVE')}
|
||||
></img>
|
||||
{isDocsListOpen && (
|
||||
<div className="absolute top-12 left-0 right-6 z-10 ml-2 mr-4 max-h-52 overflow-y-scroll bg-white shadow-lg">
|
||||
{docs ? (
|
||||
docs.map((doc, index) => {
|
||||
if (doc.model === embeddingsName) {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
onClick={() => {
|
||||
dispatch(setSelectedDocs(doc));
|
||||
setIsDocsListOpen(false);
|
||||
}}
|
||||
className="flex h-10 w-full cursor-pointer items-center justify-between border-x-2 border-b-2 hover:bg-gray-100"
|
||||
>
|
||||
<p className="ml-5 flex-1 overflow-hidden overflow-ellipsis whitespace-nowrap py-3">
|
||||
{doc.name} {doc.version}
|
||||
</p>
|
||||
{doc.location === 'local' && (
|
||||
<img
|
||||
src={Trash}
|
||||
alt="Delete"
|
||||
className="mr-4 h-4 w-4 cursor-pointer hover:opacity-50"
|
||||
id={`img-${index}`}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
handleDeleteClick(index, doc);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})
|
||||
) : (
|
||||
<div className="h-10 w-full cursor-pointer border-x-2 border-b-2 hover:bg-gray-100">
|
||||
<p className="ml-5 py-3">No default documentation.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<p className="ml-6 mt-3 text-sm font-semibold">Source Docs</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 border-b-[1px] py-2 dark:border-b-purple-taupe">
|
||||
<NavLink
|
||||
to="/settings"
|
||||
className={({ isActive }) =>
|
||||
`my-auto mx-4 flex h-9 cursor-pointer gap-4 rounded-3xl hover:bg-gray-100 dark:hover:bg-purple-taupe ${
|
||||
isActive ? 'bg-gray-3000 dark:bg-transparent' : ''
|
||||
}`
|
||||
}
|
||||
>
|
||||
<NavImage Light={SettingGear} Dark={SettingGearDark} />
|
||||
<p className="my-auto text-sm text-eerie-black dark:text-white">
|
||||
Settings
|
||||
</p>
|
||||
</NavLink>
|
||||
</div>
|
||||
<p className="ml-6 mt-3 text-sm font-semibold">Source Docs</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 border-b-2 py-2">
|
||||
<NavLink
|
||||
to="/settings"
|
||||
className={({ isActive }) =>
|
||||
`my-auto mx-4 flex h-9 cursor-pointer gap-4 rounded-3xl hover:bg-gray-100 ${
|
||||
isActive ? 'bg-gray-3000' : ''
|
||||
}`
|
||||
}
|
||||
>
|
||||
<img src={SettingGear} alt="info" className="ml-2 w-5 opacity-60" />
|
||||
<p className="my-auto text-sm text-eerie-black">Settings</p>
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2 border-b-2 py-2">
|
||||
<NavLink
|
||||
to="/about"
|
||||
className={({ isActive }) =>
|
||||
`my-auto mx-4 flex h-9 cursor-pointer gap-4 rounded-3xl hover:bg-gray-100 ${
|
||||
isActive ? 'bg-gray-3000' : ''
|
||||
}`
|
||||
}
|
||||
>
|
||||
<img src={Info} alt="info" className="ml-2 w-5" />
|
||||
<p className="my-auto text-sm text-eerie-black">About</p>
|
||||
</NavLink>
|
||||
<div className="flex flex-col gap-2 border-b-[1.5px] py-2 dark:border-b-purple-taupe">
|
||||
<NavLink
|
||||
to="/about"
|
||||
className={({ isActive }) =>
|
||||
`my-auto mx-4 flex h-9 cursor-pointer gap-4 rounded-3xl hover:bg-gray-100 dark:hover:bg-purple-taupe ${
|
||||
isActive ? 'bg-gray-3000 dark:bg-purple-taupe' : ''
|
||||
}`
|
||||
}
|
||||
>
|
||||
<NavImage Light={Info} Dark={InfoDark} />
|
||||
<p className="my-auto text-sm">About</p>
|
||||
</NavLink>
|
||||
|
||||
<a
|
||||
href="https://docs.docsgpt.co.uk/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="my-auto mx-4 flex h-9 cursor-pointer gap-4 rounded-3xl hover:bg-gray-100"
|
||||
>
|
||||
<img src={Documentation} alt="documentation" className="ml-2 w-5" />
|
||||
<p className="my-auto text-sm text-eerie-black">Documentation</p>
|
||||
</a>
|
||||
<a
|
||||
href="https://discord.gg/WHJdfbQDR4"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="my-auto mx-4 flex h-9 cursor-pointer gap-4 rounded-3xl hover:bg-gray-100"
|
||||
>
|
||||
<img src={Discord} alt="link" className="ml-2 w-5" />
|
||||
<p className="my-auto text-sm text-eerie-black">
|
||||
Visit our Discord
|
||||
</p>
|
||||
</a>
|
||||
<a
|
||||
href="https://docs.docsgpt.co.uk/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="my-auto mx-4 flex h-9 cursor-pointer gap-4 rounded-3xl hover:bg-gray-100 dark:hover:bg-purple-taupe"
|
||||
>
|
||||
<NavImage Light={Documentation} Dark={DocumentationDark} />
|
||||
<p className="my-auto text-sm ">Documentation</p>
|
||||
</a>
|
||||
<a
|
||||
href="https://discord.gg/WHJdfbQDR4"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="my-auto mx-4 flex h-9 cursor-pointer gap-4 rounded-3xl hover:bg-gray-100 dark:hover:bg-purple-taupe"
|
||||
>
|
||||
<NavImage Light={Discord} Dark={DiscordDark} />
|
||||
{/* <img src={isDarkTheme ? DiscordDark : Discord} alt="discord-link" className="ml-2 w-5" /> */}
|
||||
<p className="my-auto text-sm">Visit our Discord</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://github.com/arc53/DocsGPT"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="my-auto mx-4 flex h-9 cursor-pointer gap-4 rounded-3xl hover:bg-gray-100"
|
||||
>
|
||||
<img src={Github} alt="link" className="ml-2 w-5" />
|
||||
<p className="my-auto text-sm text-eerie-black">Visit our Github</p>
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/arc53/DocsGPT"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="mx-4 mt-auto flex h-9 cursor-pointer gap-4 rounded-3xl hover:bg-gray-100 dark:hover:bg-purple-taupe"
|
||||
>
|
||||
<NavImage Light={Github} Dark={GithubDark} />
|
||||
<p className="my-auto text-sm">Visit our Github</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="fixed z-10 h-16 w-full border-b-2 bg-gray-50 md:hidden">
|
||||
<div className="fixed z-10 h-16 w-full border-b-2 bg-gray-50 dark:border-b-purple-taupe dark:bg-chinese-black md:hidden">
|
||||
<button
|
||||
className="mt-5 ml-6 h-6 w-6 md:hidden"
|
||||
onClick={() => setNavOpen(true)}
|
||||
>
|
||||
<img src={Hamburger} alt="menu toggle" className="w-7" />
|
||||
<img
|
||||
src={isDarkTheme ? HamburgerDark : Hamburger}
|
||||
alt="menu toggle"
|
||||
className="w-7"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<SelectDocsModal
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import Arrow2 from './assets/dropdown-arrow.svg';
|
||||
import ArrowLeft from './assets/arrow-left.svg';
|
||||
import ArrowRight from './assets/arrow-right.svg';
|
||||
import Trash from './assets/trash.svg';
|
||||
@@ -8,9 +7,11 @@ import {
|
||||
selectPrompt,
|
||||
setPrompt,
|
||||
selectSourceDocs,
|
||||
setSourceDocs,
|
||||
} from './preferences/preferenceSlice';
|
||||
import { Doc } from './preferences/preferenceApi';
|
||||
|
||||
import { useDarkTheme } from './hooks';
|
||||
import Dropdown from './components/Dropdown';
|
||||
type PromptProps = {
|
||||
prompts: { name: string; id: string; type: string }[];
|
||||
selectedPrompt: { name: string; id: string; type: string };
|
||||
@@ -85,20 +86,23 @@ const Setting: React.FC = () => {
|
||||
fetch(`${apiHost}/api/delete_old?path=${docPath}`, {
|
||||
method: 'GET',
|
||||
})
|
||||
.then(() => {
|
||||
// remove the image element from the DOM
|
||||
const imageElement = document.querySelector(
|
||||
`#img-${index}`,
|
||||
) as HTMLElement;
|
||||
const parentElement = imageElement.parentNode as HTMLElement;
|
||||
parentElement.parentNode?.removeChild(parentElement);
|
||||
.then((response) => {
|
||||
if (response.ok && documents) {
|
||||
const updatedDocuments = [
|
||||
...documents.slice(0, index),
|
||||
...documents.slice(index + 1),
|
||||
];
|
||||
dispatch(setSourceDocs(updatedDocuments));
|
||||
}
|
||||
})
|
||||
.catch((error) => console.error(error));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-4 pt-20 md:p-12">
|
||||
<p className="text-2xl font-bold text-eerie-black">Settings</p>
|
||||
<div className="wa p-4 pt-20 md:p-12">
|
||||
<p className="text-2xl font-bold text-eerie-black dark:text-bright-gray">
|
||||
Settings
|
||||
</p>
|
||||
<div className="mt-6 flex flex-row items-center space-x-4 overflow-x-auto md:space-x-8 ">
|
||||
<div className="md:hidden">
|
||||
<button
|
||||
@@ -115,7 +119,7 @@ const Setting: React.FC = () => {
|
||||
onClick={() => setActiveTab(tab)}
|
||||
className={`h-9 rounded-3xl px-4 font-bold ${
|
||||
activeTab === tab
|
||||
? 'bg-purple-3000 text-purple-30'
|
||||
? 'bg-purple-3000 text-purple-30 dark:bg-dark-charcoal'
|
||||
: 'text-gray-6000'
|
||||
}`}
|
||||
>
|
||||
@@ -187,23 +191,30 @@ const Setting: React.FC = () => {
|
||||
};
|
||||
|
||||
const General: React.FC = () => {
|
||||
const themes = ['Light'];
|
||||
const themes = ['Light', 'Dark'];
|
||||
const languages = ['English'];
|
||||
const [selectedTheme, setSelectedTheme] = useState(themes[0]);
|
||||
const [isDarkTheme, toggleTheme] = useDarkTheme();
|
||||
const [selectedTheme, setSelectedTheme] = useState(
|
||||
isDarkTheme ? 'Dark' : 'Light',
|
||||
);
|
||||
const [selectedLanguage, setSelectedLanguage] = useState(languages[0]);
|
||||
|
||||
return (
|
||||
<div className="mt-[59px]">
|
||||
<div className="mb-4">
|
||||
<p className="font-bold text-jet">Select Theme</p>
|
||||
<p className="font-bold text-jet dark:text-bright-gray">Select Theme</p>
|
||||
<Dropdown
|
||||
options={themes}
|
||||
selectedValue={selectedTheme}
|
||||
onSelect={setSelectedTheme}
|
||||
onSelect={(option: string) => {
|
||||
setSelectedTheme(option);
|
||||
option !== selectedTheme && toggleTheme();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-bold text-jet">Select Language</p>
|
||||
<p className="font-bold text-jet dark:text-bright-gray">
|
||||
Select Language
|
||||
</p>
|
||||
<Dropdown
|
||||
options={languages}
|
||||
selectedValue={selectedLanguage}
|
||||
@@ -346,8 +357,8 @@ const Prompts: React.FC<PromptProps> = ({
|
||||
return (
|
||||
<div className="mt-[59px]">
|
||||
<div className="mb-4">
|
||||
<p className="font-semibold">Active Prompt</p>
|
||||
<DropdownPrompt
|
||||
<p className="font-semibold dark:text-bright-gray">Active Prompt</p>
|
||||
<Dropdown
|
||||
options={prompts}
|
||||
selectedValue={selectedPrompt.name}
|
||||
onSelect={handleSelectPrompt}
|
||||
@@ -355,23 +366,23 @@ const Prompts: React.FC<PromptProps> = ({
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<p>Prompt name </p>{' '}
|
||||
<p className="mb-2 text-xs italic text-eerie-black text-gray-500">
|
||||
<p className="dark:text-bright-gray">Prompt name </p>{' '}
|
||||
<p className="mb-2 text-xs italic text-eerie-black dark:text-bright-gray">
|
||||
start by editing name
|
||||
</p>
|
||||
<input
|
||||
type="text"
|
||||
value={newPromptName}
|
||||
placeholder="Active Prompt Name"
|
||||
className="w-full rounded-lg border-2 p-2"
|
||||
className="w-full rounded-lg border-2 p-2 dark:border-chinese-silver dark:bg-transparent dark:text-white"
|
||||
onChange={(e) => setNewPromptName(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<p className="mb-2">Prompt content</p>
|
||||
<p className="mb-2 dark:text-bright-gray">Prompt content</p>
|
||||
<textarea
|
||||
className="h-32 w-full rounded-lg border-2 p-2"
|
||||
className="h-32 w-full rounded-lg border-2 p-2 dark:border-chinese-silver dark:bg-transparent dark:text-white"
|
||||
value={newPromptContent}
|
||||
onChange={(e) => setNewPromptContent(e.target.value)}
|
||||
placeholder="Active prompt contents"
|
||||
@@ -417,120 +428,6 @@ const Prompts: React.FC<PromptProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
function DropdownPrompt({
|
||||
options,
|
||||
selectedValue,
|
||||
onSelect,
|
||||
}: {
|
||||
options: { name: string; id: string; type: string }[];
|
||||
selectedValue: string;
|
||||
onSelect: (value: { name: string; id: string; type: string }) => void;
|
||||
}) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="relative mt-2 w-32">
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className="flex w-full cursor-pointer items-center rounded-xl border-2 bg-white p-3"
|
||||
>
|
||||
<span className="flex-1 overflow-hidden text-ellipsis">
|
||||
{selectedValue}
|
||||
</span>
|
||||
<img
|
||||
src={Arrow2}
|
||||
alt="arrow"
|
||||
className={`transform ${
|
||||
isOpen ? 'rotate-180' : 'rotate-0'
|
||||
} h-3 w-3 transition-transform`}
|
||||
/>
|
||||
</button>
|
||||
{isOpen && (
|
||||
<div className="absolute left-0 right-0 z-50 -mt-3 rounded-b-xl border-2 bg-white shadow-lg">
|
||||
{options.map((option, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex cursor-pointer items-center justify-between hover:bg-gray-100"
|
||||
>
|
||||
<span
|
||||
onClick={() => {
|
||||
onSelect(option);
|
||||
setIsOpen(false);
|
||||
}}
|
||||
className="ml-2 flex-1 overflow-hidden overflow-ellipsis whitespace-nowrap py-3"
|
||||
>
|
||||
{option.name}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Dropdown({
|
||||
options,
|
||||
selectedValue,
|
||||
onSelect,
|
||||
showDelete,
|
||||
onDelete,
|
||||
}: {
|
||||
options: string[];
|
||||
selectedValue: string;
|
||||
onSelect: (value: string) => void;
|
||||
showDelete?: boolean; // optional
|
||||
onDelete?: (value: string) => void; // optional
|
||||
}) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="relative mt-2 w-32">
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className="flex w-full cursor-pointer items-center rounded-xl border-2 bg-white p-3"
|
||||
>
|
||||
<span className="flex-1 overflow-hidden text-ellipsis">
|
||||
{selectedValue}
|
||||
</span>
|
||||
<img
|
||||
src={Arrow2}
|
||||
alt="arrow"
|
||||
className={`transform ${
|
||||
isOpen ? 'rotate-180' : 'rotate-0'
|
||||
} h-3 w-3 transition-transform`}
|
||||
/>
|
||||
</button>
|
||||
{isOpen && (
|
||||
<div className="absolute left-0 right-0 z-50 -mt-3 rounded-b-xl border-2 bg-white shadow-lg">
|
||||
{options.map((option, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex cursor-pointer items-center justify-between hover:bg-gray-100"
|
||||
>
|
||||
<span
|
||||
onClick={() => {
|
||||
onSelect(option);
|
||||
setIsOpen(false);
|
||||
}}
|
||||
className="ml-2 flex-1 overflow-hidden overflow-ellipsis whitespace-nowrap py-3"
|
||||
>
|
||||
{option}
|
||||
</span>
|
||||
{showDelete && onDelete && (
|
||||
<button onClick={() => onDelete(option)} className="p-2">
|
||||
{/* Icon or text for delete button */}
|
||||
Delete
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type AddPromptModalProps = {
|
||||
newPromptName: string;
|
||||
onNewPromptNameChange: (name: string) => void;
|
||||
@@ -553,7 +450,7 @@ const AddPromptModal: React.FC<AddPromptModalProps> = ({
|
||||
placeholder="Enter Prompt Name"
|
||||
value={newPromptName}
|
||||
onChange={(e) => onNewPromptNameChange(e.target.value)}
|
||||
className="mb-4 w-full rounded-3xl border-2 p-2"
|
||||
className="mb-4 w-full rounded-3xl border-2 p-2 dark:border-chinese-silver"
|
||||
/>
|
||||
<button
|
||||
onClick={onAddPrompt}
|
||||
@@ -586,28 +483,32 @@ const Documents: React.FC<DocumentsProps> = ({
|
||||
<div className="flex flex-col">
|
||||
{/* <h2 className="text-xl font-semibold">Documents</h2> */}
|
||||
|
||||
<div className="mt-[27px] overflow-x-auto">
|
||||
<table className="block w-full table-auto content-center justify-center text-center">
|
||||
<div className="mt-[27px] w-max overflow-x-auto rounded-xl border dark:border-chinese-silver">
|
||||
<table className="block w-full table-auto content-center justify-center text-center dark:text-bright-gray">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="border px-4 py-2">Document Name</th>
|
||||
<th className="border px-4 py-2">Vector Date</th>
|
||||
<th className="border px-4 py-2">Type</th>
|
||||
<th className="border px-4 py-2"></th>
|
||||
<th className="border-r p-4 md:w-[244px]">Document Name</th>
|
||||
<th className="w-[244px] border-r px-4 py-2">Vector Date</th>
|
||||
<th className="w-[244px] border-r px-4 py-2">Type</th>
|
||||
<th className="px-4 py-2"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{documents &&
|
||||
documents.map((document, index) => (
|
||||
<tr key={index}>
|
||||
<td className="border px-4 py-2">{document.name}</td>
|
||||
<td className="border px-4 py-2">{document.date}</td>
|
||||
<td className="border px-4 py-2">
|
||||
<td className="border-r border-t px-4 py-2">
|
||||
{document.name}
|
||||
</td>
|
||||
<td className="border-r border-t px-4 py-2">
|
||||
{document.date}
|
||||
</td>
|
||||
<td className="border-r border-t px-4 py-2">
|
||||
{document.location === 'remote'
|
||||
? 'Pre-loaded'
|
||||
: 'Private'}
|
||||
</td>
|
||||
<td className="border px-4 py-2">
|
||||
<td className="border-t px-4 py-2">
|
||||
{document.location !== 'remote' && (
|
||||
<img
|
||||
src={Trash}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<svg width="14" height="17" stroke-width="1.15" viewBox="0 0 14 17" >
|
||||
<svg width="13" height="16" stroke-width="1.15" viewBox="0 0 14 17" >
|
||||
<path d="M13.8013 5.01282L8.80645 0.191795C8.67953 0.0691399 8.50734 0.000152609 8.32774 0H6.09677C5.43801 0 4.80623 0.252586 4.34041 0.702193C3.8746 1.1518 3.6129 1.7616 3.6129 2.39744V3.48718H2.48387C1.82511 3.48718 1.19332 3.73977 0.727509 4.18937C0.261693 4.63898 0 5.24878 0 5.88462V14.6026C0 15.2384 0.261693 15.8482 0.727509 16.2978C1.19332 16.7474 1.82511 17 2.48387 17H8.80645C9.46521 17 10.097 16.7474 10.5628 16.2978C11.0286 15.8482 11.2903 15.2384 11.2903 14.6026V13.5128H11.5161C12.1749 13.5128 12.8067 13.2602 13.2725 12.8106C13.7383 12.361 14 11.7512 14 11.1154V5.44872C13.9929 5.28447 13.9219 5.12884 13.8013 5.01282ZM9.03226 2.23179L11.6877 4.79487H9.03226V2.23179ZM9.93548 14.6026C9.93548 14.8916 9.81653 15.1688 9.6048 15.3731C9.39306 15.5775 9.10589 15.6923 8.80645 15.6923H2.48387C2.18443 15.6923 1.89726 15.5775 1.68552 15.3731C1.47379 15.1688 1.35484 14.8916 1.35484 14.6026V5.88462C1.35484 5.5956 1.47379 5.31842 1.68552 5.11405C1.89726 4.90968 2.18443 4.79487 2.48387 4.79487H3.6129V11.1154C3.6129 11.7512 3.8746 12.361 4.34041 12.8106C4.80623 13.2602 5.43801 13.5128 6.09677 13.5128H9.93548V14.6026ZM11.5161 12.2051H6.09677C5.79734 12.2051 5.51016 12.0903 5.29843 11.886C5.08669 11.6816 4.96774 11.4044 4.96774 11.1154V2.39744C4.96774 2.10842 5.08669 1.83124 5.29843 1.62687C5.51016 1.4225 5.79734 1.30769 6.09677 1.30769H7.67742V5.44872C7.67976 5.62143 7.75188 5.78643 7.87842 5.90856C8.00496 6.03069 8.1759 6.10031 8.35484 6.10256H12.6452V11.1154C12.6452 11.4044 12.5262 11.6816 12.3145 11.886C12.1027 12.0903 11.8156 12.2051 11.5161 12.2051Z" fill="#949494"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
3
frontend/src/assets/discord-dark.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="18" viewBox="0 0 22 18" fill="none">
|
||||
<path d="M18.7359 2.29769C17.4059 1.66546 15.9659 1.20658 14.4659 0.941453C14.4528 0.941024 14.4397 0.943541 14.4276 0.948827C14.4155 0.954112 14.4047 0.962038 14.3959 0.972045C14.2159 1.30856 14.0059 1.74704 13.8659 2.08355C12.2749 1.83882 10.6569 1.83882 9.06594 2.08355C8.92594 1.73684 8.71594 1.30856 8.52594 0.972045C8.51594 0.951651 8.48594 0.941453 8.45594 0.941453C6.95594 1.20658 5.52594 1.66546 4.18594 2.29769C4.17594 2.29769 4.16594 2.30789 4.15594 2.31809C1.43594 6.46839 0.685937 10.5065 1.05594 14.5039C1.05594 14.5243 1.06594 14.5447 1.08594 14.5548C2.88594 15.9009 4.61594 16.7167 6.32594 17.2571C6.35594 17.2673 6.38594 17.2571 6.39594 17.2367C6.79594 16.6759 7.15594 16.0844 7.46594 15.4624C7.48594 15.4216 7.46594 15.3808 7.42594 15.3706C6.85594 15.1463 6.31594 14.8812 5.78594 14.5752C5.74594 14.5549 5.74594 14.4937 5.77594 14.4631C5.88594 14.3815 5.99594 14.2897 6.10594 14.2081C6.12594 14.1877 6.15594 14.1877 6.17594 14.1979C9.61594 15.7989 13.3259 15.7989 16.7259 14.1979C16.7459 14.1877 16.7759 14.1877 16.7959 14.2081C16.9059 14.2999 17.0159 14.3815 17.1259 14.4733C17.1659 14.5039 17.1659 14.565 17.1159 14.5854C16.5959 14.9016 16.0459 15.1565 15.4759 15.3808C15.4359 15.391 15.4259 15.442 15.4359 15.4726C15.7559 16.0946 16.1159 16.6861 16.5059 17.2469C16.5359 17.2571 16.5659 17.2673 16.5959 17.2571C18.3159 16.7167 20.0459 15.9009 21.8459 14.5548C21.8659 14.5447 21.8759 14.5243 21.8759 14.5039C22.3159 9.88449 21.1459 5.87695 18.7759 2.31809C18.7659 2.30789 18.7559 2.29769 18.7359 2.29769ZM7.98594 12.0667C6.95594 12.0667 6.09594 11.098 6.09594 9.90488C6.09594 8.7118 6.93594 7.74305 7.98594 7.74305C9.04594 7.74305 9.88594 8.72199 9.87594 9.90488C9.87594 11.098 9.03594 12.0667 7.98594 12.0667ZM14.9559 12.0667C13.9259 12.0667 13.0659 11.098 13.0659 9.90488C13.0659 8.7118 13.9059 7.74305 14.9559 7.74305C16.0159 7.74305 16.8559 8.72199 16.8459 9.90488C16.8459 11.098 16.0159 12.0667 14.9559 12.0667Z" fill="#949494"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="current" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.37776 10.1001V12.9C6.37776 13.457 6.599 13.9911 6.99282 14.3849C7.38664 14.7788 7.92077 15 8.47772 15L11.2777 8.70011V1.00025H3.38181C3.04419 0.996436 2.71656 1.11477 2.45929 1.33344C2.20203 1.55212 2.03246 1.8564 1.98184 2.19023L1.01585 8.49012C0.985398 8.69076 0.998931 8.89563 1.05551 9.09053C1.1121 9.28543 1.21038 9.46569 1.34355 9.61884C1.47671 9.77198 1.64159 9.89434 1.82674 9.97744C2.01189 10.0605 2.2129 10.1024 2.41583 10.1001H6.37776ZM11.2777 1.00025H13.1466C13.5428 0.993247 13.9277 1.13195 14.2284 1.39002C14.5291 1.64809 14.7245 2.00758 14.7776 2.40023V7.30014C14.7245 7.69279 14.5291 8.05227 14.2284 8.31035C13.9277 8.56842 13.5428 8.70712 13.1466 8.70011H11.2777" fill="white"/>
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="current" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.37776 10.1001V12.9C6.37776 13.457 6.599 13.9911 6.99282 14.3849C7.38664 14.7788 7.92077 15 8.47772 15L11.2777 8.70011V1.00025H3.38181C3.04419 0.996436 2.71656 1.11477 2.45929 1.33344C2.20203 1.55212 2.03246 1.8564 1.98184 2.19023L1.01585 8.49012C0.985398 8.69076 0.998931 8.89563 1.05551 9.09053C1.1121 9.28543 1.21038 9.46569 1.34355 9.61884C1.47671 9.77198 1.64159 9.89434 1.82674 9.97744C2.01189 10.0605 2.2129 10.1024 2.41583 10.1001H6.37776ZM11.2777 1.00025H13.1466C13.5428 0.993247 13.9277 1.13195 14.2284 1.39002C14.5291 1.64809 14.7245 2.00758 14.7776 2.40023V7.30014C14.7245 7.69279 14.5291 8.05227 14.2284 8.31035C13.9277 8.56842 13.5428 8.70712 13.1466 8.70011H11.2777" fill="none"/>
|
||||
<path d="M11.2777 8.70011L8.47772 15C7.92077 15 7.38664 14.7788 6.99282 14.3849C6.599 13.9911 6.37776 13.457 6.37776 12.9V10.1001H2.41583C2.2129 10.1024 2.01189 10.0605 1.82674 9.97744C1.64159 9.89434 1.47671 9.77198 1.34355 9.61884C1.21038 9.46569 1.1121 9.28543 1.05551 9.09053C0.998931 8.89563 0.985398 8.69076 1.01585 8.49012L1.98184 2.19023C2.03246 1.8564 2.20203 1.55212 2.45929 1.33344C2.71656 1.11477 3.04419 0.996436 3.38181 1.00025H11.2777M11.2777 8.70011V1.00025M11.2777 8.70011H13.1466C13.5428 8.70712 13.9277 8.56842 14.2284 8.31035C14.5291 8.05227 14.7245 7.69279 14.7776 7.30014V2.40023C14.7245 2.00758 14.5291 1.64809 14.2284 1.39002C13.9277 1.13195 13.5428 0.993247 13.1466 1.00025H11.2777" stroke="current" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
3
frontend/src/assets/documentation-dark.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="100" height="100" viewBox="0,0,256,256">
|
||||
<g transform="translate(-19.2,-19.2) scale(1.15,1.15)"><g fill="#949494" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><g transform="scale(10.66667,10.66667)"><path d="M13.172,2h-7.172c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2v-11.172c0,-0.53 -0.211,-1.039 -0.586,-1.414l-4.828,-4.828c-0.375,-0.375 -0.884,-0.586 -1.414,-0.586zM15,18h-6c-0.552,0 -1,-0.448 -1,-1v0c0,-0.552 0.448,-1 1,-1h6c0.552,0 1,0.448 1,1v0c0,0.552 -0.448,1 -1,1zM15,14h-6c-0.552,0 -1,-0.448 -1,-1v0c0,-0.552 0.448,-1 1,-1h6c0.552,0 1,0.448 1,1v0c0,0.552 -0.448,1 -1,1zM13,9v-5.5l5.5,5.5z"></path></g></g></g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 933 B |
@@ -1,3 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="100" height="100" viewBox="0,0,256,256">
|
||||
<g transform="translate(-19.2,-19.2) scale(1.15,1.15)"><g fill="black" fill-opacity="0.5" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><g transform="scale(10.66667,10.66667)"><path d="M13.172,2h-7.172c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2v-11.172c0,-0.53 -0.211,-1.039 -0.586,-1.414l-4.828,-4.828c-0.375,-0.375 -0.884,-0.586 -1.414,-0.586zM15,18h-6c-0.552,0 -1,-0.448 -1,-1v0c0,-0.552 0.448,-1 1,-1h6c0.552,0 1,0.448 1,1v0c0,0.552 -0.448,1 -1,1zM15,14h-6c-0.552,0 -1,-0.448 -1,-1v0c0,-0.552 0.448,-1 1,-1h6c0.552,0 1,0.448 1,1v0c0,0.552 -0.448,1 -1,1zM13,9v-5.5l5.5,5.5z"></path></g></g></g>
|
||||
<g transform="translate(-19.2,-19.2) scale(1.15,1.15)"><g fill="black" fill-opacity="0.54" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><g transform="scale(10.66667,10.66667)"><path d="M13.172,2h-7.172c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2v-11.172c0,-0.53 -0.211,-1.039 -0.586,-1.414l-4.828,-4.828c-0.375,-0.375 -0.884,-0.586 -1.414,-0.586zM15,18h-6c-0.552,0 -1,-0.448 -1,-1v0c0,-0.552 0.448,-1 1,-1h6c0.552,0 1,0.448 1,1v0c0,0.552 -0.448,1 -1,1zM15,14h-6c-0.552,0 -1,-0.448 -1,-1v0c0,-0.552 0.448,-1 1,-1h6c0.552,0 1,0.448 1,1v0c0,0.552 -0.448,1 -1,1zM13,9v-5.5l5.5,5.5z"></path></g></g></g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 950 B After Width: | Height: | Size: 951 B |
@@ -1,3 +1,3 @@
|
||||
<svg width="10" height="5" viewBox="0 0 10 5" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 0L5 5L10 0H0Z" fill="black" fill-opacity="0.54"/>
|
||||
<path d="M0 0L5 5L10 0H0Z" fill="gray" fill-opacity="0.80"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 163 B After Width: | Height: | Size: 162 B |
3
frontend/src/assets/github-dark.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="21" height="22" viewBox="0 0 21 22" fill="none">
|
||||
<path d="M19.8155 2.09139C19.0825 1.34392 18.2005 0.970703 17.1685 0.970703H4.68155C3.64955 0.970703 2.76755 1.34392 2.03455 2.09139C1.30155 2.83885 0.935547 3.73825 0.935547 4.79061V17.524C0.935547 18.5763 1.30155 19.4757 2.03455 20.2232C2.76755 20.9707 3.64955 21.3439 4.68155 21.3439H7.59555C7.78555 21.3439 7.92855 21.3368 8.02455 21.3235C8.13624 21.3007 8.23705 21.2399 8.31055 21.1512C8.40555 21.0492 8.45355 20.9013 8.45355 20.7076L8.44655 19.8051C8.44255 19.23 8.44055 18.7752 8.44055 18.4387L8.14055 18.4917C7.95055 18.5274 7.71055 18.5427 7.41955 18.5386C7.11624 18.5329 6.81389 18.5019 6.51555 18.4458C6.19795 18.386 5.89897 18.2497 5.64355 18.0481C5.37604 17.8418 5.17652 17.5572 5.07155 17.2323L4.94155 16.9264C4.83198 16.6851 4.69432 16.4581 4.53155 16.2503C4.34555 16.0025 4.15655 15.8353 3.96555 15.7466L3.87555 15.6803C3.81279 15.6345 3.75571 15.5811 3.70555 15.5212C3.65764 15.4657 3.6182 15.4031 3.58855 15.3356C3.56255 15.2734 3.58455 15.2224 3.65355 15.1827C3.72355 15.1419 3.84855 15.1225 4.03155 15.1225L4.29155 15.1633C4.46455 15.198 4.67955 15.304 4.93455 15.4804C5.19261 15.6598 5.40818 15.8957 5.56555 16.1708C5.76555 16.5328 6.00555 16.8091 6.28755 16.9998C6.56955 17.1895 6.85355 17.2854 7.13955 17.2854C7.42555 17.2854 7.67255 17.2629 7.88155 17.2191C8.08366 17.1765 8.28005 17.1094 8.46655 17.0192C8.54455 16.4278 8.75655 15.9709 9.10355 15.6528C8.65392 15.6078 8.2083 15.5281 7.77055 15.4142C7.34334 15.2945 6.93249 15.1208 6.54755 14.8972C6.14479 14.6736 5.78905 14.3714 5.50055 14.008C5.22355 13.6541 4.99555 13.1901 4.81755 12.616C4.64055 12.0409 4.55155 11.377 4.55155 10.6255C4.55155 9.55581 4.89355 8.64519 5.57855 7.89263C5.25855 7.08908 5.28855 6.18662 5.66955 5.18831C5.92155 5.10775 6.29455 5.16791 6.78855 5.36676C7.28255 5.56561 7.64455 5.7359 7.87455 5.87662C8.10455 6.01939 8.28855 6.13869 8.42755 6.23557C9.24052 6.00486 10.0807 5.88889 10.9245 5.8909C11.7835 5.8909 12.6155 6.00613 13.4225 6.23557L13.9165 5.91741C14.2965 5.68476 14.6973 5.48946 15.1135 5.33413C15.5735 5.15669 15.9235 5.10877 16.1675 5.18831C16.5575 6.18764 16.5915 7.08908 16.2705 7.89365C16.9555 8.64519 17.2985 9.55581 17.2985 10.6265C17.2985 11.3781 17.2095 12.044 17.0315 12.6221C16.8545 13.2013 16.6245 13.6653 16.3425 14.0151C16.049 14.3739 15.6917 14.6731 15.2895 14.8972C14.8695 15.1358 14.4615 15.3081 14.0665 15.4142C13.6288 15.5284 13.1832 15.6085 12.7335 15.6538C13.1835 16.0515 13.4095 16.6786 13.4095 17.5362V20.7076C13.4095 20.8575 13.4305 20.9788 13.4745 21.0716C13.4948 21.1163 13.5236 21.1564 13.5593 21.1895C13.5951 21.2226 13.637 21.2481 13.6825 21.2643C13.7785 21.299 13.8625 21.3215 13.9365 21.3296C14.0105 21.3398 14.1165 21.3429 14.2545 21.3429H17.1685C18.2005 21.3429 19.0825 20.9696 19.8155 20.2222C20.5475 19.4757 20.9145 18.5753 20.9145 17.523V4.79061C20.9145 3.73825 20.5485 2.83885 19.8155 2.09139Z" fill="#949494"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
3
frontend/src/assets/hamburger-dark.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="600" height="450" viewBox="0 0 600 450" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M25 25H575M25 225H575M25 425H575" stroke="#949494" stroke-opacity="0.54" stroke-width="50" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 256 B |
3
frontend/src/assets/info-dark.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="21" height="21" viewBox="0 0 21 21" fill="none">
|
||||
<path d="M10.9355 0.442139C5.41555 0.442139 0.935547 5.01053 0.935547 10.6394C0.935547 16.2683 5.41555 20.8367 10.9355 20.8367C16.4555 20.8367 20.9355 16.2683 20.9355 10.6394C20.9355 5.01053 16.4555 0.442139 10.9355 0.442139ZM11.9355 15.7381H9.93555V9.61971H11.9355V15.7381ZM11.9355 7.58025H9.93555V5.54079H11.9355V7.58025Z" fill="#949494"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 446 B |
@@ -1,4 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="current" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.39995 5.89997V3.09999C9.39995 2.54304 9.1787 2.0089 8.78487 1.61507C8.39105 1.22125 7.85691 1 7.29996 1L4.49998 7.29996V14.9999H12.3959C12.7336 15.0037 13.0612 14.8854 13.3185 14.6667C13.5757 14.448 13.7453 14.1437 13.7959 13.8099L14.7619 7.50996C14.7924 7.30931 14.7788 7.10444 14.7222 6.90954C14.6657 6.71464 14.5674 6.53437 14.4342 6.38123C14.301 6.22808 14.1362 6.10572 13.951 6.02262C13.7659 5.93952 13.5649 5.89767 13.3619 5.89997H9.39995ZM4.49998 14.9999H2.39999C2.02869 14.9999 1.6726 14.8524 1.41005 14.5899C1.1475 14.3273 1 13.9712 1 13.5999V8.69995C1 8.32865 1.1475 7.97256 1.41005 7.71001C1.6726 7.44746 2.02869 7.29996 2.39999 7.29996H4.49998" fill="white"/>
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="current" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.39995 5.89997V3.09999C9.39995 2.54304 9.1787 2.0089 8.78487 1.61507C8.39105 1.22125 7.85691 1 7.29996 1L4.49998 7.29996V14.9999H12.3959C12.7336 15.0037 13.0612 14.8854 13.3185 14.6667C13.5757 14.448 13.7453 14.1437 13.7959 13.8099L14.7619 7.50996C14.7924 7.30931 14.7788 7.10444 14.7222 6.90954C14.6657 6.71464 14.5674 6.53437 14.4342 6.38123C14.301 6.22808 14.1362 6.10572 13.951 6.02262C13.7659 5.93952 13.5649 5.89767 13.3619 5.89997H9.39995ZM4.49998 14.9999H2.39999C2.02869 14.9999 1.6726 14.8524 1.41005 14.5899C1.1475 14.3273 1 13.9712 1 13.5999V8.69995C1 8.32865 1.1475 7.97256 1.41005 7.71001C1.6726 7.44746 2.02869 7.29996 2.39999 7.29996H4.49998" fill="none"/>
|
||||
<path d="M4.49998 7.29996L7.29996 1C7.85691 1 8.39105 1.22125 8.78487 1.61507C9.1787 2.0089 9.39995 2.54304 9.39995 3.09999V5.89997H13.3619C13.5649 5.89767 13.7659 5.93952 13.951 6.02262C14.1362 6.10572 14.301 6.22808 14.4342 6.38123C14.5674 6.53437 14.6657 6.71464 14.7223 6.90954C14.7788 7.10444 14.7924 7.30931 14.7619 7.50996L13.7959 13.8099C13.7453 14.1437 13.5757 14.448 13.3185 14.6667C13.0612 14.8854 12.7336 15.0037 12.3959 14.9999H4.49998M4.49998 7.29996V14.9999M4.49998 7.29996H2.39999C2.02869 7.29996 1.6726 7.44746 1.41005 7.71001C1.1475 7.97256 1 8.32865 1 8.69995V13.5999C1 13.9712 1.1475 14.3273 1.41005 14.5899C1.6726 14.8524 2.02869 14.9999 2.39999 14.9999H4.49998" stroke="current" stroke-width="1.39999" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
3
frontend/src/assets/message-dark.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M20 4H4C2.9 4 2 4.9 2 6V24L6 20H20C21.1 20 22 19.1 22 18V6C22 4.9 21.1 4 20 4ZM20 18H6L4 20V6H20V18Z" fill="#949494"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 232 B |
@@ -1,3 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18 0H2C0.9 0 0 0.9 0 2V20L4 16H18C19.1 16 20 15.1 20 14V2C20 0.9 19.1 0 18 0ZM18 14H4L2 16V2H18V14Z" fill="black" fill-opacity="0.54"/>
|
||||
<path d="M18 0H2C0.9 0 0 0.9 0 2V20L4 16H18C19.1 16 20 15.1 20 14V2C20 0.9 19.1 0 18 0ZM18 14H4L2 16V2H18V14Z" fill="gray" fill-opacity="0.80"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 249 B After Width: | Height: | Size: 248 B |
3
frontend/src/assets/send_dark.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="21" height="18" viewBox="0 0 21 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.00999999 18L21 9L0.00999999 0L0 7L15 9L0 11L0.00999999 18Z" fill="#949494"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 192 B |
3
frontend/src/assets/settingGear-dark.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="21" height="22" viewBox="0 0 21 22" fill="none">
|
||||
<path d="M10.5024 14.1916C9.50037 14.1916 8.5394 13.8044 7.83087 13.1152C7.12234 12.426 6.72429 11.4913 6.72429 10.5166C6.72429 9.54193 7.12234 8.60718 7.83087 7.91799C8.5394 7.22879 9.50037 6.8416 10.5024 6.8416C11.5044 6.8416 12.4654 7.22879 13.1739 7.91799C13.8824 8.60718 14.2805 9.54193 14.2805 10.5166C14.2805 11.4913 13.8824 12.426 13.1739 13.1152C12.4654 13.8044 11.5044 14.1916 10.5024 14.1916ZM18.5227 11.5351C18.5659 11.1991 18.5983 10.8631 18.5983 10.5166C18.5983 10.1701 18.5659 9.8236 18.5227 9.4666L20.8004 7.7551C21.0055 7.5976 21.0595 7.3141 20.9299 7.0831L18.771 3.4501C18.6415 3.2191 18.35 3.1246 18.1125 3.2191L15.4247 4.2691C14.8634 3.8596 14.2805 3.5026 13.6004 3.2401L13.201 0.457604C13.1791 0.333931 13.1129 0.221802 13.0141 0.141083C12.9153 0.0603633 12.7903 0.0162658 12.6613 0.0166035H8.34347C8.07361 0.0166035 7.84692 0.205604 7.80374 0.457604L7.40435 3.2401C6.72429 3.5026 6.14138 3.8596 5.58007 4.2691L2.89222 3.2191C2.65474 3.1246 2.36329 3.2191 2.23375 3.4501L0.0748404 7.0831C-0.0654889 7.3141 -0.000721403 7.5976 0.204375 7.7551L2.48203 9.4666C2.43885 9.8236 2.40647 10.1701 2.40647 10.5166C2.40647 10.8631 2.43885 11.1991 2.48203 11.5351L0.204375 13.2781C-0.000721403 13.4356 -0.0654889 13.7191 0.0748404 13.9501L2.23375 17.5831C2.36329 17.8141 2.65474 17.8981 2.89222 17.8141L5.58007 16.7536C6.14138 17.1736 6.72429 17.5306 7.40435 17.7931L7.80374 20.5756C7.84692 20.8276 8.07361 21.0166 8.34347 21.0166H12.6613C12.9312 21.0166 13.1578 20.8276 13.201 20.5756L13.6004 17.7931C14.2805 17.5201 14.8634 17.1736 15.4247 16.7536L18.1125 17.8141C18.35 17.8981 18.6415 17.8141 18.771 17.5831L20.9299 13.9501C21.0595 13.7191 21.0055 13.4356 20.8004 13.2781L18.5227 11.5351Z" fill="#949494"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |