Spaces:
Configuration error
Configuration error
Commit
Β·
c132e32
0
Parent(s):
Gradio Flow Application
Browse filesThis view is limited to 50 files because it contains too many changes. Β
See raw diff
- .gitattributes +3 -0
- .vscode/settings.json +2 -0
- README.md +146 -0
- app.png +3 -0
- architecture.png +3 -0
- backend/.gitignore +1 -0
- backend/Dockerfile +6 -0
- backend/app.py +50 -0
- backend/requirements.txt +74 -0
- backend/scripts/startup.sh +2 -0
- backend/src/__init__.py +4 -0
- backend/src/demo.py +15 -0
- backend/src/example/__pycache__/examples.cpython-37.pyc +0 -0
- backend/src/example/__pycache__/examples.cpython-38.pyc +0 -0
- backend/src/example/__pycache__/examples.cpython-39.pyc +0 -0
- backend/src/example/examples.py +137 -0
- backend/src/index.py +17 -0
- backend/src/resources/__init__.py +1 -0
- backend/src/resources/__pycache__/__init__.cpython-38.pyc +0 -0
- backend/src/resources/__pycache__/__init__.cpython-39.pyc +0 -0
- backend/src/resources/__pycache__/compiler.cpython-39.pyc +0 -0
- backend/src/resources/__pycache__/dock.cpython-39.pyc +0 -0
- backend/src/resources/__pycache__/module.cpython-38.pyc +0 -0
- backend/src/resources/__pycache__/module.cpython-39.pyc +0 -0
- backend/src/resources/dock.py +24 -0
- backend/src/resources/module.py +201 -0
- docker-compose.yml +27 -0
- frontend/.dockerignore +1 -0
- frontend/.gitignore +23 -0
- frontend/Dockerfile +4 -0
- frontend/package-lock.json +0 -0
- frontend/package.json +45 -0
- frontend/public/android-chrome-192x192.png +3 -0
- frontend/public/android-chrome-512x512.png +3 -0
- frontend/public/favicon.ico +0 -0
- frontend/public/index.html +50 -0
- frontend/public/logo192.png +3 -0
- frontend/public/manifest.json +25 -0
- frontend/public/robots.txt +3 -0
- frontend/src/App.js +20 -0
- frontend/src/Components/Navagation/navbar.js +289 -0
- frontend/src/Components/Nodes/custom.js +48 -0
- frontend/src/Components/ReactFlow/ReactFlowEnv.js +82 -0
- frontend/src/css/CustomNode.css +46 -0
- frontend/src/css/dist/output.css +1098 -0
- frontend/src/css/index.css +13 -0
- frontend/src/css/input.css +3 -0
- frontend/src/helper/visual.js +34 -0
- frontend/src/index.js +18 -0
- frontend/src/reportWebVitals.js +13 -0
.gitattributes
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
2 |
+
*.png filter=lfs diff=lfs merge=lfs -text
|
3 |
+
*.gif filter=lfs diff=lfs merge=lfs -text
|
.vscode/settings.json
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
}
|
README.md
ADDED
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Gradio Flow π€
|
2 |
+
|
3 |
+
**A web application with a backend in Flask and frontend in Rect, and React Flow that use [React flow](https://reactflow.dev/) node base environment to
|
4 |
+
stream both gradio and streamlit interfaces, within a single application.**
|
5 |
+
|
6 |
+
|
7 |
+
## Tabel Of Contents π
|
8 |
+
- [**App Architecture**](#app-architecture-%EF%B8%8F)
|
9 |
+
|
10 |
+
- [**Prerequisites**](#prerequisites-)
|
11 |
+
|
12 |
+
- [**Running The App**](#running-the-app-%EF%B8%8F)
|
13 |
+
|
14 |
+
- [**Makefile Run**](#makefile-run-docker-)
|
15 |
+
|
16 |
+
|
17 |
+
- [**Running the docker container**](#1-running-the-docker-container)
|
18 |
+
|
19 |
+
|
20 |
+
- [**Entering the backend enviorment**](#2-entering-the-backend-enviorment)
|
21 |
+
|
22 |
+
|
23 |
+
- [**Appending Nodes To Frontend From The Backend**](3-appending-nodes-to-frontend-from-the-backend)
|
24 |
+
|
25 |
+
|
26 |
+
- [**Non-Docker Build**](#non-docker-build)
|
27 |
+
|
28 |
+
|
29 |
+
- [**Build frontend**](#1-build-frontend-within-the-directory-frontend)
|
30 |
+
|
31 |
+
|
32 |
+
- [**Run frontend**](#2-run-frontend-within-the-directory-frontend)
|
33 |
+
|
34 |
+
|
35 |
+
- [**Build backend dependency**](#3-build-backend-dependency-within-the-directory-backend)
|
36 |
+
|
37 |
+
|
38 |
+
- [**Build backend**](#4-run-backend-within-the-directory-backend)
|
39 |
+
|
40 |
+
|
41 |
+
- [**Run Gradio within Gradio-Flow**](#5-run-gradio-within-gradio-flow)
|
42 |
+
|
43 |
+
- [**Application**](#application-%EF%B8%8F)
|
44 |
+
## App Architecture ποΈ
|
45 |
+

|
46 |
+
|
47 |
+
## Prerequisites π
|
48 |
+
You will need:
|
49 |
+
(Docker build π³ Currently Only on: Linux)
|
50 |
+
- [π³ Docker](https://docs.docker.com/get-docker/)
|
51 |
+
- [π Docker Compose](https://docs.docker.com/compose/install/) (included with Docker Desktop on Windows and macOS)
|
52 |
+
|
53 |
+
(Running Without docker)
|
54 |
+
- π Python 3.2+ (backend)
|
55 |
+
- npm 8.5.0 (frontend)
|
56 |
+
- node v16.14.2 (frontend)
|
57 |
+
## Running The App π₯οΈ
|
58 |
+
|
59 |
+
Starting up it's simple as every command is already within the Makefile.
|
60 |
+
|
61 |
+
### Makefile Run (Docker π³)
|
62 |
+
#### **1.** Running the docker container
|
63 |
+
```console
|
64 |
+
make up
|
65 |
+
// command running: docker-compose up -d --remove-orphans;
|
66 |
+
// **Ubuntu** sudo make up
|
67 |
+
```
|
68 |
+
The React application will be running on ``http://localhost:3001`` and the Flask will be running on ``http://localhost:2000``
|
69 |
+
#### **2.** Entering the backend enviorment
|
70 |
+
```console
|
71 |
+
make environment
|
72 |
+
// command running: docker exec -it backend bash;
|
73 |
+
// **Ubuntu** sudo make environment
|
74 |
+
```
|
75 |
+
Now that you're within the docker backend container environment you can start adding gradio/streamlit nodes within the frontend. (**Extra Note**) You do not need to be within the container environment to append nodes there is a feature to just run your own gradio application and then append it within the frontend by using the **+ button**.
|
76 |
+
|
77 |
+
#### **3.** Appending Nodes To Frontend From The Backend
|
78 |
+
|
79 |
+
```console
|
80 |
+
> cd ./src
|
81 |
+
> python index.py
|
82 |
+
//run example gradio application
|
83 |
+
```
|
84 |
+
|
85 |
+
### Non-Docker Build
|
86 |
+
|
87 |
+
#### **1.** Build Frontend (within the directory ``./frontend``)
|
88 |
+
```console
|
89 |
+
npm install
|
90 |
+
```
|
91 |
+
#### **2.** Run Frontend (within the directory ``./frontend``)
|
92 |
+
```console
|
93 |
+
npm start
|
94 |
+
```
|
95 |
+
|
96 |
+
#### **3.** Build Backend Dependency (within the directory ``./backend``)
|
97 |
+
```console
|
98 |
+
pip install -r requirements.txt
|
99 |
+
```
|
100 |
+
|
101 |
+
#### **4.** Run Backend (within the directory backend)
|
102 |
+
|
103 |
+
```console
|
104 |
+
python app.py -p 2000
|
105 |
+
//**NOTE** -p 2000 just assignes it localhost port 2000 anyother port will not work
|
106 |
+
```
|
107 |
+
#### **5.** Run Gradio within Gradio-Flow
|
108 |
+
It is quite simple, and similar within the docker build, the first way you can append your gradio to the Gradio flow is through running your application at a reachable url that is provided ed when you run Gradio and appending it via ``+ button`` within the frontend, another way that is possible is that within the directory ``./backend/src/helper`` there is a code that you can use to convert your own class or functional base code into basic gradio tabular interface by using decorators, these decorators will send the nesarry information to the backend flask api and update the frontend menu state in which you'll will be able to interact with it within the front end creating a hub for gradio build functions(**read more** [**here**](https://github.com/LVivona/GradioWrapper)).
|
109 |
+
|
110 |
+
**NOTE** If you use the gradio decorator compiler for gradio flow you need to set a listen port to 2000 or else the api will never get the key and will throw you an error, I'll also provided an example below if this isn't clear.
|
111 |
+
|
112 |
+
```python
|
113 |
+
#backend/src/demo.py (functional base)
|
114 |
+
##########
|
115 |
+
from helper.compiler import functionalCompiler, tabularGradio
|
116 |
+
import gradio as gr
|
117 |
+
|
118 |
+
@functionalCompiler(inputs=[gr.Textbox(label="name")], outputs=['text'], examples=[["Luca Vivona"]])
|
119 |
+
def Hello_World(name):
|
120 |
+
return f"Hello {name}, and welcome to Gradio Flow π€"
|
121 |
+
|
122 |
+
if __name__ == "__main__":
|
123 |
+
tabularGradio([Hello_World()], ["hello world"], listen=2000)
|
124 |
+
```
|
125 |
+
|
126 |
+
```python
|
127 |
+
#backend/src/index.py (Class Base)
|
128 |
+
###########
|
129 |
+
from helper.compiler import register, GradioCompiler
|
130 |
+
import gradio as gr
|
131 |
+
|
132 |
+
@GradioCompiler
|
133 |
+
class Greeting:
|
134 |
+
|
135 |
+
@register(inputs=[gr.Textbox(label="name")], outputs=['text'], examples=[["Luca Vivona"]])
|
136 |
+
def Hello_World(self, name):
|
137 |
+
return f"Hello {name}, and welcome to Gradio Flow π€"
|
138 |
+
|
139 |
+
|
140 |
+
|
141 |
+
if __name__ == "__main__":
|
142 |
+
Greeting().run(listen=2000)
|
143 |
+
```
|
144 |
+
|
145 |
+
## Application ποΈ
|
146 |
+

|
app.png
ADDED
![]() |
Git LFS Details
|
architecture.png
ADDED
![]() |
Git LFS Details
|
backend/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
./**/__pycache__
|
backend/Dockerfile
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.8
|
2 |
+
COPY requirements.txt /app/requirements.txt
|
3 |
+
WORKDIR /app
|
4 |
+
RUN pip install -r requirements.txt
|
5 |
+
COPY ./scripts /app/scripts
|
6 |
+
RUN chmod +x ./scripts/*
|
backend/app.py
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from flask import Flask, jsonify, request
|
2 |
+
from flask_cors import CORS
|
3 |
+
import socket
|
4 |
+
import argparse
|
5 |
+
|
6 |
+
app = Flask(__name__)
|
7 |
+
CORS(app)
|
8 |
+
|
9 |
+
def portConnection(port : int):
|
10 |
+
s = socket.socket(
|
11 |
+
socket.AF_INET, socket.SOCK_STREAM)
|
12 |
+
|
13 |
+
result = s.connect_ex(("localhost", port))
|
14 |
+
if result == 0: return True
|
15 |
+
return False
|
16 |
+
|
17 |
+
|
18 |
+
global visable, watcher, dog
|
19 |
+
visable = []
|
20 |
+
|
21 |
+
@app.route("/")
|
22 |
+
def Home():
|
23 |
+
return jsonify({"message" :"everything is up amd running... π",})
|
24 |
+
|
25 |
+
@app.route("/api/append/port" , methods=["POST"])
|
26 |
+
def append_port():
|
27 |
+
current = request.json
|
28 |
+
visable.append(current)
|
29 |
+
return jsonify({"executed" : True})
|
30 |
+
|
31 |
+
@app.route("/api/remove/port" , methods=["POST"])
|
32 |
+
def remove_port():
|
33 |
+
current = request.json
|
34 |
+
print(current)
|
35 |
+
visable.remove(current)
|
36 |
+
return jsonify({"executed" : True,
|
37 |
+
"ports" : current['port']})
|
38 |
+
|
39 |
+
|
40 |
+
@app.route("/api/open/ports", methods=["GET"])
|
41 |
+
def open_ports():
|
42 |
+
return jsonify(visable)
|
43 |
+
|
44 |
+
|
45 |
+
if __name__ == "__main__":
|
46 |
+
|
47 |
+
parser = argparse.ArgumentParser()
|
48 |
+
parser.add_argument("-p", "--port", help="location of flask api port on local host", default=5000)
|
49 |
+
args = parser.parse_args()
|
50 |
+
app.run(host="0.0.0.0", port=args.port, debug=True)
|
backend/requirements.txt
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
aiohttp==3.8.1
|
2 |
+
aiosignal==1.2.0
|
3 |
+
analytics-python==1.4.0
|
4 |
+
anyio==3.6.1
|
5 |
+
async-timeout==4.0.2
|
6 |
+
attrs==21.4.0
|
7 |
+
backoff==1.10.0
|
8 |
+
bcrypt==3.2.2
|
9 |
+
certifi==2022.6.15
|
10 |
+
cffi==1.15.1
|
11 |
+
charset-normalizer==2.1.0
|
12 |
+
click==8.1.3
|
13 |
+
cryptography==37.0.4
|
14 |
+
cycler==0.11.0
|
15 |
+
fastapi==0.79.0
|
16 |
+
ffmpy==0.3.0
|
17 |
+
filelock==3.7.1
|
18 |
+
Flask==2.1.3
|
19 |
+
Flask-Cors==3.0.10
|
20 |
+
fonttools==4.34.4
|
21 |
+
frozenlist==1.3.0
|
22 |
+
fsspec==2022.5.0
|
23 |
+
gradio==3.1.1
|
24 |
+
h11==0.12.0
|
25 |
+
httpcore==0.15.0
|
26 |
+
httpx==0.23.0
|
27 |
+
huggingface-hub==0.8.1
|
28 |
+
idna==3.3
|
29 |
+
importlib-metadata==4.12.0
|
30 |
+
itsdangerous==2.1.2
|
31 |
+
Jinja2==3.1.2
|
32 |
+
kiwisolver==1.4.4
|
33 |
+
linkify-it-py==1.0.3
|
34 |
+
markdown-it-py==2.1.0
|
35 |
+
MarkupSafe==2.1.1
|
36 |
+
#matplotlib==3.5.2
|
37 |
+
mdit-py-plugins==0.3.0
|
38 |
+
mdurl==0.1.1
|
39 |
+
monotonic==1.6
|
40 |
+
multidict==6.0.2
|
41 |
+
numpy==1.23.1
|
42 |
+
orjson==3.7.8
|
43 |
+
packaging==21.3
|
44 |
+
#pandas==1.4.3
|
45 |
+
paramiko==2.11.0
|
46 |
+
Pillow==9.2.0
|
47 |
+
pycparser==2.21
|
48 |
+
pycryptodome==3.15.0
|
49 |
+
pydantic==1.9.1
|
50 |
+
pydub==0.25.1
|
51 |
+
PyNaCl==1.5.0
|
52 |
+
pyparsing==3.0.9
|
53 |
+
python-dateutil==2.8.2
|
54 |
+
python-multipart==0.0.5
|
55 |
+
pytz==2022.1
|
56 |
+
PyYAML==6.0
|
57 |
+
regex==2022.7.24
|
58 |
+
requests==2.28.1
|
59 |
+
rfc3986==1.5.0
|
60 |
+
six==1.16.0
|
61 |
+
sniffio==1.2.0
|
62 |
+
starlette==0.19.1
|
63 |
+
gradioWrapper==0.0.4
|
64 |
+
#tokenizers==0.12.1
|
65 |
+
#torch==1.12.0
|
66 |
+
tqdm==4.64.0
|
67 |
+
#transformers==4.20.1
|
68 |
+
typing_extensions==4.3.0
|
69 |
+
uc-micro-py==1.0.1
|
70 |
+
urllib3==1.26.10
|
71 |
+
uvicorn==0.18.2
|
72 |
+
Werkzeug==2.2.0
|
73 |
+
yarl==1.7.2
|
74 |
+
zipp==3.8.1
|
backend/scripts/startup.sh
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
#/bin/sh
|
2 |
+
python app.py -p 2000;
|
backend/src/__init__.py
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import resources
|
2 |
+
from .example.examples import *
|
3 |
+
import src.demo
|
4 |
+
import src.index
|
backend/src/demo.py
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
|
3 |
+
|
4 |
+
def Hello_World(name):
|
5 |
+
return f"Hello {name}, and welcome to Gradio Flow π€"
|
6 |
+
|
7 |
+
def add(x, y):
|
8 |
+
return x + y
|
9 |
+
|
10 |
+
if __name__ == "__main__":
|
11 |
+
pass
|
12 |
+
#tabularGradio([Hello_World(), add()], ["hello world", "add"], listen=2000)
|
13 |
+
#print([key for key, _ in doc.__dict__.items() if not key.startswith("__")])
|
14 |
+
#d = Dock()
|
15 |
+
#print(d.determinePort(), d2.port_count)
|
backend/src/example/__pycache__/examples.cpython-37.pyc
ADDED
Binary file (6.13 kB). View file
|
|
backend/src/example/__pycache__/examples.cpython-38.pyc
ADDED
Binary file (4.52 kB). View file
|
|
backend/src/example/__pycache__/examples.cpython-39.pyc
ADDED
Binary file (4.58 kB). View file
|
|
backend/src/example/examples.py
ADDED
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import matplotlib
|
3 |
+
import matplotlib.pyplot as plt
|
4 |
+
import numpy as np
|
5 |
+
import PIL
|
6 |
+
from helper.compiler import GradioCompiler, register
|
7 |
+
|
8 |
+
|
9 |
+
|
10 |
+
"""
|
11 |
+
@gradio_compile
|
12 |
+
class Pictionary:
|
13 |
+
|
14 |
+
def __init__(self) -> None:
|
15 |
+
self.LABELS = Path('./src/examples/data/labels.txt').read_text().splitlines()
|
16 |
+
|
17 |
+
self.model = nn.Sequential(
|
18 |
+
nn.Conv2d(1, 32, 3, padding='same'),
|
19 |
+
nn.ReLU(),
|
20 |
+
nn.MaxPool2d(2),
|
21 |
+
nn.Conv2d(32, 64, 3, padding='same'),
|
22 |
+
nn.ReLU(),
|
23 |
+
nn.MaxPool2d(2),
|
24 |
+
nn.Conv2d(64, 128, 3, padding='same'),
|
25 |
+
nn.ReLU(),
|
26 |
+
nn.MaxPool2d(2),
|
27 |
+
nn.Flatten(),
|
28 |
+
nn.Linear(1152, 256),
|
29 |
+
nn.ReLU(),
|
30 |
+
nn.Linear(256, len(self.LABELS)),
|
31 |
+
)
|
32 |
+
state_dict = torch.load('./src/examples/data/pytorch_model.bin', map_location='cpu')
|
33 |
+
self.model.load_state_dict(state_dict, strict=False)
|
34 |
+
self.model.eval()
|
35 |
+
|
36 |
+
@register(inputs="sketchpad", outputs=gr.Label())
|
37 |
+
def perdict(self, img) -> 'dict[str, float]':
|
38 |
+
if type(img) == type(None): return {}
|
39 |
+
x = torch.tensor(img, dtype=torch.float32).unsqueeze(0).unsqueeze(0) / 255.
|
40 |
+
with torch.no_grad():
|
41 |
+
out = self.model(x)
|
42 |
+
probabilities = torch.nn.functional.softmax(out[0], dim=0)
|
43 |
+
values, indices = torch.topk(probabilities, 5)
|
44 |
+
confidences = {self.LABELS[i]: v.item() for i, v in zip(indices, values)}
|
45 |
+
return confidences
|
46 |
+
"""
|
47 |
+
|
48 |
+
@GradioCompiler
|
49 |
+
class HelloWorld_2_0:
|
50 |
+
|
51 |
+
@register(inputs=["text", "text", gr.Radio(["morning", "evening", "night"])], outputs="text")
|
52 |
+
def Hello(self, Lname : str, Fname : str, day : 'list[any]'=["morning", "evening", "night"]) -> str:
|
53 |
+
return "Hello, {} {}".format(Fname, Lname)
|
54 |
+
|
55 |
+
@register(inputs=["text", "text"], outputs="text")
|
56 |
+
def goodbye(self, Fname : str, Lname : str) -> str:
|
57 |
+
return "Goodbye, {} {}".format(Fname, Lname)
|
58 |
+
|
59 |
+
@register(inputs=["text", gr.Checkbox() , gr.Slider(0, 60)], outputs=["text", "number"])
|
60 |
+
def greet(self, name, is_morning, temperature):
|
61 |
+
salutation = "Good morning" if is_morning else "Good evening"
|
62 |
+
greeting = "%s %s. It is %s degrees today" % (salutation, name, temperature)
|
63 |
+
celsius = (temperature - 32) * 5 / 9
|
64 |
+
return (greeting, round(celsius, 2))
|
65 |
+
|
66 |
+
|
67 |
+
|
68 |
+
@GradioCompiler
|
69 |
+
class FSD:
|
70 |
+
|
71 |
+
def get_new_val(self, old_val, nc):
|
72 |
+
return np.round(old_val * (nc - 1)) / (nc - 1)
|
73 |
+
|
74 |
+
|
75 |
+
def palette_reduce(self, img : PIL.Image.Image, nc : 'tuple[float, float, float]'=(0.0000, 0, 16)):
|
76 |
+
pixels = np.array(img, dtype=float) / 255
|
77 |
+
pixels = self.get_new_val(pixels, nc)
|
78 |
+
|
79 |
+
carr = np.array(pixels / np.max(pixels) * 255, dtype=np.uint8)
|
80 |
+
return PIL.Image.fromarray(carr)
|
81 |
+
|
82 |
+
@register(inputs=[gr.Image(), gr.Slider(0.00, 16)], outputs=gr.Gallery())
|
83 |
+
def Floyd_Steinberg_dithering(self, img, nc : 'tuple[float, float, float]'=(0.0000, 0, 16) ) -> 'list[PIL.Image.Image]':
|
84 |
+
pixels = np.array(img, dtype=float) / 255
|
85 |
+
new_height, new_width, _ = img.shape
|
86 |
+
for row in range(new_height):
|
87 |
+
for col in range(new_width):
|
88 |
+
old_val = pixels[row, col].copy()
|
89 |
+
new_val = self.get_new_val(old_val, nc)
|
90 |
+
pixels[row, col] = new_val
|
91 |
+
err = old_val - new_val
|
92 |
+
if col < new_width - 1:
|
93 |
+
pixels[row, col + 1] += err * 7 / 16
|
94 |
+
if row < new_height - 1:
|
95 |
+
if col > 0:
|
96 |
+
pixels[row + 1, col - 1] += err * 3 / 16
|
97 |
+
pixels[row + 1, col] += err * 5 / 16
|
98 |
+
if col < new_width - 1:
|
99 |
+
pixels[row + 1, col + 1] += err * 1 / 16
|
100 |
+
carr = np.array(pixels / np.max(pixels, axis=(0, 1)) * 255, dtype=np.uint8)
|
101 |
+
return [PIL.Image.fromarray(carr), self.palette_reduce(img, nc) ]
|
102 |
+
|
103 |
+
|
104 |
+
|
105 |
+
@GradioCompiler
|
106 |
+
class C:
|
107 |
+
|
108 |
+
def Hello(self):
|
109 |
+
return "Hello"
|
110 |
+
|
111 |
+
@register(inputs="text", outputs="text")
|
112 |
+
def Greeting(self, name):
|
113 |
+
return self.Hello() + " " + name
|
114 |
+
|
115 |
+
@GradioCompiler
|
116 |
+
class stock_forecast:
|
117 |
+
|
118 |
+
def __init__(self):
|
119 |
+
matplotlib.use('Agg')
|
120 |
+
|
121 |
+
@register(inputs=[gr.Checkbox(label="legend"), gr.Radio([2025, 2030, 2035, 2040], label="projct"), gr.CheckboxGroup(["Google", "Microsoft", "Gradio"], label="company"), gr.Slider(label="noise"), gr.Radio(["cross", "line", "circle"], label="style")], outputs=[gr.Plot()])
|
122 |
+
def plot_forcast(self, legend, project, companies , noise , styles)-> matplotlib.figure.Figure:
|
123 |
+
start_year = 2022
|
124 |
+
x = np.arange(start_year, project + 1)
|
125 |
+
year_count = x.shape[0]
|
126 |
+
plt_format = ({"cross": "X", "line": "-", "circle": "o--"})[styles]
|
127 |
+
fig = plt.figure()
|
128 |
+
ax = fig.add_subplot(111)
|
129 |
+
for i, company in enumerate(companies):
|
130 |
+
series = np.arange(0, year_count, dtype=float)
|
131 |
+
series = series**2 * (i + 1)
|
132 |
+
series += np.random.rand(year_count) * noise
|
133 |
+
ax.plot(x, series, plt_format)
|
134 |
+
if legend:
|
135 |
+
plt.legend(companies)
|
136 |
+
print(type(fig))
|
137 |
+
return fig
|
backend/src/index.py
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from resources import GradioModule, register
|
2 |
+
import gradio as gr
|
3 |
+
|
4 |
+
@GradioModule
|
5 |
+
class Greeting:
|
6 |
+
|
7 |
+
@register(["text"], ["text"])
|
8 |
+
def Hello_World(self, name):
|
9 |
+
return f"Hello {name}, and welcome to Gradio Flow π€"
|
10 |
+
|
11 |
+
@register(["text"], ["text"])
|
12 |
+
def Hello_World(name):
|
13 |
+
return f"Hello {name}, and welcome to Gradio Flow π€"
|
14 |
+
|
15 |
+
|
16 |
+
if __name__ == "__main__":
|
17 |
+
Greeting().run(listen=2000)
|
backend/src/resources/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
from .module import *
|
backend/src/resources/__pycache__/__init__.cpython-38.pyc
ADDED
Binary file (144 Bytes). View file
|
|
backend/src/resources/__pycache__/__init__.cpython-39.pyc
ADDED
Binary file (213 Bytes). View file
|
|
backend/src/resources/__pycache__/compiler.cpython-39.pyc
ADDED
Binary file (7.74 kB). View file
|
|
backend/src/resources/__pycache__/dock.cpython-39.pyc
ADDED
Binary file (1.15 kB). View file
|
|
backend/src/resources/__pycache__/module.cpython-38.pyc
ADDED
Binary file (7.84 kB). View file
|
|
backend/src/resources/__pycache__/module.cpython-39.pyc
ADDED
Binary file (7.79 kB). View file
|
|
backend/src/resources/dock.py
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import socket
|
2 |
+
import random
|
3 |
+
|
4 |
+
class Dock:
|
5 |
+
|
6 |
+
def __init__(self) -> None:
|
7 |
+
self.num_ports = 20
|
8 |
+
self.port_range = (7860, 7880)
|
9 |
+
|
10 |
+
def portConnection(self, port : int):
|
11 |
+
s = socket.socket(
|
12 |
+
socket.AF_INET, socket.SOCK_STREAM)
|
13 |
+
result = s.connect_ex(("localhost", port))
|
14 |
+
if result == 0: return True
|
15 |
+
return False
|
16 |
+
|
17 |
+
def determinePort(self, max_trial_count=10):
|
18 |
+
trial_count = 0
|
19 |
+
while trial_count <= max_trial_count:
|
20 |
+
port=random.randint(*self.port_range)
|
21 |
+
if not self.portConnection(port):
|
22 |
+
return port
|
23 |
+
trial_count += 1
|
24 |
+
raise Exception('Exceeded Max Trial count without finding port')
|
backend/src/resources/module.py
ADDED
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from inspect import getfile, isclass, getmro
|
3 |
+
|
4 |
+
import socket
|
5 |
+
import random
|
6 |
+
import requests
|
7 |
+
|
8 |
+
class Dock:
|
9 |
+
|
10 |
+
def __init__(self) -> None:
|
11 |
+
self.num_ports = 20
|
12 |
+
self.port_range = (7860, 7880)
|
13 |
+
|
14 |
+
def portConnection(self, port : int):
|
15 |
+
s = socket.socket(
|
16 |
+
socket.AF_INET, socket.SOCK_STREAM)
|
17 |
+
result = s.connect_ex(("localhost", port))
|
18 |
+
if result == 0: return True
|
19 |
+
return False
|
20 |
+
|
21 |
+
def determinePort(self, max_trial_count=10):
|
22 |
+
trial_count = 0
|
23 |
+
while trial_count <= max_trial_count:
|
24 |
+
port=random.randint(*self.port_range)
|
25 |
+
if not self.portConnection(port):
|
26 |
+
return port
|
27 |
+
trial_count += 1
|
28 |
+
raise Exception('Exceeded Max Trial count without finding port')
|
29 |
+
|
30 |
+
|
31 |
+
DOCKER_LOCAL_HOST = '0.0.0.0'
|
32 |
+
DOCKER_PORT = Dock()
|
33 |
+
|
34 |
+
def tabularGradio(funcs, names, name="Tabular Temp Name", **kwargs):
|
35 |
+
port= kwargs["port"] if "port" in kwargs else DOCKER_PORT.determinePort()
|
36 |
+
|
37 |
+
try:
|
38 |
+
requests.post(f"http://{DOCKER_LOCAL_HOST}:{ kwargs[ 'listen' ] if 'listen' in kwargs else 5000 }/api/append/port", json={"port" : port, "host" : f'http://localhost:{port}', "file" : 'Not Applicable', "name" : name, "kwargs" : kwargs})
|
39 |
+
except Exception as e:
|
40 |
+
print(f"**{bcolor.BOLD}{bcolor.FAIL}CONNECTION ERROR{bcolor.ENDC}** πThe listening api is either not up or you choose the wrong port.π \n {e}")
|
41 |
+
return
|
42 |
+
|
43 |
+
gr.TabbedInterface(funcs, names).launch(server_port=port,
|
44 |
+
server_name=f"{DOCKER_LOCAL_HOST}",
|
45 |
+
inline= kwargs['inline'] if "inline" in kwargs else True,
|
46 |
+
share=kwargs['share'] if "share" in kwargs else None,
|
47 |
+
debug=kwargs['debug'] if "debug" in kwargs else False,
|
48 |
+
enable_queue=kwargs['enable_queue'] if "enable_queue" in kwargs else None,
|
49 |
+
max_threads=kwargs['max_threads'] if "max_threads" in kwargs else None,
|
50 |
+
auth=kwargs['auth'] if "auth" in kwargs else None,
|
51 |
+
auth_message=kwargs['auth_message'] if "auth_message" in kwargs else None,
|
52 |
+
prevent_thread_lock=kwargs['prevent_thread_lock'] if "prevent_thread_lock" in kwargs else False,
|
53 |
+
show_error=kwargs['show_error'] if "show_error" in kwargs else True,
|
54 |
+
show_tips=kwargs['show_tips'] if "show_tips" in kwargs else False,
|
55 |
+
height=kwargs['height'] if "height" in kwargs else 500,
|
56 |
+
width=kwargs['width'] if "width" in kwargs else 900,
|
57 |
+
encrypt=kwargs['encrypt'] if "encrypt" in kwargs else False,
|
58 |
+
favicon_path=kwargs['favicon_path'] if "favicon_path" in kwargs else None,
|
59 |
+
ssl_keyfile=kwargs['ssl_keyfile'] if "ssl_keyfile" in kwargs else None,
|
60 |
+
ssl_certfile=kwargs['ssl_certfile'] if "ssl_certfile" in kwargs else None,
|
61 |
+
ssl_keyfile_password=kwargs['ssl_keyfile_password'] if "ssl_keyfile_password" in kwargs else None,
|
62 |
+
quiet=kwargs['quiet'] if "quiet" in kwargs else False)
|
63 |
+
|
64 |
+
try:
|
65 |
+
requests.post(f"http://{DOCKER_LOCAL_HOST}:{ kwargs[ 'listen' ] if 'listen' in kwargs else '5000' }/api/remove/port", json={"port" : port, "host" : f'http://localhost:{port}', "file" : 'Not Applicable', "name" : name, "kwargs" : kwargs})
|
66 |
+
except Exception as e:
|
67 |
+
|
68 |
+
print(f"**{bcolor.BOLD}{bcolor.FAIL}CONNECTION ERROR{bcolor.ENDC}** πThe api either lost connection or was turned off...π \n {e}")
|
69 |
+
|
70 |
+
return
|
71 |
+
|
72 |
+
|
73 |
+
def register(inputs, outputs, examples=None):
|
74 |
+
def register_gradio(func):
|
75 |
+
def wrap(*args, **kwargs):
|
76 |
+
try:
|
77 |
+
self = args[0]
|
78 |
+
self.registered_gradio_functons
|
79 |
+
except AttributeError:
|
80 |
+
print("β¨Initializing Class Functions...β¨\n")
|
81 |
+
self.registered_gradio_functons = dict()
|
82 |
+
|
83 |
+
fn_name = func.__name__
|
84 |
+
if fn_name in self.registered_gradio_functons:
|
85 |
+
result = func(*args, **kwargs)
|
86 |
+
return result
|
87 |
+
else:
|
88 |
+
self.registered_gradio_functons[fn_name] = dict(inputs=inputs, outputs=outputs, examples=examples)
|
89 |
+
return None
|
90 |
+
return wrap
|
91 |
+
return register_gradio
|
92 |
+
|
93 |
+
def GradioModule(cls):
|
94 |
+
class Decorator:
|
95 |
+
|
96 |
+
def __init__(self) -> None:
|
97 |
+
self.cls = cls()
|
98 |
+
|
99 |
+
def get_funcs(self):
|
100 |
+
return [func for func in dir(self.cls) if not func.startswith("__") and type(getattr(self.cls, func, None)) == type(self.get_funcs) ]
|
101 |
+
|
102 |
+
def compile(self, **kwargs):
|
103 |
+
print("Just putting on the finishing touches... π§π§°")
|
104 |
+
for func in self.get_funcs():
|
105 |
+
this = getattr(self.cls, func, None)
|
106 |
+
if this.__name__ == "wrap":
|
107 |
+
this()
|
108 |
+
|
109 |
+
demos, names = [], []
|
110 |
+
for func, param in self.get_registered_gradio_functons().items():
|
111 |
+
names.append(func)
|
112 |
+
demos.append(gr.Interface(fn=getattr(self.cls, func, None),
|
113 |
+
inputs=param['inputs'],
|
114 |
+
outputs=param['outputs'],
|
115 |
+
examples=param['examples'],
|
116 |
+
cache_examples=kwargs['cache_examples'] if "cache_examples" in kwargs else None,
|
117 |
+
examples_per_page=kwargs['cache_examples'] if "cache_examples" in kwargs else 10,
|
118 |
+
interpretation=kwargs['interpretation'] if "interpretation" in kwargs else None,
|
119 |
+
num_shap=kwargs['num_shap'] if "num_shap" in kwargs else 2.0,
|
120 |
+
title=kwargs['title'] if "title" in kwargs else None,
|
121 |
+
article=kwargs['article'] if "article" in kwargs else None,
|
122 |
+
thumbnail=kwargs['thumbnail'] if "thumbnail" in kwargs else None,
|
123 |
+
css=kwargs['css'] if "css" in kwargs else None,
|
124 |
+
live=kwargs['live'] if "live" in kwargs else False,
|
125 |
+
allow_flagging=kwargs['allow_flagging'] if "allow_flagging" in kwargs else None,
|
126 |
+
theme='default',
|
127 |
+
))
|
128 |
+
print(f"{func}....{bcolor.BOLD}{bcolor.OKGREEN} done {bcolor.ENDC}")
|
129 |
+
|
130 |
+
print("\nHappy Visualizing... π")
|
131 |
+
return gr.TabbedInterface(demos, names)
|
132 |
+
|
133 |
+
def get_registered_gradio_functons(self):
|
134 |
+
try:
|
135 |
+
self.cls.registered_gradio_functons
|
136 |
+
except AttributeError:
|
137 |
+
return None
|
138 |
+
return self.cls.registered_gradio_functons
|
139 |
+
|
140 |
+
|
141 |
+
def run(self, **kwargs):
|
142 |
+
port= kwargs["port"] if "port" in kwargs else DOCKER_PORT.determinePort()
|
143 |
+
|
144 |
+
try:
|
145 |
+
requests.post(f"http://{DOCKER_LOCAL_HOST}:{ kwargs[ 'listen' ] if 'listen' in kwargs else '5000' }/api/append/port", json={"port" : port, "host" : f'http://localhost:{port}', "file" : getfile(self.cls.__class__), "name" : self.cls.__class__.__name__, "kwargs" : kwargs})
|
146 |
+
except Exception:
|
147 |
+
print(f"**{bcolor.BOLD}{bcolor.FAIL}CONNECTION ERROR{bcolor.ENDC}** πThe listening api is either not up or you choose the wrong port.π")
|
148 |
+
return
|
149 |
+
|
150 |
+
self.compile(live=kwargs[ 'live' ] if "live" in kwargs else False,
|
151 |
+
allow_flagging=kwargs[ 'allow_flagging' ] if "allow_flagging" in kwargs else None,
|
152 |
+
cache_examples=kwargs['cache_examples'] if "cache_examples" in kwargs else None,
|
153 |
+
examples_per_page=kwargs['cache_examples'] if "cache_examples" in kwargs else 10,
|
154 |
+
interpretation=kwargs['interpretation'] if "interpretation" in kwargs else None,
|
155 |
+
num_shap=kwargs['num_shap'] if "num_shap" in kwargs else 2.0,
|
156 |
+
title=kwargs['title'] if "title" in kwargs else None,
|
157 |
+
article=kwargs['article'] if "article" in kwargs else None,
|
158 |
+
thumbnail=kwargs['thumbnail'] if "thumbnail" in kwargs else None,
|
159 |
+
css=kwargs['css'] if "css" in kwargs else None,
|
160 |
+
theme=kwargs['theme'] if "theme" in kwargs else None,
|
161 |
+
).launch(server_port=port,
|
162 |
+
server_name=f"{DOCKER_LOCAL_HOST}",
|
163 |
+
inline= kwargs['inline'] if "inline" in kwargs else True,
|
164 |
+
share=kwargs['share'] if "share" in kwargs else None,
|
165 |
+
debug=kwargs['debug'] if "debug" in kwargs else False,
|
166 |
+
enable_queue=kwargs['enable_queue'] if "enable_queue" in kwargs else None,
|
167 |
+
max_threads=kwargs['max_threads'] if "max_threads" in kwargs else None,
|
168 |
+
auth=kwargs['auth'] if "auth" in kwargs else None,
|
169 |
+
auth_message=kwargs['auth_message'] if "auth_message" in kwargs else None,
|
170 |
+
prevent_thread_lock=kwargs['prevent_thread_lock'] if "prevent_thread_lock" in kwargs else False,
|
171 |
+
show_error=kwargs['show_error'] if "show_error" in kwargs else True,
|
172 |
+
show_tips=kwargs['show_tips'] if "show_tips" in kwargs else False,
|
173 |
+
height=kwargs['height'] if "height" in kwargs else 500,
|
174 |
+
width=kwargs['width'] if "width" in kwargs else 900,
|
175 |
+
encrypt=kwargs['encrypt'] if "encrypt" in kwargs else False,
|
176 |
+
favicon_path=kwargs['favicon_path'] if "favicon_path" in kwargs else None,
|
177 |
+
ssl_keyfile=kwargs['ssl_keyfile'] if "ssl_keyfile" in kwargs else None,
|
178 |
+
ssl_certfile=kwargs['ssl_certfile'] if "ssl_certfile" in kwargs else None,
|
179 |
+
ssl_keyfile_password=kwargs['ssl_keyfile_password'] if "ssl_keyfile_password" in kwargs else None,
|
180 |
+
quiet=kwargs['quiet'] if "quiet" in kwargs else False)
|
181 |
+
try:
|
182 |
+
requests.post(f"http://{DOCKER_LOCAL_HOST}:{ kwargs[ 'listen' ] if 'listen' in kwargs else '5000' }/api/remove/port", json={"port" : port, "host" : f'http://localhost:{port}', "file" : getfile(self.cls.__class__), "name" : self.cls.__class__.__name__, "kwargs" : kwargs})
|
183 |
+
except Exception:
|
184 |
+
print(f"**{bcolor.BOLD}{bcolor.FAIL}CONNECTION ERROR{bcolor.ENDC}** πThe api either lost connection or was turned off...π")
|
185 |
+
return
|
186 |
+
|
187 |
+
return Decorator
|
188 |
+
|
189 |
+
|
190 |
+
|
191 |
+
class bcolor:
|
192 |
+
HEADER = '\033[95m'
|
193 |
+
OKBLUE = '\033[94m'
|
194 |
+
OKCYAN = '\033[96m'
|
195 |
+
OKGREEN = '\033[92m'
|
196 |
+
WARNING = '\033[93m'
|
197 |
+
FAIL = '\033[91m'
|
198 |
+
ENDC = '\033[0m'
|
199 |
+
BOLD = '\033[1m'
|
200 |
+
UNDERLINE = '\033[4m'
|
201 |
+
|
docker-compose.yml
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
version : "3.9"
|
2 |
+
services:
|
3 |
+
backend :
|
4 |
+
container_name: backend
|
5 |
+
image : commune-ai/backend
|
6 |
+
build :
|
7 |
+
context : ./backend
|
8 |
+
dockerfile : Dockerfile
|
9 |
+
volumes:
|
10 |
+
- './backend:/app'
|
11 |
+
ports:
|
12 |
+
- "2000:2000"
|
13 |
+
- "7860-7880:7860-7880"
|
14 |
+
command: "python app.py -p 2000"
|
15 |
+
frontend :
|
16 |
+
container_name: frontend
|
17 |
+
image : commune-ai/frontend
|
18 |
+
build :
|
19 |
+
context : ./frontend
|
20 |
+
dockerfile : Dockerfile
|
21 |
+
ports:
|
22 |
+
- "3001:3000"
|
23 |
+
volumes:
|
24 |
+
- './frontend/src:/app/src'
|
25 |
+
- './frontend/public:/app/public'
|
26 |
+
command: "npm start"
|
27 |
+
|
frontend/.dockerignore
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
node_modules
|
frontend/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
2 |
+
|
3 |
+
# dependencies
|
4 |
+
/node_modules
|
5 |
+
/.pnp
|
6 |
+
.pnp.js
|
7 |
+
|
8 |
+
# testing
|
9 |
+
/coverage
|
10 |
+
|
11 |
+
# production
|
12 |
+
/build
|
13 |
+
|
14 |
+
# misc
|
15 |
+
.DS_Store
|
16 |
+
.env.local
|
17 |
+
.env.development.local
|
18 |
+
.env.test.local
|
19 |
+
.env.production.local
|
20 |
+
|
21 |
+
npm-debug.log*
|
22 |
+
yarn-debug.log*
|
23 |
+
yarn-error.log*
|
frontend/Dockerfile
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM node:16-alpine3.14
|
2 |
+
WORKDIR /app
|
3 |
+
COPY package*.json ./
|
4 |
+
RUN npm install
|
frontend/package-lock.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
frontend/package.json
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "gradio-board",
|
3 |
+
"version": "0.1.0",
|
4 |
+
"private": true,
|
5 |
+
"dependencies": {
|
6 |
+
"@testing-library/jest-dom": "^5.16.4",
|
7 |
+
"@testing-library/react": "^13.3.0",
|
8 |
+
"@testing-library/user-event": "^13.5.0",
|
9 |
+
"react": "^18.2.0",
|
10 |
+
"react-dom": "^18.2.0",
|
11 |
+
"react-flow-renderer": "^10.3.8",
|
12 |
+
"react-icons": "^4.4.0",
|
13 |
+
"react-scripts": "5.0.1",
|
14 |
+
"semantic-ui-css": "^2.4.1",
|
15 |
+
"semantic-ui-react": "^2.1.3",
|
16 |
+
"web-vitals": "^2.1.4"
|
17 |
+
},
|
18 |
+
"scripts": {
|
19 |
+
"start": "react-scripts start",
|
20 |
+
"build": "react-scripts build",
|
21 |
+
"test": "react-scripts test",
|
22 |
+
"eject": "react-scripts eject"
|
23 |
+
},
|
24 |
+
"eslintConfig": {
|
25 |
+
"extends": [
|
26 |
+
"react-app",
|
27 |
+
"react-app/jest"
|
28 |
+
]
|
29 |
+
},
|
30 |
+
"browserslist": {
|
31 |
+
"production": [
|
32 |
+
">0.2%",
|
33 |
+
"not dead",
|
34 |
+
"not op_mini all"
|
35 |
+
],
|
36 |
+
"development": [
|
37 |
+
"last 1 chrome version",
|
38 |
+
"last 1 firefox version",
|
39 |
+
"last 1 safari version"
|
40 |
+
]
|
41 |
+
},
|
42 |
+
"devDependencies": {
|
43 |
+
"tailwindcss": "^3.1.4"
|
44 |
+
}
|
45 |
+
}
|
frontend/public/android-chrome-192x192.png
ADDED
![]() |
Git LFS Details
|
frontend/public/android-chrome-512x512.png
ADDED
![]() |
Git LFS Details
|
frontend/public/favicon.ico
ADDED
|
frontend/public/index.html
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="utf-8" />
|
5 |
+
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
7 |
+
<meta name="theme-color" content="#000000" />
|
8 |
+
<meta
|
9 |
+
name="description"
|
10 |
+
content="Web site created using create-react-app"
|
11 |
+
/>
|
12 |
+
<link rel="apple-touch-icon" href="%PUBLIC_URL%/android-chrome-192x192.png" />
|
13 |
+
<!--
|
14 |
+
manifest.json provides metadata used when your web app is installed on a
|
15 |
+
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
16 |
+
-->
|
17 |
+
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
18 |
+
<!--
|
19 |
+
Notice the use of %PUBLIC_URL% in the tags above.
|
20 |
+
It will be replaced with the URL of the `public` folder during the build.
|
21 |
+
Only files inside the `public` folder can be referenced from the HTML.
|
22 |
+
|
23 |
+
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
24 |
+
work correctly both with client-side routing and a non-root public URL.
|
25 |
+
Learn how to configure a non-root public URL by running `npm run build`.
|
26 |
+
-->
|
27 |
+
<title>Gradio Flow</title>
|
28 |
+
<link
|
29 |
+
async
|
30 |
+
rel="stylesheet"
|
31 |
+
href="https://cdn.jsdelivr.net/npm/semantic-ui@2/dist/semantic.min.css"
|
32 |
+
/>
|
33 |
+
<script src="https://cdn.jsdelivr.net/npm/semantic-ui-react/dist/umd/semantic-ui-react.min.js"></script>
|
34 |
+
|
35 |
+
</head>
|
36 |
+
<body>
|
37 |
+
<noscript>You need to enable JavaScript to run this app.</noscript>
|
38 |
+
<div id="root"></div>
|
39 |
+
<!--
|
40 |
+
This HTML file is a template.
|
41 |
+
If you open it directly in the browser, you will see an empty page.
|
42 |
+
|
43 |
+
You can add webfonts, meta tags, or analytics to this file.
|
44 |
+
The build step will place the bundled scripts into the <body> tag.
|
45 |
+
|
46 |
+
To begin the development, run `npm start` or `yarn start`.
|
47 |
+
To create a production bundle, use `npm run build` or `yarn build`.
|
48 |
+
-->
|
49 |
+
</body>
|
50 |
+
</html>
|
frontend/public/logo192.png
ADDED
![]() |
Git LFS Details
|
frontend/public/manifest.json
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"short_name": "React App",
|
3 |
+
"name": "Create React App Sample",
|
4 |
+
"icons": [
|
5 |
+
{
|
6 |
+
"src": "favicon.ico",
|
7 |
+
"sizes": "64x64 32x32 24x24 16x16",
|
8 |
+
"type": "image/x-icon"
|
9 |
+
},
|
10 |
+
{
|
11 |
+
"src": "android-chrome-192x192.png",
|
12 |
+
"type": "image/png",
|
13 |
+
"sizes": "192x192"
|
14 |
+
},
|
15 |
+
{
|
16 |
+
"src": "android-chrome-512x512.png",
|
17 |
+
"type": "image/png",
|
18 |
+
"sizes": "512x512"
|
19 |
+
}
|
20 |
+
],
|
21 |
+
"start_url": ".",
|
22 |
+
"display": "standalone",
|
23 |
+
"theme_color": "#000000",
|
24 |
+
"background_color": "#ffffff"
|
25 |
+
}
|
frontend/public/robots.txt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
# https://www.robotstxt.org/robotstxt.html
|
2 |
+
User-agent: *
|
3 |
+
Disallow:
|
frontend/src/App.js
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import ReactEnviorment from './Components/ReactFlow/ReactFlowEnv'
|
2 |
+
import { useState } from 'react'
|
3 |
+
import { useThemeDetector } from './helper/visual'
|
4 |
+
|
5 |
+
|
6 |
+
|
7 |
+
export default function App() {
|
8 |
+
const [theme, setTheme] = useState(useThemeDetector)
|
9 |
+
return(
|
10 |
+
<>
|
11 |
+
|
12 |
+
<div className=' absolute top-4 right-5 z-50' >
|
13 |
+
<h1 className='text-3xl' onClick={()=> setTheme(theme === "" ? "dark" : "")}>{theme ? 'π' : 'βοΈ'}</h1>
|
14 |
+
</div>
|
15 |
+
|
16 |
+
<ReactEnviorment theme={theme ? 'dark' : ''}/>
|
17 |
+
</>
|
18 |
+
|
19 |
+
)
|
20 |
+
};
|
frontend/src/Components/Navagation/navbar.js
ADDED
@@ -0,0 +1,289 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { Component } from "react";
|
2 |
+
import {BsArrowLeftShort, BsSearch} from 'react-icons/bs';
|
3 |
+
import "../../css/dist/output.css"
|
4 |
+
import { random_colour, random_emoji } from "../../helper/visual";
|
5 |
+
import { Message, Header, Modal, Button, Icon } from 'semantic-ui-react'
|
6 |
+
|
7 |
+
export default class Navbar extends Component{
|
8 |
+
constructor(){
|
9 |
+
super()
|
10 |
+
this.fetch_classes()
|
11 |
+
this.temp_host = 0
|
12 |
+
}
|
13 |
+
|
14 |
+
state = {open : true,
|
15 |
+
menu : [],
|
16 |
+
colour : [],
|
17 |
+
text : "",
|
18 |
+
name : "",
|
19 |
+
emoji : [],
|
20 |
+
mode : false,
|
21 |
+
modal : false,
|
22 |
+
error : false
|
23 |
+
}
|
24 |
+
|
25 |
+
async fetch_classes(){
|
26 |
+
try {
|
27 |
+
setInterval( async () => {
|
28 |
+
const menu = []
|
29 |
+
await fetch("http://localhost:2000/api/open/ports", { method: 'GET', mode : 'cors',})
|
30 |
+
.then(response => response.json())
|
31 |
+
.then(data => {
|
32 |
+
for (var i = 0; i < data.length; i++){
|
33 |
+
menu.push(data[i])
|
34 |
+
}
|
35 |
+
})
|
36 |
+
.catch(error => {console.log(error)})
|
37 |
+
|
38 |
+
var diff = menu.length - this.state.menu.length
|
39 |
+
if(diff !== 0){
|
40 |
+
this.hanelTabs(menu, diff)
|
41 |
+
}
|
42 |
+
},1000);
|
43 |
+
}catch(e){
|
44 |
+
console.log(e)
|
45 |
+
}
|
46 |
+
}
|
47 |
+
|
48 |
+
append_gradio = () => {
|
49 |
+
// var expression = /((http([s]){0,1}:\/\/){0,1}(localhost|127.0.0.1){1}(([:]){0,1}[\0-9]{4}){0,1}\/{0,1}){1}/g;
|
50 |
+
// var regex = new RegExp(expression);
|
51 |
+
// if(!this.state.text.match(regex) || (this.state.text === "http://localhost:3001/")){
|
52 |
+
// this.setState({open : this.state.open,
|
53 |
+
// menu : this.state.menu,
|
54 |
+
// text: this.state.text,
|
55 |
+
// name : this.state.name,
|
56 |
+
// colour : this.state.colour,
|
57 |
+
// emoji : this.state.emoji,
|
58 |
+
// error : true,
|
59 |
+
// modal : this.state.modal })
|
60 |
+
// return
|
61 |
+
// }
|
62 |
+
|
63 |
+
// if((this.state.text.includes("localhost") || this.state.text.includes("127.0.0.1"))
|
64 |
+
// && this.state.menu.some(e => e.host === this.state.text)){
|
65 |
+
// this.setState({open : this.state.open,
|
66 |
+
// menu : this.state.menu,
|
67 |
+
// text: this.state.text,
|
68 |
+
// name : this.state.name,
|
69 |
+
// colour : this.state.colour,
|
70 |
+
// emoji : this.state.emoji,
|
71 |
+
// error : true,
|
72 |
+
// modal : this.state.modal })
|
73 |
+
|
74 |
+
// return
|
75 |
+
|
76 |
+
// }
|
77 |
+
fetch(this.state.text, {method : "GET", mode: 'no-cors'}).then((re) => {
|
78 |
+
console.log(re)
|
79 |
+
fetch("http://localhost:2000/api/append/port", {method: 'POST', mode : 'cors', headers : { 'Content-Type' : 'application/json' }, body: JSON.stringify({file : "", kwargs : {}, name : this.state.name === "" ?`temp_class_${this.temp_host++}` : `${this.state.name}`, port: 0 , host : this.state.text}) }).then(resp => {
|
80 |
+
this.setState({open : this.state.open,
|
81 |
+
menu : this.state.menu,
|
82 |
+
text: "",
|
83 |
+
name : "",
|
84 |
+
colour : this.state.colour,
|
85 |
+
emoji : this.state.emoji,
|
86 |
+
error : false,
|
87 |
+
modal : false })
|
88 |
+
|
89 |
+
}).catch(() => this.setState({open : this.state.open,
|
90 |
+
menu : this.state.menu,
|
91 |
+
text: this.state.text,
|
92 |
+
colour : this.state.colour,
|
93 |
+
emoji : this.state.emoji,
|
94 |
+
error : true,
|
95 |
+
modal : this.state.modal }))
|
96 |
+
})
|
97 |
+
}
|
98 |
+
|
99 |
+
handelModal = (bool) => {
|
100 |
+
this.setState({open : this.state.open,
|
101 |
+
menu : this.state.menu,
|
102 |
+
text: this.state.text,
|
103 |
+
name: this.state.name,
|
104 |
+
colour : this.state.colour,
|
105 |
+
emoji : this.state.emoji,
|
106 |
+
error : !bool ? false : this.state.error ,
|
107 |
+
modal : bool})
|
108 |
+
}
|
109 |
+
|
110 |
+
|
111 |
+
onDragStart = (event, nodeType, item, index) => {
|
112 |
+
console.log(item)
|
113 |
+
event.dataTransfer.setData('application/reactflow', nodeType);
|
114 |
+
event.dataTransfer.setData('application/host', item.host)
|
115 |
+
event.dataTransfer.setData('application/name', item.name)
|
116 |
+
event.dataTransfer.setData('application/colour', this.state.colour[index])
|
117 |
+
event.dataTransfer.setData('application/item', JSON.stringify(item))
|
118 |
+
|
119 |
+
event.dataTransfer.effectAllowed = 'move';
|
120 |
+
};
|
121 |
+
|
122 |
+
onDragDrop = (e) => {
|
123 |
+
e.preventDefault();
|
124 |
+
var item = JSON.parse(e.dataTransfer.getData('application/item'));
|
125 |
+
fetch("http://localhost:2000/api/remove/port", {method : "POST", mode: 'cors', headers : { 'Content-Type' : 'application/json' }, body: JSON.stringify(item) }).then((re)=>{
|
126 |
+
})
|
127 |
+
}
|
128 |
+
|
129 |
+
hanelTabs = (e, d) => {
|
130 |
+
|
131 |
+
// if less then 0 we must remove colour's and emoji's
|
132 |
+
// get index of the object
|
133 |
+
// remove
|
134 |
+
var c = []
|
135 |
+
var j = []
|
136 |
+
if(d < 0){
|
137 |
+
var a = this.state.menu.filter(item => !e.includes(item)) // get the items not in menu anymore
|
138 |
+
c = this.state.colour
|
139 |
+
j = this.state.emoji
|
140 |
+
for(var k=0; k < d; k++){
|
141 |
+
c.splice(this.state.menu.indexOf(a[k]), 1)
|
142 |
+
j.splice(this.state.menu.indexOf(a[k]), 1)
|
143 |
+
}
|
144 |
+
this.setState({open : this.state.open, menu : e, text: this.state.text, name: this.state.name, colour : c, emoji : j, error : this.state.error, modal : this.state.modal })
|
145 |
+
}else{
|
146 |
+
//append new colours
|
147 |
+
for(var i =0; i < d; i++){
|
148 |
+
c.push(random_colour());
|
149 |
+
j.push(random_emoji());
|
150 |
+
}
|
151 |
+
this.setState({open : this.state.open, menu : e, text: this.state.text, name: this.state.name, colour : this.state.colour.concat(c), emoji : this.state.emoji.concat(j), error : this.state.error, modal : this.state.modal })
|
152 |
+
}
|
153 |
+
}
|
154 |
+
|
155 |
+
appendTabs = () => {
|
156 |
+
this.setState({open : this.state.open, menu : this.state.menu, text: this.state.text, name: this.state.name, colour : [...this.state.colour, random_colour] , emoji : [...this.state.emoji, random_emoji], error : this.state.error, modal : this.state.modal })
|
157 |
+
}
|
158 |
+
|
159 |
+
handelNavbar = () => {
|
160 |
+
this.setState({open : !this.state.open, menu : this.state.menu, text: this.state.text, name: this.state.name, colour : this.state.colour, emoji : this.state.emoji, error : this.state.error, modal : this.state.modal })
|
161 |
+
}
|
162 |
+
|
163 |
+
updateText(e, type){
|
164 |
+
this.setState({open : this.state.open, menu : this.state.menu, text: type === "text" ? e.target.value : this.state.text, name: type === "name" ? e.target.value : this.state.name, colour : this.state.colour, emoji : this.state.emoji, error : this.state.error, modal : this.state.modal })
|
165 |
+
}
|
166 |
+
|
167 |
+
subComponents(item, index){
|
168 |
+
|
169 |
+
|
170 |
+
if (this.state.colour.length === 0 || this.state.emoji.length === 0){
|
171 |
+
this.hanelTabs()
|
172 |
+
}
|
173 |
+
return(<>
|
174 |
+
<li key={`${item.name}-${item.port}`} onDragStart={(event) => this.onDragStart(event, 'custom', item, index)}
|
175 |
+
className={` text-white text-sm flex text-center items-center cursor-pointer
|
176 |
+
p-4 px-2 mt-4 ${ this.state.open ? `hover:animate-pulse ${this.state.colour[index] === null ? "" : this.state.colour[index]} ` : `hidden`} rounded-md mt-2`} draggable>
|
177 |
+
<span className="text-2xl font-medium "> </span>
|
178 |
+
<span className={`text-base font-medium flex-1 text-left" ${this.state.open ? "" : "hidden"}`}>{`${this.state.emoji[index] === null ? "" : this.state.emoji[index]} ${item.name}`}</span>
|
179 |
+
</li >
|
180 |
+
</>)
|
181 |
+
}
|
182 |
+
|
183 |
+
|
184 |
+
render(){
|
185 |
+
|
186 |
+
|
187 |
+
|
188 |
+
// compare the menu if the menu has changed change the state of the navbar
|
189 |
+
|
190 |
+
// states to change
|
191 |
+
// 1. tabs
|
192 |
+
|
193 |
+
// 2. add colour
|
194 |
+
return (<div>
|
195 |
+
|
196 |
+
<div className={`z-10 flex-1 float-left bg-white dark:bg-stone-900 h-screen p-5 pt-8 ${this.state.open ? "lg:w-72 md:64 sm:w-60" : "w-10"} duration-300 absolute shadow-2xl border-black border-r-[1px] dark:border-white dark:text-white`}>
|
197 |
+
|
198 |
+
<BsArrowLeftShort onClick={this.handelNavbar} className={` bg-white text-Retro-darl-blue text-3xl rounded-full absolute -right-3 top-9 border border-black cursor-pointer ${!this.state.open && 'rotate-180'} dark:border-white duration-300 dark:text-white dark:bg-stone-900 `}/>
|
199 |
+
|
200 |
+
<div className="inline-flex w-full">
|
201 |
+
<h1 className={`font-sans font-bold text-lg ${this.state.open ? "" : "hidden"} duration-500 ml-auto mr-auto`}>Gradio Flow π€</h1>
|
202 |
+
</div>
|
203 |
+
|
204 |
+
<div className={`rounded-md text-center ${this.state.open ? "" : "px-0"} py-3`} onClick={() => {this.handelModal(true)}}>
|
205 |
+
<div className={` text-center bg-transparent w-full h-10 border border-slate-300 hover:border-sky-500 hover:animate-pulse border-dashed rounded-md py-2 pl-5 ${this.state.open ? "pr-3" : "hidden"} shadow-sm sm:text-sm`}>
|
206 |
+
<Icon className=" block mr-auto ml-auto" name="plus"/>
|
207 |
+
</div>
|
208 |
+
</div>
|
209 |
+
|
210 |
+
<Modal
|
211 |
+
basic
|
212 |
+
open={this.state.modal}
|
213 |
+
size='small'
|
214 |
+
>
|
215 |
+
<Header icon>
|
216 |
+
π
|
217 |
+
<br/>
|
218 |
+
Append Shared Gradio Hosts
|
219 |
+
</Header>
|
220 |
+
<Modal.Content>
|
221 |
+
<div className=" text-center">This feature just allows you to append other hosted applications like gradio/streamlit to have it within the Gradio Flow interface, that may not be sending its hosting location to the api or have shared links to other people's gradio application.</div>
|
222 |
+
<div className={`flex items-center rounded-md bg-light-white mt-6 border-dashed`}>
|
223 |
+
<label className="relative block w-full">
|
224 |
+
<span className={`absolute inset-y-0 left-0 flex items-center pl-3`}>
|
225 |
+
<BsSearch className="block float-left cursor-pointer mr-2"/>
|
226 |
+
</span>
|
227 |
+
<input className={`placeholder:italic placeholder:text-slate-400 block bg-transparent w-full border border-slate-300 border-dashed rounded-md py-2 pl-9 ${this.state.open ? "pr-3" : "hidden"} shadow-sm focus:outline-none focus:border-sky-500 focus:ring-sky-500 focus:ring-1 sm:text-sm`}
|
228 |
+
placeholder={`stream localhost...`}
|
229 |
+
type="text" name="search"
|
230 |
+
onChange={(e) => {
|
231 |
+
this.updateText(e, "text")
|
232 |
+
}}
|
233 |
+
|
234 |
+
/>
|
235 |
+
</label>
|
236 |
+
</div>
|
237 |
+
<div className={`flex items-center rounded-md bg-light-white mt-6 border-dashed`}>
|
238 |
+
<label className="relative block w-full">
|
239 |
+
<span className={`absolute inset-y-0 left-0 flex items-center pl-3`}>
|
240 |
+
<Icon className="block float-left cursor-pointer mr-2" name="address card"/>
|
241 |
+
</span>
|
242 |
+
<input className={`placeholder:italic placeholder:text-slate-400 block bg-transparent w-full border border-slate-300 border-dashed rounded-md py-2 pl-9 ${this.state.open ? "pr-3" : "hidden"} shadow-sm focus:outline-none focus:border-sky-500 focus:ring-sky-500 focus:ring-1 sm:text-sm`}
|
243 |
+
placeholder={`Name...` }
|
244 |
+
type="text" name="search"
|
245 |
+
onChange={(e) => {
|
246 |
+
this.updateText(e, "name")
|
247 |
+
}}
|
248 |
+
/>
|
249 |
+
</label>
|
250 |
+
</div>
|
251 |
+
|
252 |
+
{ this.state.error &&
|
253 |
+
<Message negative>
|
254 |
+
<Message.Header>π« Connection to url</Message.Header>
|
255 |
+
<p>π€ Either the connection is forbidden, already exist within the menu or it is unreachable... </p>
|
256 |
+
</Message>
|
257 |
+
}
|
258 |
+
|
259 |
+
</Modal.Content>
|
260 |
+
<Modal.Actions>
|
261 |
+
<Button basic color='red' inverted onClick={() => {this.handelModal(false)}}>
|
262 |
+
<Icon name='remove' /> Exit
|
263 |
+
</Button>
|
264 |
+
<Button color='green' inverted onClick={() => {this.append_gradio()}}>
|
265 |
+
<Icon name='checkmark' /> Append
|
266 |
+
</Button>
|
267 |
+
</Modal.Actions>
|
268 |
+
</Modal>
|
269 |
+
|
270 |
+
<div className=" relative z-10 h-auto overflow-auto pt-4">
|
271 |
+
<ul className="pt-2">
|
272 |
+
{this.state.menu.map((menu, index) => {return this.subComponents(menu, index)})}
|
273 |
+
</ul>
|
274 |
+
</div>
|
275 |
+
|
276 |
+
<div className={`${this.state.open ? "" : "hidden"} absolute bottom-0 left-0 w-full text-center p-5`} onDragOver={(e)=> {e.preventDefault()}} onDrop={(e)=>{this.onDragDrop(e)}}>
|
277 |
+
<div className={` text-center bg-transparent w-full h-10 border border-red-600 border-dashed rounded-md py-2 pl-5 p-4 ${this.state.open ? "pr-3" : "hidden"} shadow-sm sm:text-sm`}>
|
278 |
+
<Icon name='trash alternate' />
|
279 |
+
</div>
|
280 |
+
</div>
|
281 |
+
</div>
|
282 |
+
|
283 |
+
</div>)
|
284 |
+
}
|
285 |
+
}
|
286 |
+
|
287 |
+
/**
|
288 |
+
*
|
289 |
+
* **/
|
frontend/src/Components/Nodes/custom.js
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React from "react"
|
2 |
+
|
3 |
+
export default class CustomNodeIframe extends React.Component {
|
4 |
+
constructor({data}){
|
5 |
+
super()
|
6 |
+
this.state = {
|
7 |
+
reachable : true,
|
8 |
+
selected : true,
|
9 |
+
data : data
|
10 |
+
}
|
11 |
+
}
|
12 |
+
|
13 |
+
handelSelected = () => {
|
14 |
+
fetch(this.state.data.host, {mode: 'no-cors'}).then((re) => {
|
15 |
+
this.setState({reachable : true, selected : !this.state.selected, data : this.state.data})
|
16 |
+
}).catch(()=>{
|
17 |
+
this.setState({reachable : false, selected : !this.state.selected, data : this.state.data})
|
18 |
+
})
|
19 |
+
//>
|
20 |
+
|
21 |
+
}
|
22 |
+
render(){
|
23 |
+
|
24 |
+
console.log(this.state.reachable)
|
25 |
+
|
26 |
+
return (
|
27 |
+
<>
|
28 |
+
{ this.state.selected && this.state.reachable ?
|
29 |
+
<div className='relative h-[540px] w-[600px] overflow-hidden m-0 p-0' onClick={()=>this.handelSelected()}>
|
30 |
+
<div className={`absolute h-full w-full ${this.state.data.colour} border-1shadow-2xl shadow-black rounded-xl -z-10`}></div>
|
31 |
+
<iframe src={this.state.data.host} title={this.state.data.label} frameBorder={0} allowFullScreen className=" h-full w-full p-2 overflow-y-scroll"></iframe>
|
32 |
+
</div> :
|
33 |
+
<>
|
34 |
+
<div className='break-words'>
|
35 |
+
<div className=' h-auto text-center pointer-events-none'>
|
36 |
+
<div className='hexagon pointer-events-auto break-words text-center hover:animate-pulse dark:bg-black dark:text-white' onClick={()=>this.handelSelected()} >
|
37 |
+
<p className='z-50 pointer-events-none break-words font-sans font-bold'>{this.state.data.label}</p>
|
38 |
+
</div>
|
39 |
+
</div>
|
40 |
+
</div>
|
41 |
+
</> }
|
42 |
+
|
43 |
+
</>
|
44 |
+
)
|
45 |
+
}
|
46 |
+
|
47 |
+
}
|
48 |
+
|
frontend/src/Components/ReactFlow/ReactFlowEnv.js
ADDED
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import CustomNodeIframe from "../Nodes/custom";
|
2 |
+
import '../../css/dist/output.css'
|
3 |
+
import '../../css/CustomNode.css'
|
4 |
+
import ReactFlow, { Background,
|
5 |
+
applyNodeChanges,
|
6 |
+
applyEdgeChanges,
|
7 |
+
ReactFlowProvider,
|
8 |
+
} from 'react-flow-renderer';
|
9 |
+
import React ,{ useState, useCallback, useRef } from 'react';
|
10 |
+
import Navbar from '../Navagation/navbar';
|
11 |
+
|
12 |
+
const types = {
|
13 |
+
custom : CustomNodeIframe,
|
14 |
+
}
|
15 |
+
|
16 |
+
export default function ReactEnviorment(props) {
|
17 |
+
|
18 |
+
const [nodes, setNodes] = useState([]);
|
19 |
+
const [edges, setEdges] = useState([]);
|
20 |
+
const [reactFlowInstance, setReactFlowInstance] = useState(null);
|
21 |
+
const reactFlowWrapper = useRef(null);
|
22 |
+
|
23 |
+
|
24 |
+
const onNodesChange = useCallback(
|
25 |
+
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
|
26 |
+
[setNodes]
|
27 |
+
);
|
28 |
+
|
29 |
+
const onEdgesChange = useCallback(
|
30 |
+
(changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
|
31 |
+
[setEdges]
|
32 |
+
);
|
33 |
+
|
34 |
+
|
35 |
+
const onDragOver = useCallback((event) => {
|
36 |
+
event.preventDefault();
|
37 |
+
event.dataTransfer.dropEffect = 'move';
|
38 |
+
}, []);
|
39 |
+
|
40 |
+
const onDrop = useCallback(
|
41 |
+
(event) => {
|
42 |
+
event.preventDefault();
|
43 |
+
|
44 |
+
const reactFlowBounds = reactFlowWrapper.current.getBoundingClientRect();
|
45 |
+
const type = event.dataTransfer.getData('application/reactflow');
|
46 |
+
const host = event.dataTransfer.getData('application/host');
|
47 |
+
const name = event.dataTransfer.getData('application/name');
|
48 |
+
const colour = event.dataTransfer.getData('application/colour');
|
49 |
+
// check if the dropped element is valid
|
50 |
+
if (typeof type === 'undefined' || !type) {
|
51 |
+
return;
|
52 |
+
}
|
53 |
+
|
54 |
+
const position = reactFlowInstance.project({
|
55 |
+
x: event.clientX - reactFlowBounds.left,
|
56 |
+
y: event.clientY - reactFlowBounds.top,
|
57 |
+
});
|
58 |
+
const newNode = {
|
59 |
+
id: `${name}-${nodes.length}`,
|
60 |
+
type,
|
61 |
+
position,
|
62 |
+
data: { label: `${name}`, host : `${host}`, colour : `${colour}` },
|
63 |
+
};
|
64 |
+
setNodes((nds) => nds.concat(newNode));
|
65 |
+
},
|
66 |
+
[reactFlowInstance, nodes]);
|
67 |
+
|
68 |
+
|
69 |
+
|
70 |
+
return (
|
71 |
+
<div className={`flex h-screen w-screen ${props.theme} transition-all`}>
|
72 |
+
<Navbar/>
|
73 |
+
<ReactFlowProvider>
|
74 |
+
<div className="h-screen w-screen" ref={reactFlowWrapper}>
|
75 |
+
<ReactFlow nodes={nodes} edges={edges} nodeTypes={types} onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} onDragOver={onDragOver} onDrop={onDrop} onInit={setReactFlowInstance} fitView>
|
76 |
+
<Background variant='dots' size={1} className=" bg-white dark:bg-neutral-800"/>
|
77 |
+
</ReactFlow>
|
78 |
+
</div>
|
79 |
+
</ReactFlowProvider>
|
80 |
+
</div>
|
81 |
+
);
|
82 |
+
}
|
frontend/src/css/CustomNode.css
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.hexagon {
|
2 |
+
position: relative;
|
3 |
+
width: 200px;
|
4 |
+
height: 115.47px;
|
5 |
+
background-color: #ffffff;
|
6 |
+
margin: 57.74px 0;
|
7 |
+
border-left: solid 3px #000000;
|
8 |
+
border-right: solid 3px #000000;
|
9 |
+
margin-left: auto;
|
10 |
+
margin-right: auto;
|
11 |
+
border-image-slice: 1;
|
12 |
+
border-image-source: linear-gradient(to left, #2de2e6, #f6019d);
|
13 |
+
}
|
14 |
+
|
15 |
+
|
16 |
+
.hexagon:before,
|
17 |
+
.hexagon:after {
|
18 |
+
content: "";
|
19 |
+
position: absolute;
|
20 |
+
z-index: -1;
|
21 |
+
width: 141.42px;
|
22 |
+
height: 141.42px;
|
23 |
+
-webkit-transform: scaleY(0.5774) rotate(-30deg);
|
24 |
+
-ms-transform: scaleY(0.5774) rotate(-30deg);
|
25 |
+
transform: scaleY(0.5774) rotate(-45deg);
|
26 |
+
background-color: inherit;
|
27 |
+
left: 27.2893px;
|
28 |
+
}
|
29 |
+
|
30 |
+
.hexagon:before {
|
31 |
+
top: -70.7107px;
|
32 |
+
border-top: solid 4.8284px #000000;
|
33 |
+
border-right: solid 4.8284px #000000;
|
34 |
+
border-image-slice: 1;
|
35 |
+
border-image-source: linear-gradient(to left, #2de2e6, #f6019d);
|
36 |
+
|
37 |
+
}
|
38 |
+
|
39 |
+
.hexagon:after {
|
40 |
+
bottom: -70.7107px;
|
41 |
+
border-bottom: solid 4.8284px #000000;
|
42 |
+
border-left: solid 4.8284px;
|
43 |
+
border-image-slice: 1;
|
44 |
+
border-image-source: linear-gradient(to left, #2de2e6, #f6019d);
|
45 |
+
|
46 |
+
}
|
frontend/src/css/dist/output.css
ADDED
@@ -0,0 +1,1098 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
! tailwindcss v3.1.4 | MIT License | https://tailwindcss.com
|
3 |
+
*/
|
4 |
+
|
5 |
+
/*
|
6 |
+
1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
|
7 |
+
2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
|
8 |
+
*/
|
9 |
+
|
10 |
+
*,
|
11 |
+
::before,
|
12 |
+
::after {
|
13 |
+
box-sizing: border-box;
|
14 |
+
/* 1 */
|
15 |
+
border-width: 0;
|
16 |
+
/* 2 */
|
17 |
+
border-style: solid;
|
18 |
+
/* 2 */
|
19 |
+
border-color: #e5e7eb;
|
20 |
+
/* 2 */
|
21 |
+
}
|
22 |
+
|
23 |
+
::before,
|
24 |
+
::after {
|
25 |
+
--tw-content: '';
|
26 |
+
}
|
27 |
+
|
28 |
+
/*
|
29 |
+
1. Use a consistent sensible line-height in all browsers.
|
30 |
+
2. Prevent adjustments of font size after orientation changes in iOS.
|
31 |
+
3. Use a more readable tab size.
|
32 |
+
4. Use the user's configured `sans` font-family by default.
|
33 |
+
*/
|
34 |
+
|
35 |
+
html {
|
36 |
+
line-height: 1.5;
|
37 |
+
/* 1 */
|
38 |
+
-webkit-text-size-adjust: 100%;
|
39 |
+
/* 2 */
|
40 |
+
/* 3 */
|
41 |
+
tab-size: 4;
|
42 |
+
/* 3 */
|
43 |
+
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";
|
44 |
+
/* 4 */
|
45 |
+
}
|
46 |
+
|
47 |
+
/*
|
48 |
+
1. Remove the margin in all browsers.
|
49 |
+
2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
|
50 |
+
*/
|
51 |
+
|
52 |
+
body {
|
53 |
+
margin: 0;
|
54 |
+
/* 1 */
|
55 |
+
line-height: inherit;
|
56 |
+
/* 2 */
|
57 |
+
}
|
58 |
+
|
59 |
+
/*
|
60 |
+
1. Add the correct height in Firefox.
|
61 |
+
2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
|
62 |
+
3. Ensure horizontal rules are visible by default.
|
63 |
+
*/
|
64 |
+
|
65 |
+
hr {
|
66 |
+
height: 0;
|
67 |
+
/* 1 */
|
68 |
+
color: inherit;
|
69 |
+
/* 2 */
|
70 |
+
border-top-width: 1px;
|
71 |
+
/* 3 */
|
72 |
+
}
|
73 |
+
|
74 |
+
/*
|
75 |
+
Add the correct text decoration in Chrome, Edge, and Safari.
|
76 |
+
*/
|
77 |
+
|
78 |
+
abbr:where([title]) {
|
79 |
+
-webkit-text-decoration: underline dotted;
|
80 |
+
text-decoration: underline dotted;
|
81 |
+
}
|
82 |
+
|
83 |
+
/*
|
84 |
+
Remove the default font size and weight for headings.
|
85 |
+
*/
|
86 |
+
|
87 |
+
h1,
|
88 |
+
h2,
|
89 |
+
h3,
|
90 |
+
h4,
|
91 |
+
h5,
|
92 |
+
h6 {
|
93 |
+
font-size: inherit;
|
94 |
+
font-weight: inherit;
|
95 |
+
}
|
96 |
+
|
97 |
+
/*
|
98 |
+
Reset links to optimize for opt-in styling instead of opt-out.
|
99 |
+
*/
|
100 |
+
|
101 |
+
a {
|
102 |
+
color: inherit;
|
103 |
+
text-decoration: inherit;
|
104 |
+
}
|
105 |
+
|
106 |
+
/*
|
107 |
+
Add the correct font weight in Edge and Safari.
|
108 |
+
*/
|
109 |
+
|
110 |
+
b,
|
111 |
+
strong {
|
112 |
+
font-weight: bolder;
|
113 |
+
}
|
114 |
+
|
115 |
+
/*
|
116 |
+
1. Use the user's configured `mono` font family by default.
|
117 |
+
2. Correct the odd `em` font sizing in all browsers.
|
118 |
+
*/
|
119 |
+
|
120 |
+
code,
|
121 |
+
kbd,
|
122 |
+
samp,
|
123 |
+
pre {
|
124 |
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
125 |
+
/* 1 */
|
126 |
+
font-size: 1em;
|
127 |
+
/* 2 */
|
128 |
+
}
|
129 |
+
|
130 |
+
/*
|
131 |
+
Add the correct font size in all browsers.
|
132 |
+
*/
|
133 |
+
|
134 |
+
small {
|
135 |
+
font-size: 80%;
|
136 |
+
}
|
137 |
+
|
138 |
+
/*
|
139 |
+
Prevent `sub` and `sup` elements from affecting the line height in all browsers.
|
140 |
+
*/
|
141 |
+
|
142 |
+
sub,
|
143 |
+
sup {
|
144 |
+
font-size: 75%;
|
145 |
+
line-height: 0;
|
146 |
+
position: relative;
|
147 |
+
vertical-align: baseline;
|
148 |
+
}
|
149 |
+
|
150 |
+
sub {
|
151 |
+
bottom: -0.25em;
|
152 |
+
}
|
153 |
+
|
154 |
+
sup {
|
155 |
+
top: -0.5em;
|
156 |
+
}
|
157 |
+
|
158 |
+
/*
|
159 |
+
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)
|
160 |
+
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)
|
161 |
+
3. Remove gaps between table borders by default.
|
162 |
+
*/
|
163 |
+
|
164 |
+
table {
|
165 |
+
text-indent: 0;
|
166 |
+
/* 1 */
|
167 |
+
border-color: inherit;
|
168 |
+
/* 2 */
|
169 |
+
border-collapse: collapse;
|
170 |
+
/* 3 */
|
171 |
+
}
|
172 |
+
|
173 |
+
/*
|
174 |
+
1. Change the font styles in all browsers.
|
175 |
+
2. Remove the margin in Firefox and Safari.
|
176 |
+
3. Remove default padding in all browsers.
|
177 |
+
*/
|
178 |
+
|
179 |
+
button,
|
180 |
+
input,
|
181 |
+
optgroup,
|
182 |
+
select,
|
183 |
+
textarea {
|
184 |
+
font-family: inherit;
|
185 |
+
/* 1 */
|
186 |
+
font-size: 100%;
|
187 |
+
/* 1 */
|
188 |
+
font-weight: inherit;
|
189 |
+
/* 1 */
|
190 |
+
line-height: inherit;
|
191 |
+
/* 1 */
|
192 |
+
color: inherit;
|
193 |
+
/* 1 */
|
194 |
+
margin: 0;
|
195 |
+
/* 2 */
|
196 |
+
padding: 0;
|
197 |
+
/* 3 */
|
198 |
+
}
|
199 |
+
|
200 |
+
/*
|
201 |
+
Remove the inheritance of text transform in Edge and Firefox.
|
202 |
+
*/
|
203 |
+
|
204 |
+
button,
|
205 |
+
select {
|
206 |
+
text-transform: none;
|
207 |
+
}
|
208 |
+
|
209 |
+
/*
|
210 |
+
1. Correct the inability to style clickable types in iOS and Safari.
|
211 |
+
2. Remove default button styles.
|
212 |
+
*/
|
213 |
+
|
214 |
+
button,
|
215 |
+
[type='button'],
|
216 |
+
[type='reset'],
|
217 |
+
[type='submit'] {
|
218 |
+
-webkit-appearance: button;
|
219 |
+
/* 1 */
|
220 |
+
background-color: transparent;
|
221 |
+
/* 2 */
|
222 |
+
background-image: none;
|
223 |
+
/* 2 */
|
224 |
+
}
|
225 |
+
|
226 |
+
/*
|
227 |
+
Use the modern Firefox focus style for all focusable elements.
|
228 |
+
*/
|
229 |
+
|
230 |
+
:-moz-focusring {
|
231 |
+
outline: auto;
|
232 |
+
}
|
233 |
+
|
234 |
+
/*
|
235 |
+
Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
|
236 |
+
*/
|
237 |
+
|
238 |
+
:-moz-ui-invalid {
|
239 |
+
box-shadow: none;
|
240 |
+
}
|
241 |
+
|
242 |
+
/*
|
243 |
+
Add the correct vertical alignment in Chrome and Firefox.
|
244 |
+
*/
|
245 |
+
|
246 |
+
progress {
|
247 |
+
vertical-align: baseline;
|
248 |
+
}
|
249 |
+
|
250 |
+
/*
|
251 |
+
Correct the cursor style of increment and decrement buttons in Safari.
|
252 |
+
*/
|
253 |
+
|
254 |
+
::-webkit-inner-spin-button,
|
255 |
+
::-webkit-outer-spin-button {
|
256 |
+
height: auto;
|
257 |
+
}
|
258 |
+
|
259 |
+
/*
|
260 |
+
1. Correct the odd appearance in Chrome and Safari.
|
261 |
+
2. Correct the outline style in Safari.
|
262 |
+
*/
|
263 |
+
|
264 |
+
[type='search'] {
|
265 |
+
-webkit-appearance: textfield;
|
266 |
+
/* 1 */
|
267 |
+
outline-offset: -2px;
|
268 |
+
/* 2 */
|
269 |
+
}
|
270 |
+
|
271 |
+
/*
|
272 |
+
Remove the inner padding in Chrome and Safari on macOS.
|
273 |
+
*/
|
274 |
+
|
275 |
+
::-webkit-search-decoration {
|
276 |
+
-webkit-appearance: none;
|
277 |
+
}
|
278 |
+
|
279 |
+
/*
|
280 |
+
1. Correct the inability to style clickable types in iOS and Safari.
|
281 |
+
2. Change font properties to `inherit` in Safari.
|
282 |
+
*/
|
283 |
+
|
284 |
+
::-webkit-file-upload-button {
|
285 |
+
-webkit-appearance: button;
|
286 |
+
/* 1 */
|
287 |
+
font: inherit;
|
288 |
+
/* 2 */
|
289 |
+
}
|
290 |
+
|
291 |
+
/*
|
292 |
+
Add the correct display in Chrome and Safari.
|
293 |
+
*/
|
294 |
+
|
295 |
+
summary {
|
296 |
+
display: list-item;
|
297 |
+
}
|
298 |
+
|
299 |
+
/*
|
300 |
+
Removes the default spacing and border for appropriate elements.
|
301 |
+
*/
|
302 |
+
|
303 |
+
blockquote,
|
304 |
+
dl,
|
305 |
+
dd,
|
306 |
+
h1,
|
307 |
+
h2,
|
308 |
+
h3,
|
309 |
+
h4,
|
310 |
+
h5,
|
311 |
+
h6,
|
312 |
+
hr,
|
313 |
+
figure,
|
314 |
+
p,
|
315 |
+
pre {
|
316 |
+
margin: 0;
|
317 |
+
}
|
318 |
+
|
319 |
+
fieldset {
|
320 |
+
margin: 0;
|
321 |
+
padding: 0;
|
322 |
+
}
|
323 |
+
|
324 |
+
legend {
|
325 |
+
padding: 0;
|
326 |
+
}
|
327 |
+
|
328 |
+
ol,
|
329 |
+
ul,
|
330 |
+
menu {
|
331 |
+
list-style: none;
|
332 |
+
margin: 0;
|
333 |
+
padding: 0;
|
334 |
+
}
|
335 |
+
|
336 |
+
/*
|
337 |
+
Prevent resizing textareas horizontally by default.
|
338 |
+
*/
|
339 |
+
|
340 |
+
textarea {
|
341 |
+
resize: vertical;
|
342 |
+
}
|
343 |
+
|
344 |
+
/*
|
345 |
+
1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
|
346 |
+
2. Set the default placeholder color to the user's configured gray 400 color.
|
347 |
+
*/
|
348 |
+
|
349 |
+
input::-webkit-input-placeholder, textarea::-webkit-input-placeholder {
|
350 |
+
opacity: 1;
|
351 |
+
/* 1 */
|
352 |
+
color: #9ca3af;
|
353 |
+
/* 2 */
|
354 |
+
}
|
355 |
+
|
356 |
+
input::placeholder,
|
357 |
+
textarea::placeholder {
|
358 |
+
opacity: 1;
|
359 |
+
/* 1 */
|
360 |
+
color: #9ca3af;
|
361 |
+
/* 2 */
|
362 |
+
}
|
363 |
+
|
364 |
+
/*
|
365 |
+
Set the default cursor for buttons.
|
366 |
+
*/
|
367 |
+
|
368 |
+
button,
|
369 |
+
[role="button"] {
|
370 |
+
cursor: pointer;
|
371 |
+
}
|
372 |
+
|
373 |
+
/*
|
374 |
+
Make sure disabled buttons don't get the pointer cursor.
|
375 |
+
*/
|
376 |
+
|
377 |
+
:disabled {
|
378 |
+
cursor: default;
|
379 |
+
}
|
380 |
+
|
381 |
+
/*
|
382 |
+
1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
|
383 |
+
2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
|
384 |
+
This can trigger a poorly considered lint error in some tools but is included by design.
|
385 |
+
*/
|
386 |
+
|
387 |
+
img,
|
388 |
+
svg,
|
389 |
+
video,
|
390 |
+
canvas,
|
391 |
+
audio,
|
392 |
+
iframe,
|
393 |
+
embed,
|
394 |
+
object {
|
395 |
+
display: block;
|
396 |
+
/* 1 */
|
397 |
+
vertical-align: middle;
|
398 |
+
/* 2 */
|
399 |
+
}
|
400 |
+
|
401 |
+
/*
|
402 |
+
Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
|
403 |
+
*/
|
404 |
+
|
405 |
+
img,
|
406 |
+
video {
|
407 |
+
max-width: 100%;
|
408 |
+
height: auto;
|
409 |
+
}
|
410 |
+
|
411 |
+
*, ::before, ::after {
|
412 |
+
--tw-border-spacing-x: 0;
|
413 |
+
--tw-border-spacing-y: 0;
|
414 |
+
--tw-translate-x: 0;
|
415 |
+
--tw-translate-y: 0;
|
416 |
+
--tw-rotate: 0;
|
417 |
+
--tw-skew-x: 0;
|
418 |
+
--tw-skew-y: 0;
|
419 |
+
--tw-scale-x: 1;
|
420 |
+
--tw-scale-y: 1;
|
421 |
+
--tw-pan-x: ;
|
422 |
+
--tw-pan-y: ;
|
423 |
+
--tw-pinch-zoom: ;
|
424 |
+
--tw-scroll-snap-strictness: proximity;
|
425 |
+
--tw-ordinal: ;
|
426 |
+
--tw-slashed-zero: ;
|
427 |
+
--tw-numeric-figure: ;
|
428 |
+
--tw-numeric-spacing: ;
|
429 |
+
--tw-numeric-fraction: ;
|
430 |
+
--tw-ring-inset: ;
|
431 |
+
--tw-ring-offset-width: 0px;
|
432 |
+
--tw-ring-offset-color: #fff;
|
433 |
+
--tw-ring-color: rgb(59 130 246 / 0.5);
|
434 |
+
--tw-ring-offset-shadow: 0 0 #0000;
|
435 |
+
--tw-ring-shadow: 0 0 #0000;
|
436 |
+
--tw-shadow: 0 0 #0000;
|
437 |
+
--tw-shadow-colored: 0 0 #0000;
|
438 |
+
--tw-blur: ;
|
439 |
+
--tw-brightness: ;
|
440 |
+
--tw-contrast: ;
|
441 |
+
--tw-grayscale: ;
|
442 |
+
--tw-hue-rotate: ;
|
443 |
+
--tw-invert: ;
|
444 |
+
--tw-saturate: ;
|
445 |
+
--tw-sepia: ;
|
446 |
+
--tw-drop-shadow: ;
|
447 |
+
--tw-backdrop-blur: ;
|
448 |
+
--tw-backdrop-brightness: ;
|
449 |
+
--tw-backdrop-contrast: ;
|
450 |
+
--tw-backdrop-grayscale: ;
|
451 |
+
--tw-backdrop-hue-rotate: ;
|
452 |
+
--tw-backdrop-invert: ;
|
453 |
+
--tw-backdrop-opacity: ;
|
454 |
+
--tw-backdrop-saturate: ;
|
455 |
+
--tw-backdrop-sepia: ;
|
456 |
+
}
|
457 |
+
|
458 |
+
::-webkit-backdrop {
|
459 |
+
--tw-border-spacing-x: 0;
|
460 |
+
--tw-border-spacing-y: 0;
|
461 |
+
--tw-translate-x: 0;
|
462 |
+
--tw-translate-y: 0;
|
463 |
+
--tw-rotate: 0;
|
464 |
+
--tw-skew-x: 0;
|
465 |
+
--tw-skew-y: 0;
|
466 |
+
--tw-scale-x: 1;
|
467 |
+
--tw-scale-y: 1;
|
468 |
+
--tw-pan-x: ;
|
469 |
+
--tw-pan-y: ;
|
470 |
+
--tw-pinch-zoom: ;
|
471 |
+
--tw-scroll-snap-strictness: proximity;
|
472 |
+
--tw-ordinal: ;
|
473 |
+
--tw-slashed-zero: ;
|
474 |
+
--tw-numeric-figure: ;
|
475 |
+
--tw-numeric-spacing: ;
|
476 |
+
--tw-numeric-fraction: ;
|
477 |
+
--tw-ring-inset: ;
|
478 |
+
--tw-ring-offset-width: 0px;
|
479 |
+
--tw-ring-offset-color: #fff;
|
480 |
+
--tw-ring-color: rgb(59 130 246 / 0.5);
|
481 |
+
--tw-ring-offset-shadow: 0 0 #0000;
|
482 |
+
--tw-ring-shadow: 0 0 #0000;
|
483 |
+
--tw-shadow: 0 0 #0000;
|
484 |
+
--tw-shadow-colored: 0 0 #0000;
|
485 |
+
--tw-blur: ;
|
486 |
+
--tw-brightness: ;
|
487 |
+
--tw-contrast: ;
|
488 |
+
--tw-grayscale: ;
|
489 |
+
--tw-hue-rotate: ;
|
490 |
+
--tw-invert: ;
|
491 |
+
--tw-saturate: ;
|
492 |
+
--tw-sepia: ;
|
493 |
+
--tw-drop-shadow: ;
|
494 |
+
--tw-backdrop-blur: ;
|
495 |
+
--tw-backdrop-brightness: ;
|
496 |
+
--tw-backdrop-contrast: ;
|
497 |
+
--tw-backdrop-grayscale: ;
|
498 |
+
--tw-backdrop-hue-rotate: ;
|
499 |
+
--tw-backdrop-invert: ;
|
500 |
+
--tw-backdrop-opacity: ;
|
501 |
+
--tw-backdrop-saturate: ;
|
502 |
+
--tw-backdrop-sepia: ;
|
503 |
+
}
|
504 |
+
|
505 |
+
::backdrop {
|
506 |
+
--tw-border-spacing-x: 0;
|
507 |
+
--tw-border-spacing-y: 0;
|
508 |
+
--tw-translate-x: 0;
|
509 |
+
--tw-translate-y: 0;
|
510 |
+
--tw-rotate: 0;
|
511 |
+
--tw-skew-x: 0;
|
512 |
+
--tw-skew-y: 0;
|
513 |
+
--tw-scale-x: 1;
|
514 |
+
--tw-scale-y: 1;
|
515 |
+
--tw-pan-x: ;
|
516 |
+
--tw-pan-y: ;
|
517 |
+
--tw-pinch-zoom: ;
|
518 |
+
--tw-scroll-snap-strictness: proximity;
|
519 |
+
--tw-ordinal: ;
|
520 |
+
--tw-slashed-zero: ;
|
521 |
+
--tw-numeric-figure: ;
|
522 |
+
--tw-numeric-spacing: ;
|
523 |
+
--tw-numeric-fraction: ;
|
524 |
+
--tw-ring-inset: ;
|
525 |
+
--tw-ring-offset-width: 0px;
|
526 |
+
--tw-ring-offset-color: #fff;
|
527 |
+
--tw-ring-color: rgb(59 130 246 / 0.5);
|
528 |
+
--tw-ring-offset-shadow: 0 0 #0000;
|
529 |
+
--tw-ring-shadow: 0 0 #0000;
|
530 |
+
--tw-shadow: 0 0 #0000;
|
531 |
+
--tw-shadow-colored: 0 0 #0000;
|
532 |
+
--tw-blur: ;
|
533 |
+
--tw-brightness: ;
|
534 |
+
--tw-contrast: ;
|
535 |
+
--tw-grayscale: ;
|
536 |
+
--tw-hue-rotate: ;
|
537 |
+
--tw-invert: ;
|
538 |
+
--tw-saturate: ;
|
539 |
+
--tw-sepia: ;
|
540 |
+
--tw-drop-shadow: ;
|
541 |
+
--tw-backdrop-blur: ;
|
542 |
+
--tw-backdrop-brightness: ;
|
543 |
+
--tw-backdrop-contrast: ;
|
544 |
+
--tw-backdrop-grayscale: ;
|
545 |
+
--tw-backdrop-hue-rotate: ;
|
546 |
+
--tw-backdrop-invert: ;
|
547 |
+
--tw-backdrop-opacity: ;
|
548 |
+
--tw-backdrop-saturate: ;
|
549 |
+
--tw-backdrop-sepia: ;
|
550 |
+
}
|
551 |
+
|
552 |
+
.pointer-events-none {
|
553 |
+
pointer-events: none;
|
554 |
+
}
|
555 |
+
|
556 |
+
.pointer-events-auto {
|
557 |
+
pointer-events: auto;
|
558 |
+
}
|
559 |
+
|
560 |
+
.absolute {
|
561 |
+
position: absolute;
|
562 |
+
}
|
563 |
+
|
564 |
+
.relative {
|
565 |
+
position: relative;
|
566 |
+
}
|
567 |
+
|
568 |
+
.inset-y-0 {
|
569 |
+
top: 0px;
|
570 |
+
bottom: 0px;
|
571 |
+
}
|
572 |
+
|
573 |
+
.top-4 {
|
574 |
+
top: 1rem;
|
575 |
+
}
|
576 |
+
|
577 |
+
.right-5 {
|
578 |
+
right: 1.25rem;
|
579 |
+
}
|
580 |
+
|
581 |
+
.-right-3 {
|
582 |
+
right: -0.75rem;
|
583 |
+
}
|
584 |
+
|
585 |
+
.top-9 {
|
586 |
+
top: 2.25rem;
|
587 |
+
}
|
588 |
+
|
589 |
+
.left-0 {
|
590 |
+
left: 0px;
|
591 |
+
}
|
592 |
+
|
593 |
+
.bottom-0 {
|
594 |
+
bottom: 0px;
|
595 |
+
}
|
596 |
+
|
597 |
+
.z-50 {
|
598 |
+
z-index: 50;
|
599 |
+
}
|
600 |
+
|
601 |
+
.z-10 {
|
602 |
+
z-index: 10;
|
603 |
+
}
|
604 |
+
|
605 |
+
.-z-10 {
|
606 |
+
z-index: -10;
|
607 |
+
}
|
608 |
+
|
609 |
+
.float-left {
|
610 |
+
float: left;
|
611 |
+
}
|
612 |
+
|
613 |
+
.m-0 {
|
614 |
+
margin: 0px;
|
615 |
+
}
|
616 |
+
|
617 |
+
.mt-4 {
|
618 |
+
margin-top: 1rem;
|
619 |
+
}
|
620 |
+
|
621 |
+
.mt-2 {
|
622 |
+
margin-top: 0.5rem;
|
623 |
+
}
|
624 |
+
|
625 |
+
.ml-auto {
|
626 |
+
margin-left: auto;
|
627 |
+
}
|
628 |
+
|
629 |
+
.mr-auto {
|
630 |
+
margin-right: auto;
|
631 |
+
}
|
632 |
+
|
633 |
+
.mt-6 {
|
634 |
+
margin-top: 1.5rem;
|
635 |
+
}
|
636 |
+
|
637 |
+
.mr-2 {
|
638 |
+
margin-right: 0.5rem;
|
639 |
+
}
|
640 |
+
|
641 |
+
.block {
|
642 |
+
display: block;
|
643 |
+
}
|
644 |
+
|
645 |
+
.flex {
|
646 |
+
display: flex;
|
647 |
+
}
|
648 |
+
|
649 |
+
.inline-flex {
|
650 |
+
display: inline-flex;
|
651 |
+
}
|
652 |
+
|
653 |
+
.hidden {
|
654 |
+
display: none;
|
655 |
+
}
|
656 |
+
|
657 |
+
.h-screen {
|
658 |
+
height: 100vh;
|
659 |
+
}
|
660 |
+
|
661 |
+
.h-10 {
|
662 |
+
height: 2.5rem;
|
663 |
+
}
|
664 |
+
|
665 |
+
.h-auto {
|
666 |
+
height: auto;
|
667 |
+
}
|
668 |
+
|
669 |
+
.h-\[540px\] {
|
670 |
+
height: 540px;
|
671 |
+
}
|
672 |
+
|
673 |
+
.h-full {
|
674 |
+
height: 100%;
|
675 |
+
}
|
676 |
+
|
677 |
+
.w-10 {
|
678 |
+
width: 2.5rem;
|
679 |
+
}
|
680 |
+
|
681 |
+
.w-full {
|
682 |
+
width: 100%;
|
683 |
+
}
|
684 |
+
|
685 |
+
.w-\[600px\] {
|
686 |
+
width: 600px;
|
687 |
+
}
|
688 |
+
|
689 |
+
.w-screen {
|
690 |
+
width: 100vw;
|
691 |
+
}
|
692 |
+
|
693 |
+
.flex-1 {
|
694 |
+
flex: 1 1 0%;
|
695 |
+
}
|
696 |
+
|
697 |
+
.rotate-180 {
|
698 |
+
--tw-rotate: 180deg;
|
699 |
+
-webkit-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));
|
700 |
+
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));
|
701 |
+
}
|
702 |
+
|
703 |
+
.cursor-pointer {
|
704 |
+
cursor: pointer;
|
705 |
+
}
|
706 |
+
|
707 |
+
.items-center {
|
708 |
+
align-items: center;
|
709 |
+
}
|
710 |
+
|
711 |
+
.overflow-auto {
|
712 |
+
overflow: auto;
|
713 |
+
}
|
714 |
+
|
715 |
+
.overflow-hidden {
|
716 |
+
overflow: hidden;
|
717 |
+
}
|
718 |
+
|
719 |
+
.overflow-y-scroll {
|
720 |
+
overflow-y: scroll;
|
721 |
+
}
|
722 |
+
|
723 |
+
.break-words {
|
724 |
+
overflow-wrap: break-word;
|
725 |
+
}
|
726 |
+
|
727 |
+
.rounded-md {
|
728 |
+
border-radius: 0.375rem;
|
729 |
+
}
|
730 |
+
|
731 |
+
.rounded-full {
|
732 |
+
border-radius: 9999px;
|
733 |
+
}
|
734 |
+
|
735 |
+
.rounded-xl {
|
736 |
+
border-radius: 0.75rem;
|
737 |
+
}
|
738 |
+
|
739 |
+
.border {
|
740 |
+
border-width: 1px;
|
741 |
+
}
|
742 |
+
|
743 |
+
.border-r-\[1px\] {
|
744 |
+
border-right-width: 1px;
|
745 |
+
}
|
746 |
+
|
747 |
+
.border-dashed {
|
748 |
+
border-style: dashed;
|
749 |
+
}
|
750 |
+
|
751 |
+
.border-black {
|
752 |
+
--tw-border-opacity: 1;
|
753 |
+
border-color: rgb(0 0 0 / var(--tw-border-opacity));
|
754 |
+
}
|
755 |
+
|
756 |
+
.border-slate-300 {
|
757 |
+
--tw-border-opacity: 1;
|
758 |
+
border-color: rgb(203 213 225 / var(--tw-border-opacity));
|
759 |
+
}
|
760 |
+
|
761 |
+
.border-red-600 {
|
762 |
+
--tw-border-opacity: 1;
|
763 |
+
border-color: rgb(220 38 38 / var(--tw-border-opacity));
|
764 |
+
}
|
765 |
+
|
766 |
+
.bg-white {
|
767 |
+
--tw-bg-opacity: 1;
|
768 |
+
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
769 |
+
}
|
770 |
+
|
771 |
+
.bg-transparent {
|
772 |
+
background-color: transparent;
|
773 |
+
}
|
774 |
+
|
775 |
+
.bg-light-white {
|
776 |
+
background-color: rgba(255,255,255, 0.18);
|
777 |
+
}
|
778 |
+
|
779 |
+
.bg-gradient-to-bl {
|
780 |
+
background-image: linear-gradient(to bottom left, var(--tw-gradient-stops));
|
781 |
+
}
|
782 |
+
|
783 |
+
.from-Retro-light-blue {
|
784 |
+
--tw-gradient-from: #2de2e6;
|
785 |
+
--tw-gradient-to: rgb(45 226 230 / 0);
|
786 |
+
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
|
787 |
+
}
|
788 |
+
|
789 |
+
.from-Vapor-Violet {
|
790 |
+
--tw-gradient-from: #300350;
|
791 |
+
--tw-gradient-to: rgb(48 3 80 / 0);
|
792 |
+
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
|
793 |
+
}
|
794 |
+
|
795 |
+
.from-Retro-purple {
|
796 |
+
--tw-gradient-from: #9700cc;
|
797 |
+
--tw-gradient-to: rgb(151 0 204 / 0);
|
798 |
+
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
|
799 |
+
}
|
800 |
+
|
801 |
+
.from-Retro-light-pink {
|
802 |
+
--tw-gradient-from: #f6019d;
|
803 |
+
--tw-gradient-to: rgb(246 1 157 / 0);
|
804 |
+
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
|
805 |
+
}
|
806 |
+
|
807 |
+
.from-Vapor-Orange {
|
808 |
+
--tw-gradient-from: #f9ac53;
|
809 |
+
--tw-gradient-to: rgb(249 172 83 / 0);
|
810 |
+
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
|
811 |
+
}
|
812 |
+
|
813 |
+
.from-Vapor-Rose {
|
814 |
+
--tw-gradient-from: #f62e97;
|
815 |
+
--tw-gradient-to: rgb(246 46 151 / 0);
|
816 |
+
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
|
817 |
+
}
|
818 |
+
|
819 |
+
.to-Retro-light-pink {
|
820 |
+
--tw-gradient-to: #f6019d;
|
821 |
+
}
|
822 |
+
|
823 |
+
.to-Vapor-Orange {
|
824 |
+
--tw-gradient-to: #f9ac53;
|
825 |
+
}
|
826 |
+
|
827 |
+
.to-Vapor-Pink {
|
828 |
+
--tw-gradient-to: #e93479;
|
829 |
+
}
|
830 |
+
|
831 |
+
.to-Vapor-Blue {
|
832 |
+
--tw-gradient-to: #153cb4;
|
833 |
+
}
|
834 |
+
|
835 |
+
.to-Vapor-Violet {
|
836 |
+
--tw-gradient-to: #300350;
|
837 |
+
}
|
838 |
+
|
839 |
+
.p-4 {
|
840 |
+
padding: 1rem;
|
841 |
+
}
|
842 |
+
|
843 |
+
.p-5 {
|
844 |
+
padding: 1.25rem;
|
845 |
+
}
|
846 |
+
|
847 |
+
.p-0 {
|
848 |
+
padding: 0px;
|
849 |
+
}
|
850 |
+
|
851 |
+
.p-2 {
|
852 |
+
padding: 0.5rem;
|
853 |
+
}
|
854 |
+
|
855 |
+
.px-2 {
|
856 |
+
padding-left: 0.5rem;
|
857 |
+
padding-right: 0.5rem;
|
858 |
+
}
|
859 |
+
|
860 |
+
.px-0 {
|
861 |
+
padding-left: 0px;
|
862 |
+
padding-right: 0px;
|
863 |
+
}
|
864 |
+
|
865 |
+
.py-3 {
|
866 |
+
padding-top: 0.75rem;
|
867 |
+
padding-bottom: 0.75rem;
|
868 |
+
}
|
869 |
+
|
870 |
+
.py-2 {
|
871 |
+
padding-top: 0.5rem;
|
872 |
+
padding-bottom: 0.5rem;
|
873 |
+
}
|
874 |
+
|
875 |
+
.pt-8 {
|
876 |
+
padding-top: 2rem;
|
877 |
+
}
|
878 |
+
|
879 |
+
.pl-5 {
|
880 |
+
padding-left: 1.25rem;
|
881 |
+
}
|
882 |
+
|
883 |
+
.pr-3 {
|
884 |
+
padding-right: 0.75rem;
|
885 |
+
}
|
886 |
+
|
887 |
+
.pl-3 {
|
888 |
+
padding-left: 0.75rem;
|
889 |
+
}
|
890 |
+
|
891 |
+
.pl-9 {
|
892 |
+
padding-left: 2.25rem;
|
893 |
+
}
|
894 |
+
|
895 |
+
.pt-4 {
|
896 |
+
padding-top: 1rem;
|
897 |
+
}
|
898 |
+
|
899 |
+
.pt-2 {
|
900 |
+
padding-top: 0.5rem;
|
901 |
+
}
|
902 |
+
|
903 |
+
.text-left {
|
904 |
+
text-align: left;
|
905 |
+
}
|
906 |
+
|
907 |
+
.text-center {
|
908 |
+
text-align: center;
|
909 |
+
}
|
910 |
+
|
911 |
+
.font-sans {
|
912 |
+
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";
|
913 |
+
}
|
914 |
+
|
915 |
+
.text-3xl {
|
916 |
+
font-size: 1.875rem;
|
917 |
+
line-height: 2.25rem;
|
918 |
+
}
|
919 |
+
|
920 |
+
.text-sm {
|
921 |
+
font-size: 0.875rem;
|
922 |
+
line-height: 1.25rem;
|
923 |
+
}
|
924 |
+
|
925 |
+
.text-2xl {
|
926 |
+
font-size: 1.5rem;
|
927 |
+
line-height: 2rem;
|
928 |
+
}
|
929 |
+
|
930 |
+
.text-base {
|
931 |
+
font-size: 1rem;
|
932 |
+
line-height: 1.5rem;
|
933 |
+
}
|
934 |
+
|
935 |
+
.text-lg {
|
936 |
+
font-size: 1.125rem;
|
937 |
+
line-height: 1.75rem;
|
938 |
+
}
|
939 |
+
|
940 |
+
.font-medium {
|
941 |
+
font-weight: 500;
|
942 |
+
}
|
943 |
+
|
944 |
+
.font-bold {
|
945 |
+
font-weight: 700;
|
946 |
+
}
|
947 |
+
|
948 |
+
.text-white {
|
949 |
+
--tw-text-opacity: 1;
|
950 |
+
color: rgb(255 255 255 / var(--tw-text-opacity));
|
951 |
+
}
|
952 |
+
|
953 |
+
.shadow-2xl {
|
954 |
+
--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
|
955 |
+
--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);
|
956 |
+
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
957 |
+
}
|
958 |
+
|
959 |
+
.shadow-sm {
|
960 |
+
--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
961 |
+
--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);
|
962 |
+
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
963 |
+
}
|
964 |
+
|
965 |
+
.shadow-black {
|
966 |
+
--tw-shadow-color: #000;
|
967 |
+
--tw-shadow: var(--tw-shadow-colored);
|
968 |
+
}
|
969 |
+
|
970 |
+
.filter {
|
971 |
+
-webkit-filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
972 |
+
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
973 |
+
}
|
974 |
+
|
975 |
+
.transition-all {
|
976 |
+
transition-property: all;
|
977 |
+
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
978 |
+
transition-duration: 150ms;
|
979 |
+
}
|
980 |
+
|
981 |
+
.duration-300 {
|
982 |
+
transition-duration: 300ms;
|
983 |
+
}
|
984 |
+
|
985 |
+
.duration-500 {
|
986 |
+
transition-duration: 500ms;
|
987 |
+
}
|
988 |
+
|
989 |
+
.placeholder\:italic::-webkit-input-placeholder {
|
990 |
+
font-style: italic;
|
991 |
+
}
|
992 |
+
|
993 |
+
.placeholder\:italic::placeholder {
|
994 |
+
font-style: italic;
|
995 |
+
}
|
996 |
+
|
997 |
+
.placeholder\:text-slate-400::-webkit-input-placeholder {
|
998 |
+
--tw-text-opacity: 1;
|
999 |
+
color: rgb(148 163 184 / var(--tw-text-opacity));
|
1000 |
+
}
|
1001 |
+
|
1002 |
+
.placeholder\:text-slate-400::placeholder {
|
1003 |
+
--tw-text-opacity: 1;
|
1004 |
+
color: rgb(148 163 184 / var(--tw-text-opacity));
|
1005 |
+
}
|
1006 |
+
|
1007 |
+
@-webkit-keyframes pulse {
|
1008 |
+
50% {
|
1009 |
+
opacity: .5;
|
1010 |
+
}
|
1011 |
+
}
|
1012 |
+
|
1013 |
+
@keyframes pulse {
|
1014 |
+
50% {
|
1015 |
+
opacity: .5;
|
1016 |
+
}
|
1017 |
+
}
|
1018 |
+
|
1019 |
+
.hover\:animate-pulse:hover {
|
1020 |
+
-webkit-animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
1021 |
+
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
1022 |
+
}
|
1023 |
+
|
1024 |
+
.hover\:border-sky-500:hover {
|
1025 |
+
--tw-border-opacity: 1;
|
1026 |
+
border-color: rgb(14 165 233 / var(--tw-border-opacity));
|
1027 |
+
}
|
1028 |
+
|
1029 |
+
.focus\:border-sky-500:focus {
|
1030 |
+
--tw-border-opacity: 1;
|
1031 |
+
border-color: rgb(14 165 233 / var(--tw-border-opacity));
|
1032 |
+
}
|
1033 |
+
|
1034 |
+
.focus\:outline-none:focus {
|
1035 |
+
outline: 2px solid transparent;
|
1036 |
+
outline-offset: 2px;
|
1037 |
+
}
|
1038 |
+
|
1039 |
+
.focus\:ring-1:focus {
|
1040 |
+
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
1041 |
+
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
1042 |
+
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
1043 |
+
}
|
1044 |
+
|
1045 |
+
.focus\:ring-sky-500:focus {
|
1046 |
+
--tw-ring-opacity: 1;
|
1047 |
+
--tw-ring-color: rgb(14 165 233 / var(--tw-ring-opacity));
|
1048 |
+
}
|
1049 |
+
|
1050 |
+
.dark .dark\:border-white {
|
1051 |
+
--tw-border-opacity: 1;
|
1052 |
+
border-color: rgb(255 255 255 / var(--tw-border-opacity));
|
1053 |
+
}
|
1054 |
+
|
1055 |
+
.dark .dark\:bg-stone-900 {
|
1056 |
+
--tw-bg-opacity: 1;
|
1057 |
+
background-color: rgb(28 25 23 / var(--tw-bg-opacity));
|
1058 |
+
}
|
1059 |
+
|
1060 |
+
.dark .dark\:bg-black {
|
1061 |
+
--tw-bg-opacity: 1;
|
1062 |
+
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
|
1063 |
+
}
|
1064 |
+
|
1065 |
+
.dark .dark\:bg-neutral-800 {
|
1066 |
+
--tw-bg-opacity: 1;
|
1067 |
+
background-color: rgb(38 38 38 / var(--tw-bg-opacity));
|
1068 |
+
}
|
1069 |
+
|
1070 |
+
.dark .dark\:text-white {
|
1071 |
+
--tw-text-opacity: 1;
|
1072 |
+
color: rgb(255 255 255 / var(--tw-text-opacity));
|
1073 |
+
}
|
1074 |
+
|
1075 |
+
@media (min-width: 640px) {
|
1076 |
+
.sm\:w-60 {
|
1077 |
+
width: 15rem;
|
1078 |
+
}
|
1079 |
+
|
1080 |
+
.sm\:w-\[250pxs\] {
|
1081 |
+
width: 250pxs;
|
1082 |
+
}
|
1083 |
+
|
1084 |
+
.sm\:w-\[250px\] {
|
1085 |
+
width: 250px;
|
1086 |
+
}
|
1087 |
+
|
1088 |
+
.sm\:text-sm {
|
1089 |
+
font-size: 0.875rem;
|
1090 |
+
line-height: 1.25rem;
|
1091 |
+
}
|
1092 |
+
}
|
1093 |
+
|
1094 |
+
@media (min-width: 1024px) {
|
1095 |
+
.lg\:w-72 {
|
1096 |
+
width: 18rem;
|
1097 |
+
}
|
1098 |
+
}
|
frontend/src/css/index.css
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
body {
|
2 |
+
margin: 0;
|
3 |
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
4 |
+
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
5 |
+
sans-serif;
|
6 |
+
-webkit-font-smoothing: antialiased;
|
7 |
+
-moz-osx-font-smoothing: grayscale;
|
8 |
+
}
|
9 |
+
|
10 |
+
code {
|
11 |
+
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
12 |
+
monospace;
|
13 |
+
}
|
frontend/src/css/input.css
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
@tailwind base;
|
2 |
+
@tailwind components;
|
3 |
+
@tailwind utilities;
|
frontend/src/helper/visual.js
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import '../css/dist/output.css'
|
2 |
+
const emote = ['πΊ', 'πΎ', 'π€', 'π§ ', 'π¦Ύ', 'π²', 'π¦', 'π΅', 'π¦', 'π' ,'π', 'π₯', 'π', 'π§¬', 'π', 'π', 'π±', 'π']
|
3 |
+
|
4 |
+
const colour_map = {
|
5 |
+
0 : 'bg-gradient-to-bl from-Retro-light-blue to-Retro-light-pink',
|
6 |
+
1 : 'bg-gradient-to-bl from-Vapor-Violet to-Vapor-Orange',
|
7 |
+
2 : 'bg-gradient-to-bl from-Retro-purple to-Vapor-Pink',
|
8 |
+
3 : 'bg-gradient-to-bl from-Retro-purple to-Vapor-Blue',
|
9 |
+
4 : 'bg-gradient-to-bl from-Retro-light-pink to-Vapor-Blue',
|
10 |
+
5 : 'bg-gradient-to-bl from-Vapor-Orange to-Vapor-Violet',
|
11 |
+
6 : 'bg-gradient-to-bl from-Vapor-Rose to-Vapor-Blue'
|
12 |
+
}
|
13 |
+
|
14 |
+
export const random_emoji = () =>{
|
15 |
+
return emote[Math.floor(Math.random() * emote.length)]
|
16 |
+
}
|
17 |
+
|
18 |
+
export const random_colour = () => {
|
19 |
+
return colour_map[Math.floor(Math.random() * Object.keys(colour_map).length)]
|
20 |
+
}
|
21 |
+
|
22 |
+
export const list_of_null = (idx) => {
|
23 |
+
var list = []
|
24 |
+
for(var i = 0; i < idx; i++ ) {
|
25 |
+
list.push(null)
|
26 |
+
}
|
27 |
+
return list
|
28 |
+
}
|
29 |
+
|
30 |
+
|
31 |
+
export const useThemeDetector = () => {
|
32 |
+
const getCurrentTheme = () => window.matchMedia("(prefers-color-scheme: dark)").matches;
|
33 |
+
return getCurrentTheme;
|
34 |
+
}
|
frontend/src/index.js
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React from 'react';
|
2 |
+
import ReactDOM from 'react-dom/client';
|
3 |
+
import './css/index.css';
|
4 |
+
import App from './App';
|
5 |
+
//import Navbar from './Components/Navagation/navbar';
|
6 |
+
import reportWebVitals from './reportWebVitals';
|
7 |
+
|
8 |
+
const root = ReactDOM.createRoot(document.getElementById('root'));
|
9 |
+
root.render(
|
10 |
+
<React.StrictMode>
|
11 |
+
< App/>
|
12 |
+
</React.StrictMode>
|
13 |
+
);
|
14 |
+
|
15 |
+
// If you want to start measuring performance in your app, pass a function
|
16 |
+
// to log results (for example: reportWebVitals(console.log))
|
17 |
+
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
18 |
+
reportWebVitals();
|
frontend/src/reportWebVitals.js
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const reportWebVitals = onPerfEntry => {
|
2 |
+
if (onPerfEntry && onPerfEntry instanceof Function) {
|
3 |
+
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
4 |
+
getCLS(onPerfEntry);
|
5 |
+
getFID(onPerfEntry);
|
6 |
+
getFCP(onPerfEntry);
|
7 |
+
getLCP(onPerfEntry);
|
8 |
+
getTTFB(onPerfEntry);
|
9 |
+
});
|
10 |
+
}
|
11 |
+
};
|
12 |
+
|
13 |
+
export default reportWebVitals;
|