import React from 'react'; import Card from '@mui/material/Card'; import CardContent from '@mui/material/CardContent'; import Typography from '@mui/material/Typography'; import Link from '@mui/material/Link'; import './SourcePopup.css'; // Helper function to extract a friendly domain name from a URL. const getDomainName = (url) => { try { if (!url) return 'Unknown Source'; const hostname = new URL(url).hostname; const domain = hostname.startsWith('www.') ? hostname.slice(4) : hostname; const parts = domain.split('.'); return parts[0].charAt(0).toUpperCase() + parts[0].slice(1); } catch (err) { console.error("Error parsing URL for domain name:", url, err); return 'Invalid URL'; } }; // Helper function for Levenshtein distance calculation function levenshtein(a, b) { if (a.length === 0) return b.length; if (b.length === 0) return a.length; const matrix = []; for (let i = 0; i <= b.length; i++) matrix[i] = [i]; for (let j = 0; j <= a.length; j++) matrix[0][j] = j; for (let i = 1; i <= b.length; i++) { for (let j = 1; j <= a.length; j++) { if (b.charAt(i - 1) === a.charAt(j - 1)) { matrix[i][j] = matrix[i - 1][j - 1]; } else { matrix[i][j] = Math.min( matrix[i - 1][j - 1] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j] + 1 ); } } } return matrix[b.length][a.length]; } // SourcePopup component to display source information and excerpts function SourcePopup({ sourceData, excerptsData, position, onMouseEnter, onMouseLeave, statementText }) { if (!sourceData || !position) return null; const domain = getDomainName(sourceData.link); let hostname = ''; try { hostname = sourceData.link ? new URL(sourceData.link).hostname : ''; } catch (err) { hostname = sourceData.link || ''; // Fallback to link if URL parsing fails } let displayExcerpt = null; const sourceIdStr = String(sourceData.id); // Find the relevant excerpt if (excerptsData && Array.isArray(excerptsData) && statementText) { let foundExcerpt = null; let foundByFuzzy = false; const norm = s => s.replace(/\s+/g, ' ').trim(); const lower = s => norm(s).toLowerCase(); const statementNorm = norm(statementText); const statementLower = lower(statementText); console.log(`[SourcePopup] Searching for excerpt for source ID ${sourceIdStr}: ${statementText}`); // Iterate through the list of statement-to-excerpt mappings for (const entry of excerptsData) { const [thisStatement, sourcesMap] = Object.entries(entry)[0]; const thisNorm = norm(thisStatement); const thisLower = lower(thisStatement); console.log(`[SourcePopup] Checking against statement: ${thisStatement}`); // Normalized exact match if (thisNorm === statementNorm && sourcesMap && sourceIdStr in sourcesMap) { foundExcerpt = sourcesMap[sourceIdStr]; break; } // Case-insensitive match if (thisLower === statementLower && sourcesMap && sourceIdStr in sourcesMap) { foundExcerpt = sourcesMap[sourceIdStr]; break; } // Substring containment if ( (statementNorm && thisNorm && statementNorm.includes(thisNorm)) || (thisNorm && statementNorm && thisNorm.includes(statementNorm)) ) { if (sourcesMap && sourceIdStr in sourcesMap) { foundExcerpt = sourcesMap[sourceIdStr]; foundByFuzzy = true; break; } } // Levenshtein distance if ( levenshtein(statementNorm, thisNorm) <= 5 && sourcesMap && sourceIdStr in sourcesMap ) { foundExcerpt = sourcesMap[sourceIdStr]; foundByFuzzy = true; break; } } // Set displayExcerpt based on what was found if (foundExcerpt && foundExcerpt.toLowerCase() !== 'excerpt not found') { if (foundByFuzzy) { // Fuzzy match found an excerpt console.log("[SourcePopup] Fuzzy match found an excerpt:", foundExcerpt); } else { // Exact match found an excerpt console.log("[SourcePopup] Exact match found an excerpt:", foundExcerpt); } // Exact match found an excerpt displayExcerpt = foundExcerpt; } else if (foundExcerpt) { // Handle case where LLM explicitly said "Excerpt not found" displayExcerpt = "Relevant excerpt could not be automatically extracted."; console.log("[SourcePopup] Excerpt marked as not found or invalid type:", foundExcerpt); } else { // Excerpt for this specific source ID wasn't found in the loaded data displayExcerpt = "Excerpt not found for this citation."; console.log(`[SourcePopup] Excerpt not found for source ID ${sourceIdStr}: ${statementText}`); } } return (