LucaVivona commited on
Commit
a335474
Β·
1 Parent(s): ca09a0b

Custom connected edge πŸ”—

Browse files
frontend/src/components/Edges/Custom.js ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ style = {},
22
+ markerEnd,
23
+ data
24
+ }) {
25
+ const edgePath = getBezierPath({
26
+ sourceX,
27
+ sourceY,
28
+ sourcePosition,
29
+ targetX,
30
+ targetY,
31
+ targetPosition,
32
+ });
33
+ const [edgeCenterX, edgeCenterY] = getEdgeCenter({
34
+ sourceX,
35
+ sourceY,
36
+ targetX,
37
+ targetY,
38
+ });
39
+
40
+ return (
41
+ <>
42
+ <path
43
+ id={id}
44
+ style={style}
45
+ className="react-flow__edge-path"
46
+ d={edgePath}
47
+ markerEnd={markerEnd}
48
+ />
49
+ <foreignObject
50
+ width={foreignObjectSize}
51
+ height={200}
52
+ x={edgeCenterX - foreignObjectSize / 2}
53
+ y={edgeCenterY - foreignObjectSize / 2}
54
+ className="edgebutton-foreignobject"
55
+ requiredExtensions="http://www.w3.org/1999/xhtml"
56
+ >
57
+ <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)}>
58
+ <BiX className=' flex-1 w-9 h-9 text-black dark:text-white'/>
59
+ </div>
60
+ </foreignObject>
61
+ </>
62
+ );
63
+ }
frontend/src/components/Modal/importer.js CHANGED
@@ -105,97 +105,6 @@ function Local(props){
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)
 
105
  )
106
  }
107
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  function Shared(props){
109
  const [preview, setPreview] = useState("")
110
  const [fetchable, setFetch] = useState(false)
frontend/src/components/Nodes/Custom.js CHANGED
@@ -69,7 +69,7 @@ export default class CustomNodeIframe extends React.Component {
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
 
 
69
 
70
  handelSize(evt, increment, change){
71
  if (evt === "increment") {
72
+ this.setState({[`${change}`] : change === "width" ? this.state.width + increment : this.state.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
 
frontend/src/components/ReactFlow/ReactFlowEnv.js CHANGED
@@ -11,15 +11,21 @@ import ReactFlow, { Background,
11
  } from 'react-flow-renderer';
12
  import React ,{ useState, useCallback, useRef, useEffect } from 'react';
13
  import Navbar from '../Navagation/navbar';
 
14
  import { useThemeDetector } from '../../helper/visual'
15
  import {CgMoreVerticalAlt} from 'react-icons/cg'
16
  import {BsFillEraserFill} from 'react-icons/bs'
17
  import {FaRegSave} from 'react-icons/fa'
18
 
19
- const types = {
20
  custom : CustomNodeIframe,
21
  }
22
 
 
 
 
 
 
23
  export default function ReactEnviorment() {
24
 
25
  const [theme, setTheme] = useState(useThemeDetector)
@@ -42,6 +48,8 @@ export default function ReactEnviorment() {
42
  restore()
43
  },[])
44
 
 
 
45
 
46
  const onNodesChange = useCallback(
47
  (changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
@@ -61,7 +69,7 @@ export default function ReactEnviorment() {
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 => {
@@ -80,7 +88,7 @@ export default function ReactEnviorment() {
80
 
81
  const deleteNodeContains = (id) =>{setNodes((nds) => nds.filter(n => !n.id.includes(`${id}-`) ))}
82
  const deleteNode = (id) =>{setNodes((nds) => nds.filter(n => n.id !== id ))}
83
-
84
  const onSave = useCallback(() => {
85
  if (reactFlowInstance) {
86
  const flow = reactFlowInstance.toObject();
@@ -155,7 +163,7 @@ export default function ReactEnviorment() {
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>
 
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 { useThemeDetector } from '../../helper/visual'
16
  import {CgMoreVerticalAlt} from 'react-icons/cg'
17
  import {BsFillEraserFill} from 'react-icons/bs'
18
  import {FaRegSave} from 'react-icons/fa'
19
 
20
+ const NODE = {
21
  custom : CustomNodeIframe,
22
  }
23
 
24
+ const EDGE = {
25
+ custom : CustomEdge
26
+ }
27
+
28
+
29
  export default function ReactEnviorment() {
30
 
31
  const [theme, setTheme] = useState(useThemeDetector)
 
48
  restore()
49
  },[])
50
 
51
+ const deleteEdge = (id) => setEdges((eds) => eds.filter(e => e.id !== id))
52
+
53
 
54
  const onNodesChange = useCallback(
55
  (changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
 
69
  const onConnect = useCallback(
70
  (params) => {
71
  console.log(params)
72
+ setEdges((els) => addEdge({...params, type: "custom", animated : true, style : {stroke : "#00FF4A", strokeWidth : "3"}, markerEnd: {type: MarkerType.ArrowClosed, color : "#00FF4A"}, data : { delete : deleteEdge}}, els))
73
  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 => {
74
  console.log(res)
75
  }).catch(error => {
 
88
 
89
  const deleteNodeContains = (id) =>{setNodes((nds) => nds.filter(n => !n.id.includes(`${id}-`) ))}
90
  const deleteNode = (id) =>{setNodes((nds) => nds.filter(n => n.id !== id ))}
91
+
92
  const onSave = useCallback(() => {
93
  if (reactFlowInstance) {
94
  const flow = reactFlowInstance.toObject();
 
163
  <ReactFlowProvider>
164
  <Navbar onDelete={deleteNodeContains} colour={JSON.parse(localStorage.getItem('colour'))} emoji={JSON.parse(localStorage.getItem('emoji'))}/>
165
  <div className="h-screen w-screen" ref={reactFlowWrapper}>
166
+ <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} fitView>
167
  <Background variant='dots' size={1} className=" bg-white dark:bg-neutral-800"/>
168
  <Controls/>
169
  </ReactFlow>
frontend/src/css/dist/output.css CHANGED
@@ -557,10 +557,6 @@ video {
557
  visibility: hidden;
558
  }
559
 
560
- .fixed {
561
- position: fixed;
562
- }
563
-
564
  .absolute {
565
  position: absolute;
566
  }
@@ -638,14 +634,6 @@ video {
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
  }
@@ -658,6 +646,14 @@ video {
658
  margin: 0px;
659
  }
660
 
 
 
 
 
 
 
 
 
661
  .mr-5 {
662
  margin-right: 1.25rem;
663
  }
@@ -670,14 +666,6 @@ video {
670
  margin-bottom: 0.5rem;
671
  }
672
 
673
- .mr-auto {
674
- margin-right: auto;
675
- }
676
-
677
- .ml-auto {
678
- margin-left: auto;
679
- }
680
-
681
  .-ml-\[5px\] {
682
  margin-left: -5px;
683
  }
@@ -706,12 +694,8 @@ video {
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 {
@@ -738,6 +722,10 @@ video {
738
  height: 2.5rem;
739
  }
740
 
 
 
 
 
741
  .h-\[20px\] {
742
  height: 20px;
743
  }
@@ -754,10 +742,6 @@ video {
754
  height: 100vh;
755
  }
756
 
757
- .h-9 {
758
- height: 2.25rem;
759
- }
760
-
761
  .h-auto {
762
  height: auto;
763
  }
@@ -766,6 +750,10 @@ video {
766
  height: 600px;
767
  }
768
 
 
 
 
 
769
  .h-\[203\.3333px\] {
770
  height: 203.3333px;
771
  }
@@ -774,20 +762,28 @@ video {
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 {
@@ -806,14 +802,6 @@ video {
806
  width: 75%;
807
  }
808
 
809
- .w-10 {
810
- width: 2.5rem;
811
- }
812
-
813
- .w-9 {
814
- width: 2.25rem;
815
- }
816
-
817
  .w-32 {
818
  width: 8rem;
819
  }
@@ -846,12 +834,12 @@ video {
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 {
@@ -941,6 +929,10 @@ video {
941
  word-break: break-all;
942
  }
943
 
 
 
 
 
944
  .rounded-lg {
945
  border-radius: 0.5rem;
946
  }
@@ -953,10 +945,6 @@ video {
953
  border-radius: 0.375rem;
954
  }
955
 
956
- .rounded-xl {
957
- border-radius: 0.75rem;
958
- }
959
-
960
  .rounded-full {
961
  border-radius: 9999px;
962
  }
@@ -980,14 +968,14 @@ video {
980
  border-top-left-radius: 0.5rem;
981
  }
982
 
983
- .border {
984
- border-width: 1px;
985
- }
986
-
987
  .border-2 {
988
  border-width: 2px;
989
  }
990
 
 
 
 
 
991
  .border-y-2 {
992
  border-top-width: 2px;
993
  border-bottom-width: 2px;
@@ -1035,6 +1023,11 @@ video {
1035
  border-color: rgb(255 255 255 / var(--tw-border-opacity));
1036
  }
1037
 
 
 
 
 
 
1038
  .bg-gray-100 {
1039
  --tw-bg-opacity: 1;
1040
  background-color: rgb(243 244 246 / var(--tw-bg-opacity));
@@ -1050,11 +1043,6 @@ video {
1050
  background-color: rgb(212 212 212 / var(--tw-bg-opacity));
1051
  }
1052
 
1053
- .bg-white {
1054
- --tw-bg-opacity: 1;
1055
- background-color: rgb(255 255 255 / var(--tw-bg-opacity));
1056
- }
1057
-
1058
  .bg-gray-300 {
1059
  --tw-bg-opacity: 1;
1060
  background-color: rgb(209 213 219 / var(--tw-bg-opacity));
@@ -1093,6 +1081,11 @@ video {
1093
  background-color: rgb(254 169 89 / var(--tw-bg-opacity));
1094
  }
1095
 
 
 
 
 
 
1096
  .bg-gradient-to-bl {
1097
  background-image: linear-gradient(to bottom left, var(--tw-gradient-stops));
1098
  }
@@ -1396,16 +1389,12 @@ video {
1396
  padding-top: 0.5rem;
1397
  }
1398
 
1399
- .pb-0 {
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 {
@@ -1568,10 +1557,6 @@ video {
1568
  transition-duration: 500ms;
1569
  }
1570
 
1571
- .duration-100 {
1572
- transition-duration: 100ms;
1573
- }
1574
-
1575
  .duration-200 {
1576
  transition-duration: 200ms;
1577
  }
@@ -1598,6 +1583,10 @@ video {
1598
  color: rgb(148 163 184 / var(--tw-text-opacity));
1599
  }
1600
 
 
 
 
 
1601
  @-webkit-keyframes pulse {
1602
  50% {
1603
  opacity: .5;
@@ -1630,6 +1619,16 @@ video {
1630
  border-color: rgb(74 222 128 / var(--tw-border-opacity));
1631
  }
1632
 
 
 
 
 
 
 
 
 
 
 
1633
  .hover\:bg-gray-200:hover {
1634
  --tw-bg-opacity: 1;
1635
  background-color: rgb(229 231 235 / var(--tw-bg-opacity));
@@ -1653,6 +1652,18 @@ video {
1653
  opacity: 0.5;
1654
  }
1655
 
 
 
 
 
 
 
 
 
 
 
 
 
1656
  .focus\:border-sky-500:focus {
1657
  --tw-border-opacity: 1;
1658
  border-color: rgb(14 165 233 / var(--tw-border-opacity));
@@ -1751,6 +1762,16 @@ video {
1751
  background-color: rgb(38 38 38 / var(--tw-bg-opacity));
1752
  }
1753
 
 
 
 
 
 
 
 
 
 
 
1754
  .dark .dark\:bg-gradient-to-bl {
1755
  background-image: linear-gradient(to bottom left, var(--tw-gradient-stops));
1756
  }
@@ -1775,6 +1796,11 @@ video {
1775
  color: rgb(255 255 255 / var(--tw-text-opacity));
1776
  }
1777
 
 
 
 
 
 
1778
  .dark .dark\:hover\:bg-gray-700:hover {
1779
  --tw-bg-opacity: 1;
1780
  background-color: rgb(55 65 81 / var(--tw-bg-opacity));
 
557
  visibility: hidden;
558
  }
559
 
 
 
 
 
560
  .absolute {
561
  position: absolute;
562
  }
 
634
  z-index: 50;
635
  }
636
 
 
 
 
 
 
 
 
 
637
  .float-left {
638
  float: left;
639
  }
 
646
  margin: 0px;
647
  }
648
 
649
+ .ml-auto {
650
+ margin-left: auto;
651
+ }
652
+
653
+ .mr-auto {
654
+ margin-right: auto;
655
+ }
656
+
657
  .mr-5 {
658
  margin-right: 1.25rem;
659
  }
 
666
  margin-bottom: 0.5rem;
667
  }
668
 
 
 
 
 
 
 
 
 
669
  .-ml-\[5px\] {
670
  margin-left: -5px;
671
  }
 
694
  margin-top: 0.25rem;
695
  }
696
 
697
+ .-mt-1 {
698
+ margin-top: -0.25rem;
 
 
 
 
699
  }
700
 
701
  .block {
 
722
  height: 2.5rem;
723
  }
724
 
725
+ .h-9 {
726
+ height: 2.25rem;
727
+ }
728
+
729
  .h-\[20px\] {
730
  height: 20px;
731
  }
 
742
  height: 100vh;
743
  }
744
 
 
 
 
 
745
  .h-auto {
746
  height: auto;
747
  }
 
750
  height: 600px;
751
  }
752
 
753
+ .h-\[140px\] {
754
+ height: 140px;
755
+ }
756
+
757
  .h-\[203\.3333px\] {
758
  height: 203.3333px;
759
  }
 
762
  height: 41px;
763
  }
764
 
765
+ .h-8 {
766
+ height: 2rem;
767
  }
768
 
769
+ .h-11 {
770
+ height: 2.75rem;
771
+ }
772
+
773
+ .h-40 {
774
+ height: 10rem;
775
+ }
776
+
777
+ .h-60 {
778
+ height: 15rem;
779
  }
780
 
781
+ .w-10 {
782
+ width: 2.5rem;
783
  }
784
 
785
+ .w-9 {
786
+ width: 2.25rem;
787
  }
788
 
789
  .w-full {
 
802
  width: 75%;
803
  }
804
 
 
 
 
 
 
 
 
 
805
  .w-32 {
806
  width: 8rem;
807
  }
 
834
  width: 100vw;
835
  }
836
 
837
+ .w-8 {
838
+ width: 2rem;
839
  }
840
 
841
+ .w-11 {
842
+ width: 2.75rem;
843
  }
844
 
845
  .max-w-full {
 
929
  word-break: break-all;
930
  }
931
 
932
+ .rounded-xl {
933
+ border-radius: 0.75rem;
934
+ }
935
+
936
  .rounded-lg {
937
  border-radius: 0.5rem;
938
  }
 
945
  border-radius: 0.375rem;
946
  }
947
 
 
 
 
 
948
  .rounded-full {
949
  border-radius: 9999px;
950
  }
 
968
  border-top-left-radius: 0.5rem;
969
  }
970
 
 
 
 
 
971
  .border-2 {
972
  border-width: 2px;
973
  }
974
 
975
+ .border {
976
+ border-width: 1px;
977
+ }
978
+
979
  .border-y-2 {
980
  border-top-width: 2px;
981
  border-bottom-width: 2px;
 
1023
  border-color: rgb(255 255 255 / var(--tw-border-opacity));
1024
  }
1025
 
1026
+ .bg-white {
1027
+ --tw-bg-opacity: 1;
1028
+ background-color: rgb(255 255 255 / var(--tw-bg-opacity));
1029
+ }
1030
+
1031
  .bg-gray-100 {
1032
  --tw-bg-opacity: 1;
1033
  background-color: rgb(243 244 246 / var(--tw-bg-opacity));
 
1043
  background-color: rgb(212 212 212 / var(--tw-bg-opacity));
1044
  }
1045
 
 
 
 
 
 
1046
  .bg-gray-300 {
1047
  --tw-bg-opacity: 1;
1048
  background-color: rgb(209 213 219 / var(--tw-bg-opacity));
 
1081
  background-color: rgb(254 169 89 / var(--tw-bg-opacity));
1082
  }
1083
 
1084
+ .bg-black {
1085
+ --tw-bg-opacity: 1;
1086
+ background-color: rgb(0 0 0 / var(--tw-bg-opacity));
1087
+ }
1088
+
1089
  .bg-gradient-to-bl {
1090
  background-image: linear-gradient(to bottom left, var(--tw-gradient-stops));
1091
  }
 
1389
  padding-top: 0.5rem;
1390
  }
1391
 
 
 
 
 
1392
  .pt-10 {
1393
  padding-top: 2.5rem;
1394
  }
1395
 
1396
+ .pb-0 {
1397
+ padding-bottom: 0px;
1398
  }
1399
 
1400
  .text-center {
 
1557
  transition-duration: 500ms;
1558
  }
1559
 
 
 
 
 
1560
  .duration-200 {
1561
  transition-duration: 200ms;
1562
  }
 
1583
  color: rgb(148 163 184 / var(--tw-text-opacity));
1584
  }
1585
 
1586
+ .hover\:h-52:hover {
1587
+ height: 13rem;
1588
+ }
1589
+
1590
  @-webkit-keyframes pulse {
1591
  50% {
1592
  opacity: .5;
 
1619
  border-color: rgb(74 222 128 / var(--tw-border-opacity));
1620
  }
1621
 
1622
+ .hover\:border-white:hover {
1623
+ --tw-border-opacity: 1;
1624
+ border-color: rgb(255 255 255 / var(--tw-border-opacity));
1625
+ }
1626
+
1627
+ .hover\:border-black:hover {
1628
+ --tw-border-opacity: 1;
1629
+ border-color: rgb(0 0 0 / var(--tw-border-opacity));
1630
+ }
1631
+
1632
  .hover\:bg-gray-200:hover {
1633
  --tw-bg-opacity: 1;
1634
  background-color: rgb(229 231 235 / var(--tw-bg-opacity));
 
1652
  opacity: 0.5;
1653
  }
1654
 
1655
+ .hover\:shadow-xl:hover {
1656
+ --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
1657
+ --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);
1658
+ box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
1659
+ }
1660
+
1661
+ .hover\:shadow-lg:hover {
1662
+ --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
1663
+ --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);
1664
+ box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
1665
+ }
1666
+
1667
  .focus\:border-sky-500:focus {
1668
  --tw-border-opacity: 1;
1669
  border-color: rgb(14 165 233 / var(--tw-border-opacity));
 
1762
  background-color: rgb(38 38 38 / var(--tw-bg-opacity));
1763
  }
1764
 
1765
+ .dark .dark\:bg-black {
1766
+ --tw-bg-opacity: 1;
1767
+ background-color: rgb(0 0 0 / var(--tw-bg-opacity));
1768
+ }
1769
+
1770
+ .dark .dark\:bg-white {
1771
+ --tw-bg-opacity: 1;
1772
+ background-color: rgb(255 255 255 / var(--tw-bg-opacity));
1773
+ }
1774
+
1775
  .dark .dark\:bg-gradient-to-bl {
1776
  background-image: linear-gradient(to bottom left, var(--tw-gradient-stops));
1777
  }
 
1796
  color: rgb(255 255 255 / var(--tw-text-opacity));
1797
  }
1798
 
1799
+ .dark .hover\:dark\:border-black:hover {
1800
+ --tw-border-opacity: 1;
1801
+ border-color: rgb(0 0 0 / var(--tw-border-opacity));
1802
+ }
1803
+
1804
  .dark .dark\:hover\:bg-gray-700:hover {
1805
  --tw-bg-opacity: 1;
1806
  background-color: rgb(55 65 81 / var(--tw-bg-opacity));