Spaces:
Running
Running
File size: 27,502 Bytes
c1bec47 933c910 c1bec47 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 | (function (global) {
'use strict';
const DEFINITIONS = {
reroute: { number: '01', title: 'One word changed.', short: 'Follow the routing', summary: 'Compare two prompts and the measured expert selections they produced.' },
quant: { number: '02', title: 'Spot the 4-bit model.', short: 'A blind precision test', summary: 'Read two answers before seeing their precision, timings and checks.' },
surgery: { number: '03', title: 'What else broke?', short: 'A controlled intervention', summary: 'Inspect a behavior intervention and its effects across the same test cases.' },
memory: { number: '04', title: 'Remembered, but outdated.', short: 'The correction detective', summary: 'Predict the right answer, then see whether the model used a correction or repeated an old fact.' },
sound: { number: '05', title: 'A model, heard.', short: 'Listen to real routing', summary: 'Turn measured model activity into a musical score you can inspect.' }
};
function assert(condition, message) { if (!condition) throw new Error(message); }
function validateResults(data) {
assert(data && data.schema_version === 1, 'This results file uses an unsupported format.');
assert(data.experiments && typeof data.experiments === 'object', 'Experiment results are missing.');
for (const key of ['reroute', 'quant', 'surgery', 'memory']) {
const exp = data.experiments[key];
assert(exp && Array.isArray(exp.cases) && exp.cases.length > 0, `The ${key} experiment has no cases.`);
const seen = new Set();
for (const item of exp.cases) {
assert(item && typeof item.id === 'string' && item.id.length > 0 && !seen.has(item.id), `Invalid or repeated case ID in ${key}.`);
seen.add(item.id);
assert(Array.isArray(item.conditions) && item.conditions.length >= 1 && item.conditions.length <= 12, `Conditions are missing in ${key}/${item.id}.`);
const conditionIds = new Set();
for (const condition of item.conditions) {
assert(condition && typeof condition.id === 'string' && !conditionIds.has(condition.id), `Invalid condition ID in ${key}/${item.id}.`);
conditionIds.add(condition.id);
assert(typeof condition.output === 'string', `A captured answer is missing in ${key}/${item.id}.`);
if (condition.routing && condition.routing.tokens !== undefined) {
assert(Array.isArray(condition.routing.tokens), `Routing tokens must be an array in ${key}/${item.id}.`);
for (const frame of condition.routing.tokens) {
assert(frame && typeof frame.token === 'string' && Array.isArray(frame.experts), 'A routing frame is invalid.');
assert(frame.experts.every(id => Number.isInteger(id) && id >= 0 && id < (condition.routing.expert_count || 256)), 'An expert index is outside this model.');
if (frame.weights !== undefined) assert(Array.isArray(frame.weights) && frame.weights.length === frame.experts.length && frame.weights.every(w => Number.isFinite(w) && w >= 0), 'Routing weights are invalid.');
}
}
}
if (key === 'quant') assert(item.conditions.length === 2, 'The blind comparison needs exactly two answers.');
if (key === 'memory') assert(Array.isArray(item.choices) && item.choices.length >= 2 && item.choices.every(c => typeof c === 'string'), 'The memory case needs answer choices.');
}
}
return data;
}
function stablePairOrder(id, conditions) {
let hash = 2166136261;
for (const char of String(id)) hash = Math.imul(hash ^ char.charCodeAt(0), 16777619) >>> 0;
return hash % 2 ? conditions.slice().reverse() : conditions.slice();
}
function isLowBit(condition) { return /(?:^|[^0-9])q?4(?:[^0-9]|$)/i.test(String(condition && condition.quant || '')); }
function formatMetric(value) {
if (typeof value === 'number') return Number.isInteger(value) ? value.toLocaleString('en-US') : value.toLocaleString('en-US', { maximumFractionDigits: 5 });
if (typeof value === 'boolean') return value ? 'Yes' : 'No';
return typeof value === 'object' && value !== null ? JSON.stringify(value) : String(value ?? '');
}
function readableKey(key) { return key.replace(/_/g, ' ').replace(/\bms\b/g, '(ms)').replace(/\bseconds\b/g, '(s)'); }
function promptFor(item, condition) {
if (typeof condition.prompt === 'string') return condition.prompt;
const linked = Array.isArray(item.prompts) && item.prompts.find(p => p.id === condition.prompt_id);
return linked ? linked.text : typeof item.prompt === 'string' ? item.prompt : '';
}
function fixturesFrom(data) {
const experiments = {};
for (const [key, exp] of Object.entries(data.experiments)) {
experiments[key] = {
method: exp.method,
cases: exp.cases.map(item => ({ id: item.id, title: item.title, prompt: item.prompt, prompts: item.prompts, question: item.question, ground_truth: item.ground_truth, choices: item.choices, intervention: item.intervention, conditions: item.conditions.map(c => ({ id: c.id, model: c.model, quant: c.quant, prompt: promptFor(item, c), settings: c.settings, intervention: c.intervention })) }))
};
}
return { schema_version: 1, generated_at: data.generated_at, provenance: data.provenance, methodology: data.methodology, experiments };
}
function routingFrame(condition, step) {
const frames = condition.routing && condition.routing.tokens;
return Array.isArray(frames) && step < frames.length ? frames[step] : null;
}
const api = { validateResults, stablePairOrder, isLowBit, formatMetric, promptFor, fixturesFrom, routingFrame };
if (typeof module !== 'undefined' && module.exports) module.exports = api;
if (typeof document === 'undefined') return;
const $ = id => document.getElementById(id);
let data, active = 'reroute', currentCase, tokenStep = 0, toastTimer;
const reveals = new Set();
const stateKey = 'alphabell-experiments-v1';
let guesses = { quant: {}, memory: {} };
try { const saved = JSON.parse(localStorage.getItem(stateKey) || 'null'); if (saved && saved.quant && saved.memory) guesses = saved; } catch (_) { /* Private browser storage may be unavailable. */ }
function node(tag, className, content) {
const element = document.createElement(tag);
if (className) element.className = className;
if (content !== undefined) element.textContent = String(content);
return element;
}
function append(parent, ...children) { for (const child of children) if (child) parent.append(child); return parent; }
function button(text, action, className) { const el = node('button', className, text); el.type = 'button'; el.addEventListener('click', action); return el; }
function safeURL(url) {
if (typeof url !== 'string') return null;
try { const parsed = new URL(url, location.href); return ['http:', 'https:'].includes(parsed.protocol) ? parsed.href : null; } catch (_) { return null; }
}
function link(text, url, className) {
const href = safeURL(url);
if (!href) return null;
const el = node('a', className, text); el.href = href;
if (new URL(href).origin !== location.origin) { el.target = '_blank'; el.rel = 'noreferrer'; }
return el;
}
function toast(message) { $('toast').textContent = message; $('toast').hidden = false; clearTimeout(toastTimer); toastTimer = setTimeout(() => { $('toast').hidden = true; }, 3000); }
function save() { try { localStorage.setItem(stateKey, JSON.stringify(guesses)); } catch (_) { toast('This browser cannot save guesses. Your current answer is still recorded for this visit.'); } }
function download(object, name) {
const url = URL.createObjectURL(new Blob([JSON.stringify(object, null, 2) + '\n'], { type: 'application/json' }));
const anchor = document.createElement('a'); anchor.href = url; anchor.download = name; anchor.click(); setTimeout(() => URL.revokeObjectURL(url), 1000);
}
function displayValue(parent, value) {
if (value === undefined || value === null || value === '') return;
if (typeof value === 'string') { parent.append(node('p', '', value)); return; }
if (Array.isArray(value)) { const list = node('ul'); value.forEach(item => append(list, node('li', '', typeof item === 'object' ? JSON.stringify(item) : item))); parent.append(list); return; }
parent.append(node('pre', '', JSON.stringify(value, null, 2)));
}
function metrics(object) {
const list = node('dl', 'metrics');
for (const [key, value] of Object.entries(object || {})) {
if (value === undefined || value === null) continue;
list.append(append(node('div'), node('dt', '', readableKey(key)), node('dd', '', formatMetric(value))));
}
return list.children.length ? list : null;
}
function setupNav() {
$('experimentNav').replaceChildren();
for (const [key, def] of Object.entries(DEFINITIONS)) {
const anchor = node('a', 'experiment-card'); anchor.href = key === 'sound' ? 'sound.html' : 'index.html?experiment=' + key;
anchor.dataset.experiment = key;
append(anchor, node('span', 'card-number', def.number), node('strong', '', def.title), node('small', '', def.short), node('span', 'card-arrow', '↗'));
if (key !== 'sound') anchor.addEventListener('click', event => { event.preventDefault(); selectExperiment(key, null, true); });
$('experimentNav').append(anchor);
}
}
function selectExperiment(key, id, push) {
active = ['reroute', 'quant', 'surgery', 'memory'].includes(key) ? key : 'reroute';
if (!data) {
const url = new URL(location.href); url.searchParams.set('experiment', active); url.searchParams.delete('case'); history.pushState(null, '', url);
return;
}
const cases = data.experiments[active].cases;
currentCase = cases.find(item => item.id === id) || cases[0];
tokenStep = 0;
if (push) {
const url = new URL(location.href); url.searchParams.set('experiment', active); url.searchParams.set('case', currentCase.id); history.pushState(null, '', url);
}
document.querySelectorAll('[data-experiment]').forEach(a => { if (a.dataset.experiment === active) a.setAttribute('aria-current', 'page'); else a.removeAttribute('aria-current'); });
render();
if (push) $('experiment').scrollIntoView({block:'start',behavior:'smooth'});
}
function conditionCard(item, condition, options = {}) {
const card = node('section', 'condition');
const heading = node('div', 'condition-heading');
append(heading, node('h4', '', options.label || condition.label || condition.id));
if (!options.blind) heading.append(node('span', 'tag', [condition.model, condition.quant].filter(Boolean).join(' · ')));
const content = node('div', 'condition-content');
const prompt = promptFor(item, condition);
if (options.showPrompt && prompt) content.append(node('div', 'prompt', prompt));
content.append(node('div', 'output', condition.output || '(The model produced no answer.)'));
if (!options.blind) {
append(content, metrics(condition.metrics));
if (condition.checks) { const detail = node('details'); append(detail, node('summary', '', 'Answer checks')); displayValue(detail, condition.checks); content.append(detail); }
append(content, link('Open measured trace ↗', condition.trace_url, 'condition-link'));
}
append(card, heading, content); return card;
}
function shareButton() {
return button('Share this case ↗', async () => {
const url = new URL(location.href); url.searchParams.set('experiment', active); url.searchParams.set('case', currentCase.id);
try { await navigator.clipboard.writeText(url.href); toast('Case link copied.'); } catch (_) { toast('Copy the address in your browser to share this case.'); }
}, 'quiet share');
}
function render() {
const exp = data.experiments[active], def = DEFINITIONS[active], item = currentCase;
document.title = def.title + ' | alphaBell Experiments';
const fragment = document.createDocumentFragment(), head = node('div', 'experiment-head');
const title = node('h2', '', exp.title || def.title); title.id = 'experimentTitle';
append(head, append(node('div'), node('p', 'eyebrow', 'EXPERIMENT ' + def.number), title, node('p', '', exp.summary || def.summary)));
const picker = node('label', 'case-picker', 'Choose a recorded case'); const select = node('select'); select.id = 'caseSelect';
exp.cases.forEach((c, index) => { const option = node('option', '', String(index + 1).padStart(2, '0') + ' · ' + (c.title || c.id)); option.value = c.id; option.selected = c.id === item.id; select.append(option); });
select.addEventListener('change', () => selectExperiment(active, select.value, true)); append(picker, select); head.append(picker); fragment.append(head);
const layout = node('article', 'case-layout');
const top = node('div', 'case-top'), context = append(node('div'), node('h3', '', item.title || item.id));
if (item.description) context.append(node('p', '', item.description));
if (item.prompt && active !== 'reroute') context.append(node('div', 'prompt', item.prompt));
append(top, context, node('span', 'tag', 'Recorded experiment'));
const body = node('div', 'case-body');
if (active === 'reroute') renderReroute(body, item);
if (active === 'quant') renderQuant(body, item);
if (active === 'surgery') renderSurgery(body, item);
if (active === 'memory') renderMemory(body, item);
const controls = node('div', 'controls');
append(controls, button('Download this case ↓', () => download({ schema_version: data.schema_version, experiment: active, provenance: data.provenance, method: exp.method, case: item }, 'alphabell-' + active + '-' + item.id.replace(/[^a-z0-9_-]/gi, '_') + '.json'), 'quiet'), shareButton());
body.append(controls);
const method = node('details'); append(method, node('summary', '', 'How to read this experiment')); displayValue(method, exp.method); displayValue(method, exp.limitations); displayValue(method, exp.reproduce); if (item.provenance) displayValue(method, item.provenance); body.append(method);
append(layout, top, body); fragment.append(layout); $('experiment').replaceChildren(fragment);
}
function renderReroute(body, item) {
if (item.conditions.every(c => !c.routing)) body.append(node('p', 'annotation', 'Qwen Open uses recurrent heads and dense feed-forward blocks. Compare measured head activity in the full traces; expert grids apply to Flint.'));
body.append(node('div', 'notice', 'A routing change is a measured change in expert selection. It does not identify what an expert means, or prove that an expert caused a particular answer.'));
const frames = item.conditions.map(c => c.routing && c.routing.tokens || []), maxFrames = Math.max(...frames.map(f => f.length));
const columns = node('div', item.conditions.length > 2 ? 'columns three' : 'columns');
const panels = [];
if (maxFrames > 0) {
const controls = node('div', 'token-controls'), label = node('label', '', 'Generated token step'); label.htmlFor = 'routingStep';
const slider = node('input'); slider.type = 'range'; slider.id = 'routingStep'; slider.min = '0'; slider.max = String(maxFrames - 1); slider.value = String(tokenStep);
const readout = node('span', 'token-readout'); readout.setAttribute('aria-live', 'polite');
const update = step => { tokenStep = Math.max(0, Math.min(maxFrames - 1, step)); slider.value = String(tokenStep); readout.textContent = (tokenStep + 1) + ' / ' + maxFrames; panels.forEach(panel => paintRouting(panel, tokenStep)); };
const previous = button('‹', () => update(tokenStep - 1)); previous.setAttribute('aria-label', 'Previous token');
const next = button('›', () => update(tokenStep + 1)); next.setAttribute('aria-label', 'Next token');
slider.addEventListener('input', () => update(Number(slider.value))); append(controls, label, previous, slider, next, readout); body.append(controls);
body.append(node('p', 'annotation', 'The slider pairs tokens by generated position. Different answers may use different token boundaries and stop at different lengths.'));
readout.textContent = (tokenStep + 1) + ' / ' + maxFrames;
}
for (const condition of item.conditions) {
const card = conditionCard(item, condition, { showPrompt: true });
if (condition.routing && Array.isArray(condition.routing.tokens) && condition.routing.tokens.length) {
const panel = node('div', 'routing-panel'); panel.condition = condition;
const label = 'Selected experts' + (Number.isInteger(condition.routing.layer) ? ', block ' + condition.routing.layer : '');
append(panel, node('p', 'routing-label', label), node('code', 'routing-token'), node('div', 'expert-grid'), node('p', 'expert-list'), node('p', 'routing-key', 'Each square is an expert ID. Dark squares were selected in this captured token.'));
const grid = panel.querySelector('.expert-grid');
for (let id = 0; id < (condition.routing.expert_count || 256); id++) { const dot = node('span', 'expert-dot'); dot.title = 'Expert ' + id; dot.setAttribute('aria-hidden', 'true'); grid.append(dot); }
card.querySelector('.condition-content').prepend(panel); panels.push(panel); paintRouting(panel, tokenStep);
}
columns.append(card);
}
body.append(columns);
if (item.comparison) { const details = node('details'); append(details, node('summary', '', 'Measured comparison')); displayValue(details, item.comparison); body.append(details); }
}
function paintRouting(panel, step) {
const frame = routingFrame(panel.condition, step), token = panel.querySelector('.routing-token'), list = panel.querySelector('.expert-list');
token.textContent = frame ? frame.token : 'This answer has ended.';
const experts = new Set(frame ? frame.experts : []);
Array.from(panel.querySelector('.expert-grid').children).forEach((dot, id) => dot.classList.toggle('active', experts.has(id)));
list.textContent = frame ? frame.experts.map((id, index) => '#' + id + (frame.weights ? ' (' + formatMetric(frame.weights[index]) + ')' : '')).join(' · ') : 'No captured frame at this position.';
}
function renderQuant(body, item) {
const ordered = stablePairOrder(item.id, item.conditions), key = 'quant:' + item.id, revealed = reveals.has(key);
body.append(node('p', 'annotation', 'Both answers use the same case prompt. Model names, precision and measurements stay hidden until you reveal the evidence. Answer order is deterministic for each case.'));
const columns = node('div', 'columns'); ordered.forEach((condition, index) => columns.append(conditionCard(item, condition, { blind: !revealed, label: revealed ? 'Answer ' + String.fromCharCode(65 + index) + ' · ' + (condition.label || condition.id) : 'Answer ' + String.fromCharCode(65 + index) }))); body.append(columns);
const panel = node('section', 'guess-panel'); append(panel, node('h4', 'question', 'Which answer came from the 4-bit model?'));
const choices = node('div', 'choice-row');
ordered.forEach((condition, index) => {
const pick = button('Answer ' + String.fromCharCode(65 + index) + ' is 4-bit', () => { guesses.quant[item.id] = condition.id; save(); reveals.add(key); render(); });
pick.disabled = revealed; pick.setAttribute('aria-pressed', String(guesses.quant[item.id] === condition.id)); choices.append(pick);
}); panel.append(choices);
if (!revealed) { const controls = node('div', 'controls'); controls.append(button('Reveal without guessing', () => { reveals.add(key); render(); }, 'quiet')); panel.append(controls); }
else {
const low = ordered.find(isLowBit), guess = guesses.quant[item.id], chosen = ordered.find(c => c.id === guess);
const message = !low ? 'Precision labels are shown above.' : guess ? (isLowBit(chosen) ? 'You spotted it.' : 'The other answer was 4-bit.') : 'The precision is revealed.';
const result = node('div', 'result-note', message);
if (low) result.append(node('p', '', 'Answer ' + String.fromCharCode(65 + ordered.indexOf(low)) + ' used ' + low.quant + '. A single answer is not a quality benchmark. Check the full outputs and measured results.'));
append(panel, result); if (item.checks) displayValue(panel, item.checks);
}
let total = 0, correct = 0;
for (const test of data.experiments.quant.cases) { const guess = guesses.quant[test.id]; if (guess) { total++; const cond = test.conditions.find(c => c.id === guess); if (cond && isLowBit(cond)) correct++; } }
panel.append(node('p', 'local-tally', 'LOCAL ONLY · ' + correct + ' / ' + total + ' correct guesses saved in this browser. No votes are uploaded.'));
body.append(panel);
}
function renderSurgery(body, item) {
const intervention = item.intervention || {};
const notice = node('div', 'notice'); append(notice, node('strong', '', intervention.kind ? readableKey(intervention.kind) : 'Recorded intervention'));
if (intervention.description) notice.append(node('div', '', intervention.description));
if (/prompt/i.test(intervention.kind || '')) notice.append(node('div', '', 'This intervention changes the prompt. It does not modify model weights.'));
if (/activation/i.test(intervention.kind || '')) notice.append(node('div', '', 'This intervention changes activations during execution. It does not rewrite the model weights.'));
body.append(notice);
const columns = node('div', item.conditions.length > 2 ? 'columns three' : 'columns'); item.conditions.forEach(c => columns.append(conditionCard(item, c, { showPrompt: true }))); body.append(columns);
if (item.checks) { const results = node('div', 'result-note'); results.append(node('strong', '', 'Behavior and regression checks')); displayValue(results, item.checks); body.append(results); }
if (item.comparison) { const results = node('details'); results.append(node('summary', '', 'Intervention comparison')); displayValue(results, item.comparison); body.append(results); }
body.append(node('p', 'annotation', 'A visible change is a result for this case and intervention. Check the other cases before generalizing about the model. Negative and unchanged results are useful evidence too.'));
}
function truthText(item) {
return typeof item.ground_truth === 'string' ? item.ground_truth : item.ground_truth && (item.ground_truth.answer || item.ground_truth.value) || JSON.stringify(item.ground_truth);
}
function renderMemory(body, item) {
const key = 'memory:' + item.id, revealed = reveals.has(key), selected = guesses.memory[item.id];
const panel = node('section', 'guess-panel'); append(panel, node('h4', 'question', item.question || 'What should the model answer after the correction?'));
const choices = node('div', 'choice-row');
item.choices.forEach(choice => {
const pick = button(choice, () => { guesses.memory[item.id] = choice; save(); render(); }); pick.disabled = revealed; pick.setAttribute('aria-pressed', String(selected === choice)); choices.append(pick);
}); panel.append(choices);
if (!revealed) {
const reveal = button('Reveal model answers', () => { reveals.add(key); render(); }, 'primary'); reveal.disabled = !selected;
const controls = node('div', 'controls'); controls.append(reveal); panel.append(controls);
panel.append(node('p', 'local-tally', 'Choose the answer the model should give. Your choice stays in this browser.'));
body.append(panel, node('div', 'reveal-blank', 'The captured answers and scoring stay hidden until you make a prediction.'));
} else {
const truth = truthText(item), correct = String(selected).trim().toLowerCase() === String(truth).trim().toLowerCase();
const result = node('div', 'result-note', (correct ? 'Your answer matches the ground truth. ' : 'Compare your prediction with the ground truth. ') + 'Expected answer: ' + truth);
result.append(node('p', '', 'Your prediction: ' + selected + '. The model answers below are separate measured results.'));
panel.append(result); body.append(panel);
const columns = node('div', item.conditions.length > 2 ? 'columns three' : 'columns'); item.conditions.forEach(c => columns.append(conditionCard(item, c, { showPrompt: true }))); body.append(columns);
if (item.checks) { const check = node('div', 'result-note'); check.append(node('strong', '', 'Correction and recall checks')); displayValue(check, item.checks); body.append(check); }
}
body.append(node('p', 'annotation', 'Using an in-context correction and remembering a fact are different tests. The prompts show which information each condition received. These results do not measure a permanent change to model memory.'));
}
function renderMethods() {
const target = $('methodBody'); target.replaceChildren();
if (data.generated_at) target.append(node('p', '', 'Results prepared: ' + new Intl.DateTimeFormat('en-US', {dateStyle:'medium',timeStyle:'short',timeZone:'America/Los_Angeles'}).format(new Date(data.generated_at)) + ' Pacific time.'));
displayValue(target, data.methodology);
const provenance = data.provenance || {};
if (Array.isArray(provenance.model_sources)) {
target.append(node('h3', '', 'Source models'));
const list = node('ul'); provenance.model_sources.forEach(source => { const li = node('li'); append(li, link(source.name || source.url, source.url)); if (source.revision) li.append(document.createTextNode(' · revision ' + source.revision)); list.append(li); }); target.append(list);
}
const detail = { ...provenance }; delete detail.model_sources; displayValue(target, detail);
}
async function load() {
$('experiment').replaceChildren(node('div', 'loading', 'Opening measured experiments…'));
try {
const response = await fetch('data/results.json', { cache: 'no-cache' });
if (!response.ok) throw new Error('The results could not be downloaded (HTTP ' + response.status + ').');
data = validateResults(await response.json());
renderMethods(); $('evidence').hidden = false;
const params = new URLSearchParams(location.search); selectExperiment(params.get('experiment'), params.get('case'), false); if (params.has('experiment')) requestAnimationFrame(() => $('experiment').scrollIntoView({block:'start'}));
} catch (error) {
$('evidence').hidden = true;
const panel = node('div', 'error'); panel.setAttribute('role', 'alert');
append(panel, node('h2', '', 'The measured results could not be opened.'), node('p', '', error.message || 'Please try loading the experiment again.'), button('Try again', load));
$('experiment').replaceChildren(panel);
}
}
setupNav();
$('downloadResults').addEventListener('click', () => { if (data) download(data, 'alphabell-experiment-results.json'); });
$('downloadFixtures').addEventListener('click', () => { if (data) download(fixturesFrom(data), 'alphabell-experiment-prompts.json'); });
$('resetGuesses').addEventListener('click', () => { guesses = { quant: {}, memory: {} }; reveals.clear(); save(); if (data) render(); toast('Local guesses cleared.'); });
window.addEventListener('popstate', () => { if (data) { const params = new URLSearchParams(location.search); selectExperiment(params.get('experiment'), params.get('case'), false); } });
load();
})(typeof window !== 'undefined' ? window : globalThis);
|