import React, { useState, useEffect, useMemo } from 'react'; import { motion, AnimatePresence } from 'motion/react'; import { Network, Lightbulb, Layers, Sparkles, ArrowRight, RefreshCw, Trophy, Zap, Tag, Link as LinkIcon } from 'lucide-react'; import { Note, NoteCluster, BridgeNote, ConnectionSuggestion } from '../types'; import { runClustering, detectBridges, calculateCentroid } from '../services/clusteringService'; import { nameCluster, suggestBridgeIdeas } from '../services/geminiService'; import { NetworkGraph } from './NetworkGraph'; interface InsightsViewProps { notes: Note[]; onUpdateNotes: (updatedNotes: Note[]) => void; onNoteSelect: (noteId: string) => void; } export const InsightsView: React.FC = ({ notes, onUpdateNotes, onNoteSelect }) => { const [isCalculating, setIsCalculating] = useState(false); const [clusters, setClusters] = useState([]); const [bridgeNotes, setBridgeNotes] = useState([]); const [suggestions, setSuggestions] = useState([]); const [selectedClusterId, setSelectedClusterId] = useState(null); const performAnalysis = async () => { setIsCalculating(true); try { // 1. Run clustering const { clusters: newClusters } = runClustering(notes); // 2. Name clusters (first 5 unique notes per cluster) const namedClusters = await Promise.all(newClusters.map(async (c) => { const clusterNoteSummaries = notes .filter(n => c.noteIds.includes(n.id)) .slice(0, 5) .map(n => n.title); const name = await nameCluster(clusterNoteSummaries); const centroid = calculateCentroid(c.noteIds, notes); return { ...c, name, centroid }; })); // 3. Update notes with cluster IDs const updatedNotes = notes.map(n => { const cluster = namedClusters.find(c => c.noteIds.includes(n.id)); return { ...n, clusterId: cluster?.id }; }); onUpdateNotes(updatedNotes); // 4. Detect bridges const bridges = detectBridges(updatedNotes, namedClusters); // 5. Build suggestions for isolated cluster pairs // For demo, we'll just pick a few interesting pairs const newSuggestions: ConnectionSuggestion[] = []; if (namedClusters.length >= 2) { // Find clusters with no mutual bridge notes or low connectivity for (let i = 0; i < Math.min(namedClusters.length, 3); i++) { for (let j = i + 1; j < Math.min(namedClusters.length, 3); j++) { const cA = namedClusters[i]; const cB = namedClusters[j]; const cA_notes = updatedNotes.filter(n => cA.noteIds.includes(n.id)).map(n => n.title).join(', '); const cB_notes = updatedNotes.filter(n => cB.noteIds.includes(n.id)).map(n => n.title).join(', '); const bridgeIdeas = await suggestBridgeIdeas(cA.name, cB.name, cA_notes, cB_notes); bridgeIdeas.forEach((idea, idx) => { newSuggestions.push({ id: `suggestion-${i}-${j}-${idx}`, ...idea, clusterAId: cA.id, clusterBId: cB.id }); }); } } } setClusters(namedClusters); setBridgeNotes(bridges); setSuggestions(newSuggestions); } catch (error) { console.error("Analysis failed:", error); } finally { setIsCalculating(false); } }; useEffect(() => { if (notes.some(n => n.embedding) && clusters.length === 0) { performAnalysis(); } }, [notes]); const bridgeList = useMemo(() => { return bridgeNotes.map(b => { const note = notes.find(n => n.id === b.noteId); return { ...b, title: note?.title || 'Unknown Note' }; }); }, [bridgeNotes, notes]); return (
{/* Header */}

Semantic Insights

Discovering the hidden architecture of your knowledge

{/* Left: Graph View */}
{/* Right: Insight Dashboard */}
{/* Stats Summary */}
Clusters
{clusters.length}
Bridge Notes
{bridgeNotes.length}
{/* Bridge Notes Section */}

Powerful Bridge Notes

{bridgeList.map(bridge => ( onNoteSelect(bridge.noteId)} className="p-4 rounded-xl bg-white dark:bg-white/5 border border-border hover:border-ochre/40 transition-all cursor-pointer group" >

{bridge.title}

Score: {(bridge.bridgeScore * 100).toFixed(0)}%
{bridge.connectedClusterIds.map(cid => { const c = clusters.find(cl => cl.id === cid); return (
{c?.name}
); })}
))} {bridgeList.length === 0 && !isCalculating && (
No significant bridge notes found yet. Deepen your research to find new connections.
)}
{/* Connection Suggestions */}

Missing Links (AI Generated)

{suggestions.map((s, idx) => (
A
B
Bridging {clusters.find(c => c.id === s.clusterAId)?.name} & {clusters.find(c => c.id === s.clusterBId)?.name}

{s.title}

{s.description}

{s.reasoning}
))} {isCalculating && (
{[1, 2].map(i => (
))}
)}
); };