Add mass selection in history: select-all, shift-click range, count badge

Select All checkbox in header row:
- Toggles all visible checkboxes at once
- Shows indeterminate state when partially selected
- Syncs with individual checkbox changes

Shift-click range selection:
- Hold Shift + click to select/deselect a range of rows
- Tracks last clicked index for range calculation

Selection count badge:
- Shows "N selected / M runs" in the run count badge when items selected
- Updates on every checkbox change

Header updated to include Score column matching the data grid.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
root 2026-03-29 08:53:51 -05:00
parent d4d114b2fd
commit 39c421806d

View File

@ -6834,7 +6834,7 @@ h1 span{color:var(--accent)}
<!-- Run table -->
<div class="run-table" id="run-table">
<div class="run-row run-hdr">
<span></span><span>ID</span><span>Mode</span><span>Prompt</span><span>Models</span><span>Tags</span><span>Date</span>
<span><input type="checkbox" id="select-all-cb" onclick="toggleSelectAll(this)" style="width:14px;height:14px;accent-color:#e2b55a;cursor:pointer" title="Select all"></span><span>ID</span><span>Mode</span><span>Prompt</span><span>Models</span><span>Score</span><span>Tags</span><span>Date</span>
</div>
<div id="run-list"><div class="empty">Loading...</div></div>
</div>
@ -7030,13 +7030,13 @@ function renderTable(runs) {
var el = document.getElementById('run-list');
if (!runs.length) { el.textContent = ''; var e = document.createElement('div'); e.className = 'empty'; e.textContent = 'No runs found'; el.appendChild(e); return; }
el.textContent = '';
runs.forEach(function(r) {
runs.forEach(function(r, idx) {
var row = document.createElement('div');
row.className = 'run-row' + (r.archived ? ' archived' : '');
// Checkbox
var cb = document.createElement('input'); cb.type = 'checkbox'; cb.dataset.id = r.id;
cb.style.cssText = 'width:14px;height:14px;accent-color:#e2b55a;cursor:pointer';
cb.onclick = function(e){e.stopPropagation()};
cb.onclick = function(e){e.stopPropagation(); handleRowCheck(this, idx);};
row.appendChild(cb);
// ID
var idEl = document.createElement('span'); idEl.className = 'run-id'; idEl.textContent = '#'+r.id; row.appendChild(idEl);
@ -7290,6 +7290,34 @@ async function saveTags(id, tags, notes) {
if (tags !== null) loadRuns();
}
function toggleSelectAll(masterCb) {
var checked = masterCb.checked;
document.querySelectorAll('#run-list input[type=checkbox]').forEach(function(cb) { cb.checked = checked; });
updateSelCount();
}
var _lastCheckedIdx = null;
function handleRowCheck(cb, idx) {
// Shift-click range selection
if (window.event && window.event.shiftKey && _lastCheckedIdx !== null) {
var cbs = Array.from(document.querySelectorAll('#run-list input[type=checkbox]'));
var start = Math.min(_lastCheckedIdx, idx);
var end = Math.max(_lastCheckedIdx, idx);
for (var i = start; i <= end; i++) { cbs[i].checked = cb.checked; }
}
_lastCheckedIdx = idx;
updateSelCount();
}
function updateSelCount() {
var count = document.querySelectorAll('#run-list input[type=checkbox]:checked').length;
var total = document.querySelectorAll('#run-list input[type=checkbox]').length;
var masterCb = document.getElementById('select-all-cb');
if (masterCb) { masterCb.checked = count > 0 && count === total; masterCb.indeterminate = count > 0 && count < total; }
var badge = document.getElementById('run-count');
if (badge && count > 0) badge.textContent = count + ' selected / ' + total + ' runs';
}
function getSelectedIds() {
var ids = [];
document.querySelectorAll('#run-list input[type=checkbox]:checked').forEach(function(cb){ids.push(parseInt(cb.dataset.id))});