11import { toast } from "@/components/ui/use-toast" ;
2- import { Dispatch , FormEvent , MutableRefObject , SetStateAction , useEffect , useRef , useState } from "react" ;
2+ import { Dispatch , FormEvent , SetStateAction , useEffect , useRef , useState } from "react" ;
33import Image from "next/image" ;
44import { AlignLeft , ArrowDown , ArrowRight , ChevronDown , Lightbulb , Undo2 } from "lucide-react" ;
55import { Path } from "../page" ;
66import Input from "./Input" ;
7- import { Graph } from "./model" ;
7+ import { Graph , GraphData } from "./model" ;
88import { cn } from "@/lib/utils" ;
9- import { LAYOUT } from "./code-graph" ;
109import { TypeAnimation } from "react-type-animation" ;
1110import { DropdownMenu , DropdownMenuContent , DropdownMenuTrigger } from "@/components/ui/dropdown-menu" ;
12- import cytoscape from "cytoscape" ;
1311import { prepareArg } from "../utils" ;
1412
13+ type PathData = {
14+ nodes : any [ ]
15+ links : any [ ]
16+ }
17+
1518enum MessageTypes {
1619 Query ,
1720 Response ,
@@ -68,7 +71,7 @@ const SELECTED_PATH_NODE_STYLE = {
6871interface Message {
6972 type : MessageTypes ;
7073 text ?: string ;
71- paths ?: { nodes : any [ ] , edges : any [ ] } [ ] ;
74+ paths ?: { nodes : any [ ] , links : any [ ] } [ ] ;
7275 graphName ?: string ;
7376}
7477
@@ -77,10 +80,10 @@ interface Props {
7780 path : Path | undefined
7881 setPath : Dispatch < SetStateAction < Path | undefined > >
7982 graph : Graph
80- chartRef : MutableRefObject < cytoscape . Core | null >
81- selectedPathId : string | undefined
82- isPath : boolean
83- setIsPath : ( isPathResponse : boolean ) => void
83+ selectedPathId : number | undefined
84+ isPathResponse : boolean | undefined
85+ setIsPathResponse : ( isPathResponse : boolean | undefined ) => void
86+ setData : Dispatch < SetStateAction < GraphData > >
8487}
8588
8689const SUGGESTIONS = [
@@ -102,21 +105,19 @@ const RemoveLastPath = (messages: Message[]) => {
102105 return messages
103106}
104107
105- export function Chat ( { repo, path, setPath, graph, chartRef , selectedPathId , isPath , setIsPath } : Props ) {
108+ export function Chat ( { repo, path, setPath, graph, selectedPathId , isPathResponse , setIsPathResponse , setData } : Props ) {
106109
107110 // Holds the messages in the chat
108111 const [ messages , setMessages ] = useState < Message [ ] > ( [ ] ) ;
109112
110113 // Holds the messages in the chat
111- const [ paths , setPaths ] = useState < { nodes : any [ ] , edges : any [ ] } [ ] > ( [ ] ) ;
114+ const [ paths , setPaths ] = useState < PathData [ ] > ( [ ] ) ;
112115
113- const [ selectedPath , setSelectedPath ] = useState < { nodes : any [ ] , edges : any [ ] } > ( ) ;
116+ const [ selectedPath , setSelectedPath ] = useState < PathData > ( ) ;
114117
115118 // Holds the user input while typing
116119 const [ query , setQuery ] = useState ( '' ) ;
117120
118- const [ isPathResponse , setIsPathResponse ] = useState ( false ) ;
119-
120121 const [ tipOpen , setTipOpen ] = useState ( false ) ;
121122
122123 const [ sugOpen , setSugOpen ] = useState ( false ) ;
@@ -127,16 +128,11 @@ export function Chat({ repo, path, setPath, graph, chartRef, selectedPathId, isP
127128 const isSendMessage = messages . some ( m => m . type === MessageTypes . Pending ) || ( messages . some ( m => m . text === "Please select a starting point and the end point. Select or press relevant item on the graph" ) && ! messages . some ( m => m . type === MessageTypes . Path ) )
128129
129130 useEffect ( ( ) => {
130- setSelectedPath ( undefined )
131- setIsPathResponse ( false )
132- } , [ graph . Id ] )
133-
134- useEffect ( ( ) => {
135- const p = paths . find ( ( path ) => [ ...path . edges , ...path . nodes ] . some ( ( e : any ) => e . id === selectedPathId ) )
131+ const p = paths . find ( ( path ) => [ ...path . links , ...path . nodes ] . some ( ( e : any ) => e . id === selectedPathId ) )
136132
137133 if ( ! p ) return
138134
139- handleSetSelectedPath ( p )
135+ handelSetSelectedPath ( p )
140136 } , [ selectedPathId ] )
141137
142138 // Scroll to the bottom of the chat on new message
@@ -151,90 +147,65 @@ export function Chat({ repo, path, setPath, graph, chartRef, selectedPathId, isP
151147 } , [ path ] )
152148
153149 useEffect ( ( ) => {
154- if ( isPath ) return
150+ if ( isPathResponse || isPathResponse === undefined ) return
155151 setIsPathResponse ( false )
156152 setSelectedPath ( undefined )
157153 setPaths ( [ ] )
158- } , [ isPath ] )
159-
160- useEffect ( ( ) => {
161- setIsPath ( isPathResponse )
162154 } , [ isPathResponse ] )
163155
164- const updatePreviousPath = ( chart : cytoscape . Core , p : { nodes : any [ ] , edges : any [ ] } ) => {
156+ const handelSetSelectedPath = ( p : PathData ) => {
165157 setSelectedPath ( prev => {
166158 if ( prev ) {
167- if ( isPathResponse && paths . some ( ( path ) => [ ...path . nodes , ...path . edges ] . every ( ( e : any ) => [ ...prev . nodes , ...prev . edges ] . some ( ( el : any ) => el . id === e . id ) ) ) ) {
168- chart . edges ( ) . forEach ( e => {
169- const id = e . id ( )
159+ if ( isPathResponse && paths . some ( ( path ) => [ ...path . nodes , ...path . links ] . every ( ( e : any ) => [ ...prev . nodes , ...prev . links ] . some ( ( e : any ) => e . id === e . id ) ) ) ) {
160+ graph . getElements ( ) . forEach ( link => {
161+ const { id } = link
170162
171- if ( prev . edges . some ( el => el . id == id ) && ! p . edges . some ( el => el . id == id ) ) {
172- e . style ( PATH_EDGE_STYLE )
163+ if ( prev . links . some ( e => e . id === id ) && ! p . links . some ( e => e . id = == id ) ) {
164+ link . isPathSelected = false
173165 }
174166 } )
175167 } else {
176- const elements = chart . elements ( ) . filter ( e => [ ...prev . edges , ...prev . nodes ] . some ( el => el . id == e . id ( ) && ! [ ...p . nodes , ...p . edges ] . some ( ele => ele . id == e . id ( ) ) ) ) . removeStyle ( )
177- if ( isPathResponse ) {
168+ const elements = graph . getElements ( ) . filter ( e => [ ...prev . links , ...prev . nodes ] . some ( el => el . id === e . id && ! [ ...p . nodes , ...p . links ] . some ( ele => ele . id === el . id ) ) )
169+ if ( isPathResponse || isPathResponse === undefined ) {
178170 elements . forEach ( e => {
179- if ( e . isNode ( ) ) {
180- e . style ( NODE_STYLE ) ;
181- }
182-
183- if ( e . isEdge ( ) ) {
184- e . style ( EDGE_STYLE ) ;
185- }
171+ e . isPath = false
172+ e . isPathSelected = false
186173 } )
187174 }
188175 }
189176 }
190177 return p
191178 } )
192- }
193-
194- const handleSetSelectedPath = ( p : { nodes : any [ ] , edges : any [ ] } ) => {
195- const chart = chartRef . current
196-
197- if ( ! chart ) return
198-
199- updatePreviousPath ( chart , p )
200-
201- if ( isPathResponse && paths . some ( ( path ) => [ ...path . nodes , ...path . edges ] . every ( ( e : any ) => [ ...p . nodes , ...p . edges ] . some ( ( el : any ) => el . id === e . id ) ) ) ) {
202- chart . edges ( ) . forEach ( e => {
203- const id = e . id ( )
204-
205- if ( p . edges . some ( el => el . id == id ) ) {
206- e . style ( SELECTED_PATH_EDGE_STYLE )
179+ if ( isPathResponse && paths . length > 0 && paths . some ( ( path ) => [ ...path . nodes , ...path . links ] . every ( ( e : any ) => [ ...p . nodes , ...p . links ] . some ( ( el : any ) => el . id === e . id ) ) ) ) {
180+ graph . Elements . links . forEach ( e => {
181+ if ( p . links . some ( el => el . id === e . id ) ) {
182+ e . isPathSelected = true
207183 }
208184 } )
209- chart . elements ( ) . filter ( el => [ ...p . nodes , ...p . edges ] . some ( e => e . id == el . id ( ) ) ) . layout ( LAYOUT ) . run ( ) ;
210185 } else {
211- const elements : any = { nodes : [ ] , edges : [ ] } ;
212- [ ...p . nodes , ...p . edges ] . forEach ( e => {
213- let element = chart . elements ( `#${ e . id } ` )
214- if ( element . length === 0 ) {
215- const type = e . id . startsWith ( "_" )
216- e = type ? { ...e , id : e . id . slice ( 1 ) } : e
217- type
218- ? elements . edges . push ( e )
219- : elements . nodes . push ( e )
186+ const elements : PathData = { nodes : [ ] , links : [ ] } ;
187+ p . nodes . forEach ( node => {
188+ let element = graph . Elements . nodes . find ( n => n . id === node . id )
189+ if ( ! element ) {
190+ elements . nodes . push ( node )
220191 }
221192 } )
222-
223- chart . elements ( ) . filter ( ( e ) => {
224- console . log ( e . id ( ) ) ;
225- return [ ...p . nodes , ...p . edges ] . some ( ( el ) => el . id == e . id ( ) )
226- } ) . forEach ( ( e ) => {
227- if ( e . id ( ) == p . nodes [ 0 ] . id || e . id ( ) == p . nodes [ p . nodes . length - 1 ] . id ) {
228- e . removeStyle ( ) . style ( SELECTED_PATH_NODE_STYLE ) ;
229- } else if ( e . isNode ( ) ) {
230- e . removeStyle ( ) . style ( PATH_NODE_STYLE ) ;
193+ p . links . forEach ( link => {
194+ let element = graph . Elements . links . find ( l => l . id === link . id )
195+ if ( ! element ) {
196+ elements . links . push ( link )
231197 }
232-
233- if ( e . isEdge ( ) ) {
234- e . removeStyle ( ) . style ( SELECTED_PATH_EDGE_STYLE ) ;
198+ } )
199+ graph . extend ( elements , true , { start : p . nodes [ 0 ] , end : p . nodes [ p . nodes . length - 1 ] } )
200+ graph . getElements ( ) . filter ( e => "source" in e ? p . links . some ( l => l . id === e . id ) : p . nodes . some ( n => n . id === e . id ) ) . forEach ( e => {
201+ if ( ( e . id === p . nodes [ 0 ] . id || e . id === p . nodes [ p . nodes . length - 1 ] . id ) || "source" in e ) {
202+ e . isPathSelected = true
203+ } else {
204+ e . isPath = true
235205 }
236- } ) . layout ( LAYOUT ) . run ( ) ;
206+ } ) ;
237207 }
208+ setData ( { ...graph . Elements } )
238209 }
239210
240211 // A function that handles the change event of the url input box
@@ -293,9 +264,7 @@ export function Chat({ repo, path, setPath, graph, chartRef, selectedPathId, isP
293264 const handleSubmit = async ( ) => {
294265 setSelectedPath ( undefined )
295266
296- const chart = chartRef ?. current
297-
298- if ( ! chart || ! path ?. start ?. id || ! path . end ?. id ) return
267+ if ( ! path ?. start ?. id || ! path . end ?. id ) return
299268
300269 const result = await fetch ( `/api/repo/${ prepareArg ( repo ) } /${ prepareArg ( String ( path . start . id ) ) } /?targetId=${ prepareArg ( String ( path . end . id ) ) } ` , {
301270 method : 'POST'
@@ -320,40 +289,14 @@ export function Chat({ repo, path, setPath, graph, chartRef, selectedPathId, isP
320289 return
321290 }
322291
323- const formattedPaths : { nodes : any [ ] , edges : any [ ] } [ ] = json . result . paths . map ( ( p : any ) => ( { nodes : p . filter ( ( node : any , i : number ) => i % 2 === 0 ) , edges : p . filter ( ( edge : any , i : number ) => i % 2 !== 0 ) } ) )
324- chart . add ( formattedPaths . flatMap ( ( p : any ) => graph . extend ( p , false , path ) ) )
325- formattedPaths . forEach ( p => p . edges . forEach ( e => e . id = `_${ e . id } ` ) )
326- graph . Elements . forEach ( ( element : any ) => {
327- const { id } = element . data
328- const e = chart . elements ( ) . filter ( el => el . id ( ) == id )
329- if ( id == path . start ?. id || id == path . end ?. id ) {
330- e . style ( SELECTED_PATH_NODE_STYLE ) ;
331- } else if ( formattedPaths . some ( ( p : any ) => [ ...p . nodes , ...p . edges ] . some ( ( el : any ) => el . id == id ) ) ) {
332- if ( e . isNode ( ) ) {
333- e . style ( PATH_NODE_STYLE ) ;
334- }
335-
336- if ( e . isEdge ( ) ) {
337- e . style ( PATH_EDGE_STYLE ) ;
338- }
339- } else {
340- if ( e . isNode ( ) ) {
341- e . style ( NODE_STYLE ) ;
342- }
292+ const formattedPaths : PathData [ ] = json . result . paths . map ( ( p : any ) => ( { nodes : p . filter ( ( n : any , i : number ) => i % 2 === 0 ) , links : p . filter ( ( l : any , i : number ) => i % 2 !== 0 ) } ) )
293+ formattedPaths . forEach ( ( p : any ) => graph . extend ( p , false , path ) )
343294
344- if ( e . isEdge ( ) ) {
345- e . style ( EDGE_STYLE ) ;
346- }
347- }
348- } )
349- const elements = chart . elements ( ) . filter ( ( element ) => {
350- return formattedPaths . some ( p => [ ...p . nodes , ...p . edges ] . some ( ( node ) => node . id == element . id ( ) ) )
351- } ) ;
352- elements . layout ( LAYOUT ) . run ( )
353295 setPaths ( formattedPaths )
354296 setMessages ( ( prev ) => [ ...RemoveLastPath ( prev ) , { type : MessageTypes . PathResponse , paths : formattedPaths , graphName : graph . Id } ] ) ;
355297 setPath ( undefined )
356298 setIsPathResponse ( true )
299+ setData ( { ...graph . Elements } )
357300 }
358301
359302 const getTip = ( disabled = false ) =>
@@ -369,17 +312,20 @@ export function Chat({ repo, path, setPath, graph, chartRef, selectedPathId, isP
369312 ] )
370313
371314 if ( isPathResponse ) {
372- chartRef . current ?. elements ( ) . removeStyle ( ) . layout ( LAYOUT ) . run ( )
373315 setIsPathResponse ( false )
316+ graph . getElements ( ) . forEach ( e => {
317+ e . isPath = false
318+ e . isPathSelected = false
319+ } )
374320 }
375321
376322 setTimeout ( ( ) => setMessages ( prev => [ ...prev , {
377323 type : MessageTypes . Response ,
378324 text : "Please select a starting point and the end point. Select or press relevant item on the graph"
379325 } ] ) , 300 )
380326 setTimeout ( ( ) => {
381- setPath ( { } )
382327 setMessages ( prev => [ ...prev , { type : MessageTypes . Path } ] )
328+ setPath ( { } )
383329 } , 4000 )
384330 } }
385331 >
@@ -457,9 +403,9 @@ export function Chat({ repo, path, setPath, graph, chartRef, selectedPathId, isP
457403 key = { i }
458404 className = { cn (
459405 "flex text-wrap border p-2 gap-2 rounded-md" ,
460- p . nodes . length === selectedPath ?. nodes . length
461- && selectedPath ?. nodes . every ( node => p ?. nodes . some ( ( n ) => n . id === node . id ) )
462- && "border-[#FF66B3 ] bg-[#FFF0F7 ]" ,
406+ p . nodes . length === selectedPath ?. nodes . length &&
407+ selectedPath ?. nodes . every ( node => p ?. nodes . some ( ( n ) => n . id === node . id ) ) &&
408+ "border-[#ffde21 ] bg-[#ffde2133 ]" ,
463409 message . graphName !== graph . Id && "opacity-50 bg-gray-200"
464410 ) }
465411 title = { message . graphName !== graph . Id ? `Move to graph ${ message . graphName } to use this path` : undefined }
@@ -473,10 +419,13 @@ export function Chat({ repo, path, setPath, graph, chartRef, selectedPathId, isP
473419 return ;
474420 }
475421
476- if ( p . nodes . length === selectedPath ?. nodes . length &&
477- selectedPath ?. nodes . every ( node => p ?. nodes . some ( ( n ) => n . id === node . id ) ) ) return ;
478- handleSetSelectedPath ( p ) ;
479- setIsPath ( true ) ;
422+ if ( selectedPath ?. nodes . every ( node => p ?. nodes . some ( ( n ) => n . id === node . id ) ) && selectedPath . nodes . length === p . nodes . length ) return
423+
424+ if ( ! isPathResponse ) {
425+ setIsPathResponse ( undefined )
426+
427+ }
428+ handelSetSelectedPath ( p )
480429 } }
481430 >
482431 < p className = "font-bold" > #{ i + 1 } </ p >
0 commit comments