ai-research-survey

Systematic scan of agentic development research. What's signal, what's noise.
git clone https://git.shiptheloop.com/ai-research-survey.git
Log | Files | Refs

papers.ts (4597B)


      1 import { loadDashboard, loadPapersIndex, type PaperIndex } from '../data';
      2 import { navigate } from '../router';
      3 import { createFilters, updateFilterCount, type FilterState } from '../components/search-filter';
      4 import { renderSortableTable, type Column } from '../components/table';
      5 
      6 const DNA_CATS = [
      7   'artifacts', 'stat_method', 'eval_design',
      8   'claims', 'setup', 'limitations',
      9   'data_integrity', 'conflicts', 'contamination',
     10   'human', 'cost',
     11 ];
     12 
     13 const ENG_DIMS = [
     14   'practical', 'surprise', 'fear', 'drama', 'demo', 'brand',
     15 ];
     16 
     17 function scoreColor(s: number): string {
     18   if (s < 30) return 'var(--red)';
     19   if (s < 50) return 'var(--yellow)';
     20   if (s < 70) return 'var(--accent)';
     21   return 'var(--green)';
     22 }
     23 
     24 function dnaCellColor(v: number | null): string {
     25   if (v === null) return 'var(--border)';
     26   if (v < 25) return '#d03030';
     27   if (v < 50) return '#c08020';
     28   if (v < 75) return '#4070cc';
     29   return '#20a060';
     30 }
     31 
     32 function engCellColor(v: number | null): string {
     33   if (v === null) return 'var(--border)';
     34   if (v === 0) return '#555';
     35   if (v === 1) return '#7060a0';
     36   if (v === 2) return '#9070d0';
     37   return '#b080ff';
     38 }
     39 
     40 function renderDna(dna: (number | null)[] | null, engagement: (number | null)[] | null): string {
     41   if (!dna) return '';
     42   const methodCells = dna.map((v, i) =>
     43     `<span class="dna-cell" style="background:${dnaCellColor(v)}" title="${DNA_CATS[i]}: ${v !== null ? v + '%' : 'N/A'}"></span>`
     44   ).join('');
     45   const engCells = engagement ? engagement.map((v, i) =>
     46     `<span class="dna-cell" style="background:${engCellColor(v)}" title="${ENG_DIMS[i]}: ${v !== null ? v + '/3' : '?'}"></span>`
     47   ).join('') : '';
     48   return `<span class="dna-strip">${methodCells}</span>${engCells ? `<span class="dna-strip" style="margin-left:3px">${engCells}</span>` : ''}`;
     49 }
     50 
     51 export async function renderPapers(app: HTMLElement) {
     52   app.innerHTML = '<div class="spinner"></div>';
     53   const [papers, dashboard] = await Promise.all([loadPapersIndex(), loadDashboard()]);
     54 
     55   app.innerHTML = '';
     56   const archetypes = [...new Set(papers.map(p => p.archetype).filter((a): a is string => a != null))].sort();
     57   const tags = Object.keys(dashboard.tag_counts).sort();
     58 
     59   let filtered = [...papers];
     60 
     61   const filtersEl = createFilters(archetypes, tags, (state: FilterState) => {
     62     filtered = papers.filter(p => {
     63       if (state.search && !p.title.toLowerCase().includes(state.search)) return false;
     64       if (state.year && p.year !== parseInt(state.year)) return false;
     65       if (state.archetype && p.archetype !== state.archetype) return false;
     66       if (state.tag && !p.tags.includes(state.tag)) return false;
     67       if (p.score != null && (p.score < state.minScore || p.score > state.maxScore)) return false;
     68       if (p.score == null && state.minScore > 0) return false;
     69       return true;
     70     });
     71     updateFilterCount(filtersEl, filtered.length, papers.length);
     72     renderTable();
     73   });
     74 
     75   app.appendChild(filtersEl);
     76   updateFilterCount(filtersEl, papers.length, papers.length);
     77 
     78   const tableContainer = document.createElement('div');
     79   app.appendChild(tableContainer);
     80 
     81   const columns: Column<PaperIndex>[] = [
     82     { key: 'title', label: 'Title', render: p => p.title.length > 60 ? p.title.slice(0, 57) + '...' : p.title, sortValue: p => p.title },
     83     { key: 'year', label: 'Year', render: p => String(p.year || ''), sortValue: p => p.year || 0 },
     84     { key: 'score', label: 'Score', render: p => {
     85       if (p.score == null) return '<span style="color:var(--gray)">--</span>';
     86       if (p.paper_type === 'non-empirical') return `<span style="color:var(--gray)" title="Non-empirical paper — scored on limited criteria">${p.score}%*</span>`;
     87       return `<span style="color:${scoreColor(p.score)}">${p.score}%</span>`;
     88     }, sortValue: p => p.score ?? -1 },
     89     { key: 'dna', label: 'Profile', render: p => renderDna(p.dna, p.engagement), sortValue: p => p.score ?? -1 },
     90     { key: 'archetype', label: 'Type', render: p => {
     91       if (p.paper_type === 'non-empirical') return '<span class="archetype" style="background:rgba(139,143,163,0.15);color:var(--text-dim)">Non-empirical</span>';
     92       return p.archetype ? `<span class="archetype ${p.archetype}">${p.archetype}</span>` : '<span style="color:var(--gray)">--</span>';
     93     }, sortValue: p => p.paper_type === 'non-empirical' ? 'ZZ' : (p.archetype || '') },
     94   ];
     95 
     96   function renderTable() {
     97     tableContainer.innerHTML = '';
     98     const el = renderSortableTable(filtered, columns, p => {
     99       if (p.score != null) navigate(`/paper/${p.id}`);
    100     });
    101     tableContainer.appendChild(el);
    102   }
    103 
    104   renderTable();
    105 }

Impressum · Datenschutz