Skip to content

Commit e1306d1

Browse files
committed
paper graph
1 parent 5801e4f commit e1306d1

File tree

6 files changed

+336
-333
lines changed

6 files changed

+336
-333
lines changed

@types/paper-graph.d.ts

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,46 @@
11
type Paper = {
2-
citationCount: number,
3-
authors: string[],
42
id: string,
3+
scid: string,
4+
doi: string | null,
5+
arxivId: string | null,
6+
aclId: string | null,
7+
dblpId: string | null,
8+
59
title: string,
6-
references: string[],
7-
abstract: string | null,
8-
citations: string[],
9-
year: number
10-
}
10+
year: number,
11+
authors: _Author[],
12+
abstract: string | null
13+
14+
referenceCount: number,
15+
citationCount: number,
16+
17+
references: RelatedPaper[] | null,
18+
citations: RelatedPaper[] | null,
19+
referenceScids: string[],
20+
citationScids: string[],
21+
}
22+
23+
type RelatedPaper = Omit<Paper, 'abstract' | 'references' | 'citations'>
24+
type _Author = {
25+
id: string,
26+
scid: string,
27+
name: string,
28+
}
29+
30+
31+
32+
type Field = keyof Paper;
33+
34+
type GraphNode = Paper & {
35+
x: number, y: number, fx: number | null, fy: number | null
36+
}
37+
38+
type GraphLink = {
39+
source: string;
40+
target: string;
41+
};
42+
43+
type GraphData = {
44+
nodes: GraphNode[];
45+
links: GraphLink[];
46+
};

app/tools/paper-graph/Canvas.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Canvas({ data }: { data: GraphData }) {
2+
3+
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import { LightbulbIcon, OpenIcon } from "@public/icons";
2+
import { useEffect, useRef, useState } from "react";
3+
import { fetchFromSemanticScholar, fetchNotionBlock, fetchNotionPage, getOpenUrl } from "./utils";
4+
import { useClickOutside } from "@hooks";
5+
import PaperCard from "./PaperCard";
6+
7+
8+
export default function PaperDetail({ paper }: { paper: Paper | null }) {
9+
const [abstractExpanded, setAbstractExpanded] = useState(false)
10+
const [cachedRelatedPapers, setCitationCache] = useState<{
11+
[id: string]: {
12+
references: RelatedPaper[],
13+
citations: RelatedPaper[],
14+
}
15+
}>({});
16+
const [lightbulbData, setLightbulbData] = useState<{
17+
references: any[],
18+
citations: any[],
19+
open: boolean,
20+
}>({ references: [], citations: [], open: false });
21+
const [suggestionLoading, setSuggestionLoading] = useState(false);
22+
const suggestionRef = useRef<HTMLDivElement>(null);
23+
useClickOutside(suggestionRef, () => setLightbulbData(d => ({ ...d, open: false })))
24+
25+
async function handleSuggest() {
26+
if (!paper) return;
27+
28+
if (cachedRelatedPapers[paper.id]) {
29+
setLightbulbData({
30+
...cachedRelatedPapers[paper.id],
31+
open: true,
32+
});
33+
return;
34+
}
35+
36+
setSuggestionLoading(true);
37+
const paperData = await fetchFromSemanticScholar(paper.scid)
38+
const newEntry = {
39+
references: paperData.references ?? [],
40+
citations: paperData.citations ?? [],
41+
}
42+
43+
// Cache it
44+
setCitationCache(prev => ({
45+
...prev,
46+
[paper.id]: newEntry,
47+
}));
48+
49+
// Show modal
50+
setLightbulbData({
51+
...newEntry,
52+
open: true,
53+
});
54+
55+
setSuggestionLoading(false)
56+
}
57+
58+
return (
59+
paper
60+
?
61+
<>
62+
<div className="paper-detail w-[30%] bg-slate-100 rounded-md p-2 h-full overflow-auto scrollbar-thin scrollbar-thumb-slate-400 scrollbar-track-slate-200">
63+
<div className='flex justify-between'>
64+
<span className="font-semibold text-sm text-blue-700 truncate">
65+
{paper.title}
66+
</span>
67+
<span className='flex'>
68+
<LightbulbIcon className='mr-2 cursor-pointer' size={18} onClick={(e) => {
69+
e.stopPropagation();
70+
handleSuggest()
71+
}} />
72+
<OpenIcon size={18} className='cursor-pointer' onClick={(e) => {
73+
e.stopPropagation();
74+
const url = getOpenUrl(paper);
75+
if (url) window.open(url, '_blank');
76+
}} />
77+
</span>
78+
79+
</div>
80+
<div className='flex justify-between'>
81+
{paper.authors && (
82+
<span className="text-xs text-gray-600 mt-1">
83+
{paper.authors.map(author => author.name).join(', ')}
84+
</span>
85+
)}
86+
{typeof paper.citationCount === 'number' && (
87+
<span className="text-xs text-gray-600 mt-1">
88+
{paper.citationCount} citations
89+
</span>
90+
)}
91+
</div>
92+
93+
{<div onClick={() => setAbstractExpanded(!abstractExpanded)}>
94+
<strong>Abstract. </strong>
95+
{
96+
paper.abstract ?
97+
(abstractExpanded
98+
? paper.abstract
99+
: paper.abstract?.slice(0, 200) + '...')
100+
: "No abstract found."
101+
}
102+
</div>
103+
}
104+
{lightbulbData.open && (
105+
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
106+
<div ref={suggestionRef} className="bg-white w-[1/2] max-h-[80vh] p-6 rounded shadow overflow-y-auto">
107+
<div className="mb-4">
108+
<h3 className="font-semibold text-gray-700">References</h3>
109+
{lightbulbData.references.length === 0 ? (
110+
<p className="text-sm text-gray-500"> No references found.</p>
111+
) : (
112+
<>
113+
{lightbulbData.references
114+
.sort((a, b) => b.citationCount - a.citationCount)
115+
.map((paper) => <PaperCard key={paper.id} paper={paper} />)}
116+
</>
117+
)}
118+
</div>
119+
120+
<div>
121+
<h3 className="font-semibold text-gray-700">Citations</h3>
122+
{lightbulbData.citations.length === 0 ? (
123+
<p className="text-sm text-gray-500">No citations found.</p>
124+
) : (
125+
<>
126+
{lightbulbData.citations
127+
.sort((a, b) => b.citationCount - a.citationCount)
128+
.map((paper) =>
129+
<PaperCard key={paper.scid} paper={paper} />)}
130+
</>
131+
)}
132+
</div>
133+
</div>
134+
</div>
135+
)}
136+
</div>
137+
</>
138+
: <></>
139+
)
140+
}

app/tools/paper-graph/Search.tsx

Lines changed: 38 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import { useRef, useState } from 'react';
44
import {
5-
sleep,
65
searchFromSemanticScholar,
76
fetchFromSemanticScholar,
87
addToNotionDatabase,
@@ -20,10 +19,10 @@ type Suggestion = {
2019
};
2120

2221
export default function Search({
23-
knownIds,
22+
knownPapers,
2423
onSelect,
2524
}: {
26-
knownIds: Set<string>;
25+
knownPapers: Paper[];
2726
onSelect: (paper: any) => void;
2827
}) {
2928
const [modalOpen, setModalOpen] = useState(false);
@@ -36,6 +35,7 @@ export default function Search({
3635
const ref = useRef<HTMLDivElement>(null);
3736
useClickOutside(ref, () => setModalOpen(false));
3837

38+
const knownIds = new Set(knownPapers.map(p => p.id))
3939

4040
async function handleSearch() {
4141
if (!query.trim()) {
@@ -54,67 +54,61 @@ export default function Search({
5454

5555
setQuery('');
5656
setSuggestions([]);
57-
setStatus('Fetching selected paper...');
57+
setStatus('Fetching paper...');
5858
const paper = await fetchFromSemanticScholar(id);
5959

60-
setStatus('Adding selected paper to database...');
61-
const returnedPaper = await addToNotionDatabase({ ...paper, status: 'shown' });
62-
if (!returnedPaper) return;
63-
64-
knownIds.add(paper.paperId);
65-
66-
returnedPaper.authors = paper.authors.map((a: any) => a.name);
67-
onSelect(returnedPaper);
68-
69-
const referenceIds: string[] = [];
70-
const citationIds: string[] = [];
71-
72-
setStatus('Adding references...');
73-
for (const [index, ref] of (paper.references ?? []).entries()) {
74-
if (ref.paperId && !knownIds.has(ref.paperId)) {
75-
const returnedRef = await addToNotionDatabase({ ...ref, status: 'suggested' });
76-
if (!returnedRef) continue;
77-
knownIds.add(ref.paperId);
78-
referenceIds.push(returnedRef.id);
79-
await sleep(200);
80-
setStatus(`Added references ${index + 1}/${paper.references.length}...`);
81-
}
60+
setStatus('Adding paper to database...');
61+
const returnedPaper = await addToNotionDatabase(paper) as Paper
62+
if (!returnedPaper) {
63+
setStatus('Failed to add paper to database.');
64+
return;
8265
}
8366

84-
setStatus('Adding citations...');
85-
for (const [index, cite] of (paper.citations ?? []).entries()) {
86-
if (cite.paperId && !knownIds.has(cite.paperId)) {
87-
const returnedCite = await addToNotionDatabase({ ...cite, status: 'suggested' });
88-
if (!returnedCite) continue;
89-
knownIds.add(cite.paperId);
90-
citationIds.push(returnedCite.id);
91-
await sleep(2000);
92-
setStatus(`Added citations ${index + 1}/${paper.citations.length}...`);
67+
returnedPaper.authors = paper.authors
68+
returnedPaper.references = []
69+
returnedPaper.citations = []
70+
71+
72+
setStatus('Adjusting links...')
73+
const referencesScidSet = new Set(paper.references?.map(ref => ref.scid))
74+
const citationsScidSet = new Set(paper.citations?.map(ref => ref.scid))
75+
for (const notionPaper of knownPapers) {
76+
if (referencesScidSet.has(notionPaper.scid)
77+
|| notionPaper.citationScids.includes(paper.scid)) {
78+
await updateNotionPage(notionPaper.id, {
79+
citations: [...notionPaper.references ?? [], { id: returnedPaper.id }],
80+
})
81+
returnedPaper.references?.push({ id: notionPaper.id } as any)
82+
}
83+
if (citationsScidSet.has(notionPaper.scid)
84+
|| notionPaper.referenceScids.includes(paper.scid)) {
85+
await updateNotionPage(notionPaper.id, {
86+
references: [...notionPaper.citations ?? [], { id: returnedPaper.id }],
87+
})
88+
returnedPaper.citations?.push({ id: notionPaper.id } as any)
9389
}
9490
}
9591

96-
await updateNotionPage(returnedPaper.id, {
97-
references: referenceIds,
98-
citations: citationIds,
99-
});
92+
onSelect(returnedPaper);
93+
knownIds.add(paper.id);
94+
knownPapers.push(returnedPaper);
10095

10196
setStatus('');
10297
setModalOpen(false);
10398
}
10499

105100
return (
106101
<>
107-
<div className='search flex justify-between'>
102+
<div className='search flex items-center justify-between'>
108103
<button
109104
onClick={() => setModalOpen(true)}
110105
className="p-2 rounded-full bg-gray-100 hover:bg-gray-200"
111106
>
112107
<SearchIcon className="w-5 h-5 text-gray-700" />
113108
</button>
114-
{status !== "" &&
115-
<span>
116-
{status}
117-
</span>}
109+
<span>
110+
{status}
111+
</span>
118112
</div>
119113

120114

0 commit comments

Comments
 (0)