From 3bf597fed3bd1a58b4880e3c26d49ce0022d7c21 Mon Sep 17 00:00:00 2001 From: Haoxiang Cheng <2739441541@qq.com> Date: Thu, 24 Sep 2026 02:13:28 +0800 Subject: [PATCH 1/2] fix: style the research back button and reuse the ATL Community pill The research workbench back button and the research shelf's empty-state Community link rendered as bare unstyled controls. The back button now matches the app's secondary button look; the Community link reuses the existing agents-empty-community-btn pill and its delegated handler. --- dashboard/backend/tests/test_admin_analytics_frontend.py | 4 ++-- dashboard/backend/tests/test_analytics_frontend.py | 2 +- .../backend/tests/test_backtest_comparison_frontend.py | 4 ++-- dashboard/backend/tests/test_frontend_fast_boot.py | 4 ++-- dashboard/frontend/app.html | 4 ++-- dashboard/frontend/app.js | 6 ++---- dashboard/frontend/styles.css | 2 ++ 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/dashboard/backend/tests/test_admin_analytics_frontend.py b/dashboard/backend/tests/test_admin_analytics_frontend.py index 3054ac0b..b7303012 100644 --- a/dashboard/backend/tests/test_admin_analytics_frontend.py +++ b/dashboard/backend/tests/test_admin_analytics_frontend.py @@ -161,8 +161,8 @@ def test_profile_menu_admin_entry_opens_the_admin_page(): def test_app_lifecycle_and_cache_versions_are_wired(): # Lockstep owner for the console's bumped tags and the /admin page's pins: # every bump edits this test in the same change (Global Constraints). - assert 'styles.css?v=145' in APP_HTML - assert 'app.js?v=138' in APP_HTML + assert 'styles.css?v=146' in APP_HTML + assert 'app.js?v=139' in APP_HTML assert 'js/admin-tabs.js?v=12' in APP_HTML for tag in ( 'href="admin.css?v=4"', diff --git a/dashboard/backend/tests/test_analytics_frontend.py b/dashboard/backend/tests/test_analytics_frontend.py index c620362b..37fdaf75 100644 --- a/dashboard/backend/tests/test_analytics_frontend.py +++ b/dashboard/backend/tests/test_analytics_frontend.py @@ -41,7 +41,7 @@ def _function_body(source: str, signature: str) -> str: def test_analytics_script_loads_between_app_and_page_scripts(): - app_at = APP_HTML.index('') + app_at = APP_HTML.index('') analytics_at = APP_HTML.index( '' ) diff --git a/dashboard/backend/tests/test_backtest_comparison_frontend.py b/dashboard/backend/tests/test_backtest_comparison_frontend.py index fe0e9925..7f22c9c3 100644 --- a/dashboard/backend/tests/test_backtest_comparison_frontend.py +++ b/dashboard/backend/tests/test_backtest_comparison_frontend.py @@ -191,8 +191,8 @@ def test_exact_raw_ties_mark_every_tied_series_best(): def test_comparison_script_and_semantic_table_ship_before_app(): helper = '' - app = '' - assert 'href="styles.css?v=145"' in APP_HTML + app = '' + assert 'href="styles.css?v=146"' in APP_HTML assert APP_HTML.index(helper) < APP_HTML.index(app) for element_id in ( "performanceLegend", diff --git a/dashboard/backend/tests/test_frontend_fast_boot.py b/dashboard/backend/tests/test_frontend_fast_boot.py index 80bc3f1b..6f6b5f13 100644 --- a/dashboard/backend/tests/test_frontend_fast_boot.py +++ b/dashboard/backend/tests/test_frontend_fast_boot.py @@ -191,9 +191,9 @@ def test_cache_busters_bumped(): # the next bump, so the exact one looks like the broken guard and gets # "fixed" by loosening it. That collision has already cost this repo one # round of follow-ups (#347/#348). - assert "app.js?v=138" in APP_HTML + assert "app.js?v=139" in APP_HTML assert "js/agent-editor.js?v=31" in APP_HTML - assert "styles.css?v=145" in APP_HTML + assert "styles.css?v=146" in APP_HTML assert "js/leaderboard.js?v=33" in APP_HTML assert "home-page.js?v=50" in APP_HTML assert "js/credit-format.js?v=1" in APP_HTML diff --git a/dashboard/frontend/app.html b/dashboard/frontend/app.html index 245a4b67..c9b62f51 100644 --- a/dashboard/frontend/app.html +++ b/dashboard/frontend/app.html @@ -13,7 +13,7 @@ because every API call is a CORS request. --> - + @@ -2393,7 +2393,7 @@

Refund Credits purchase

- + diff --git a/dashboard/frontend/app.js b/dashboard/frontend/app.js index 0922ac1c..b5a591ef 100644 --- a/dashboard/frontend/app.js +++ b/dashboard/frontend/app.js @@ -13027,11 +13027,9 @@ async function renderResearchShelf() { grid.innerHTML = ''; if (emptyEl) { emptyEl.hidden = false; - emptyEl.innerHTML = 'No research agents yet. Add one from .'; + emptyEl.innerHTML = 'No research agents yet. Add one from ' + + communityShelfButtonHtml('all') + '.'; } - grid.querySelectorAll('[data-goto-community]').forEach((btn) => { - btn.addEventListener('click', () => navigateToPage('community')); - }); return; } if (emptyEl) emptyEl.hidden = true; diff --git a/dashboard/frontend/styles.css b/dashboard/frontend/styles.css index 984ab3d4..2066094f 100644 --- a/dashboard/frontend/styles.css +++ b/dashboard/frontend/styles.css @@ -13880,3 +13880,5 @@ table.sr-only { @media(max-width:900px){.research-columns{grid-template-columns:1fr}} .marketplace-card--research{border-color:rgba(103,232,249,.25)} .research-fact-icon{width:14px;height:14px;vertical-align:-2px;margin-right:2px} +.research-back-btn{display:inline-flex;align-items:center;gap:6px;padding:7px 14px;margin-bottom:16px;border-radius:8px;border:1px solid rgba(148,163,184,.28);background:transparent;color:var(--text-muted,#94a3b8);font:inherit;font-size:13px;cursor:pointer} +.research-back-btn:hover{color:var(--accent,#67e8f9);border-color:rgba(103,232,249,.4)} From 2ef38c2208a4944983c03950ef3f83bd249db612 Mon Sep 17 00:00:00 2001 From: Haoxiang Cheng <2739441541@qq.com> Date: Thu, 24 Sep 2026 02:29:49 +0800 Subject: [PATCH 2/2] feat: research agent Remove action + always-bound workbench Back MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - My Agents research cards gain a Remove action (confirm dialog, DELETE to the research add endpoint, shelf refresh) — re-addable from Community at any time. - The workbench Back button is bound once at init instead of after a successful manifest fetch: on the manifest-failure path the old binding never ran, trapping the user on a dead workbench (observed on the Vercel deploy while the agent service is unreachable). - app.js cache version 139→140 with test pins. --- .../tests/test_admin_analytics_frontend.py | 2 +- .../backend/tests/test_analytics_frontend.py | 2 +- .../test_backtest_comparison_frontend.py | 2 +- .../backend/tests/test_frontend_fast_boot.py | 2 +- dashboard/frontend/app.html | 2 +- dashboard/frontend/app.js | 22 +++++++++++++++++-- dashboard/frontend/styles.css | 3 +++ 7 files changed, 28 insertions(+), 7 deletions(-) diff --git a/dashboard/backend/tests/test_admin_analytics_frontend.py b/dashboard/backend/tests/test_admin_analytics_frontend.py index b7303012..3830a206 100644 --- a/dashboard/backend/tests/test_admin_analytics_frontend.py +++ b/dashboard/backend/tests/test_admin_analytics_frontend.py @@ -162,7 +162,7 @@ def test_app_lifecycle_and_cache_versions_are_wired(): # Lockstep owner for the console's bumped tags and the /admin page's pins: # every bump edits this test in the same change (Global Constraints). assert 'styles.css?v=146' in APP_HTML - assert 'app.js?v=139' in APP_HTML + assert 'app.js?v=140' in APP_HTML assert 'js/admin-tabs.js?v=12' in APP_HTML for tag in ( 'href="admin.css?v=4"', diff --git a/dashboard/backend/tests/test_analytics_frontend.py b/dashboard/backend/tests/test_analytics_frontend.py index 37fdaf75..008a64d4 100644 --- a/dashboard/backend/tests/test_analytics_frontend.py +++ b/dashboard/backend/tests/test_analytics_frontend.py @@ -41,7 +41,7 @@ def _function_body(source: str, signature: str) -> str: def test_analytics_script_loads_between_app_and_page_scripts(): - app_at = APP_HTML.index('') + app_at = APP_HTML.index('') analytics_at = APP_HTML.index( '' ) diff --git a/dashboard/backend/tests/test_backtest_comparison_frontend.py b/dashboard/backend/tests/test_backtest_comparison_frontend.py index 7f22c9c3..b4104090 100644 --- a/dashboard/backend/tests/test_backtest_comparison_frontend.py +++ b/dashboard/backend/tests/test_backtest_comparison_frontend.py @@ -191,7 +191,7 @@ def test_exact_raw_ties_mark_every_tied_series_best(): def test_comparison_script_and_semantic_table_ship_before_app(): helper = '' - app = '' + app = '' assert 'href="styles.css?v=146"' in APP_HTML assert APP_HTML.index(helper) < APP_HTML.index(app) for element_id in ( diff --git a/dashboard/backend/tests/test_frontend_fast_boot.py b/dashboard/backend/tests/test_frontend_fast_boot.py index 6f6b5f13..fa6547f1 100644 --- a/dashboard/backend/tests/test_frontend_fast_boot.py +++ b/dashboard/backend/tests/test_frontend_fast_boot.py @@ -191,7 +191,7 @@ def test_cache_busters_bumped(): # the next bump, so the exact one looks like the broken guard and gets # "fixed" by loosening it. That collision has already cost this repo one # round of follow-ups (#347/#348). - assert "app.js?v=139" in APP_HTML + assert "app.js?v=140" in APP_HTML assert "js/agent-editor.js?v=31" in APP_HTML assert "styles.css?v=146" in APP_HTML assert "js/leaderboard.js?v=33" in APP_HTML diff --git a/dashboard/frontend/app.html b/dashboard/frontend/app.html index c9b62f51..9f404f33 100644 --- a/dashboard/frontend/app.html +++ b/dashboard/frontend/app.html @@ -2393,7 +2393,7 @@

Refund Credits purchase

- + diff --git a/dashboard/frontend/app.js b/dashboard/frontend/app.js index b5a591ef..db657001 100644 --- a/dashboard/frontend/app.js +++ b/dashboard/frontend/app.js @@ -12983,7 +12983,13 @@ let researchWorkbenchTemplateId = null; let researchWorkbenchReturnView = null; let researchPollTimer = null; let researchSubmitInFlight = false; +document.addEventListener('DOMContentLoaded', () => { + document.getElementById('researchBackBtn')?.addEventListener('click', hideResearchWorkbench); +}); +// Back is bound once at init, not per-open: the manifest-failure path returns +// before openResearchWorkbench would reach its binding, and a dead Back on a +// failed-to-load workbench is a trapped user. function stopResearchPolling() { if (researchPollTimer) { clearInterval(researchPollTimer); @@ -13046,13 +13052,26 @@ async function renderResearchShelf() { ~${runtimeMin} min · ${escapeHtml(((agent.research || {}).output_formats || []).join(' / '))}

- +
+ + +
`; }).join(''); grid.querySelectorAll('.research-agent-card').forEach((card) => { card.querySelector('.research-open-btn')?.addEventListener('click', () => { openResearchWorkbench(card.dataset.templateId); }); + card.querySelector('.research-remove-btn')?.addEventListener('click', async (event) => { + const templateId = event.currentTarget.dataset.removeTemplateId; + if (!window.confirm(`Remove "${event.currentTarget.dataset.agentName}" from My Agents?\n\nYou can re-add it from Community at any time.`)) return; + try { + await API.request(`${RESEARCH_API}/agents/${encodeURIComponent(templateId)}/add`, { method: 'DELETE' }); + await renderResearchShelf(); + } catch (error) { + alert(error.message || 'Remove failed.'); + } + }); }); } @@ -13195,7 +13214,6 @@ async function openResearchWorkbench(templateId) { submitResearchRun(); }); } - document.getElementById('researchBackBtn').onclick = hideResearchWorkbench; await loadResearchRuns(templateId); } diff --git a/dashboard/frontend/styles.css b/dashboard/frontend/styles.css index 2066094f..4547fbe7 100644 --- a/dashboard/frontend/styles.css +++ b/dashboard/frontend/styles.css @@ -13882,3 +13882,6 @@ table.sr-only { .research-fact-icon{width:14px;height:14px;vertical-align:-2px;margin-right:2px} .research-back-btn{display:inline-flex;align-items:center;gap:6px;padding:7px 14px;margin-bottom:16px;border-radius:8px;border:1px solid rgba(148,163,184,.28);background:transparent;color:var(--text-muted,#94a3b8);font:inherit;font-size:13px;cursor:pointer} .research-back-btn:hover{color:var(--accent,#67e8f9);border-color:rgba(103,232,249,.4)} +.research-agent-card-actions{display:flex;gap:8px;align-items:center} +.research-remove-btn{padding:7px 12px;border-radius:8px;border:1px solid rgba(240,128,128,.35);background:transparent;color:#f08080;font:inherit;font-size:12.5px;cursor:pointer} +.research-remove-btn:hover{border-color:#f08080;background:rgba(240,128,128,.08)}