Skip to content

Commit 7b75a2c

Browse files
authored
Merge branch 'staging' into fix-collapse
2 parents 3281912 + e4f1d2b commit 7b75a2c

7 files changed

Lines changed: 268 additions & 95 deletions

File tree

app/components/code-graph.tsx

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Dispatch, RefObject, SetStateAction, useContext, useEffect, useRef, useState } from "react";
2-
import { GraphData, Node } from "./model";
2+
import { GraphData, Link, Node } from "./model";
33
import { GraphContext } from "./provider";
44
import { Toolbar } from "./toolbar";
55
import { Labels } from "./labels";
6-
import { GitFork, Search, X } from "lucide-react";
6+
import { Download, GitFork, Search, X } from "lucide-react";
77
import ElementMenu from "./elementMenu";
88
import Combobox from "./combobox";
99
import { toast } from '@/components/ui/use-toast';
@@ -21,7 +21,7 @@ const GraphView = dynamic(() => import('./graphView'));
2121
interface Props {
2222
data: GraphData,
2323
setData: Dispatch<SetStateAction<GraphData>>,
24-
onFetchGraph: (graphName: string) => void,
24+
onFetchGraph: (graphName: string) => Promise<void>,
2525
onFetchNode: (nodeIds: number[]) => Promise<GraphData>,
2626
options: string[]
2727
setOptions: Dispatch<SetStateAction<string[]>>
@@ -55,7 +55,7 @@ export function CodeGraph({
5555
let graph = useContext(GraphContext)
5656

5757
const [url, setURL] = useState("");
58-
const [selectedObj, setSelectedObj] = useState<Node>();
58+
const [selectedObj, setSelectedObj] = useState<Node | Link>();
5959
const [selectedObjects, setSelectedObjects] = useState<Node[]>([]);
6060
const [position, setPosition] = useState<Position>();
6161
const [graphName, setGraphName] = useState<string>("");
@@ -145,9 +145,10 @@ export function CodeGraph({
145145
}
146146

147147
run()
148+
148149
}, [graphName])
149150

150-
function handleSelectedValue(value: string) {
151+
async function handleSelectedValue(value: string) {
151152
setGraphName(value)
152153
onFetchGraph(value)
153154
}
@@ -241,7 +242,8 @@ export function CodeGraph({
241242
graph.visibleLinks(true, [chartNode!.id])
242243
setData({ ...graph.Elements })
243244
}
244-
245+
246+
setSearchNode(n)
245247
setTimeout(() => {
246248
chart.zoomToFit(1000, 150, (n: NodeObject<Node>) => n.id === chartNode!.id);
247249
}, 0)
@@ -259,6 +261,33 @@ export function CodeGraph({
259261
setData({ ...graph.Elements })
260262
}
261263

264+
const handleDownloadImage = async () => {
265+
try {
266+
const canvas = document.querySelector('.force-graph-container canvas') as HTMLCanvasElement;
267+
if (!canvas) {
268+
toast({
269+
title: "Error",
270+
description: "Canvas not found",
271+
variant: "destructive",
272+
});
273+
return;
274+
}
275+
276+
const dataURL = canvas.toDataURL('image/webp');
277+
const link = document.createElement('a');
278+
link.href = dataURL;
279+
link.download = `${graphName}.webp`;
280+
link.click();
281+
} catch (error) {
282+
console.error('Error downloading graph image:', error);
283+
toast({
284+
title: "Error",
285+
description: "Failed to download image. Please try again.",
286+
variant: "destructive",
287+
});
288+
}
289+
};
290+
262291
return (
263292
<div className="h-full w-full flex flex-col gap-4 p-8 bg-gray-100">
264293
<header className="flex flex-col gap-4">
@@ -351,6 +380,12 @@ export function CodeGraph({
351380
className="pointer-events-auto"
352381
chartRef={chartRef}
353382
/>
383+
<button
384+
className="pointer-events-auto bg-white p-2 rounded-md"
385+
onClick={handleDownloadImage}
386+
>
387+
<Download />
388+
</button>
354389
</div>
355390
</div>
356391
<ElementMenu

app/components/dataPanel.tsx

Lines changed: 87 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,30 @@
1-
import { Dispatch, SetStateAction, useRef, useEffect, useState } from "react";
2-
import { Node } from "./model";
1+
import { Dispatch, SetStateAction } from "react";
2+
import { JSONTree } from 'react-json-tree';
3+
import { Link, Node } from "./model";
34
import { Copy, SquareArrowOutUpRight, X } from "lucide-react";
45
import SyntaxHighlighter from 'react-syntax-highlighter';
56
import { dark } from 'react-syntax-highlighter/dist/esm/styles/hljs';
67

78
interface Props {
8-
obj: Node | undefined;
9-
setObj: Dispatch<SetStateAction<Node | undefined>>;
9+
obj: Node | Link | undefined;
10+
setObj: Dispatch<SetStateAction<Node | Link | undefined>>;
1011
url: string;
1112
}
1213

1314
const excludedProperties = [
1415
"category",
16+
"label",
1517
"color",
1618
"expand",
1719
"collapsed",
1820
"isPath",
1921
"isPathStartEnd",
2022
"visible",
2123
"index",
24+
"curve",
2225
"__indexColor",
26+
"isPathSelected",
27+
"__controlPoints",
2328
"x",
2429
"y",
2530
"vx",
@@ -30,20 +35,16 @@ const excludedProperties = [
3035

3136
export default function DataPanel({ obj, setObj, url }: Props) {
3237

33-
const containerRef = useRef<HTMLDivElement>(null);
34-
const [containerHeight, setContainerHeight] = useState(0);
35-
36-
useEffect(() => {
37-
if (containerRef.current) {
38-
setContainerHeight(containerRef.current.clientHeight);
39-
}
40-
}, [containerRef.current]);
38+
debugger
4139

4240
if (!obj) return null;
4341

44-
const label = `${obj.category}: ${obj.name}`
42+
const type = "category" in obj
43+
const label = type ? `${obj.category}: ${obj.name}` : obj.label
4544
const object = Object.entries(obj).filter(([k]) => !excludedProperties.includes(k))
4645

46+
console.log(obj)
47+
4748
return (
4849
<div data-name="node-details-panel" className="z-20 absolute -top-10 left-20 bg-[#343434] text-white shadow-lg rounded-lg flex flex-col max-h-[88%] max-w-[56%] overflow-hidden" >
4950
<header className="bg-[#191919] flex items-center gap-8 justify-between p-8">
@@ -52,7 +53,7 @@ export default function DataPanel({ obj, setObj, url }: Props) {
5253
<X color="white" />
5354
</button>
5455
</header>
55-
<main ref={containerRef} className="bg-[#343434] flex flex-col grow overflow-y-auto p-4">
56+
<main className="bg-[#343434] flex flex-col grow overflow-y-auto p-4">
5657
{
5758
object.map(([key, value]) => (
5859
<div key={key} className="flex gap-2">
@@ -73,40 +74,86 @@ export default function DataPanel({ obj, setObj, url }: Props) {
7374
>
7475
{value}
7576
</SyntaxHighlighter>
76-
: <p className="text-white">{value}</p>
77+
: typeof value === "object" ?
78+
<JSONTree
79+
data={Object.fromEntries(Object.entries(value).filter(([k]) => !excludedProperties.includes(k)))}
80+
theme={{
81+
base00: '#343434', // background
82+
base01: '#000000',
83+
base02: '#CE9178',
84+
base03: '#CE9178', // open values
85+
base04: '#CE9178',
86+
base05: '#CE9178',
87+
base06: '#CE9178',
88+
base07: '#CE9178',
89+
base08: '#CE9178',
90+
base09: '#b5cea8', // numbers
91+
base0A: '#CE9178',
92+
base0B: '#CE9178', // close values
93+
base0C: '#CE9178',
94+
base0D: '#99E4E5', // * keys
95+
base0E: '#ae81ff',
96+
base0F: '#cc6633'
97+
}}
98+
valueRenderer={(valueAsString, value, keyPath) => {
99+
if (keyPath === "src") {
100+
return <SyntaxHighlighter
101+
language="python"
102+
style={{
103+
...dark,
104+
hljs: {
105+
...dark.hljs,
106+
maxHeight: `9rem`,
107+
background: '#343434',
108+
padding: 2,
109+
}
110+
}}
111+
>
112+
{value as string}
113+
</SyntaxHighlighter>
114+
}
115+
return <span className="text-white">{value as string}</span>
116+
}}
117+
/>
118+
: <span className="text-white">{value}</span>
77119
}
78120
</div>
79121
))
80122
}
81123
</main>
82124
<footer className="bg-[#191919] flex items-center justify-between p-4">
83-
<button
84-
className="flex items-center gap-2 p-2"
85-
title="Copy src to clipboard"
86-
onClick={() => navigator.clipboard.writeText(obj.src || "")}
87-
>
88-
<Copy color="white" />
89-
Copy
90-
</button>
91-
<a
92-
className="flex items-center gap-2 p-2"
93-
href={url}
94-
target="_blank"
95-
title="Go to repo"
96-
onClick={() => {
97-
const newTab = window.open(url, '_blank');
125+
{
126+
"category" in obj &&
127+
<>
128+
<button
129+
className="flex items-center gap-2 p-2"
130+
title="Copy src to clipboard"
131+
onClick={() => navigator.clipboard.writeText(obj.src || "")}
132+
>
133+
<Copy color="white" />
134+
Copy
135+
</button>
136+
<a
137+
className="flex items-center gap-2 p-2"
138+
href={url}
139+
target="_blank"
140+
title="Go to repo"
141+
onClick={() => {
142+
const newTab = window.open(url, '_blank');
98143

99-
if (!obj.src_start || !obj.src_end || !newTab) return
144+
if (!obj.src_start || !obj.src_end || !newTab) return
100145

101-
newTab.scroll({
102-
top: obj.src_start,
103-
behavior: 'smooth'
104-
})
105-
}}
106-
>
107-
<SquareArrowOutUpRight color="white" />
108-
Go to repo
109-
</a>
146+
newTab.scroll({
147+
top: obj.src_start,
148+
behavior: 'smooth'
149+
})
150+
}}
151+
>
152+
<SquareArrowOutUpRight color="white" />
153+
Go to repo
154+
</a>
155+
</>
156+
}
110157
</footer>
111158
</div>
112159
)

0 commit comments

Comments
 (0)