programmable-vla / model-cards.js
yzzhao's picture
Add selectable model cards with saved per-panel preferences
ccaf22c verified
Raw History Blame Contribute Delete
3.4 kB
/* Model-card visibility is saved separately for each benchmark panel. */
window.ModelCards = (() => {
const selections = new Map();
function mount(grid, panel) {
const storageKey = `programvla:model-cards:${panel}`;
if (!selections.has(panel)) {
let saved = [];
try {
const value = JSON.parse(localStorage.getItem(storageKey));
if (Array.isArray(value)) saved = value.filter(id => typeof id === 'string');
} catch {}
selections.set(panel, new Set(saved));
}
const hidden = selections.get(panel);
const picker = document.createElement('section');
picker.className = 'panel model-card-picker';
picker.setAttribute('aria-labelledby', 'model-card-picker-title');
picker.innerHTML = `<div class="column-picker-heading"><div>
<h2 id="model-card-picker-title">Models to display</h2>
<p id="model-card-status" role="status"></p>
</div><div class="column-picker-actions">
<button type="button" class="text-button" data-model-cards="all">Show all</button>
<button type="button" class="text-button" data-model-cards="none">Hide all</button>
</div></div><div class="task-column-options" role="group" aria-label="Models to display"></div>`;
const options = picker.querySelector('.task-column-options');
const items = [...grid.querySelectorAll('.agent-card')].map(card => {
const id = card.getAttribute('href').slice('#runs/'.length);
card.id = `model-card-${id}`;
const label = document.createElement('label');
label.className = 'model-card-toggle';
label.style.setProperty('--variant', card.style.getPropertyValue('--variant'));
label.title = card.querySelector('.card-model').textContent.trim();
const input = document.createElement('input');
input.type = 'checkbox';
input.value = id;
input.setAttribute('aria-controls', card.id);
const name = document.createElement('span');
name.textContent = card.querySelector('.agent-name').textContent.trim();
label.append(input, name);
options.append(label);
input.addEventListener('change', () => {
if (input.checked) hidden.delete(id); else hidden.add(id);
update(true);
});
return {id, card, input, label};
});
function update(save = false) {
let count = 0;
for (const {id, card, input, label} of items) {
const visible = !hidden.has(id);
card.hidden = !visible;
input.checked = visible;
label.classList.toggle('selected', visible);
if (visible) count++;
}
grid.hidden = count === 0;
picker.querySelector('#model-card-status').textContent = `${count} of ${items.length} model cards shown`;
picker.querySelector('[data-model-cards="all"]').disabled = count === items.length;
picker.querySelector('[data-model-cards="none"]').disabled = count === 0;
if (save) {
try { localStorage.setItem(storageKey, JSON.stringify([...hidden])); } catch {}
}
}
picker.addEventListener('click', event => {
const button = event.target.closest('[data-model-cards]');
if (!button) return;
for (const {id} of items) {
if (button.dataset.modelCards === 'all') hidden.delete(id); else hidden.add(id);
}
update(true);
});
grid.before(picker);
update();
}
return {mount};
})();