LucaVivona commited on
Commit
ca09a0b
1 Parent(s): 06d4327

Node Connection

Browse files
README.md CHANGED
@@ -112,6 +112,10 @@ stream both [Gradio](https://gradio.app) ( and later [Streamlit](https://streaml
112
 
113
 
114
  ### Frontend 🖥️
 
 
 
 
115
  - Node Menu
116
  - fixed some bugs from ``+ button`` for catching errors and wrong inputs
117
  - ``+ button`` now includes hugginface spaces, and gradio share
@@ -128,9 +132,7 @@ stream both [Gradio](https://gradio.app) ( and later [Streamlit](https://streaml
128
 
129
  ### In The Works 🚧
130
  - App Name change
131
- - Streamlit application within Gradio-Flow
132
  - Mutiple windows within the react-flow environment
133
- - Appending streamlit into gradio-flow
134
  - Directory tree search that looks for files that contain classes and functions that are registered under the decorators that are in ``backend/src/resources`` allowing you to append all your registered functions with only using the frontend.
135
 
136
 
 
112
 
113
 
114
  ### Frontend 🖥️
115
+ - Node
116
+ - Append edges together
117
+ - (In the works) Connection API paramters; Allow people to use Gradio Flow as a module base platform
118
+
119
  - Node Menu
120
  - fixed some bugs from ``+ button`` for catching errors and wrong inputs
121
  - ``+ button`` now includes hugginface spaces, and gradio share
 
132
 
133
  ### In The Works 🚧
134
  - App Name change
 
135
  - Mutiple windows within the react-flow environment
 
136
  - Directory tree search that looks for files that contain classes and functions that are registered under the decorators that are in ``backend/src/resources`` allowing you to append all your registered functions with only using the frontend.
137
 
138
 
backend/app.py CHANGED
@@ -72,6 +72,11 @@ def append_port():
72
  visable.append(current)
73
  return jsonify({"executed" : True})
74
 
 
 
 
 
 
75
  @app.route("/api/remove/port" , methods=["POST"])
76
  def remove_port():
77
  current = request.json
 
72
  visable.append(current)
73
  return jsonify({"executed" : True})
74
 
75
+ @app.route("/api/append/connection", methods=["POST"])
76
+ def append_connection():
77
+ current = request.json
78
+ return jsonify({"executed" : True})
79
+
80
  @app.route("/api/remove/port" , methods=["POST"])
81
  def remove_port():
82
  current = request.json
backend/src/demo/stream.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+
3
+ def add_port(type='p2p', mode='input', key="bitconnect", additional_info={}):
4
+ return {
5
+ 'type' : type,
6
+ 'mode' : mode,
7
+ 'key' : key,
8
+ 'additional': additional_info
9
+ }
10
+
11
+ if __name__ == "__main__":
12
+ st.title("Test Add Handel")
13
+ with open('./stream.py') as f:
14
+ st.code(f.read(), "python")
15
+ with st.expander("Add_Port"):
16
+ type = st.multiselect("type", ["p2p", 'https', 'local'])
17
+ mode = st.selectbox("mode", ("input", "output"))
18
+ key = st.text_input("Key", "")
19
+ btn_click = st.button("package")
20
+
21
+ if btn_click:
22
+ st.json(add_port(type, mode, key))
backend/src/resources/module.py CHANGED
@@ -62,10 +62,10 @@ def InterLauncher(name, interface, listen=2000, **kwargs):
62
  package and send it to the flaks api
63
  """
64
  port= kwargs["port"] if "port" in kwargs else DOCKER_PORT.determinePort() # determine the next open port is no port is listed **kwargs
65
-
66
  try:
67
  # (POST) send the information of the gradio to the flask api
68
- requests.post(f"http://{DOCKER_LOCAL_HOST}:{listen}/api/append/port", json={"port" : port, "host" : f'http://localhost:{port}', "file" : "Not Applicable", "name" : name, "kwargs" : kwargs})
69
  except Exception as e:
70
  # If there is an Exception then notify the user and end the method
71
  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}")
@@ -93,13 +93,14 @@ def InterLauncher(name, interface, listen=2000, **kwargs):
93
  ssl_keyfile_password=kwargs['ssl_keyfile_password'] if "ssl_keyfile_password" in kwargs else None,
94
  quiet=kwargs['quiet'] if "quiet" in kwargs else False)
95
 
96
- try:
97
- # (POST) stop the interface if user hits ctrl+c
98
- # send the information of the gradio to the flask
99
- # api to remove it from the list within the flask api
100
- requests.post(f"http://{DOCKER_LOCAL_HOST}:{ listen }/api/remove/port", json={"port" : port, "host" : f'http://localhost:{port}', "file" : 'Not Applicable', "name" : name, "kwargs" : kwargs})
101
- except Exception as e:
102
- print(f"**{bcolor.BOLD}{bcolor.FAIL}CONNECTION ERROR{bcolor.ENDC}** 🐛The api either lost connection or was turned off...🐛 \n {e}")
 
103
 
104
  def tabularGradio(funcs, names=[], name="Tabular Temp Name", **kwargs):
105
  """
@@ -112,12 +113,13 @@ def tabularGradio(funcs, names=[], name="Tabular Temp Name", **kwargs):
112
  names = [_.__name__ for _ in fn]
113
 
114
  port= kwargs["port"] if "port" in kwargs else DOCKER_PORT.determinePort()
115
-
116
  # send this to the backend api for it to be read by the react frontend
117
  if 'listen' in kwargs:
 
118
  try:
119
  # (POST) send the information of the gradio to the flask api
120
- requests.post(f"http://{DOCKER_LOCAL_HOST}:{ kwargs[ 'listen' ] }/api/append/port", json={"port" : port, "host" : f'http://localhost:{port}', "file" : 'Not Applicable', "name" : name, "kwargs" : kwargs})
121
  except Exception as e:
122
  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}")
123
  return
@@ -145,9 +147,10 @@ def tabularGradio(funcs, names=[], name="Tabular Temp Name", **kwargs):
145
  quiet=kwargs['quiet'] if "quiet" in kwargs else False)
146
 
147
  # Ctrl+C that ends the process and then continue the code which will remove from the api
148
- if 'listen' in kwargs:
 
149
  try:
150
- requests.post(f"http://{DOCKER_LOCAL_HOST}:{ kwargs[ 'listen' ] }/api/remove/port", json={"port" : port, "host" : f'http://localhost:{port}', "file" : 'Not Applicable', "name" : name, "kwargs" : kwargs})
151
  except Exception as e:
152
  print(f"**{bcolor.BOLD}{bcolor.FAIL}CONNECTION ERROR{bcolor.ENDC}** 🐛The api either lost connection or was turned off...🐛 \n {e}")
153
 
@@ -304,9 +307,11 @@ def GradioModule(cls):
304
  then when the gradio stops then remove it from the api
305
  """
306
  port= kwargs["port"] if "port" in kwargs else DOCKER_PORT.determinePort()
 
307
  if 'listen' in kwargs:
 
308
  try:
309
- requests.post(f"http://{DOCKER_LOCAL_HOST}:{ kwargs[ 'listen' ] }/api/append/port", json={"port" : port, "host" : f'http://localhost:{port}', "file" : getfile(self.__cls__.__class__), "name" : self.__cls__.__class__.__name__, "kwargs" : kwargs})
310
  except Exception:
311
  print(f"**{bcolor.BOLD}{bcolor.FAIL}CONNECTION ERROR{bcolor.ENDC}** 🐛The listening api is either not up or you choose the wrong port.🐛")
312
  return
@@ -331,14 +336,25 @@ def GradioModule(cls):
331
  ssl_certfile=kwargs['ssl_certfile'] if "ssl_certfile" in kwargs else None,
332
  ssl_keyfile_password=kwargs['ssl_keyfile_password'] if "ssl_keyfile_password" in kwargs else None,
333
  quiet=kwargs['quiet'] if "quiet" in kwargs else False)
334
- if 'listen' in kwargs:
335
  try:
336
- requests.post(f"http://{DOCKER_LOCAL_HOST}:{ kwargs[ 'listen' ] }/api/remove/port", json={"port" : port, "host" : f'http://localhost:{port}', "file" : getfile(self.__cls__.__class__), "name" : self.__cls__.__class__.__name__, "kwargs" : kwargs})
337
  except Exception:
338
  print(f"**{bcolor.BOLD}{bcolor.FAIL}CONNECTION ERROR{bcolor.ENDC}** 🐛The api either lost connection or was turned off...🐛")
339
  return
340
  return Decorator
341
 
 
 
 
 
 
 
 
 
 
 
 
342
  # console colour changer
343
  class bcolor:
344
  HEADER = '\033[95m'
 
62
  package and send it to the flaks api
63
  """
64
  port= kwargs["port"] if "port" in kwargs else DOCKER_PORT.determinePort() # determine the next open port is no port is listed **kwargs
65
+ packet = {"port" : port, "host" : f'http://localhost:{port}', "file" : "Not Applicable", "name" : name, "kwargs" : kwargs}
66
  try:
67
  # (POST) send the information of the gradio to the flask api
68
+ requests.post(f"http://{DOCKER_LOCAL_HOST}:{listen}/api/append/port", json=packet)
69
  except Exception as e:
70
  # If there is an Exception then notify the user and end the method
71
  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}")
 
93
  ssl_keyfile_password=kwargs['ssl_keyfile_password'] if "ssl_keyfile_password" in kwargs else None,
94
  quiet=kwargs['quiet'] if "quiet" in kwargs else False)
95
 
96
+ if stream_exist(packet=packet, listen=listen):
97
+ try:
98
+ # (POST) stop the interface if user hits ctrl+c
99
+ # send the information of the gradio to the flask
100
+ # api to remove it from the list within the flask api
101
+ requests.post(f"http://{DOCKER_LOCAL_HOST}:{ listen }/api/remove/port", json=packet)
102
+ except Exception as e:
103
+ print(f"**{bcolor.BOLD}{bcolor.FAIL}CONNECTION ERROR{bcolor.ENDC}** 🐛The api either lost connection or was turned off...🐛 \n {e}")
104
 
105
  def tabularGradio(funcs, names=[], name="Tabular Temp Name", **kwargs):
106
  """
 
113
  names = [_.__name__ for _ in fn]
114
 
115
  port= kwargs["port"] if "port" in kwargs else DOCKER_PORT.determinePort()
116
+ packet = None
117
  # send this to the backend api for it to be read by the react frontend
118
  if 'listen' in kwargs:
119
+ packet = {"port" : port, "host" : f'http://localhost:{port}', "file" : 'Not Applicable', "name" : name, "kwargs" : kwargs}
120
  try:
121
  # (POST) send the information of the gradio to the flask api
122
+ requests.post(f"http://{DOCKER_LOCAL_HOST}:{ kwargs[ 'listen' ] }/api/append/port", json=packet)
123
  except Exception as e:
124
  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}")
125
  return
 
147
  quiet=kwargs['quiet'] if "quiet" in kwargs else False)
148
 
149
  # Ctrl+C that ends the process and then continue the code which will remove from the api
150
+ if 'listen' in kwargs and stream_exist(packet=packet,listen=kwargs['listen']):
151
+
152
  try:
153
+ requests.post(f"http://{DOCKER_LOCAL_HOST}:{ kwargs[ 'listen' ] }/api/remove/port", json=packet)
154
  except Exception as e:
155
  print(f"**{bcolor.BOLD}{bcolor.FAIL}CONNECTION ERROR{bcolor.ENDC}** 🐛The api either lost connection or was turned off...🐛 \n {e}")
156
 
 
307
  then when the gradio stops then remove it from the api
308
  """
309
  port= kwargs["port"] if "port" in kwargs else DOCKER_PORT.determinePort()
310
+ packet = None
311
  if 'listen' in kwargs:
312
+ packet = { type : { 'format' : "class", 'stream' : "gradio" }, "port" : port, "host" : f'http://localhost:{port}', "file" : getfile(self.__cls__.__class__), "name" : self.__cls__.__class__.__name__, "kwargs" : kwargs}
313
  try:
314
+ requests.post(f"http://{DOCKER_LOCAL_HOST}:{ kwargs[ 'listen' ] }/api/append/port", json=packet)
315
  except Exception:
316
  print(f"**{bcolor.BOLD}{bcolor.FAIL}CONNECTION ERROR{bcolor.ENDC}** 🐛The listening api is either not up or you choose the wrong port.🐛")
317
  return
 
336
  ssl_certfile=kwargs['ssl_certfile'] if "ssl_certfile" in kwargs else None,
337
  ssl_keyfile_password=kwargs['ssl_keyfile_password'] if "ssl_keyfile_password" in kwargs else None,
338
  quiet=kwargs['quiet'] if "quiet" in kwargs else False)
339
+ if 'listen' in kwargs and stream_exist(packet=packet, listen=kwargs[ 'listen' ]):
340
  try:
341
+ requests.post(f"http://{DOCKER_LOCAL_HOST}:{ kwargs[ 'listen' ] }/api/remove/port", json=packet)
342
  except Exception:
343
  print(f"**{bcolor.BOLD}{bcolor.FAIL}CONNECTION ERROR{bcolor.ENDC}** 🐛The api either lost connection or was turned off...🐛")
344
  return
345
  return Decorator
346
 
347
+ def stream_exist(packet, listen):
348
+ """
349
+ Given the port is still up this will return if the
350
+ json sent in the beginning still exsit with the api
351
+ """
352
+ state = requests.get(f"http://{DOCKER_LOCAL_HOST}:{listen}/api/open/ports").json()
353
+ for mod in state:
354
+ if packet == mod:
355
+ return True
356
+ return False
357
+
358
  # console colour changer
359
  class bcolor:
360
  HEADER = '\033[95m'
frontend/src/components/Modal/importer.js CHANGED
@@ -55,7 +55,7 @@ export default function Import(props){
55
  </li>
56
  </ul>
57
  {subTab === 0 && <Local/>}
58
- {subTab === 1 && <Shared textHandler={props.textHandler} appendHandler={props.appendHandler} handelError={props.handelError} catch={props.catch}/>}
59
 
60
  {props.catch && <div className='p-5'>
61
  <Message floating negative>
@@ -79,6 +79,12 @@ export default function Import(props){
79
  </div>}
80
  </div>
81
  }
 
 
 
 
 
 
82
  </Modal>
83
  </div>)
84
  }
@@ -99,6 +105,97 @@ function Local(props){
99
  )
100
  }
101
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  function Shared(props){
103
  const [preview, setPreview] = useState("")
104
  const [fetchable, setFetch] = useState(false)
@@ -133,7 +230,7 @@ function Shared(props){
133
 
134
  return (
135
  <div className='w-full shadow-lg' onKeyPress={(e)=>{
136
- if (e.key.includes("Enter")) props.appendHandler()
137
  }}>
138
  <div className='p-5'>
139
  <Message floating>
@@ -178,7 +275,7 @@ function Shared(props){
178
  </div>
179
  <div className=' right-0 ml-5'>
180
  <button className="relative inline-flex justify-center p-0.5 mb-2 mr-2 overflow-hidden text-sm font-sans font-bold text-gray-900 rounded-lg group bg-gradient-to-br from-purple-600 to-blue-500 group-hover:from-purple-600 group-hover:to-blue-500 hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-blue-300 dark:focus:ring-blue-800"
181
- onClick={()=>{props.appendHandler()}}>
182
  <span className="relative px-5 py-2.5 transition-all ease-in duration-75 bg-white dark:bg-[#1b1c1d] rounded-md group-hover:bg-opacity-0">
183
  Enter
184
  </span>
 
55
  </li>
56
  </ul>
57
  {subTab === 0 && <Local/>}
58
+ {subTab === 1 && <Shared type="gradio" textHandler={props.textHandler} appendHandler={props.appendHandler} handelError={props.handelError} catch={props.catch}/>}
59
 
60
  {props.catch && <div className='p-5'>
61
  <Message floating negative>
 
79
  </div>}
80
  </div>
81
  }
82
+ { tab === "streamlit" &&
83
+ <div className='w-full bg-white'>
84
+ <Shared type="streamlit" textHandler={props.textHandler} appendHandler={props.appendHandler} handelError={props.handelError} catch={props.catch}/>
85
+ </div>
86
+ }
87
+
88
  </Modal>
89
  </div>)
90
  }
 
105
  )
106
  }
107
 
108
+ function Stream(props){
109
+ const [preview, setPreview] = useState("")
110
+ const [fetchable, setFetch] = useState(false)
111
+
112
+ const isFetchable = async (url) => {
113
+ const pattern = {
114
+ share : /^https?:\/\/*([0-9]{5})*(-gradio)*(.app)?(\/)?$/,
115
+ hugginFace : /^https?:\/\/*(hf.space)\/*(embed)\/*([a-zA-Z0-9+_-]+)\/*([a-zA-Z0-9+_-]+)\/*([+])?(\/)?$/
116
+ }
117
+
118
+ if (!pattern.share.test(url) &&
119
+ !pattern.hugginFace.test(url)){
120
+ setFetch(false)
121
+ return
122
+ }
123
+
124
+
125
+ fetch(url, {mode : "no-cors"}).then((re) => {
126
+ console.log(re)
127
+ if(re.url.includes("http://localhost:3000")){
128
+ setFetch(false)
129
+ } else {
130
+ setFetch(true)
131
+ props.catch ? props.handelError(false) : props.handelError(props.catch)
132
+ }
133
+
134
+ }).catch((err)=>{
135
+ setFetch(false)
136
+ })
137
+ setFetch(false)
138
+ }
139
+
140
+ return (
141
+ <div className='w-full shadow-lg' onKeyPress={(e)=>{
142
+ if (e.key.includes("Enter")) props.appendHandler(props.type)
143
+ }}>
144
+ <div className='p-5'>
145
+ <Message floating>
146
+ <div className={`flex items-center rounded-md bg-light-white mt-6 border-dashed`}>
147
+ <label className="relative block w-full p-5 focus:shadow-xl">
148
+ <span className={`absolute inset-y-0 left-0 flex items-center pl-8`}>
149
+ <BsSearch className="block float-left cursor-pointer text-gray-500"/>
150
+ </span>
151
+ <input className={`placeholder:italic placeholder:text-slate-400 text-black dark:text-white block w-full border border-slate-300 border-dashed rounded-md py-2 pl-9 pr-3 focus:shadow-xl focus:outline-none focus:border-sky-500 focus:ring-sky-500 focus:ring-1 sm:text-sm bg-transparent`}
152
+ placeholder={`URL`}
153
+ type="text" name="search"
154
+ onChange={(e) => {
155
+ props.textHandler(e, "text")
156
+ setPreview(e.target.value)
157
+ setFetch(isFetchable(e.target.value))
158
+ }}
159
+ />
160
+ </label>
161
+ </div>
162
+ { fetchable === true && <div className=' w-full'>
163
+ <h1 className=' text-xl font-sans font-bold text-center text-black mb-2'> Preview </h1>
164
+ <div className='p-3 px-1 w-3/4 h-80 bg-gray-200 mr-auto ml-auto rounded-xl'>
165
+ <div className='w-full h-full overflow-hidden relative -ml-[5px]'>
166
+ <iframe title='Preview' src={preview} className=' absolute top-0 bottom-0 left-0 -right-[25px] overflow-y-scroll w-full h-full mr-auto ml-auto'/>
167
+ </div>
168
+ </div>
169
+ </div>}
170
+ <div className={`flex items-center rounded-md bg-light-white dark:bg-[#1b1c1d] mt-6 border-dashed`}>
171
+ <label className="relative block p-5 w-full focus:shadow-xl">
172
+ <span className={`absolute inset-y-0 left-0 flex items-center pl-7`}>
173
+ <Icon className=" text-gray-500 block float-left cursor-pointer mr-2" name="address card"/>
174
+ </span>
175
+ <input className={`placeholder:italic placeholder:text-slate-400 text-black dark:text-white block bg-transparent w-full border border-slate-300 border-dashed rounded-md py-2 pl-9 pr-3 focus:shadow-xl focus:outline-none focus:border-sky-500 focus:ring-sky-500 focus:ring-1 sm:text-sm`}
176
+ placeholder={`Name ( > 20 Characters)` }
177
+ type="text" name="search"
178
+ autoComplete='off'
179
+ onChange={(e) => {
180
+ props.textHandler(e, "name")
181
+ }}
182
+ />
183
+ </label>
184
+ </div>
185
+ <div className=' right-0 ml-5'>
186
+ <button className="relative inline-flex justify-center p-0.5 mb-2 mr-2 overflow-hidden text-sm font-sans font-bold text-gray-900 rounded-lg group bg-gradient-to-br from-purple-600 to-blue-500 group-hover:from-purple-600 group-hover:to-blue-500 hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-blue-300 dark:focus:ring-blue-800"
187
+ onClick={()=>{props.appendHandler(props.type)}}>
188
+ <span className="relative px-5 py-2.5 transition-all ease-in duration-75 bg-white dark:bg-[#1b1c1d] rounded-md group-hover:bg-opacity-0">
189
+ Enter
190
+ </span>
191
+ </button>
192
+ </div>
193
+ </Message>
194
+ </div>
195
+ </div>
196
+ )
197
+ }
198
+
199
  function Shared(props){
200
  const [preview, setPreview] = useState("")
201
  const [fetchable, setFetch] = useState(false)
 
230
 
231
  return (
232
  <div className='w-full shadow-lg' onKeyPress={(e)=>{
233
+ if (e.key.includes("Enter")) props.appendHandler(props.type)
234
  }}>
235
  <div className='p-5'>
236
  <Message floating>
 
275
  </div>
276
  <div className=' right-0 ml-5'>
277
  <button className="relative inline-flex justify-center p-0.5 mb-2 mr-2 overflow-hidden text-sm font-sans font-bold text-gray-900 rounded-lg group bg-gradient-to-br from-purple-600 to-blue-500 group-hover:from-purple-600 group-hover:to-blue-500 hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-blue-300 dark:focus:ring-blue-800"
278
+ onClick={()=>{props.appendHandler(props.type)}}>
279
  <span className="relative px-5 py-2.5 transition-all ease-in duration-75 bg-white dark:bg-[#1b1c1d] rounded-md group-hover:bg-opacity-0">
280
  Enter
281
  </span>
frontend/src/components/Navagation/navbar.js CHANGED
@@ -1,10 +1,13 @@
1
  import React, { Component } from "react";
2
- import {BsArrowLeftShort} from 'react-icons/bs';
3
- import "../../css/dist/output.css"
4
- import {ReactComponent as ReactLogo} from '../../images/logo.svg'
5
- import { random_colour, random_emoji } from "../../helper/visual";
6
  import { Icon } from 'semantic-ui-react'
7
  import Import from '../Modal/importer'
 
 
 
 
 
 
 
8
  export default class Navbar extends Component{
9
  constructor(props){
10
  super(props)
@@ -55,7 +58,7 @@ export default class Navbar extends Component{
55
  /**
56
  * Append new node from the user
57
  */
58
- append_gradio = async () => {
59
  const pattern = {
60
  local : /^https?:\/\/(localhost)*(:[0-9]+)?(\/)?$/,
61
  share : /^https?:\/\/*([0-9]{5})*(-gradio)*(.app)?(\/)?$/,
@@ -78,7 +81,7 @@ export default class Navbar extends Component{
78
  }
79
 
80
  fetch(this.state.text, {method : "GET", mode: 'no-cors'}).then((re) => {
81
- 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 => {
82
  this.setState({'text': "",'name' : "",'error' : false,'modal' : false })
83
 
84
  }).catch(() => this.setState({'text': '', 'name' : '', 'error' : true, }))
@@ -218,7 +221,7 @@ export default class Navbar extends Component{
218
  <Import open={this.state.modal}
219
  quitHandeler={this.handelModal}
220
  textHandler={this.updateText}
221
- appendHandler={this.append_gradio}
222
  handelError={this.handelError}
223
  catch={this.state.error}/>
224
 
 
1
  import React, { Component } from "react";
 
 
 
 
2
  import { Icon } from 'semantic-ui-react'
3
  import Import from '../Modal/importer'
4
+ import { random_colour, random_emoji } from "../../helper/visual";
5
+
6
+ import "../../css/dist/output.css"
7
+
8
+ import {BsArrowLeftShort} from 'react-icons/bs';
9
+ import {ReactComponent as ReactLogo} from '../../images/logo.svg'
10
+
11
  export default class Navbar extends Component{
12
  constructor(props){
13
  super(props)
 
58
  /**
59
  * Append new node from the user
60
  */
61
+ appendStreamNode = async (type) => {
62
  const pattern = {
63
  local : /^https?:\/\/(localhost)*(:[0-9]+)?(\/)?$/,
64
  share : /^https?:\/\/*([0-9]{5})*(-gradio)*(.app)?(\/)?$/,
 
81
  }
82
 
83
  fetch(this.state.text, {method : "GET", mode: 'no-cors'}).then((re) => {
84
+ fetch("http://localhost:2000/api/append/port", {method: 'POST', mode : 'cors', headers : { 'Content-Type' : 'application/json' }, body: JSON.stringify({file : "", kwargs : { type : type }, name : this.state.name === "" ?`temp_class_${this.temp_host++}` : `${this.state.name}`, port: 0 , host : this.state.text}) }).then(resp => {
85
  this.setState({'text': "",'name' : "",'error' : false,'modal' : false })
86
 
87
  }).catch(() => this.setState({'text': '', 'name' : '', 'error' : true, }))
 
221
  <Import open={this.state.modal}
222
  quitHandeler={this.handelModal}
223
  textHandler={this.updateText}
224
+ appendHandler={this.appendStreamNode}
225
  handelError={this.handelError}
226
  catch={this.state.error}/>
227
 
frontend/src/components/Nodes/Custom.js CHANGED
@@ -1,4 +1,5 @@
1
  import React from "react"
 
2
  import {TbResize} from 'react-icons/tb'
3
  import {BiCube, BiRefresh} from 'react-icons/bi'
4
  import {BsTrash, BsArrowDownRightSquare} from 'react-icons/bs'
@@ -20,22 +21,22 @@ export default class CustomNodeIframe extends React.Component {
20
  this.state = {
21
  id : id,
22
  reachable : this.isFetchable(data.host),
23
- selected : true,
24
  data : data,
25
  width : 540,
26
  height : 600,
27
- size : true,
28
  iframe : 0,
29
  }
30
 
31
  }
32
 
33
  handelSelected = () => {
34
- this.setState({'selected' : !this.state.selected})
35
  }
36
 
37
  handelSizeState = () => {
38
- this.setState({'size' : !this.state.size})
39
  }
40
 
41
  isFetchable = async (host) => {
@@ -57,7 +58,7 @@ export default class CustomNodeIframe extends React.Component {
57
  if(!this.isFetchable(this.state.data.host)){
58
  this.onNodeClick(this.state.id)
59
  } else{
60
- this.setState({'iframe' : this.state.iframe + 1})
61
  }
62
  }
63
 
@@ -68,7 +69,7 @@ export default class CustomNodeIframe extends React.Component {
68
 
69
  handelSize(evt, increment, change){
70
  if (evt === "increment") {
71
- this.setState({[`${change}`] : change === "width" ? this.state.width + increment : this.state.height + increment })
72
  change === "width" ? this.myRef.current.style.width = `${this.state.width + increment}px` : this.myRef.current.style.height = `${this.state.height + increment}px`
73
  }
74
 
@@ -128,7 +129,7 @@ export default class CustomNodeIframe extends React.Component {
128
  if (!this.state.reachable) {this.onNodeClick(this.state.id) }
129
  return (<>
130
  <div className=" flex w-full h-10 top-0 cursor-pointer" onClick={this.handelEvent}>
131
- <div title="Collaspse Node" className=" duration-300 cursor-pointer shadow-xl border-2 border-white h-10 w-10 mr-2 -mt-3 bg-Warm-Blue rounded-xl" onClick={this.handelSelected}><CgLayoutGridSmall className="h-full w-full text-white p-1"/></div>
132
 
133
 
134
  <div className={` flex ${this.state.selected ? '' : 'w-0 hidden'}`}>
@@ -145,7 +146,7 @@ export default class CustomNodeIframe extends React.Component {
145
 
146
  </div>
147
 
148
- <div id={`draggable`} className={`relative w-[540px] h-[600px] overflow-hidden m-0 p-0 shadow-2xl`} ref={this.myRef}>
149
 
150
  <div className={`absolute p-5 h-full w-full ${this.state.data.colour} shadow-2xl rounded-xl -z-20`}></div>
151
  <iframe
@@ -158,6 +159,32 @@ export default class CustomNodeIframe extends React.Component {
158
  sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"
159
  ></iframe>
160
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  { this.state.size && !navigator.userAgent.match(/firefox|fxios/i) && <>
162
 
163
  <div id="remove-ghost" className={`absolute select-none -bottom-0 right-0 w-5 h-5border-2 shadow-2xl rounded-xl z-10 cursor-nwse-resize hover:opacity-50 `}
@@ -172,6 +199,7 @@ export default class CustomNodeIframe extends React.Component {
172
 
173
  </>
174
  }
 
175
 
176
  </>)
177
  }
 
1
  import React from "react"
2
+ import { Handle, Position } from "react-flow-renderer"
3
  import {TbResize} from 'react-icons/tb'
4
  import {BiCube, BiRefresh} from 'react-icons/bi'
5
  import {BsTrash, BsArrowDownRightSquare} from 'react-icons/bs'
 
21
  this.state = {
22
  id : id,
23
  reachable : this.isFetchable(data.host),
24
+ selected : false,
25
  data : data,
26
  width : 540,
27
  height : 600,
28
+ size : false,
29
  iframe : 0,
30
  }
31
 
32
  }
33
 
34
  handelSelected = () => {
35
+ this.setState(prevState => ({'selected' : !prevState.selected, 'size' : false }))
36
  }
37
 
38
  handelSizeState = () => {
39
+ this.setState(prevState => ({'size' : !prevState.size}))
40
  }
41
 
42
  isFetchable = async (host) => {
 
58
  if(!this.isFetchable(this.state.data.host)){
59
  this.onNodeClick(this.state.id)
60
  } else{
61
+ this.setState(prevState => ({'iframe' : prevState.iframe + 1}))
62
  }
63
  }
64
 
 
69
 
70
  handelSize(evt, increment, change){
71
  if (evt === "increment") {
72
+ this.setState(prevState => ({[`${change}`] : change === "width" ? prevState.width + increment : prevState.height + increment }))
73
  change === "width" ? this.myRef.current.style.width = `${this.state.width + increment}px` : this.myRef.current.style.height = `${this.state.height + increment}px`
74
  }
75
 
 
129
  if (!this.state.reachable) {this.onNodeClick(this.state.id) }
130
  return (<>
131
  <div className=" flex w-full h-10 top-0 cursor-pointer" onClick={this.handelEvent}>
132
+ <div title={this.state.selected ? "Collaspse Node" : "Expand Node"} className=" duration-300 cursor-pointer shadow-xl border-2 border-white h-10 w-10 mr-2 -mt-3 bg-Warm-Blue rounded-xl" onClick={this.handelSelected}><CgLayoutGridSmall className="h-full w-full text-white p-1"/></div>
133
 
134
 
135
  <div className={` flex ${this.state.selected ? '' : 'w-0 hidden'}`}>
 
146
 
147
  </div>
148
 
149
+ <div id={`draggable`} className={`relative overflow-hidden m-0 p-0 shadow-2xl ${this.state.selected ? "w-[540px] h-[600px]" : "hidden"} duration-200`} ref={this.myRef}>
150
 
151
  <div className={`absolute p-5 h-full w-full ${this.state.data.colour} shadow-2xl rounded-xl -z-20`}></div>
152
  <iframe
 
159
  sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"
160
  ></iframe>
161
  </div>
162
+
163
+ {/*Input*/}
164
+ <Handle type="target"
165
+ id="input"
166
+ position={Position.Left}
167
+ style={!this.state.selected ?
168
+ {"paddingRight" : "5px" , "marginTop" : "15px", "height" : "25px", "width" : "25px", "borderRadius" : "3px", "zIndex" : "10000", "background" : "white", "boxShadow" : "3px 3px #888888"}
169
+ :{"paddingRight" : "5px" ,"height" : "25px", "width" : "25px", "borderRadius" : "3px", "zIndex" : "10000", "background" : "white", "boxShadow" : "3px 3px #888888"}}
170
+ />
171
+
172
+ {/*Output*/}
173
+ <Handle type="source" id="output" position={Position.Right} style={ !this.state.selected ?
174
+ {"paddingLeft" : "5px", "marginTop" : "15px" ,"height" : "25px", "width" : "25px", "borderRadius" : "3px", "zIndex" : "10000", "background" : "white", "boxShadow" : "3px 3px #888888"}
175
+ : {"paddingLeft" : "5px", "marginTop" : "0px" ,"height" : "25px", "width" : "25px", "borderRadius" : "3px", "zIndex" : "10000", "background" : "white", "boxShadow" : "3px 3px #888888"}}/>
176
+
177
+ {
178
+ !this.state.selected &&
179
+ <div
180
+ id={`draggable`}
181
+ className={` w-[340px] h-[140px] text-white text-md flex flex-col text-center items-center cursor-grab shadow-lg
182
+ p-5 px-2 rounded-md hover:animate-pulse break-all -z-20 ${this.state.data.colour}`}>
183
+
184
+ <div className="absolute text-6xl opacity-60 z-10 pt-8 ">{this.state.data.emoji}</div>
185
+ <h2 className={`max-w-full font-sans text-blue-50 leading-tight font-bold text-3xl flex-1 z-20 pt-10`} style={{"textShadow" : "0px 1px 2px rgba(0, 0, 0, 0.25)"}} >{this.state.data.label}</h2>
186
+ </div >
187
+ }
188
  { this.state.size && !navigator.userAgent.match(/firefox|fxios/i) && <>
189
 
190
  <div id="remove-ghost" className={`absolute select-none -bottom-0 right-0 w-5 h-5border-2 shadow-2xl rounded-xl z-10 cursor-nwse-resize hover:opacity-50 `}
 
199
 
200
  </>
201
  }
202
+
203
 
204
  </>)
205
  }
frontend/src/components/ReactFlow/ReactFlowEnv.js CHANGED
@@ -3,6 +3,11 @@ import '../../css/dist/output.css'
3
  import ReactFlow, { Background,
4
  applyNodeChanges,
5
  ReactFlowProvider,
 
 
 
 
 
6
  } from 'react-flow-renderer';
7
  import React ,{ useState, useCallback, useRef, useEffect } from 'react';
8
  import Navbar from '../Navagation/navbar';
@@ -19,6 +24,7 @@ export default function ReactEnviorment() {
19
 
20
  const [theme, setTheme] = useState(useThemeDetector)
21
  const [nodes, setNodes] = useState([]);
 
22
  const [reactFlowInstance, setReactFlowInstance] = useState(null);
23
  const reactFlowWrapper = useRef(null);
24
  const [tool, setTool] = useState(false)
@@ -42,6 +48,30 @@ export default function ReactEnviorment() {
42
  [setNodes]
43
  );
44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
  const onDragOver = useCallback((event) => {
47
  event.preventDefault();
@@ -122,12 +152,13 @@ export default function ReactEnviorment() {
122
  <BsFillEraserFill title="Erase" className={`mt-6 text-black dark:text-white ml-auto mr-auto ${tool ? "visible" : " invisible"} `} onClick={() => onErase()}/>
123
  </div>
124
  <div className={`flex h-screen w-screen ${theme ? "dark" : ""} transition-all`}>
125
- <Navbar onDelete={deleteNodeContains} colour={JSON.parse(localStorage.getItem('colour'))} emoji={JSON.parse(localStorage.getItem('emoji'))}/>
126
  <ReactFlowProvider>
 
127
  <div className="h-screen w-screen" ref={reactFlowWrapper}>
128
- <ReactFlow nodes={nodes} nodeTypes={types} onNodesChange={onNodesChange} onNodesDelete={deleteNode} onDragOver={onDragOver} onDrop={onDrop} onInit={setReactFlowInstance} fitView>
129
- <Background variant='dots' size={1} className=" bg-white dark:bg-neutral-800"/>
130
- </ReactFlow>
 
131
  </div>
132
  </ReactFlowProvider>
133
  </div>
 
3
  import ReactFlow, { Background,
4
  applyNodeChanges,
5
  ReactFlowProvider,
6
+ addEdge,
7
+ updateEdge,
8
+ applyEdgeChanges,
9
+ Controls,
10
+ MarkerType
11
  } from 'react-flow-renderer';
12
  import React ,{ useState, useCallback, useRef, useEffect } from 'react';
13
  import Navbar from '../Navagation/navbar';
 
24
 
25
  const [theme, setTheme] = useState(useThemeDetector)
26
  const [nodes, setNodes] = useState([]);
27
+ const [edges, setEdges] = useState([])
28
  const [reactFlowInstance, setReactFlowInstance] = useState(null);
29
  const reactFlowWrapper = useRef(null);
30
  const [tool, setTool] = useState(false)
 
48
  [setNodes]
49
  );
50
 
51
+ const onEdgesChange = useCallback(
52
+ (changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
53
+ [setEdges]
54
+ );
55
+
56
+ const onEdgeUpdate = useCallback(
57
+ (oldEdge, newConnection) => setEdges((els) => updateEdge(oldEdge, newConnection, els)),
58
+ []
59
+ );
60
+
61
+ const onConnect = useCallback(
62
+ (params) => {
63
+ console.log(params)
64
+ setEdges((els) => addEdge({...params, animated : true, style : {stroke : "#00FF4A", strokeWidth : "3"}, markerEnd: {type: MarkerType.ArrowClosed, color : "#00FF4A"}}, els))
65
+ fetch("http://localhost:2000/api/append/connection", {method : "POST", mode : 'cors', headers : { 'Content-Type' : 'application/json' }, body: JSON.stringify({"source": params.source, "target" : params.target})}).then( res => {
66
+ console.log(res)
67
+ }).catch(error => {
68
+ console.log(error)
69
+ })
70
+ },
71
+ [setEdges]
72
+ );
73
+
74
+
75
 
76
  const onDragOver = useCallback((event) => {
77
  event.preventDefault();
 
152
  <BsFillEraserFill title="Erase" className={`mt-6 text-black dark:text-white ml-auto mr-auto ${tool ? "visible" : " invisible"} `} onClick={() => onErase()}/>
153
  </div>
154
  <div className={`flex h-screen w-screen ${theme ? "dark" : ""} transition-all`}>
 
155
  <ReactFlowProvider>
156
+ <Navbar onDelete={deleteNodeContains} colour={JSON.parse(localStorage.getItem('colour'))} emoji={JSON.parse(localStorage.getItem('emoji'))}/>
157
  <div className="h-screen w-screen" ref={reactFlowWrapper}>
158
+ <ReactFlow nodes={nodes} edges={edges} nodeTypes={types} onNodesChange={onNodesChange} onNodesDelete={deleteNode} onEdgesChange={onEdgesChange} onEdgeUpdate={onEdgeUpdate} onConnect={onConnect} onDragOver={onDragOver} onDrop={onDrop} onInit={setReactFlowInstance} fitView>
159
+ <Background variant='dots' size={1} className=" bg-white dark:bg-neutral-800"/>
160
+ <Controls/>
161
+ </ReactFlow>
162
  </div>
163
  </ReactFlowProvider>
164
  </div>
frontend/src/css/dist/output.css CHANGED
@@ -557,6 +557,10 @@ video {
557
  visibility: hidden;
558
  }
559
 
 
 
 
 
560
  .absolute {
561
  position: absolute;
562
  }
@@ -634,6 +638,14 @@ video {
634
  z-index: 50;
635
  }
636
 
 
 
 
 
 
 
 
 
637
  .float-left {
638
  float: left;
639
  }
@@ -694,6 +706,14 @@ video {
694
  margin-top: 0.25rem;
695
  }
696
 
 
 
 
 
 
 
 
 
697
  .block {
698
  display: block;
699
  }
@@ -754,6 +774,22 @@ video {
754
  height: 41px;
755
  }
756
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
757
  .w-full {
758
  width: 100%;
759
  }
@@ -794,6 +830,10 @@ video {
794
  width: 540px;
795
  }
796
 
 
 
 
 
797
  .w-5 {
798
  width: 1.25rem;
799
  }
@@ -806,6 +846,14 @@ video {
806
  width: 100vw;
807
  }
808
 
 
 
 
 
 
 
 
 
809
  .max-w-full {
810
  max-width: 100%;
811
  }
@@ -1125,6 +1173,12 @@ video {
1125
  --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
1126
  }
1127
 
 
 
 
 
 
 
1128
  .from-Deep-Space-Black {
1129
  --tw-gradient-from: #000000;
1130
  --tw-gradient-to: rgb(0 0 0 / 0);
@@ -1143,12 +1197,6 @@ video {
1143
  --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
1144
  }
1145
 
1146
- .from-Peach-Yellow {
1147
- --tw-gradient-from: #FFEDBC;
1148
- --tw-gradient-to: rgb(255 237 188 / 0);
1149
- --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
1150
- }
1151
-
1152
  .via-Vapor-Purple {
1153
  --tw-gradient-to: rgb(148 22 127 / 0);
1154
  --tw-gradient-stops: var(--tw-gradient-from), #94167f, var(--tw-gradient-to);
@@ -1224,6 +1272,10 @@ video {
1224
  --tw-gradient-to: #6E48AA;
1225
  }
1226
 
 
 
 
 
1227
  .to-Peach-Red {
1228
  --tw-gradient-to: #ED4264;
1229
  }
@@ -1240,10 +1292,6 @@ video {
1240
  --tw-gradient-to: #3b82f6;
1241
  }
1242
 
1243
- .to-Peach-Yellow {
1244
- --tw-gradient-to: #FFEDBC;
1245
- }
1246
-
1247
  .p-4 {
1248
  padding: 1rem;
1249
  }
@@ -1352,6 +1400,14 @@ video {
1352
  padding-bottom: 0px;
1353
  }
1354
 
 
 
 
 
 
 
 
 
1355
  .text-center {
1356
  text-align: center;
1357
  }
@@ -1395,6 +1451,11 @@ video {
1395
  line-height: 2rem;
1396
  }
1397
 
 
 
 
 
 
1398
  .font-medium {
1399
  font-weight: 500;
1400
  }
@@ -1507,6 +1568,14 @@ video {
1507
  transition-duration: 500ms;
1508
  }
1509
 
 
 
 
 
 
 
 
 
1510
  .ease-in {
1511
  transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
1512
  }
 
557
  visibility: hidden;
558
  }
559
 
560
+ .fixed {
561
+ position: fixed;
562
+ }
563
+
564
  .absolute {
565
  position: absolute;
566
  }
 
638
  z-index: 50;
639
  }
640
 
641
+ .z-40 {
642
+ z-index: 40;
643
+ }
644
+
645
+ .z-30 {
646
+ z-index: 30;
647
+ }
648
+
649
  .float-left {
650
  float: left;
651
  }
 
706
  margin-top: 0.25rem;
707
  }
708
 
709
+ .mt-auto {
710
+ margin-top: auto;
711
+ }
712
+
713
+ .mb-auto {
714
+ margin-bottom: auto;
715
+ }
716
+
717
  .block {
718
  display: block;
719
  }
 
774
  height: 41px;
775
  }
776
 
777
+ .h-\[200px\] {
778
+ height: 200px;
779
+ }
780
+
781
+ .h-\[140px\] {
782
+ height: 140px;
783
+ }
784
+
785
+ .h-\[0px\] {
786
+ height: 0px;
787
+ }
788
+
789
+ .h-0 {
790
+ height: 0px;
791
+ }
792
+
793
  .w-full {
794
  width: 100%;
795
  }
 
830
  width: 540px;
831
  }
832
 
833
+ .w-\[340px\] {
834
+ width: 340px;
835
+ }
836
+
837
  .w-5 {
838
  width: 1.25rem;
839
  }
 
846
  width: 100vw;
847
  }
848
 
849
+ .w-\[0px\] {
850
+ width: 0px;
851
+ }
852
+
853
+ .w-7 {
854
+ width: 1.75rem;
855
+ }
856
+
857
  .max-w-full {
858
  max-width: 100%;
859
  }
 
1173
  --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
1174
  }
1175
 
1176
+ .from-Peach-Yellow {
1177
+ --tw-gradient-from: #FFEDBC;
1178
+ --tw-gradient-to: rgb(255 237 188 / 0);
1179
+ --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
1180
+ }
1181
+
1182
  .from-Deep-Space-Black {
1183
  --tw-gradient-from: #000000;
1184
  --tw-gradient-to: rgb(0 0 0 / 0);
 
1197
  --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
1198
  }
1199
 
 
 
 
 
 
 
1200
  .via-Vapor-Purple {
1201
  --tw-gradient-to: rgb(148 22 127 / 0);
1202
  --tw-gradient-stops: var(--tw-gradient-from), #94167f, var(--tw-gradient-to);
 
1272
  --tw-gradient-to: #6E48AA;
1273
  }
1274
 
1275
+ .to-Peach-Yellow {
1276
+ --tw-gradient-to: #FFEDBC;
1277
+ }
1278
+
1279
  .to-Peach-Red {
1280
  --tw-gradient-to: #ED4264;
1281
  }
 
1292
  --tw-gradient-to: #3b82f6;
1293
  }
1294
 
 
 
 
 
1295
  .p-4 {
1296
  padding: 1rem;
1297
  }
 
1400
  padding-bottom: 0px;
1401
  }
1402
 
1403
+ .pt-10 {
1404
+ padding-top: 2.5rem;
1405
+ }
1406
+
1407
+ .pt-9 {
1408
+ padding-top: 2.25rem;
1409
+ }
1410
+
1411
  .text-center {
1412
  text-align: center;
1413
  }
 
1451
  line-height: 2rem;
1452
  }
1453
 
1454
+ .text-6xl {
1455
+ font-size: 3.75rem;
1456
+ line-height: 1;
1457
+ }
1458
+
1459
  .font-medium {
1460
  font-weight: 500;
1461
  }
 
1568
  transition-duration: 500ms;
1569
  }
1570
 
1571
+ .duration-100 {
1572
+ transition-duration: 100ms;
1573
+ }
1574
+
1575
+ .duration-200 {
1576
+ transition-duration: 200ms;
1577
+ }
1578
+
1579
  .ease-in {
1580
  transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
1581
  }