Skip to content

Commit 14879f8

Browse files
authored
Merge branch 'staging' into update-nodes-color
2 parents f165bbb + 77f11ca commit 14879f8

23 files changed

+2952
-1685
lines changed

app/api/utils.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
export function getEnvVariables() {
2-
const url = process.env.BACKEND_URL
3-
const token = process.env.SECRET_TOKEN
2+
const { SECRET_TOKEN, BACKEND_URL } = process.env
43

5-
if (!url) {
4+
if (!BACKEND_URL) {
65
throw new Error("Environment variable BACKEND_URL must be set");
76
}
8-
if (!token) {
7+
if (!SECRET_TOKEN) {
98
throw new Error("Environment variable SECRET_TOKEN must be set");
109
}
1110

12-
return { url, token }
11+
return { url: BACKEND_URL, token: SECRET_TOKEN };
1312
}

app/components/chat.tsx

Lines changed: 69 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
import { 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";
33
import Image from "next/image";
44
import { AlignLeft, ArrowDown, ArrowRight, ChevronDown, Lightbulb, Undo2 } from "lucide-react";
55
import { Path } from "../page";
66
import Input from "./Input";
7-
import { Graph } from "./model";
7+
import { Graph, GraphData } from "./model";
88
import { cn } from "@/lib/utils";
9-
import { LAYOUT } from "./code-graph";
109
import { TypeAnimation } from "react-type-animation";
1110
import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
12-
import cytoscape from "cytoscape";
1311
import { prepareArg } from "../utils";
1412

13+
type PathData = {
14+
nodes: any[]
15+
links: any[]
16+
}
17+
1518
enum MessageTypes {
1619
Query,
1720
Response,
@@ -68,7 +71,7 @@ const SELECTED_PATH_NODE_STYLE = {
6871
interface 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

8689
const 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

Comments
 (0)