diff --git a/static/sessions.js b/static/sessions.js index 485132f6..56c8d410 100644 --- a/static/sessions.js +++ b/static/sessions.js @@ -4766,6 +4766,28 @@ function _partitionSidebarSessionRows(allMatched, activeSidForSidebar){ }; } +function _renderSidebarRowsFromRawSessions(sessionsRaw){ + return _attachChildSessionsToSidebarRows(_collapseSessionLineageForSidebar(sessionsRaw), sessionsRaw); +} + +function _countRenderedSidebarRows(allMatched, activeSidForSidebar, showCliOnly){ + const sessionsRaw=[]; + for(const s of allMatched){ + if(!_sidebarRowHasVisibleMessages(s, activeSidForSidebar)) continue; + const isCli=_isCliSession(s); + if(showCliOnly ? !isCli : isCli) continue; + if(s.default_hidden&&!(_activeProject&&_activeProject!==NO_PROJECT_FILTER&&s.project_id===_activeProject)) continue; + if(_activeProject===NO_PROJECT_FILTER){ + if(s.project_id) continue; + } else if(_activeProject){ + if(s.project_id!==_activeProject) continue; + } + if(!_showArchived&&s.archived) continue; + sessionsRaw.push(s); + } + return _renderSidebarRowsFromRawSessions(sessionsRaw).length; +} + function renderSessionListFromCache(){ // Don't re-render while user is actively renaming a session (would destroy the input) if(_renamingSid) return; @@ -4795,7 +4817,13 @@ function renderSessionListFromCache(){ sessionsRaw, archivedCount, }=_partitionSidebarSessionRows(allMatched, activeSidForSidebar); - const sessions=_attachChildSessionsToSidebarRows(_collapseSessionLineageForSidebar(sessionsRaw), sessionsRaw); + const sessions=_renderSidebarRowsFromRawSessions(sessionsRaw); + const renderedWebuiSessionCount=_sessionSourceFilter==='webui' + ? sessions.length + : _countRenderedSidebarRows(allMatched, activeSidForSidebar, false); + const renderedCliSessionCount=_sessionSourceFilter==='cli' + ? sessions.length + : _countRenderedSidebarRows(allMatched, activeSidForSidebar, true); _syncSidebarExpansionForActiveSession(sessions, activeSidForSidebar); const list=$('sessionList'); const animateRefresh=_sessionListRefreshAnimationPending; @@ -4830,7 +4858,7 @@ function renderSessionListFromCache(){ const sourceTabs=document.createElement('div'); sourceTabs.className='session-source-tabs'; for(const filter of ['webui','cli']){ - const count=filter==='cli'?cliSessionCount:webuiSessionCount; + const count=filter==='cli'?renderedCliSessionCount:renderedWebuiSessionCount; const btn=document.createElement('button'); btn.type='button'; btn.className='session-source-tab'+(_sessionSourceFilter===filter?' active':''); diff --git a/tests/test_issue3966_session_count_label.py b/tests/test_issue3966_session_count_label.py new file mode 100644 index 00000000..e561d0ed --- /dev/null +++ b/tests/test_issue3966_session_count_label.py @@ -0,0 +1,32 @@ +"""Regression coverage for sidebar source counts using rendered rows.""" + +from __future__ import annotations + +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +SESSIONS_JS = (ROOT / "static" / "sessions.js").read_text(encoding="utf-8") + + +def test_render_counts_use_post_collapse_rows(): + render_start = SESSIONS_JS.index("function renderSessionListFromCache()") + render_end = SESSIONS_JS.index("function _showProjectPicker", render_start) + render_body = SESSIONS_JS[render_start:render_end] + + assert "const sessions=_renderSidebarRowsFromRawSessions(sessionsRaw);" in render_body + assert "? sessions.length" in render_body + assert ": _countRenderedSidebarRows(allMatched, activeSidForSidebar, false);" in render_body + assert ": _countRenderedSidebarRows(allMatched, activeSidForSidebar, true);" in render_body + assert "const count=filter==='cli'?renderedCliSessionCount:renderedWebuiSessionCount;" in render_body + assert "const count=filter==='cli'?cliSessionCount:webuiSessionCount;" not in render_body + + +def test_rendered_count_helper_collapses_before_counting(): + helper_start = SESSIONS_JS.index("function _countRenderedSidebarRows(") + helper_end = SESSIONS_JS.index("function renderSessionListFromCache()", helper_start) + helper_body = SESSIONS_JS[helper_start:helper_end] + + assert "sessionsRaw.push(s);" in helper_body + assert "return _renderSidebarRowsFromRawSessions(sessionsRaw).length;" in helper_body + assert "function _renderSidebarRowsFromRawSessions(sessionsRaw){" in SESSIONS_JS + assert "_attachChildSessionsToSidebarRows(_collapseSessionLineageForSidebar(sessionsRaw), sessionsRaw)" in SESSIONS_JS diff --git a/tests/test_sidebar_session_partition.py b/tests/test_sidebar_session_partition.py index c99f66b4..aa54b61f 100644 --- a/tests/test_sidebar_session_partition.py +++ b/tests/test_sidebar_session_partition.py @@ -20,6 +20,7 @@ def test_render_uses_single_pass_partition_helper(): render_body = SESSIONS_JS[render_start:render_end] assert "_partitionSidebarSessionRows(allMatched, activeSidForSidebar)" in render_body + assert "_renderSidebarRowsFromRawSessions(sessionsRaw)" in render_body assert "withMessages.filter(" not in render_body @@ -35,3 +36,14 @@ def test_partition_helper_applies_message_source_project_and_archive_gates(): assert "return {" in block assert "profileFiltered," in block assert "sessionsRaw," in block + + +def test_partition_helper_keeps_raw_source_counts_while_render_owns_visible_counts(): + render_start = SESSIONS_JS.index("function renderSessionListFromCache()") + render_end = SESSIONS_JS.index("function _showProjectPicker", render_start) + render_body = SESSIONS_JS[render_start:render_end] + + assert "webuiSessionCount," in _partition_block() + assert "cliSessionCount," in _partition_block() + assert "const renderedWebuiSessionCount=" in render_body + assert "const renderedCliSessionCount=" in render_body