From efecc78f54b857fcc649c52cc182766bdc4c8495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AF=BA=E6=96=AF=E8=B4=B9=E6=8B=89=E5=9B=BE?= <1132505822@qq.com> Date: Sun, 12 Apr 2026 17:28:51 +0800 Subject: [PATCH] feat: add Asset Library panel with papers list and drag-to-canvas --- research_web/css/research.css | 39 +++++++++++ research_web/js/app.js | 4 ++ research_web/js/panels/AssetPanel.js | 97 ++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+) create mode 100644 research_web/js/panels/AssetPanel.js diff --git a/research_web/css/research.css b/research_web/css/research.css index c41047aa1..20a21aa6e 100644 --- a/research_web/css/research.css +++ b/research_web/css/research.css @@ -108,3 +108,42 @@ body { .empty-state { color: #858585; font-style: italic; padding: 1rem; } .error { color: #f48771; padding: 1rem; } + +/* Asset Panel */ +.asset-panel { max-width: 1200px; margin: 0 auto; } +.asset-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; } +.asset-header h2 { color: #fff; } +.asset-tabs { display: flex; gap: 0.25rem; } +.tab-btn { + background: transparent; border: none; color: #d4d4d4; + padding: 0.5rem 1rem; cursor: pointer; border-radius: 4px; +} +.tab-btn:hover { background: #3c3c3c; } +.tab-btn.active { background: #37373d; color: #fff; } + +.asset-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1rem; } + +.asset-card { + background: #252526; border-radius: 6px; padding: 1rem; + cursor: grab; +} +.asset-card:active { cursor: grabbing; } +.asset-card h4 { color: #fff; margin-bottom: 0.5rem; } +.asset-meta { color: #858585; font-size: 0.85rem; } +.asset-badges { display: flex; gap: 0.5rem; margin: 0.5rem 0; } +.badge { + font-size: 0.7rem; padding: 0.2rem 0.4rem; + border-radius: 3px; background: #37373d; color: #d4d4d4; +} +.badge.unread { background: #d29922; color: #000; } +.badge.library { background: #2ea043; color: #fff; } +.badge.pending { background: #6c6c6c; color: #fff; } +.badge.enabled { background: #2ea043; color: #fff; } +.badge.disabled { background: #6c6c6c; color: #fff; } + +.asset-actions { display: flex; gap: 0.5rem; margin-top: 0.75rem; } +.btn-quick-read, .btn-drag { + background: #37373d; border: none; color: #d4d4d4; + padding: 0.4rem 0.8rem; border-radius: 4px; cursor: pointer; font-size: 0.8rem; +} +.btn-quick-read:hover, .btn-drag:hover { background: #4f4f4f; } diff --git a/research_web/js/app.js b/research_web/js/app.js index 5e7a7db3e..2d4d63070 100644 --- a/research_web/js/app.js +++ b/research_web/js/app.js @@ -1,5 +1,6 @@ /** Main Research Workbench app - Home and Project Workspace panels. */ import * as api from "./api_client.js"; +import { renderAssetPanel } from "./panels/AssetPanel.js"; const mainContent = document.getElementById("main-content"); const navButtons = document.querySelectorAll(".nav-btn"); @@ -38,6 +39,9 @@ async function render() { } } else if (currentPanel === "canvas") { window.location.href = "/"; + } else if (currentPanel === "assets") { + mainContent.innerHTML = ""; + renderAssetPanel(mainContent, api); } } diff --git a/research_web/js/panels/AssetPanel.js b/research_web/js/panels/AssetPanel.js new file mode 100644 index 000000000..0462c1c2b --- /dev/null +++ b/research_web/js/panels/AssetPanel.js @@ -0,0 +1,97 @@ +// research_web/js/panels/AssetPanel.js +/**Asset Library panel - browse and manage paper assets.*/ + +export function renderAssetPanel(container, api) { + container.innerHTML = ` +
+
+

Asset Library

+
+ + + +
+
+
+
+

Loading...

+
+
+
+ `; + + // Tab switching + container.querySelectorAll('.tab-btn').forEach(btn => { + btn.addEventListener('click', () => { + container.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); + btn.classList.add('active'); + loadAssets(btn.dataset.tab); + }); + }); + + // Initial load + loadAssets('papers'); + + function loadAssets(type) { + const list = container.querySelector('#asset-list'); + if (type === 'papers') { + list.innerHTML = '

Loading papers...

'; + api.listPapers().then(papers => { + if (!papers || papers.length === 0) { + list.innerHTML = '

No papers in library. Search papers in ComfyUI canvas to add them.

'; + return; + } + list.innerHTML = papers.map(p => ` +
+

${p.title || 'Untitled'}

+

${p.authors_text || 'Unknown authors'}

+

${p.journal_or_source || ''} ${p.published_at || ''}

+
+ ${p.read_status || 'unread'} + ${p.library_status || 'pending'} +
+
+ + +
+
+ `).join(''); + + // Make cards draggable for canvas + list.querySelectorAll('.asset-card').forEach(card => { + card.addEventListener('dragstart', (e) => { + e.dataTransfer.setData('application/json', JSON.stringify({ + type: 'paper_asset', + id: card.dataset.id, + title: card.querySelector('h4').textContent + })); + e.dataTransfer.effectAllowed = 'copy'; + }); + }); + }).catch(err => { + list.innerHTML = `

Failed to load: ${err.message}

`; + }); + } else if (type === 'claims') { + list.innerHTML = '

Claims panel coming in Phase 3.

'; + } else if (type === 'sources') { + list.innerHTML = '

Loading sources...

'; + api.listSources().then(sources => { + if (!sources || sources.length === 0) { + list.innerHTML = '

No sources configured. Add sources to start your feed.

'; + return; + } + list.innerHTML = sources.map(s => ` +
+

${s.name}

+

${s.category || ''} - ${s.intake_type}

+
+ ${s.enabled ? 'enabled' : 'disabled'} +
+
+ `).join(''); + }).catch(err => { + list.innerHTML = `

Failed to load: ${err.message}

`; + }); + } + } +} \ No newline at end of file