// Theme Management class ThemeManager { constructor() { this.theme = localStorage.getItem('theme') || 'light'; this.init(); } init() { document.documentElement.setAttribute('data-theme', this.theme); this.updateThemeIcon(); } toggle() { this.theme = this.theme === 'light' ? 'dark' : 'light'; document.documentElement.setAttribute('data-theme', this.theme); localStorage.setItem('theme', this.theme); this.updateThemeIcon(); } updateThemeIcon() { const lightIcon = document.querySelector('.light-icon'); const darkIcon = document.querySelector('.dark-icon'); if (this.theme === 'light') { lightIcon.style.display = 'block'; darkIcon.style.display = 'none'; } else { lightIcon.style.display = 'none'; darkIcon.style.display = 'block'; } } } // Utility functions function getParam(name) { const url = new URL(window.location.href); return url.searchParams.get(name); } function esc(s) { return String(s).replace(/[&<>"]/g, (c) => ({'&':'&','<':'<','>':'>','"':'"'}[c])); } function parseMaybeJSON(str) { if (typeof str !== 'string') return str; try { return JSON.parse(str); } catch (e) { const start = str.indexOf('{'); const end = str.lastIndexOf('}'); if (start !== -1 && end !== -1 && end > start) { const sliced = str.slice(start, end + 1); try { return JSON.parse(sliced); } catch {} } } return str; } // Paper Evaluation Renderer class PaperEvaluationRenderer { constructor() { this.themeManager = new ThemeManager(); this.init(); } init() { this.bindEvents(); } bindEvents() { // Theme toggle const themeToggle = document.getElementById('themeToggle'); if (themeToggle) { themeToggle.addEventListener('click', () => { this.themeManager.toggle(); }); } } renderMetaGrid(meta, paperAuthors = '') { const metaGrid = document.getElementById('metaGrid'); if (!metaGrid) return; const metaItems = [ { label: 'Assessed At', value: meta.assessed_at || '-', icon: 'fas fa-calendar' }, { label: 'Model', value: meta.model || '-', icon: 'fas fa-robot' }, { label: 'Version', value: meta.version || '-', icon: 'fas fa-tag' }, { label: 'Authors', value: paperAuthors || '-', icon: 'fas fa-users' }, { label: 'Paper Path', value: meta.paper_path || '-', icon: 'fas fa-file-pdf', isLink: true } ]; metaGrid.innerHTML = metaItems.map(item => `
${item.label} ${item.isLink && item.value !== '-' ? `${esc(item.value)}` : esc(item.value) }
`).join(''); } renderDimensionCard(label, key, data, icon = 'fas fa-chart-bar') { const item = data[key] || {}; const score = item.score !== undefined ? item.score : null; const probability = item.probability_pct !== undefined ? item.probability_pct : null; const analysis = item.analysis || ''; const extras = []; if (Array.isArray(item.tools_models)) { extras.push(` Tools/Models: ${item.tools_models.map(esc).join(', ')}`); } if (item.coverage_pct_estimate !== undefined) { extras.push(` Coverage: ${esc(item.coverage_pct_estimate)}%`); } return `
${esc(label)}
${score !== null ? `${score}` : ''} ${probability !== null ? `${probability}%` : ''}
${extras.length > 0 ? `
${extras.join('')}
` : ''} ${analysis ? `
${esc(analysis).replace(/\n/g, '
')}
` : ''}
`; } async renderContent(json) { const contentEl = document.getElementById('content'); const titleEl = document.getElementById('title'); if (!contentEl || !titleEl) return; const meta = json.metadata || {}; const paperId = getParam('id'); // Fetch paper details from database let paperTitle = `Paper Evaluation - ${paperId}`; let paperAuthors = ''; let paperAbstract = ''; try { const response = await fetch(`/api/paper/${encodeURIComponent(paperId)}`); if (response.ok) { const paperData = await response.json(); if (paperData.title) { paperTitle = paperData.title; paperAuthors = paperData.authors || ''; paperAbstract = paperData.abstract || ''; } } } catch (error) { console.error('Error fetching paper details:', error); } // Update title with actual paper title titleEl.textContent = paperTitle; // Render meta grid with paper info this.renderMetaGrid(meta, paperAuthors); // Executive Summary - styled like Hugging Face abstract const execSummary = json.executive_summary ? `

Executive Summary

${esc(json.executive_summary)}

` : ''; // Dimensions - create beautiful cards const d = parseMaybeJSON(json.dimensions) || {}; const dims = [ ['Task Formalization', 'task_formalization', 'fas fa-tasks'], ['Data & Resource Availability', 'data_resource_availability', 'fas fa-database'], ['Input-Output Complexity', 'input_output_complexity', 'fas fa-exchange-alt'], ['Real-World Interaction', 'real_world_interaction', 'fas fa-globe'], ['Existing AI Coverage', 'existing_ai_coverage', 'fas fa-robot'], ['Automation Barriers', 'automation_barriers', 'fas fa-shield-alt'], ['Human Originality', 'human_originality', 'fas fa-lightbulb'], ['Safety & Ethics', 'safety_ethics', 'fas fa-balance-scale'], ['Societal/Economic Impact', 'societal_economic_impact', 'fas fa-chart-line'], ['Technical Maturity Needed', 'technical_maturity_needed', 'fas fa-cogs'], ['3-Year Feasibility', 'three_year_feasibility', 'fas fa-calendar-alt'], ['Overall Automatability', 'overall_automatability', 'fas fa-magic'], ]; const dimensionsHtml = dims.map(([label, key, icon]) => this.renderDimensionCard(label, key, d, icon) ).join(''); // Recommendations - styled sections const rec = json.recommendations || {}; const renderList = (arr) => { return Array.isArray(arr) && arr.length ? `` : '

No recommendations available.

'; }; const recommendationsHtml = `

Recommendations

For Researchers

${renderList(rec.for_researchers)}

For Institutions

${renderList(rec.for_institutions)}

For AI Development

${renderList(rec.for_ai_development)}
`; // Limitations const lim = Array.isArray(json.limitations_uncertainties) ? json.limitations_uncertainties : []; const limitationsHtml = `

Limitations & Uncertainties

${lim.length ? `
    ${lim.map(x => `
  • ${esc(x)}
  • `).join('')}
` : '

No limitations documented.

'}
`; // Add action buttons at the top const actionButtons = `

Evaluation Actions

`; contentEl.innerHTML = actionButtons + execSummary + `

Detailed Dimensional Analysis

${dimensionsHtml}
` + recommendationsHtml + limitationsHtml; } updateRadarChart(json) { const radarEl = document.getElementById('radar'); const legendEl = document.getElementById('radar-legend'); const overallScoreEl = document.getElementById('overallScore'); if (!radarEl) return; try { const score = json.scores || {}; const d = parseMaybeJSON(json.dimensions) || {}; const labels = [ 'Task Formalization', 'Data & Resources', 'Input-Output Complexity', 'Real-World Interaction', 'Existing AI Coverage', 'Human Originality', 'Safety & Ethics', 'Technical Maturity', '3-Year Feasibility', 'Overall Automatability', ]; const values = [ Number(score.task_formalization ?? d.task_formalization?.score ?? 0), Number(score.data_resource_availability ?? d.data_resource_availability?.score ?? 0), Number(score.input_output_complexity ?? d.input_output_complexity?.score ?? 0), Number(score.real_world_interaction ?? d.real_world_interaction?.score ?? 0), Number(score.existing_ai_coverage ?? d.existing_ai_coverage?.score ?? 0), Number(score.human_originality ?? d.human_originality?.score ?? 0), Number(score.safety_ethics ?? d.safety_ethics?.score ?? 0), Number(score.technical_maturity_needed ?? d.technical_maturity_needed?.score ?? 0), Number((score.three_year_feasibility_pct ?? d.three_year_feasibility?.probability_pct ?? 0) / 25), Number(score.overall_automatability ?? d.overall_automatability?.score ?? 0), ]; // Calculate overall score const validScores = values.filter(v => v > 0); const overallScore = validScores.length > 0 ? (validScores.reduce((a, b) => a + b, 0) / validScores.length).toFixed(1) : '-'; if (overallScoreEl) { overallScoreEl.innerHTML = ` ${overallScore} Overall `; } this.drawRadar(radarEl, labels, values, 4); this.setupRadarInteractions(radarEl); if (legendEl) { const labelConfig = [ { abbr: 'TASK', color: '#3b82f6', full: 'Task Formalization' }, { abbr: 'DATA', color: '#10b981', full: 'Data & Resources' }, { abbr: 'I/O', color: '#f59e0b', full: 'Input-Output Complexity' }, { abbr: 'REAL', color: '#8b5cf6', full: 'Real-World Interaction' }, { abbr: 'AI', color: '#ef4444', full: 'Existing AI Coverage' }, { abbr: 'HUMAN', color: '#06b6d4', full: 'Human Originality' }, { abbr: 'SAFETY', color: '#84cc16', full: 'Safety & Ethics' }, { abbr: 'TECH', color: '#f97316', full: 'Technical Maturity' }, { abbr: '3YR', color: '#ec4899', full: '3-Year Feasibility' }, { abbr: 'AUTO', color: '#6366f1', full: 'Overall Automatability' } ]; legendEl.innerHTML = ` `; } } catch (error) { console.error('Error updating radar chart:', error); } } setupRadarInteractions(canvas) { if (!canvas || !this.dotPositions) return; // Create tooltip element let tooltip = document.getElementById('radar-tooltip'); if (!tooltip) { tooltip = document.createElement('div'); tooltip.id = 'radar-tooltip'; tooltip.className = 'radar-tooltip'; document.body.appendChild(tooltip); } const handleMouseMove = (e) => { const rect = canvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; // Check if mouse is near any dot let hoveredDot = null; const hoverRadius = 15; for (const dot of this.dotPositions) { const distance = Math.sqrt((x - dot.x) ** 2 + (y - dot.y) ** 2); if (distance <= hoverRadius) { hoveredDot = dot; break; } } if (hoveredDot) { tooltip.style.display = 'block'; tooltip.style.left = e.clientX + 10 + 'px'; tooltip.style.top = e.clientY - 30 + 'px'; tooltip.innerHTML = `
${hoveredDot.config.abbr} ${hoveredDot.config.full || ''}
`; canvas.style.cursor = 'pointer'; } else { tooltip.style.display = 'none'; canvas.style.cursor = 'default'; } }; const handleMouseLeave = () => { tooltip.style.display = 'none'; canvas.style.cursor = 'default'; }; // Remove existing listeners canvas.removeEventListener('mousemove', handleMouseMove); canvas.removeEventListener('mouseleave', handleMouseLeave); // Add new listeners canvas.addEventListener('mousemove', handleMouseMove); canvas.addEventListener('mouseleave', handleMouseLeave); } drawRadar(canvas, labels, values, maxValue) { if (!canvas || !canvas.getContext) return; const ctx = canvas.getContext('2d'); const w = canvas.width, h = canvas.height; ctx.clearRect(0, 0, w, h); const cx = w / 2, cy = h / 2, radius = Math.min(w, h) * 0.42; const n = values.length; const angleStep = (Math.PI * 2) / n; // Get theme colors const isDark = document.documentElement.getAttribute('data-theme') === 'dark'; const gridColor = isDark ? '#475569' : '#e2e8f0'; const axisColor = isDark ? '#64748b' : '#cbd5e1'; const fillColor = isDark ? 'rgba(34, 211, 238, 0.2)' : 'rgba(59, 130, 246, 0.2)'; const strokeColor = isDark ? '#22d3ee' : '#3b82f6'; const textColor = isDark ? '#e2e8f0' : '#475569'; // Define colors and abbreviations for each dimension const labelConfig = [ { color: '#3b82f6', abbr: 'TASK' }, // Task Formalization { color: '#10b981', abbr: 'DATA' }, // Data & Resources { color: '#f59e0b', abbr: 'I/O' }, // Input-Output Complexity { color: '#8b5cf6', abbr: 'REAL' }, // Real-World Interaction { color: '#ef4444', abbr: 'AI' }, // Existing AI Coverage { color: '#06b6d4', abbr: 'HUMAN' }, // Human Originality { color: '#84cc16', abbr: 'SAFETY' }, // Safety & Ethics { color: '#f97316', abbr: 'TECH' }, // Technical Maturity { color: '#ec4899', abbr: '3YR' }, // 3-Year Feasibility { color: '#6366f1', abbr: 'AUTO' } // Overall Automatability ]; // Store dot positions for mouse interaction this.dotPositions = []; // Draw grid (5 rings) ctx.strokeStyle = gridColor; ctx.lineWidth = 1; for (let r = 1; r <= 5; r++) { const rr = (radius * r) / 5; ctx.beginPath(); for (let i = 0; i < n; i++) { const ang = i * angleStep - Math.PI / 2; const x = cx + rr * Math.cos(ang); const y = cy + rr * Math.sin(ang); if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); } ctx.closePath(); ctx.stroke(); } // Draw axes ctx.strokeStyle = axisColor; for (let i = 0; i < n; i++) { const ang = i * angleStep - Math.PI / 2; const x = cx + radius * Math.cos(ang); const y = cy + radius * Math.sin(ang); ctx.beginPath(); ctx.moveTo(cx, cy); ctx.lineTo(x, y); ctx.stroke(); } // Draw polygon ctx.fillStyle = fillColor; ctx.strokeStyle = strokeColor; ctx.lineWidth = 2; ctx.beginPath(); for (let i = 0; i < n; i++) { const v = Math.max(0, Math.min(maxValue, Number(values[i] || 0))); const r = (v / maxValue) * radius; const ang = i * angleStep - Math.PI / 2; const x = cx + r * Math.cos(ang); const y = cy + r * Math.sin(ang); if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); } ctx.closePath(); ctx.fill(); ctx.stroke(); // Draw axis endpoints with colored dots and store positions for (let i = 0; i < n; i++) { const ang = i * angleStep - Math.PI / 2; const x = cx + radius * Math.cos(ang); const y = cy + radius * Math.sin(ang); // Store position for mouse interaction this.dotPositions[i] = { x, y, index: i, config: labelConfig[i] }; // Draw colored dot at the end of each axis const dotRadius = 6; // Slightly larger for better hover detection ctx.fillStyle = labelConfig[i]?.color || textColor; ctx.beginPath(); ctx.arc(x, y, dotRadius, 0, Math.PI * 2); ctx.fill(); // Draw white border around dot ctx.strokeStyle = isDark ? '#1e293b' : '#ffffff'; ctx.lineWidth = 2; ctx.stroke(); } } async render(json) { await this.renderContent(json); this.updateRadarChart(json); } } // Main Application class PaperEvaluationApp { constructor() { this.renderer = new PaperEvaluationRenderer(); this.paperId = getParam('id'); this.init(); } async init() { const id = getParam('id'); console.log('PaperEvaluationApp init with ID:', id); if (!id) { const contentEl = document.getElementById('content'); if (contentEl) { contentEl.innerHTML = `

Missing Paper ID

Please provide a valid paper ID in the URL.

`; } return; } try { console.log('Fetching evaluation for ID:', id); const response = await fetch(`/api/eval/${encodeURIComponent(id)}`); console.log('Response status:', response.status); if (!response.ok) { throw new Error(`Evaluation not found: ${response.status}`); } const json = await response.json(); console.log('Received JSON data:', Object.keys(json)); // Fix stringified dimensions if (json && typeof json.dimensions === 'string') { try { json.dimensions = JSON.parse(json.dimensions); console.log('Successfully parsed dimensions JSON'); } catch (e) { console.warn('Failed to parse dimensions JSON:', e); } } console.log('Rendering evaluation...'); await this.renderer.render(json); console.log('Evaluation rendered successfully'); } catch (error) { console.error('Error loading evaluation:', error); const contentEl = document.getElementById('content'); if (contentEl) { contentEl.innerHTML = `

Evaluation Not Found

The requested evaluation could not be loaded: ${error.message}

Back to Daily Papers
`; } } } } // Initialize the application when DOM is loaded document.addEventListener('DOMContentLoaded', () => { window.paperApp = new PaperEvaluationApp(); });