@johndimm/constellations 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/App.tsx +480 -0
- package/FullPageConstellations.tsx +74 -0
- package/FullPageConstellationsHostShell.tsx +27 -0
- package/README.md +116 -0
- package/components/AppConfirmDialog.tsx +46 -0
- package/components/AppHeader.tsx +73 -0
- package/components/AppNotifications.tsx +21 -0
- package/components/BrowsePeople.tsx +832 -0
- package/components/ControlPanel.tsx +1023 -0
- package/components/Graph.tsx +1525 -0
- package/components/HelpOverlay.tsx +168 -0
- package/components/NodeContextMenu.tsx +160 -0
- package/components/PeopleBrowserSidebar.tsx +690 -0
- package/components/Sidebar.tsx +271 -0
- package/components/TimelineView.tsx +4 -0
- package/hooks/useExpansion.ts +889 -0
- package/hooks/useGraphActions.ts +325 -0
- package/hooks/useGraphState.ts +414 -0
- package/hooks/useKioskMode.ts +47 -0
- package/hooks/useNodeClickHandler.ts +172 -0
- package/hooks/useSearchHandlers.ts +369 -0
- package/host.ts +16 -0
- package/index.css +101 -0
- package/index.tsx +16 -0
- package/kioskDomains.ts +307 -0
- package/package.json +78 -0
- package/services/aiUtils.ts +364 -0
- package/services/cacheService.ts +76 -0
- package/services/crossrefService.ts +107 -0
- package/services/geminiService.ts +1359 -0
- package/services/get-local-graphs.js +5 -0
- package/services/graphUtils.ts +347 -0
- package/services/imageService.ts +39 -0
- package/services/llmClient.ts +194 -0
- package/services/openAlexService.ts +173 -0
- package/services/wikipediaImage.ts +40 -0
- package/services/wikipediaService.ts +1175 -0
- package/sessionHandoff.ts +132 -0
- package/types.ts +99 -0
- package/useFullPageConstellationsHost.ts +116 -0
- package/utils/evidenceUtils.ts +107 -0
- package/utils/graphLogicUtils.ts +32 -0
- package/utils/graphNodeToChannelNotes.ts +71 -0
- package/utils/wikiUtils.ts +34 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { HelpCircle, X, Link as LinkIcon } from 'lucide-react';
|
|
3
|
+
|
|
4
|
+
interface HelpOverlayProps {
|
|
5
|
+
isOpen: boolean;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
onHelpHoverChange?: (value: string | null) => void;
|
|
8
|
+
onOpenPeopleBrowser?: () => void;
|
|
9
|
+
isExtension?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const HelpOverlay: React.FC<HelpOverlayProps> = ({
|
|
13
|
+
isOpen,
|
|
14
|
+
onClose,
|
|
15
|
+
onHelpHoverChange = (_value: string | null) => { },
|
|
16
|
+
onOpenPeopleBrowser,
|
|
17
|
+
isExtension = false
|
|
18
|
+
}) => {
|
|
19
|
+
if (!isOpen) return null;
|
|
20
|
+
|
|
21
|
+
const handleHover = (val: string | null) => onHelpHoverChange(val);
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div className={`fixed ${isExtension ? 'top-20 left-6 right-6' : 'top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2'} z-[10000] max-w-lg w-full bg-slate-900/98 backdrop-blur-xl p-6 rounded-2xl border border-slate-700 shadow-[0_32px_80px_rgba(0,0,0,0.6)] animate-in fade-in zoom-in-95 duration-200 overflow-y-auto max-h-[85vh]`}>
|
|
25
|
+
<div className="flex justify-between items-center mb-5">
|
|
26
|
+
<h3 className="text-lg font-bold text-white flex items-center gap-2">
|
|
27
|
+
<HelpCircle className="text-indigo-400" size={20} /> Help & Info
|
|
28
|
+
</h3>
|
|
29
|
+
<button
|
|
30
|
+
onClick={onClose}
|
|
31
|
+
className="p-1.5 rounded-lg hover:bg-slate-800 text-slate-400 hover:text-white transition-colors"
|
|
32
|
+
>
|
|
33
|
+
<X size={20} />
|
|
34
|
+
</button>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<div className="space-y-6 text-sm text-slate-300">
|
|
38
|
+
<section>
|
|
39
|
+
<p className="text-slate-200 leading-relaxed">
|
|
40
|
+
<strong>Constellations</strong> is an interactive graph for exploring lives, events, and their hidden connections.
|
|
41
|
+
</p>
|
|
42
|
+
</section>
|
|
43
|
+
|
|
44
|
+
{isExtension && (
|
|
45
|
+
<section className="bg-indigo-900/40 rounded-xl p-4 border border-indigo-500/30">
|
|
46
|
+
<h4 className="text-xs font-bold uppercase tracking-widest text-indigo-400 mb-2">How to use</h4>
|
|
47
|
+
<p className="text-slate-300">
|
|
48
|
+
Select any text on a web page, right-click, and choose <strong className="text-white">Graph '<selection>' on Constellations</strong>.
|
|
49
|
+
</p>
|
|
50
|
+
</section>
|
|
51
|
+
)}
|
|
52
|
+
|
|
53
|
+
<section className="bg-slate-800/40 rounded-xl p-4 border border-slate-700/50">
|
|
54
|
+
<h4 className="text-xs font-bold uppercase tracking-widest text-slate-400 mb-3">Controls</h4>
|
|
55
|
+
<div className="grid grid-cols-[100px_1fr] gap-x-3 gap-y-2 text-xs leading-tight">
|
|
56
|
+
<span className="font-bold text-slate-100">Click</span>
|
|
57
|
+
<span className="text-slate-400">Select a node or connection</span>
|
|
58
|
+
|
|
59
|
+
<span className="font-bold text-slate-100">Dbl-Click</span>
|
|
60
|
+
<span className="text-slate-400">Open node context menu</span>
|
|
61
|
+
|
|
62
|
+
<span className="font-bold text-slate-100">Drag</span>
|
|
63
|
+
<span className="text-slate-400">Move nodes (Graph mode)</span>
|
|
64
|
+
|
|
65
|
+
<span className="font-bold text-slate-100">Scroll</span>
|
|
66
|
+
<span className="text-slate-400">Zoom in/out</span>
|
|
67
|
+
|
|
68
|
+
<span className="font-bold text-slate-100">Arrows</span>
|
|
69
|
+
<span className="text-slate-400">Move between timeline events</span>
|
|
70
|
+
</div>
|
|
71
|
+
</section>
|
|
72
|
+
|
|
73
|
+
{!isExtension && (
|
|
74
|
+
<section>
|
|
75
|
+
<h4 className="text-xs font-bold uppercase tracking-widest text-slate-400 mb-2">Features</h4>
|
|
76
|
+
<div className="space-y-2">
|
|
77
|
+
<p>
|
|
78
|
+
<strong>Browse People:</strong> Filter Wikipedia's database:{" "}
|
|
79
|
+
<button
|
|
80
|
+
className="text-indigo-400 hover:text-indigo-300 font-semibold underline"
|
|
81
|
+
onClick={(e) => { e.preventDefault(); if (onOpenPeopleBrowser) onOpenPeopleBrowser(); onClose(); }}
|
|
82
|
+
>
|
|
83
|
+
Open Browser
|
|
84
|
+
</button>
|
|
85
|
+
</p>
|
|
86
|
+
<ul className="list-disc pl-5 space-y-1 text-slate-400">
|
|
87
|
+
<li><strong>Explore:</strong> Search and expand connections.</li>
|
|
88
|
+
<li><strong>Connect:</strong> Find paths between two entities.</li>
|
|
89
|
+
</ul>
|
|
90
|
+
</div>
|
|
91
|
+
</section>
|
|
92
|
+
)}
|
|
93
|
+
|
|
94
|
+
<section className="pt-4 border-t border-slate-800 space-y-3">
|
|
95
|
+
<a
|
|
96
|
+
href="/doc/journal.html"
|
|
97
|
+
target="_blank"
|
|
98
|
+
rel="noopener noreferrer"
|
|
99
|
+
className="flex items-center gap-2 text-slate-300 hover:text-white transition-colors"
|
|
100
|
+
>
|
|
101
|
+
<LinkIcon size={14} className="text-slate-500" />
|
|
102
|
+
<span>Development Journal</span>
|
|
103
|
+
</a>
|
|
104
|
+
<a
|
|
105
|
+
href="/doc/prompt.html"
|
|
106
|
+
target="_blank"
|
|
107
|
+
rel="noopener noreferrer"
|
|
108
|
+
className="flex items-center gap-2 text-slate-300 hover:text-white transition-colors"
|
|
109
|
+
>
|
|
110
|
+
<LinkIcon size={14} className="text-slate-500" />
|
|
111
|
+
<span>Regeneration Prompt</span>
|
|
112
|
+
</a>
|
|
113
|
+
<a
|
|
114
|
+
href="/doc/api_queries.html"
|
|
115
|
+
target="_blank"
|
|
116
|
+
rel="noopener noreferrer"
|
|
117
|
+
className="flex items-center gap-2 text-slate-300 hover:text-white transition-colors"
|
|
118
|
+
>
|
|
119
|
+
<LinkIcon size={14} className="text-slate-500" />
|
|
120
|
+
<span>Example API queries</span>
|
|
121
|
+
</a>
|
|
122
|
+
<a
|
|
123
|
+
href="/paper/rendered/paper.pdf"
|
|
124
|
+
target="_blank"
|
|
125
|
+
rel="noopener noreferrer"
|
|
126
|
+
className="flex items-center gap-2 text-slate-300 hover:text-white transition-colors"
|
|
127
|
+
>
|
|
128
|
+
<LinkIcon size={14} className="text-slate-500" />
|
|
129
|
+
<span>Technical Paper (PDF)</span>
|
|
130
|
+
</a>
|
|
131
|
+
<a
|
|
132
|
+
href="https://www.linkedin.com/in/johndimm/"
|
|
133
|
+
target="_blank"
|
|
134
|
+
rel="noopener noreferrer"
|
|
135
|
+
className="flex items-center gap-2 text-amber-500 hover:text-amber-400 transition-colors"
|
|
136
|
+
>
|
|
137
|
+
<LinkIcon size={14} />
|
|
138
|
+
<span>Follow John Dimm on LinkedIn</span>
|
|
139
|
+
</a>
|
|
140
|
+
|
|
141
|
+
{isExtension ? (
|
|
142
|
+
<a
|
|
143
|
+
href="https://constellations-delta.vercel.app"
|
|
144
|
+
target="_blank"
|
|
145
|
+
rel="noopener noreferrer"
|
|
146
|
+
className="flex items-center gap-2 text-indigo-400 hover:text-indigo-300 transition-colors"
|
|
147
|
+
>
|
|
148
|
+
<LinkIcon size={14} />
|
|
149
|
+
<span>Visit Standalone Website</span>
|
|
150
|
+
</a>
|
|
151
|
+
) : (
|
|
152
|
+
<a
|
|
153
|
+
href="https://chromewebstore.google.com/detail/nphipbpoephgjgapmeanccnaikljggkg"
|
|
154
|
+
target="_blank"
|
|
155
|
+
rel="noopener noreferrer"
|
|
156
|
+
className="flex items-center gap-2 text-indigo-400 hover:text-indigo-300 transition-colors"
|
|
157
|
+
>
|
|
158
|
+
<LinkIcon size={14} />
|
|
159
|
+
<span>Install Chrome Extension</span>
|
|
160
|
+
</a>
|
|
161
|
+
)}
|
|
162
|
+
</section>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
);
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export default HelpOverlay;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { GraphNode } from '../types';
|
|
3
|
+
import { Maximize, Plus, Sparkles, Trash2 } from 'lucide-react';
|
|
4
|
+
|
|
5
|
+
interface NodeContextMenuProps {
|
|
6
|
+
node: GraphNode;
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
onExpandLeaves: (node: GraphNode) => void;
|
|
10
|
+
onAddMore: (node: GraphNode) => void;
|
|
11
|
+
onFindBetterPhoto: (nodeId: number) => void;
|
|
12
|
+
onDelete: (node: GraphNode) => void;
|
|
13
|
+
onClose: () => void;
|
|
14
|
+
isProcessing?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const NodeContextMenu: React.FC<NodeContextMenuProps> = ({
|
|
18
|
+
node,
|
|
19
|
+
x,
|
|
20
|
+
y,
|
|
21
|
+
onExpandLeaves,
|
|
22
|
+
onAddMore,
|
|
23
|
+
onFindBetterPhoto,
|
|
24
|
+
onDelete,
|
|
25
|
+
onClose,
|
|
26
|
+
isProcessing
|
|
27
|
+
}) => {
|
|
28
|
+
const handleAction = (action: () => void) => {
|
|
29
|
+
action();
|
|
30
|
+
onClose();
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Calculate position to keep menu on screen
|
|
34
|
+
const menuWidth = 220;
|
|
35
|
+
const menuHeight = 180;
|
|
36
|
+
const adjustedX = Math.min(x, window.innerWidth - menuWidth - 20);
|
|
37
|
+
const adjustedY = Math.min(y, window.innerHeight - menuHeight - 20);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<>
|
|
41
|
+
{/* Backdrop to close menu on click outside */}
|
|
42
|
+
<div
|
|
43
|
+
className="fixed inset-0 z-40"
|
|
44
|
+
style={{ position: 'fixed', inset: 0, zIndex: 999998 }}
|
|
45
|
+
onClick={onClose}
|
|
46
|
+
/>
|
|
47
|
+
|
|
48
|
+
{/* Context Menu */}
|
|
49
|
+
<div
|
|
50
|
+
className="fixed z-50"
|
|
51
|
+
style={{
|
|
52
|
+
position: 'fixed',
|
|
53
|
+
zIndex: 999999,
|
|
54
|
+
left: `${adjustedX}px`,
|
|
55
|
+
top: `${adjustedY}px`,
|
|
56
|
+
minWidth: '220px',
|
|
57
|
+
padding: '8px',
|
|
58
|
+
borderRadius: '10px',
|
|
59
|
+
backgroundColor: 'rgba(15, 23, 42, 0.98)',
|
|
60
|
+
border: '1px solid #334155',
|
|
61
|
+
boxShadow: '0 20px 45px rgba(0,0,0,0.35)',
|
|
62
|
+
display: 'flex',
|
|
63
|
+
flexDirection: 'column',
|
|
64
|
+
gap: '4px',
|
|
65
|
+
color: '#f8fafc'
|
|
66
|
+
}}
|
|
67
|
+
>
|
|
68
|
+
<button
|
|
69
|
+
onClick={() => handleAction(() => onExpandLeaves(node))}
|
|
70
|
+
disabled={isProcessing || !node.expanded}
|
|
71
|
+
className="disabled:opacity-50 disabled:cursor-not-allowed"
|
|
72
|
+
style={{
|
|
73
|
+
width: '100%',
|
|
74
|
+
padding: '8px 12px',
|
|
75
|
+
textAlign: 'left',
|
|
76
|
+
fontSize: '13px',
|
|
77
|
+
color: 'inherit',
|
|
78
|
+
background: 'transparent',
|
|
79
|
+
border: 'none',
|
|
80
|
+
display: 'flex',
|
|
81
|
+
alignItems: 'center',
|
|
82
|
+
gap: '10px',
|
|
83
|
+
cursor: (isProcessing || !node.expanded) ? 'not-allowed' : 'pointer'
|
|
84
|
+
}}
|
|
85
|
+
>
|
|
86
|
+
<Maximize size={16} className="text-emerald-400" />
|
|
87
|
+
<span>Expand Leaf Nodes</span>
|
|
88
|
+
</button>
|
|
89
|
+
|
|
90
|
+
<button
|
|
91
|
+
onClick={() => handleAction(() => onAddMore(node))}
|
|
92
|
+
disabled={isProcessing || !node.expanded}
|
|
93
|
+
className="disabled:opacity-50 disabled:cursor-not-allowed"
|
|
94
|
+
style={{
|
|
95
|
+
width: '100%',
|
|
96
|
+
padding: '8px 12px',
|
|
97
|
+
textAlign: 'left',
|
|
98
|
+
fontSize: '13px',
|
|
99
|
+
color: 'inherit',
|
|
100
|
+
background: 'transparent',
|
|
101
|
+
border: 'none',
|
|
102
|
+
display: 'flex',
|
|
103
|
+
alignItems: 'center',
|
|
104
|
+
gap: '10px',
|
|
105
|
+
cursor: (isProcessing || !node.expanded) ? 'not-allowed' : 'pointer'
|
|
106
|
+
}}
|
|
107
|
+
>
|
|
108
|
+
<Plus size={16} className="text-indigo-400" />
|
|
109
|
+
<span>Expand More</span>
|
|
110
|
+
</button>
|
|
111
|
+
|
|
112
|
+
<button
|
|
113
|
+
onClick={() => handleAction(() => onFindBetterPhoto(node.id))}
|
|
114
|
+
disabled={isProcessing}
|
|
115
|
+
className="disabled:opacity-50 disabled:cursor-not-allowed"
|
|
116
|
+
style={{
|
|
117
|
+
width: '100%',
|
|
118
|
+
padding: '8px 12px',
|
|
119
|
+
textAlign: 'left',
|
|
120
|
+
fontSize: '13px',
|
|
121
|
+
color: 'inherit',
|
|
122
|
+
background: 'transparent',
|
|
123
|
+
border: 'none',
|
|
124
|
+
display: 'flex',
|
|
125
|
+
alignItems: 'center',
|
|
126
|
+
gap: '10px',
|
|
127
|
+
cursor: isProcessing ? 'not-allowed' : 'pointer'
|
|
128
|
+
}}
|
|
129
|
+
>
|
|
130
|
+
<Sparkles size={16} className="text-amber-300" />
|
|
131
|
+
<span>Find Better Photo</span>
|
|
132
|
+
</button>
|
|
133
|
+
|
|
134
|
+
<div style={{ height: '1px', background: '#334155', margin: '6px 0' }} />
|
|
135
|
+
|
|
136
|
+
<button
|
|
137
|
+
onClick={() => handleAction(() => onDelete(node))}
|
|
138
|
+
style={{
|
|
139
|
+
width: '100%',
|
|
140
|
+
padding: '8px 12px',
|
|
141
|
+
textAlign: 'left',
|
|
142
|
+
fontSize: '13px',
|
|
143
|
+
color: '#f87171',
|
|
144
|
+
background: 'transparent',
|
|
145
|
+
border: 'none',
|
|
146
|
+
display: 'flex',
|
|
147
|
+
alignItems: 'center',
|
|
148
|
+
gap: '10px',
|
|
149
|
+
cursor: 'pointer'
|
|
150
|
+
}}
|
|
151
|
+
>
|
|
152
|
+
<Trash2 size={16} />
|
|
153
|
+
<span>Delete Node</span>
|
|
154
|
+
</button>
|
|
155
|
+
</div>
|
|
156
|
+
</>
|
|
157
|
+
);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export default NodeContextMenu;
|