@@ -924,7 +924,7 @@ function renderThreeSectionCoverage(summary) {
924924 // Coverage sections (progress bars + detail lists)
925925 html += '<div class="coverage-sections">' ;
926926 html += renderCoverageSection ( 'Documentation Coverage' , summary . documentation , 'documents' , renderDocDetails ) ;
927- html += renderCoverageSection ( 'Acceptance Criteria Coverage' , summary . acceptance_criteria , 'acceptance_criteria ' , renderCriteriaDetails ) ;
927+ html += renderCoverageSection ( 'Acceptance Criteria Coverage' , summary . acceptance_criteria , 'criteria ' , renderCriteriaDetails ) ;
928928 html += renderCoverageSection ( 'Automation Coverage' , summary . automation , 'tests' , renderAutoDetails ) ;
929929 html += '</div>' ;
930930
@@ -1354,7 +1354,15 @@ function renderCoverageSection(label, sectionData, unit, renderDetailsFn) {
13541354 html += emptyHtml ;
13551355 } else if ( sectionData . details && sectionData . details . length > 0 ) {
13561356 // Expandable detail list
1357- html += `<button class="coverage-toggle-btn" onclick="toggleCoverageDetails('${ sectionId } ')">Show details</button>` ;
1357+ const detailCount = sectionData . details . length ;
1358+ html += `<button class="coverage-toggle-btn" onclick="toggleCoverageDetails('${ sectionId } ')">Show details (${ detailCount } )</button>` ;
1359+ // Add search filter for large lists
1360+ if ( detailCount > 20 ) {
1361+ html += `<div class="coverage-detail-filter collapsed" id="filter-${ sectionId } ">
1362+ <input type="text" placeholder="Filter by ID or text..." oninput="filterCoverageDetails('${ sectionId } ', this.value)" />
1363+ <span class="filter-count" id="filter-count-${ sectionId } ">${ detailCount } items</span>
1364+ </div>` ;
1365+ }
13581366 html += `<ul class="coverage-detail-list collapsed" id="details-${ sectionId } ">` ;
13591367 html += renderDetailsFn ( sectionData ) ;
13601368 html += '</ul>' ;
@@ -1384,7 +1392,7 @@ function getEmptyState(label, sectionData, total, pct) {
13841392 return `<div class="coverage-empty-state">
13851393 <span class="empty-icon">ⓘ</span>
13861394 <div class="empty-text"><strong>No acceptance criteria tracked yet.</strong>
1387- Add a <code>criteria</code> field to test YAML frontmatter, or create a <code>_criteria_index.yaml </code> file in your docs directory .</div>
1395+ Add a <code>criteria</code> field to test YAML frontmatter, or run <code>spectra ai analyze --extract-criteria </code> to extract from documentation .</div>
13881396 </div>` ;
13891397 }
13901398 return null ;
@@ -1409,15 +1417,44 @@ function getEmptyState(label, sectionData, total, pct) {
14091417 */
14101418function toggleCoverageDetails ( sectionId ) {
14111419 const list = document . getElementById ( 'details-' + sectionId ) ;
1412- const btn = list ?. previousElementSibling ;
1413- if ( ! list || ! btn ) return ;
1420+ const filter = document . getElementById ( 'filter-' + sectionId ) ;
1421+ if ( ! list ) return ;
1422+
1423+ // Find the toggle button — it's the sibling before the filter (if present) or the list
1424+ const btn = ( filter || list ) . previousElementSibling ;
1425+ const count = list . children . length ;
14141426
14151427 if ( list . classList . contains ( 'collapsed' ) ) {
14161428 list . classList . remove ( 'collapsed' ) ;
1417- btn . textContent = 'Hide details' ;
1429+ if ( filter ) filter . classList . remove ( 'collapsed' ) ;
1430+ if ( btn ) btn . textContent = 'Hide details' ;
14181431 } else {
14191432 list . classList . add ( 'collapsed' ) ;
1420- btn . textContent = 'Show details' ;
1433+ if ( filter ) filter . classList . add ( 'collapsed' ) ;
1434+ if ( btn ) btn . textContent = `Show details (${ count } )` ;
1435+ }
1436+ }
1437+
1438+ /**
1439+ * Filter coverage detail list items by search text.
1440+ */
1441+ function filterCoverageDetails ( sectionId , query ) {
1442+ const list = document . getElementById ( 'details-' + sectionId ) ;
1443+ const countEl = document . getElementById ( 'filter-count-' + sectionId ) ;
1444+ if ( ! list ) return ;
1445+
1446+ const q = query . toLowerCase ( ) . trim ( ) ;
1447+ let visible = 0 ;
1448+
1449+ for ( const li of list . children ) {
1450+ const text = li . textContent . toLowerCase ( ) ;
1451+ const show = ! q || text . includes ( q ) ;
1452+ li . style . display = show ? '' : 'none' ;
1453+ if ( show ) visible ++ ;
1454+ }
1455+
1456+ if ( countEl ) {
1457+ countEl . textContent = q ? `${ visible } of ${ list . children . length } ` : `${ list . children . length } items` ;
14211458 }
14221459}
14231460
@@ -1449,8 +1486,12 @@ function renderCriteriaDetails(section) {
14491486 ? '<span class="detail-icon coverage-green">✓</span>'
14501487 : '<span class="detail-icon coverage-red">✗</span>' ;
14511488 const testList = d . tests && d . tests . length > 0 ? d . tests . join ( ', ' ) : 'none' ;
1452- html += `<li>
1453- <span class="detail-name" title="${ escapeHtml ( d . title || d . id ) } ">${ escapeHtml ( d . id ) } : ${ escapeHtml ( d . title || '' ) } </span>
1489+ const titleText = d . title || d . text || '' ;
1490+ const displayName = titleText
1491+ ? `<strong>${ escapeHtml ( d . id ) } </strong> ${ escapeHtml ( titleText ) } `
1492+ : `<strong>${ escapeHtml ( d . id ) } </strong>` ;
1493+ html += `<li title="${ escapeHtml ( titleText ) } ">
1494+ <span class="detail-name">${ displayName } </span>
14541495 <span class="detail-meta">${ testList } </span>
14551496 ${ icon }
14561497 </li>` ;
0 commit comments