LucaVivona commited on
Commit
c132e32
Β·
0 Parent(s):

Gradio Flow Application

Browse files
This view is limited to 50 files because it contains too many changes. Β  See raw diff
Files changed (50) hide show
  1. .gitattributes +3 -0
  2. .vscode/settings.json +2 -0
  3. README.md +146 -0
  4. app.png +3 -0
  5. architecture.png +3 -0
  6. backend/.gitignore +1 -0
  7. backend/Dockerfile +6 -0
  8. backend/app.py +50 -0
  9. backend/requirements.txt +74 -0
  10. backend/scripts/startup.sh +2 -0
  11. backend/src/__init__.py +4 -0
  12. backend/src/demo.py +15 -0
  13. backend/src/example/__pycache__/examples.cpython-37.pyc +0 -0
  14. backend/src/example/__pycache__/examples.cpython-38.pyc +0 -0
  15. backend/src/example/__pycache__/examples.cpython-39.pyc +0 -0
  16. backend/src/example/examples.py +137 -0
  17. backend/src/index.py +17 -0
  18. backend/src/resources/__init__.py +1 -0
  19. backend/src/resources/__pycache__/__init__.cpython-38.pyc +0 -0
  20. backend/src/resources/__pycache__/__init__.cpython-39.pyc +0 -0
  21. backend/src/resources/__pycache__/compiler.cpython-39.pyc +0 -0
  22. backend/src/resources/__pycache__/dock.cpython-39.pyc +0 -0
  23. backend/src/resources/__pycache__/module.cpython-38.pyc +0 -0
  24. backend/src/resources/__pycache__/module.cpython-39.pyc +0 -0
  25. backend/src/resources/dock.py +24 -0
  26. backend/src/resources/module.py +201 -0
  27. docker-compose.yml +27 -0
  28. frontend/.dockerignore +1 -0
  29. frontend/.gitignore +23 -0
  30. frontend/Dockerfile +4 -0
  31. frontend/package-lock.json +0 -0
  32. frontend/package.json +45 -0
  33. frontend/public/android-chrome-192x192.png +3 -0
  34. frontend/public/android-chrome-512x512.png +3 -0
  35. frontend/public/favicon.ico +0 -0
  36. frontend/public/index.html +50 -0
  37. frontend/public/logo192.png +3 -0
  38. frontend/public/manifest.json +25 -0
  39. frontend/public/robots.txt +3 -0
  40. frontend/src/App.js +20 -0
  41. frontend/src/Components/Navagation/navbar.js +289 -0
  42. frontend/src/Components/Nodes/custom.js +48 -0
  43. frontend/src/Components/ReactFlow/ReactFlowEnv.js +82 -0
  44. frontend/src/css/CustomNode.css +46 -0
  45. frontend/src/css/dist/output.css +1098 -0
  46. frontend/src/css/index.css +13 -0
  47. frontend/src/css/input.css +3 -0
  48. frontend/src/helper/visual.js +34 -0
  49. frontend/src/index.js +18 -0
  50. 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
+ ![architecture](https://github.com/commune-ai/Gradio-Flow/blob/gradio-flow/gradio-only/architecture.png)
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
+ ![Application](https://github.com/commune-ai/Gradio-Flow/blob/gradio-flow/gradio-only/app.png)
app.png ADDED

Git LFS Details

  • SHA256: b45d569a39f3c8c2fed327775c9fc46bd437a3aa97da7f64f84302f1b5b881ce
  • Pointer size: 130 Bytes
  • Size of remote file: 89.4 kB
architecture.png ADDED

Git LFS Details

  • SHA256: caf6aa533b627d11b4f62a14f6142b94f96d0b050e1ee0b8ea72a30437cf0369
  • Pointer size: 131 Bytes
  • Size of remote file: 177 kB
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

  • SHA256: e5e62a0af55c6a57c8a94ad25a6c9d2511b927080c02cd9fa8930488bdb0d5c1
  • Pointer size: 130 Bytes
  • Size of remote file: 16.5 kB
frontend/public/android-chrome-512x512.png ADDED

Git LFS Details

  • SHA256: 4f3e478e27199315fbbf7a8eb3771066ca716a9035b7e8d22267a864f88e95f7
  • Pointer size: 130 Bytes
  • Size of remote file: 51.8 kB
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

  • SHA256: c386396ec70db3608075b5fbfaac4ab1ccaa86ba05a68ab393ec551eb66c3e00
  • Pointer size: 129 Bytes
  • Size of remote file: 5.35 kB
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;