zack commited on
Commit
0563a7e
β€’
1 Parent(s): 9bcd0c7

feat: move components directory to frontend root 🚚

Browse files
frontend/components/Edges/Custom.js ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import {BiX} from 'react-icons/bi'
3
+ import { getBezierPath, getEdgeCenter } from 'react-flow-renderer';
4
+
5
+ import '../../css/dist/output.css';
6
+
7
+ const foreignObjectSize = 40;
8
+
9
+ const onEdgeClick = (evt, id) => {
10
+ evt(id)
11
+ };
12
+
13
+ export default function CustomEdge({
14
+ id,
15
+ sourceX,
16
+ sourceY,
17
+ targetX,
18
+ targetY,
19
+ sourcePosition,
20
+ targetPosition,
21
+ markerEnd,
22
+ data
23
+ }) {
24
+ const edgePath = getBezierPath({
25
+ sourceX,
26
+ sourceY,
27
+ sourcePosition,
28
+ targetX,
29
+ targetY,
30
+ targetPosition,
31
+ });
32
+ const [edgeCenterX, edgeCenterY] = getEdgeCenter({
33
+ sourceX,
34
+ sourceY,
35
+ targetX,
36
+ targetY,
37
+ });
38
+
39
+ return (
40
+ <>
41
+ <path
42
+ id={id}
43
+ style={{stroke : "#00FF4A", strokeWidth : "6"}}
44
+ className="react-flow__edge-path"
45
+ d={edgePath}
46
+ markerEnd={markerEnd}
47
+ />
48
+ <foreignObject
49
+ width={foreignObjectSize}
50
+ height={200}
51
+ x={edgeCenterX - foreignObjectSize / 2}
52
+ y={edgeCenterY - foreignObjectSize / 2}
53
+ className="edgebutton-foreignobject"
54
+ requiredExtensions="http://www.w3.org/1999/xhtml"
55
+ >
56
+ <div className=" flex w-10 h-10 dark:bg-black bg-white border-2 rounded-xl hover:shadow-lg text-center duration-200" onClick={() => onEdgeClick(data.delete, id)}>
57
+ <BiX className=' flex-1 w-9 h-9 text-black dark:text-white'/>
58
+ </div>
59
+ </foreignObject>
60
+ </>
61
+ );
62
+ }
frontend/components/Edges/CustomLine.js ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import { getBezierPath } from 'react-flow-renderer';
3
+
4
+ const CustomLine = ({
5
+ sourceX,
6
+ sourceY,
7
+ sourcePosition,
8
+ targetX,
9
+ targetY,
10
+ targetPosition,
11
+ connectionLineType,
12
+ connectionLineStyle,
13
+ }) => {
14
+
15
+ const edgePath = getBezierPath({
16
+ sourceX,
17
+ sourceY,
18
+ sourcePosition,
19
+ targetX,
20
+ targetY,
21
+ targetPosition,
22
+ });
23
+ return (
24
+ <g>
25
+ <path
26
+ fill="none"
27
+ stroke="#00FF4A"
28
+ strokeWidth={7}
29
+ className="animated"
30
+ d={edgePath}
31
+ />
32
+ </g>
33
+ );
34
+ };
35
+
36
+ export default CustomLine;
frontend/components/Modal/importer.js ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Modal, Icon, Message} from 'semantic-ui-react'
2
+ import "../../css/dist/output.css"
3
+ import {ReactComponent as Gradio} from '../../images/gradio.svg'
4
+ import {ReactComponent as Streamlit} from '../../images/streamlit.svg'
5
+ import {ReactComponent as Exit} from '../../images/exit.svg'
6
+ import { useState } from 'react'
7
+ import {BsSearch} from 'react-icons/bs';
8
+
9
+ export default function Import(props){
10
+ const [tab, setTab] = useState("gradio")
11
+ const [subTab, setSubTab] = useState(0)
12
+
13
+ return (<div>
14
+ <Modal
15
+ basic
16
+ className=''
17
+ open={props.open}
18
+ size='fullscreen'
19
+ >
20
+ <div className='w-full shadow-lg rounded-lg'>
21
+ <ul className="flex flex-wrap text-sm font-medium text-center text-gray-500 bg-gray-100 rounded-t-lg border-gray-200 dark:border-gray-700 dark:text-gray-400 dark:bg-gray-800" id="defaultTab" data-tabs-toggle="#defaultTabContent" role="tablist">
22
+ <li className="" onClick={()=>{
23
+ setTab("gradio")
24
+ props.catch ? props.handelError(false) : props.handelError(props.catch) }}>
25
+ <button id="gradio-tab" data-tabs-target="#Gradio" type="button" role="tab" aria-controls="gradio" aria-selected={tab === "gradio" ? "true" : "false"} className={`inline-block p-4 rounded-tl-lg ${ tab === "gradio" ? 'bg-gray-200' : 'hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 focus:bg-gray-700'}`}><Gradio className=" w-20 h-10"/></button>
26
+ </li>
27
+ <li className="" onClick={()=>{
28
+ setTab("streamlit")
29
+ props.catch ? props.handelError(false) : props.handelError(props.catch) }}>
30
+ <button id="services-tab" data-tabs-target="#Streamlit" type="button" role="tab" aria-controls="services" aria-selected="false" className={`inline-block p-4 rounded-tl-lg ${ tab === "streamlit" ? 'bg-gray-200' : 'hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 focus:bg-gray-700'}`}><Streamlit className=" w-20 h-10"/></button>
31
+ </li>
32
+ </ul>
33
+ <div className='absolute right-5 top-5 z-20 mr-5'
34
+ onClick={()=>{
35
+ props.quitHandeler(false)
36
+ props.catch ? props.handelError(false) : props.handelError(props.catch) }}>
37
+ <button type="button"
38
+ className=" bg-neutral-300 rounded-2xl p-2 inline-flex items-center justify-center dark:bg-neutral-700 hover:opacity-70 focus:outline-none">
39
+ <Exit className=" w-[20px] h-[20px] text-gray-400 dark:text-white"/>
40
+ </button>
41
+ </div>
42
+ </div>
43
+ { tab === "gradio" &&
44
+ <div className='w-full bg-white'>
45
+ <ul className="flex flex-wrap text-sm font-medium text-center text-gray-500 bg-gray-200 border-gray-200 dark:border-gray-700 dark:text-gray-400 dark:bg-gray-800" id="defaultTab" data-tabs-toggle="#defaultTabContent" role="tablist">
46
+ <li className="" onClick={()=>{
47
+ setSubTab(0)
48
+ props.catch ? props.handelError(false) : props.handelError(props.catch) }}>
49
+ <button id="local-sub-tab" data-tabs-target="#local" type="button" role="tab" aria-controls="local-gradio" aria-selected={tab === "gradio" ? "true" : "false"} className={`inline-block p-4 px-6 text-base font-sans font-bold ${subTab === 0 ? 'bg-gray-300' : '' } hover:bg-gray-300 `}>Local</button>
50
+ </li>
51
+ <li className="" onClick={()=>{
52
+ setSubTab(1)
53
+ props.catch ? props.handelError(false) : props.handelError(props.catch) }}>
54
+ <button id="shared-sub-tab" data-tabs-target="#Gradio" type="button" role="tab" aria-controls="shared-gradio" aria-selected={tab === "gradio" ? "true" : "false"} className={`inline-block p-4 px-6 text-base font-sans font-bold ${subTab === 1 ? 'bg-gray-300' : '' } hover:bg-gray-300 `}>Shared</button>
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>
62
+ <Message.Header className=" text-lg text-center">🚫 Something went wrong...</Message.Header>
63
+ <br/>
64
+ <h1 className=" underline pb-3 font-bold text-lg">πŸ€” Possible Things That could of happen <br/></h1>
65
+ <ul className="font-bold">
66
+ <li key={"error_1"}>- The input was empty</li>
67
+ <li key={"error_2"}>- The connection was forbidden</li>
68
+ <li key={"error_3"}>- The name was already taken</li>
69
+ <li key={"error_4"}>- The link you gave did not pass the regex</li>
70
+ <ul className="px-6">
71
+ <li key={"error_5"}>- http://localhost:xxxx</li>
72
+ <li key={"error_6"}>- http://xxxxx.gradio.app</li>
73
+ <li key={"error_7"}>- https://hf.space/embed/$user/$space_name/+</li>
74
+ </ul>
75
+ <li>- link already exist within the menu</li>
76
+ </ul>
77
+
78
+ </Message>
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
+ }
91
+
92
+ function Local(props){
93
+ return (
94
+ <div className='p-5'>
95
+ <Message floating>
96
+
97
+ <Message.Header>πŸ—οΈ Comming soon...</Message.Header>
98
+ <Message.Content className='p-5'>
99
+ This tab will allow you grab your function from a given directory and build
100
+ your own tabular module gradio functions
101
+ </Message.Content>
102
+
103
+ </Message>
104
+ </div>
105
+ )
106
+ }
107
+
108
+ function Shared(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
+ }
frontend/components/Navagation/navbar.js ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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)
14
+ this.temp_host = 0
15
+ this.deleteNode = props.onDelete
16
+ this.state = {
17
+ open : true,
18
+ menu : [],
19
+ colour : props.colour || [],
20
+ text : "",
21
+ name : "",
22
+ emoji : props.emoji || [],
23
+ mode : false,
24
+ modal : false,
25
+ error : false
26
+ }
27
+
28
+ }
29
+
30
+ componentDidMount(){
31
+ this.fetch_classes()
32
+
33
+ }
34
+
35
+ /**
36
+ * Asynchronously call the Flask api server every second to check if there exist a gradio application info
37
+ * @return null
38
+ */
39
+ fetch_classes = async () => {
40
+ try {
41
+ setInterval( async () => {
42
+ await fetch("http://localhost:2000/api/open/ports", { method: 'GET', mode : 'cors',})
43
+ .then(response => response.json())
44
+ .then(data => {
45
+ this.handelTabs(this.state.menu,data, data)
46
+ this.setState({menu : data})
47
+ })
48
+ .catch(error => {console.log(error)})
49
+
50
+ },1000);
51
+ }catch(e){
52
+ console.log(e)
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Append new node from the user
58
+ */
59
+ appendStreamNode = async (type) => {
60
+ const pattern = {
61
+ local : /^https?:\/\/(localhost)*(:[0-9]+)?(\/)?$/,
62
+ share : /^https?:\/\/*([0-9]{5})*(-gradio)*(.app)?(\/)?$/,
63
+ hugginFace : /^https?:\/\/*(hf.space)\/*(embed)\/*([a-zA-Z0-9+_-]+)\/*([a-zA-Z0-9+_-]+)\/*([+])?(\/)?$/
64
+ }
65
+
66
+ if (this.state.name.length > 20 ||
67
+ this.state.text === ""||
68
+ this.state.menu.findIndex(element => {return element.name.toLowerCase() === this.state.name.toLowerCase() || element.host.includes(this.state.text) }) !== -1 ||
69
+ this.state.text.includes(" ") ||
70
+ (!pattern.local.test(this.state.text) &&
71
+ !pattern.share.test(this.state.text) &&
72
+ !pattern.hugginFace.test(this.state.text))){
73
+
74
+ this.setState({
75
+ 'text': '',
76
+ 'name': '',
77
+ 'error': true})
78
+ return
79
+ }
80
+
81
+ fetch(this.state.text, {method : "GET", mode: 'no-cors'}).then((re) => {
82
+ 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 => {
83
+ this.setState({'text': "",'name' : "",'error' : false,'modal' : false })
84
+
85
+ }).catch(() => this.setState({'text': '', 'name' : '', 'error' : true, }))
86
+ }).catch((err)=> this.setState({'text': '','name' : '', 'error' : true,}))
87
+ }
88
+
89
+ /**
90
+ * error check the user input
91
+ * @param {*} bool boolean of the current state of the modal
92
+ */
93
+ handelModal = (bool) => {
94
+ this.setState({'error' : !bool ? false : this.state.error ,
95
+ 'modal' : bool})
96
+ }
97
+
98
+ /**
99
+ * when dragged get all the information needed
100
+ * @param {*} event
101
+ * @param {*} nodeType string 'custom' node type
102
+ * @param {*} item object information returned from the api
103
+ * @param {*} index current index
104
+ */
105
+ onDragStart = (event, nodeType, item, index) => {
106
+ event.dataTransfer.setData('application/reactflow', nodeType);
107
+ event.dataTransfer.setData('application/style', JSON.stringify({colour : this.state.colour[index], emoji : this.state.emoji[index] }))
108
+ event.dataTransfer.setData('application/item', JSON.stringify(item))
109
+ event.dataTransfer.effectAllowed = 'move';
110
+ };
111
+
112
+ /**
113
+ * droped event that occurs when the user drops the Tab within the tash div.
114
+ * The function just deletes all nodes within React-Flow enviorment related,
115
+ * and remove it from the api.
116
+ * @param {*} e drop event
117
+ */
118
+ onDragDrop = (e) => {
119
+ e.preventDefault();
120
+ var item = JSON.parse(e.dataTransfer.getData('application/item'));
121
+ fetch("http://localhost:2000/api/remove/port", {method : "POST", mode: 'cors', headers : { 'Content-Type' : 'application/json' }, body: JSON.stringify(item) }).then((re)=>{
122
+ this.deleteNode(item.name)
123
+ })
124
+
125
+ }
126
+
127
+ /**
128
+ * update the tabs within the navbar
129
+ * @param {*} e current menu
130
+ * @param {*} d integer variable of the diffence between the current menu and new menu updated ment
131
+ */
132
+ handelTabs = async (e, d) => {
133
+ // if less then 0 we must remove colour's and emoji's
134
+ // get index of the object
135
+ // remove
136
+ var c = []
137
+ var j = []
138
+ if (d.length - e.length === 0) return
139
+ else if(d.length - e.length < 0){
140
+ var a = this.state.menu.filter(item => e.includes(item)) // get the items not in menu anymore
141
+ c = this.state.colour
142
+ j = this.state.emoji
143
+ for(var k=0; k < d.length; k++){
144
+ c.splice(this.state.menu.indexOf(a[k]), 1)
145
+ j.splice(this.state.menu.indexOf(a[k]), 1)
146
+ }
147
+ this.setState({'colour' : c, 'emoji' : j})
148
+ }else{
149
+ //append new colours
150
+ for(var i =0; i < d.length; i++){
151
+ c.push(random_colour(i === 0 ? null : c[i-1]));
152
+ j.push(random_emoji(i === 0 ? null : c[i-1]));
153
+
154
+ }
155
+ const colour = [...this.state.colour]
156
+ const emoji = [...this.state.emoji]
157
+ this.setState({'colour' : [...colour, ...c], 'emoji' : [...emoji, ...j],})
158
+ }
159
+ }
160
+
161
+ handelError = (boolean) => {
162
+ this.setState({'error' : boolean})
163
+ }
164
+
165
+ /**
166
+ * handel navagation open and close function
167
+ */
168
+ handelNavbar = () => {
169
+ this.setState({'open' : !this.state.open})
170
+ }
171
+
172
+ /**
173
+ *
174
+ * @param {*} e : event type to get the target value of the current input
175
+ * @param {*} type : text | name string that set the changed value of the input to the current value
176
+ */
177
+ updateText = (e, type) => {
178
+ this.setState({[`${type}`] : e.target.value })
179
+ }
180
+
181
+ /**
182
+ *
183
+ * @param {*} item : object infomation from the flask api
184
+ * @param {*} index : current index with in the list
185
+ * @returns div component that contians infomation of gradio
186
+ */
187
+ subComponents(item, index){
188
+
189
+ return(<>
190
+ <li key={`${index}-li`} onDragStart={(event) => this.onDragStart(event, 'custom', item, index)}
191
+ className={` text-white text-md flex flex-col text-center items-center cursor-grab shadow-lg
192
+ p-5 px-2 mt-4 rounded-md ${ this.state.open ? `hover:animate-pulse ${this.state.colour[index] === null ? "" : this.state.colour[index]} ` : `hidden`} break-all -z-20`} draggable>
193
+
194
+ <div key={`${index}-div`} className=" absolute -mt-2 text-4xl opacity-60 z-10 ">{`${this.state.emoji[index] === null ? "" : this.state.emoji[index]}`}</div>
195
+ <h4 key={`${index}-h4`} className={` max-w-full font-sans text-blue-50 leading-tight font-bold text-xl flex-1 z-20 ${this.state.open ? "" : "hidden"}`} style={{"textShadow" : "0px 1px 2px rgba(0, 0, 0, 0.25)"}} >{`${item.name}`} </h4>
196
+
197
+ </li >
198
+
199
+ </>)
200
+ }
201
+
202
+
203
+ render(){
204
+
205
+ return (<div>
206
+
207
+ <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`} >
208
+
209
+ <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 `}/>
210
+
211
+ <div className="inline-flex w-full">
212
+ <h1 className={`font-sans font-bold text-lg ${this.state.open ? "" : "hidden"} duration-500 ml-auto mr-auto`}> <ReactLogo className="w-9 h-9 ml-auto mr-auto"/> Gradio Flow </h1>
213
+ </div>
214
+
215
+ <div className={`rounded-md text-center ${this.state.open ? "" : "px-0"} py-3`} onClick={() => {this.handelModal(true)}}>
216
+ <div className={` text-center bg-transparent w-full h-10 border border-slate-300 hover:border-Retro-purple hover:animate-pulse border-dashed rounded-md py-2 pl-5 ${this.state.open ? "pr-3" : "hidden"} shadow-sm sm:text-sm`}>
217
+ <Icon className=" block mr-auto ml-auto" name="plus"/>
218
+ </div>
219
+ </div>
220
+ <Import open={this.state.modal}
221
+ quitHandeler={this.handelModal}
222
+ textHandler={this.updateText}
223
+ appendHandler={this.appendStreamNode}
224
+ handelError={this.handelError}
225
+ catch={this.state.error}/>
226
+
227
+ <div className=" relative z-10 h-auto overflow-auto pt-4">
228
+ <ul className="pt-2">
229
+ {this.state.menu.map((menu, index) => {return this.subComponents(menu, index)})}
230
+ </ul>
231
+ </div>
232
+
233
+ <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)}}>
234
+ <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`}>
235
+ <Icon name='trash alternate' />
236
+ </div>
237
+ </div>
238
+ </div>
239
+
240
+ </div>)
241
+ }
242
+ }
frontend/components/Nodes/Custom.js ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useCallback, useEffect, useRef, useState } 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} from 'react-icons/bs'
6
+ import {CgLayoutGridSmall} from 'react-icons/cg'
7
+ import {useDrag} from '@use-gesture/react'
8
+ import { useSpring, animated } from 'react-spring'
9
+
10
+ import '../../css/counter.css'
11
+
12
+ const MINIMUM_HEIGHT = 600;
13
+ const MINIMUM_WIDTH = 540;
14
+
15
+ export default function CustomNodeIframe({id, data}){
16
+ const [collapsed, setCollapsible] = useState(true)
17
+ const [{width, height}, api] = useSpring(() => ({width: MINIMUM_WIDTH, height: MINIMUM_HEIGHT }))
18
+ const [sizeAdjuster, setSizeAdjuster] = useState(false)
19
+ const [reachable ,setReachable] = useState(false)
20
+ const [refresh, setRefresh] = useState(0)
21
+ const dragElement = useRef()
22
+
23
+ const bind = useDrag((state) => {
24
+ const isResizing = (state?.event.target === dragElement.current);
25
+ if (isResizing) {
26
+ api.set({
27
+ width: state.offset[0],
28
+ height: state.offset[1],
29
+
30
+ });
31
+ }
32
+ }, {
33
+ from: (event) => {
34
+ const isResizing = (event.target === dragElement.current);
35
+ if (isResizing) {
36
+ return [width.get(), height.get()];
37
+ }
38
+ },
39
+ });
40
+
41
+ const isFetchable =useCallback(async () => {
42
+ return fetch(data.host, {mode: 'no-cors'}).then((res) => {
43
+ return true
44
+ }).catch((err)=>{
45
+ return false
46
+ })
47
+ }, [data.host])
48
+
49
+ useEffect(() => {
50
+ const fetched = setInterval(
51
+ async () => {
52
+ const fetch = await isFetchable()
53
+ if (fetch){
54
+ setReachable(true)
55
+ clearInterval(fetched)
56
+ }
57
+ },1000)
58
+ },[isFetchable])
59
+
60
+
61
+ return (
62
+ <div className="w-10 h-10">
63
+
64
+ <div id={'draggable'}className=" flex w-full h-10 top-0 cursor-pointer" onClick={() => {}}>
65
+ <div id={'draggable'} title={collapsed ? "Collaspse Node" : "Expand Node"} className=" flex-none duration-300 cursor-pointer shadow-xl border-2 border-white h-10 w-10 mr-2 -mt-3 bg-Warm-Blue rounded-xl" onClick={() => {setCollapsible((clps) => !clps)}}><CgLayoutGridSmall className="h-full w-full text-white p-1"/></div>
66
+
67
+ <div className={` flex ${!collapsed ? '' : 'w-0 hidden'}`}>
68
+ <div title="Adjust Node Size" className="duration-300 cursor-pointer shadow-xl border-2 dark:border-white border-white h-10 w-10 mr-2 -mt-3 bg-Warm-Violet rounded-xl" onClick={() => {setSizeAdjuster((size) => !size)}}><TbResize className="h-full w-full text-white p-1"/></div>
69
+ <a href={data.host} target="_blank" rel="noopener noreferrer"><div title="Gradio Host Site" className="duration-300 cursor-pointer shadow-xl border-2 dark:border-white border-white h-10 w-10 mr-2 -mt-3 bg-Warm-Pink rounded-xl"><BiCube className="h-full w-full text-white p-1"/></div></a>
70
+ <div title="Delete Node" className="duration-300 cursor-pointer shadow-xl border-2 dark:border-white border-white h-10 w-10 mr-2 -mt-3 bg-Warm-Red rounded-xl" onClick={() => {data.delete([{id}])}}><BsTrash className="h-full w-full text-white p-1"/></div>
71
+ <div title="Refresh Node" className="duration-300 cursor-pointer shadow-xl border-2 dark:border-white border-white h-10 w-10 mr-2 -mt-3 bg-Warm-Orange rounded-xl" onClick={() => {setRefresh((old) => old++)}}><BiRefresh className="h-full w-full text-white p-1"/></div>
72
+ </div>
73
+ </div>
74
+
75
+ { !collapsed && reachable && <>
76
+ <animated.div className={`border-dashed ${sizeAdjuster ? 'border-4 border-white' : ''} relative top-0 left-0 z-[1000] touch-none shadow-lg rounded-xl`} style={{width, height }} {...bind()}>
77
+ <div id="draggable" className={`absolute h-full w-full ${data.colour} shadow-2xl rounded-xl -z-20`}></div>
78
+ <iframe id="iframe"
79
+ key={refresh}
80
+ src={data.host}
81
+ title={data.label}
82
+ frameBorder="0"
83
+ className=" p-[0.6rem] -z-10 h-full w-full ml-auto mr-auto overflow-y-scroll"/>
84
+ <div className={` ${sizeAdjuster ? '' : 'hidden'} rounded-full border-2 absolute -bottom-4 -right-4 w-7 h-7 bg-Blue-Royal cursor-nwse-resize touch-none shadow-lg`} ref={dragElement}>
85
+ </div>
86
+ </animated.div>
87
+ </>
88
+ }
89
+
90
+ { collapsed &&
91
+ <div id={`draggable`}
92
+ className={` w-[340px] h-[140px] text-white text-md flex flex-col text-center items-center cursor-grab shadow-lg
93
+ p-5 px-2 rounded-md break-all -z-20 ${data.colour} hover:opacity-70 duration-300`} onClick={() => setCollapsible(collapsed => !collapsed)}>
94
+ <div className="absolute text-6xl opacity-60 z-10 pt-8 ">{data.emoji}</div>
95
+ <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)"}} >{data.label}</h2>
96
+ </div >
97
+ }
98
+ </div>)
99
+ }
frontend/components/ReactFlow/ReactFlowEnv.js ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import CustomNodeIframe from "../Nodes/Custom.js";
2
+ import '../../css/dist/output.css'
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';
14
+ import CustomEdge from '../Edges/Custom'
15
+ import CustomLine from "../Edges/CustomLine.js";
16
+ import { useThemeDetector } from '../../helper/visual'
17
+ import {CgMoreVerticalAlt} from 'react-icons/cg'
18
+ import {BsFillEraserFill} from 'react-icons/bs'
19
+ import {FaRegSave} from 'react-icons/fa'
20
+
21
+ const NODE = {
22
+ custom : CustomNodeIframe,
23
+ }
24
+
25
+ const EDGE = {
26
+ custom : CustomEdge
27
+ }
28
+
29
+
30
+ export default function ReactEnviorment() {
31
+
32
+ const [theme, setTheme] = useState(useThemeDetector)
33
+ const [nodes, setNodes] = useState([]);
34
+ const [edges, setEdges] = useState([])
35
+ const [reactFlowInstance, setReactFlowInstance] = useState(null);
36
+ const reactFlowWrapper = useRef(null);
37
+ const [tool, setTool] = useState(false)
38
+
39
+ const deleteNodeContains = (id) =>{setNodes((nds) => nds.filter(n => !n.id.includes(`${id}-`) ))}
40
+ const deleteEdge = useCallback((id) => setEdges((eds) => eds.filter(e => e.id !== id)), [setEdges])
41
+ const deleteNode = useCallback((id) =>{
42
+ setNodes(() => nodes.filter(n => n.id !== id ))
43
+ }, [setNodes, nodes])
44
+
45
+
46
+ useEffect(() => {
47
+ const restore = () => {
48
+ const flow = JSON.parse(localStorage.getItem('flowkey'));
49
+
50
+ if(flow){
51
+ flow.nodes.map((nds) => nds.data.delete = deleteNode)
52
+ flow.edges.map((eds) => eds.data.delete = deleteEdge)
53
+ setNodes(flow.nodes || [])
54
+ setEdges(flow.edges || [])
55
+ console.log(flow)
56
+ }
57
+ }
58
+ restore()
59
+ },[deleteNode, deleteEdge])
60
+
61
+
62
+
63
+ const onNodesChange = useCallback(
64
+ (changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
65
+ [setNodes]
66
+ );
67
+
68
+ const onEdgesChange = useCallback(
69
+ (changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
70
+ [setEdges]
71
+ );
72
+
73
+ const onEdgeUpdate = useCallback(
74
+ (oldEdge, newConnection) => setEdges((els) => updateEdge(oldEdge, newConnection, els)),
75
+ []
76
+ );
77
+
78
+ const onConnect = useCallback(
79
+ (params) => {
80
+ console.log(params)
81
+ setEdges((els) => addEdge({...params, type: "custom", animated : true, style : {stroke : "#00FF4A", strokeWidth : "6"}, markerEnd: {type: MarkerType.ArrowClosed, color : "#00FF4A"}, data : { delete : deleteEdge}}, els))
82
+ 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 => {
83
+ console.log(res)
84
+ }).catch(error => {
85
+ console.log(error)
86
+ })
87
+ },
88
+ [setEdges, deleteEdge]
89
+ );
90
+
91
+
92
+
93
+ const onDragOver = useCallback((event) => {
94
+ event.preventDefault();
95
+ event.dataTransfer.dropEffect = 'move';
96
+ }, []);
97
+
98
+
99
+ const onSave = useCallback(() => {
100
+ if (reactFlowInstance) {
101
+ const flow = reactFlowInstance.toObject();
102
+ alert("The current nodes have been saved into the localstorage πŸ’Ύ")
103
+ localStorage.setItem('flowkey', JSON.stringify(flow));
104
+ var labels = [];
105
+ var colour = [];
106
+ var emoji = [];
107
+ for(let i = 0; i < flow.nodes.length; i++){
108
+ if (!labels.includes(flow.nodes[i].data.label))
109
+ colour.push(flow.nodes[i].data.colour)
110
+ emoji.push(flow.nodes[i].data.emoji)
111
+ labels.push(flow.nodes[i].data.label)
112
+ }
113
+ localStorage.setItem('colour',JSON.stringify(colour))
114
+ localStorage.setItem('emoji', JSON.stringify(emoji))
115
+ }
116
+ }, [reactFlowInstance]);
117
+
118
+ const onErase = useCallback(() => {
119
+ const flow = localStorage.getItem("flowkey")
120
+ if (reactFlowInstance && flow){
121
+ alert("The current nodes have been erased from the localstorage")
122
+ localStorage.removeItem("flowkey")
123
+ localStorage.removeItem('colour')
124
+ localStorage.removeItem('emoji')
125
+ }
126
+ },[reactFlowInstance])
127
+
128
+
129
+ const onDrop = useCallback(
130
+ (event) => {
131
+ event.preventDefault();
132
+
133
+ if(event.dataTransfer.getData('application/reactflow') !== ""){
134
+ const reactFlowBounds = reactFlowWrapper.current.getBoundingClientRect();
135
+ const type = event.dataTransfer.getData('application/reactflow');
136
+ const item = JSON.parse(event.dataTransfer.getData('application/item'));
137
+ const style = JSON.parse(event.dataTransfer.getData('application/style'));
138
+ // check if the dropped element is valid
139
+ if (typeof type === 'undefined' || !type) {
140
+ return;
141
+ }
142
+
143
+ const position = reactFlowInstance.project({
144
+ x: event.clientX - reactFlowBounds.left,
145
+ y: event.clientY - reactFlowBounds.top,
146
+ });
147
+
148
+ const newNode = {
149
+ id: `${item.name}-${nodes.length+1}`,
150
+ type,
151
+ position,
152
+ dragHandle : `#draggable`,
153
+ data: { label: `${item.name}`, host : `${item.host}`, colour : `${style.colour}`, emoji : `${style.emoji}`, delete : deleteNode },
154
+ };
155
+
156
+ setNodes((nds) => nds.concat(newNode));
157
+ }
158
+ },
159
+ [reactFlowInstance, nodes, deleteNode]);
160
+
161
+ return (
162
+ <div className={`${theme ? "dark" : ""}`}>
163
+ <div className={` absolute text-center ${tool ? "h-[203.3333px]" : "h-[41px]"} overflow-hidden w-[41px] text-4xl top-4 right-5 z-50 cursor-default select-none bg-white dark:bg-stone-900 rounded-full border border-black dark:border-white duration-500`} >
164
+ <CgMoreVerticalAlt className={` text-black dark:text-white ${tool ? "-rotate-0 mr-auto ml-auto mt-1" : " rotate-180 mr-auto ml-auto mt-1"} duration-300`} onClick={() => setTool(!tool)}/>
165
+ <h1 title={theme ? 'Dark Mode' : 'Light Mode'} className={`p-4 px-1 pb-0 ${tool ? "visible" : "invisible"} text-3xl`} onClick={() => setTheme(!theme)} >{theme ? 'πŸŒ™' : 'β˜€οΈ'}</h1>
166
+ <FaRegSave title="Save" className={`mt-6 text-black dark:text-white ${tool ? "visible" : " invisible"} ml-auto mr-auto `} onClick={() => onSave()}/>
167
+ <BsFillEraserFill title="Erase" className={`mt-6 text-black dark:text-white ml-auto mr-auto ${tool ? "visible" : " invisible"} `} onClick={() => onErase()}/>
168
+ </div>
169
+ <div className={`flex h-screen w-screen ${theme ? "dark" : ""} transition-all`}>
170
+ <ReactFlowProvider>
171
+ <Navbar onDelete={deleteNodeContains} colour={JSON.parse(localStorage.getItem('colour'))} emoji={JSON.parse(localStorage.getItem('emoji'))}/>
172
+ <div className="h-screen w-screen" ref={reactFlowWrapper}>
173
+ <ReactFlow nodes={nodes} edges={edges} nodeTypes={NODE} edgeTypes={EDGE} onNodesChange={onNodesChange} onNodesDelete={deleteNode} onEdgesChange={onEdgesChange} onEdgeUpdate={onEdgeUpdate} onConnect={onConnect} onDragOver={onDragOver} onDrop={onDrop} onInit={setReactFlowInstance} connectionLineComponent={CustomLine} fitView>
174
+ <Background variant='dots' size={1} className=" bg-white dark:bg-neutral-800"/>
175
+ <Controls/>
176
+ </ReactFlow>
177
+ </div>
178
+ </ReactFlowProvider>
179
+ </div>
180
+ </div>
181
+ );
182
+ }
183
+