Spreadsheet
GPT 5.6 terra · typescript

Browser check: Artifact runtime error. This is a technical check, not a quality review.
Reviews & generation details
Reviews
No human review yet. Unreviewed does not mean unsuccessful.
Generation context
Imported historical artifact. Its exact original instructions and lineage are not known.
Generation prompt · exact text unavailable
See the current prompt for reference. It may differ from the instructions used to generate this artifact.
Inspect original source 17,613 bytes · SHA-256 83b7a95dca49
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Sheet — Formula Spreadsheet</title>
<style>
/* ── css: base ── */
*{box-sizing:border-box}html,body,#app{height:100%;margin:0}body{font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;color:#202124;background:#f8fafd}.sheet-app{height:100%;min-width:760px;display:flex;flex-direction:column;background:white}.topbar{height:58px;display:flex;align-items:center;gap:14px;padding:0 20px;border-bottom:1px solid #e5e9ef;background:#fff;flex:none}.brand{font-size:22px;font-weight:730;letter-spacing:-.7px;color:#1a73e8}.brand span{color:#fbbc04}.icon-btn{border:0;background:transparent;font-size:25px;color:#5f6368;padding:2px 7px}.file-name{font-size:15px;color:#3c4043;font-weight:500;white-space:nowrap}.saved{font-size:11px;font-weight:600;color:#188038;background:#e6f4ea;border-radius:10px;padding:3px 7px;margin-left:7px}.toolbar{margin-left:auto;display:flex;gap:8px}.toolbar button{height:30px;border:1px solid #d6dce5;border-radius:5px;background:#fff;color:#3c4043;font:600 12px inherit;padding:0 10px;cursor:pointer}.toolbar button:hover{border-color:#1a73e8;color:#1a73e8;background:#f4f8ff}.formula-area{height:46px;display:flex;align-items:center;padding:7px 20px;gap:10px;border-bottom:1px solid #dfe3e8;background:#fff;flex:none}.cell-ref{width:56px;text-align:center;font-size:13px;font-weight:650;color:#1a73e8;border-right:1px solid #e3e7eb;padding-right:10px}.fx{font-family:Georgia,serif;font-style:italic;font-size:18px;color:#5f6368;border-right:1px solid #e3e7eb;padding:0 10px 0 2px}#formulaInput{height:30px;min-width:0;flex:1;border:0;outline:0;font:14px ui-monospace,SFMono-Regular,Menlo,monospace;color:#202124;background:transparent}.grid-shell{flex:1;overflow:auto;position:relative;background:#fff}.statusbar{height:28px;flex:none;border-top:1px solid #e4e8ee;background:#f8fafd;padding:0 18px;display:flex;justify-content:space-between;align-items:center;color:#65717d;font-size:11px}table{border-collapse:separate;border-spacing:0;table-layout:fixed;font-size:13px}th,td{height:25px;border-right:1px solid #e6e9ed;border-bottom:1px solid #e6e9ed;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}thead th{height:28px;position:sticky;top:0;z-index:3;background:#f8f9fa;color:#5f6368;font-size:12px;font-weight:600;text-align:center;border-bottom:1px solid #cfd5dc}th.corner{left:0;z-index:5;width:50px;min-width:50px;background:#f1f3f4}th.row-head{position:sticky;left:0;z-index:2;width:50px;min-width:50px;text-align:right;padding-right:10px;background:#f8f9fa;color:#69737d;font-size:11px;font-weight:500}td{width:112px;min-width:112px;padding:3px 7px;background:#fff;outline:none;cursor:cell;color:#202124}td.selected{position:relative;z-index:1;box-shadow:inset 0 0 0 2px #1a73e8;background:#f8fbff}td.editing{padding:0;box-shadow:inset 0 0 0 2px #1a73e8}td.editing input{width:100%;height:100%;padding:3px 7px;border:0;outline:0;font:13px ui-monospace,SFMono-Regular,Menlo,monospace;color:#202124;background:#fff}td.number{text-align:right;font-variant-numeric:tabular-nums}td.error{color:#d93025;font-weight:600}th.active-head{background:#e8f0fe;color:#1967d2}tr:last-child td,tr:last-child th{border-bottom:0}
</style>
</head>
<body>
<div class="sheet-app"><header class="topbar"><div class="brand">Sheet<span>•</span></div><button id="undoBtn" class="icon-btn" title="Undo" disabled="">↶</button><div class="file-name">Untitled spreadsheet <span class="saved" id="saveState">Saved</span></div><div class="toolbar"><button id="insertRowBtn">+ Row</button><button id="deleteRowBtn">− Row</button><button id="insertColBtn">+ Column</button><button id="deleteColBtn">− Column</button></div></header><section class="formula-area"><div id="cellRef" class="cell-ref">A1</div><div class="fx">fx</div><input id="formulaInput" aria-label="Formula bar" autocomplete="off" spellcheck="false" placeholder="Enter a value or formula"></section><main class="grid-shell"><table id="sheetGrid" aria-label="Spreadsheet grid"></table></main><footer class="statusbar"><span id="statusMessage">Ready</span><span>26 columns · 100 rows</span></footer></div>
<script>
(function () {
'use strict';
var __mods = {};
var __cache = {};
var __map = {};
function __require(id) {
if (__cache[id]) return __cache[id].exports;
var module = __cache[id] = { exports: {} };
var factory = __mods[id];
if (!factory) throw new Error("Module not found: " + id);
factory(module.exports, function (spec) {
var target = (__map[id] && __map[id][spec]) || spec;
return __require(target);
}, module);
return module.exports;
}
// ── module: src/main.ts ──
__mods["src/main.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/* Tunable constants */
const COLS = 26, ROWS = 100, STORAGE_KEY = 'sheet-formula-v1';
/* Address helpers */
const colName = (n) => String.fromCharCode(65 + n);
const idOf = (r, c) => `${colName(c)}${r + 1}`;
function pos(id) { const m = /^([A-Z])([1-9]\d*)$/.exec(id); return m ? { r: +m[2] - 1, c: m[1].charCodeAt(0) - 65 } : null; }
function valid(id) { const p = pos(id); return !!p && p.c < COLS && p.r < ROWS; }
function tokens(s) { const a = []; let i = 0; while (i < s.length) {
if (/\s/.test(s[i])) {
i++;
continue;
}
const rest = s.slice(i), n = /^(?:\d+\.?\d*|\.\d+)/.exec(rest), ref = /^[A-Za-z][1-9]\d*/.exec(rest), word = /^[A-Za-z]+/.exec(rest);
if (n) {
a.push({ t: 'num', s: n[0] });
i += n[0].length;
}
else if (ref) {
a.push({ t: 'ref', s: ref[0].toUpperCase() });
i += ref[0].length;
}
else if (word) {
a.push({ t: 'word', s: word[0].toUpperCase() });
i += word[0].length;
}
else if ('+-*/(),:'.includes(s[i])) {
a.push({ t: s[i], s: s[i++] });
}
else
throw Error('token');
} a.push({ t: 'end', s: '' }); return a; }
function parseFormula(raw) { const a = tokens(raw.slice(1)), deps = []; let i = 0; const eat = (t) => { if (a[i].t !== t)
throw Error('parse'); return a[i++]; }; const expression = () => { let x = term(); while (a[i].t === '+' || a[i].t === '-') {
const op = a[i++].t;
x = { k: 'bin', op, l: x, r: term() };
} return x; }; const term = () => { let x = factor(); while (a[i].t === '*' || a[i].t === '/') {
const op = a[i++].t;
x = { k: 'bin', op, l: x, r: factor() };
} return x; }; const factor = () => { if (a[i].t === '-') {
i++;
return { k: 'un', x: factor() };
} if (a[i].t === 'num')
return { k: 'num', v: +a[i++].s }; if (a[i].t === 'ref') {
const x = eat('ref').s;
if (a[i].t === ':') {
i++;
return { k: 'range', a: x, b: eat('ref').s };
}
return { k: 'ref', id: x };
} if (a[i].t === 'word') {
const n = eat('word').s;
eat('(');
const args = [];
if (a[i].t !== ')') {
args.push(expression());
while (a[i].t === ',') {
i++;
args.push(expression());
}
}
eat(')');
if (!['SUM', 'AVG', 'AVERAGE', 'MIN', 'MAX', 'COUNT'].includes(n))
throw Error('function');
return { k: 'fn', n, args };
} if (a[i].t === '(') {
i++;
const x = expression();
eat(')');
return x;
} throw Error('factor'); }; const ast = expression(); if (a[i].t !== 'end')
throw Error('trailing'); return ast; }
function refs(ast, out = new Set()) { if (ast.k === 'ref')
out.add(ast.id);
else if (ast.k === 'range') {
const x = pos(ast.a), y = pos(ast.b);
if (x && y)
for (let r = Math.min(x.r, y.r); r <= Math.max(x.r, y.r); r++)
for (let c = Math.min(x.c, y.c); c <= Math.max(x.c, y.c); c++)
out.add(idOf(r, c));
else {
out.add(ast.a);
out.add(ast.b);
}
}
else if (ast.k === 'un')
refs(ast.x, out);
else if (ast.k === 'bin') {
refs(ast.l, out);
refs(ast.r, out);
}
else if (ast.k === 'fn')
ast.args.forEach(x => refs(x, out)); return out; }
/* Model, dependency graph and incremental recalculation */
const raw = new Map(), output = new Map(), asts = new Map(), dependents = new Map();
function rebuild() { asts.clear(); dependents.clear(); raw.forEach((v, id) => { if (v.startsWith('=')) {
try {
const ast = parseFormula(v);
asts.set(id, ast);
refs(ast).forEach(d => { let x = dependents.get(d); if (!x)
dependents.set(d, x = new Set); x.add(id); });
}
catch { }
} }); }
function evalAst(ast, get) { const num = (z) => { if (z.error)
return z; if (typeof z.value === 'number')
return z; const n = Number(z.value); return z.value !== '' && !Number.isNaN(n) ? { value: n } : { value: NaN, error: '#ERR!' }; }; const vals = (x) => { if (x.k === 'range') {
const a = pos(x.a), b = pos(x.b);
if (!a || !b)
return [{ value: '', error: '#REF!' }];
const z = [];
for (let r = Math.min(a.r, b.r); r <= Math.max(a.r, b.r); r++)
for (let c = Math.min(a.c, b.c); c <= Math.max(a.c, b.c); c++)
z.push(get(idOf(r, c)));
return z;
} return [evalAst(x, get)]; }; if (ast.k === 'num')
return { value: ast.v }; if (ast.k === 'ref')
return get(ast.id); if (ast.k === 'range')
return { value: '', error: '#ERR!' }; if (ast.k === 'un') {
const x = num(evalAst(ast.x, get));
return x.error ? x : { value: -x.value };
} if (ast.k === 'bin') {
const l = num(evalAst(ast.l, get)), r = num(evalAst(ast.r, get));
if (l.error)
return l;
if (r.error)
return r;
const lv = l.value, rv = r.value;
if (ast.op === '/' && rv === 0)
return { value: '', error: '#DIV/0!' };
return { value: ast.op === '+' ? lv + rv : ast.op === '-' ? lv - rv : ast.op === '*' ? lv * rv : lv / rv };
} const all = ast.args.flatMap(vals); const bad = all.find(x => x.error); if (bad)
return bad; const ns = all.map(x => typeof x.value === 'number' ? x.value : (x.value !== '' ? Number(x.value) : NaN)).filter(x => !Number.isNaN(x)); if (ast.n === 'COUNT')
return { value: ns.length }; if (ast.n === 'SUM')
return { value: ns.reduce((a, b) => a + b, 0) }; if (!ns.length)
return { value: 0 }; if (ast.n === 'MIN')
return { value: Math.min(...ns) }; if (ast.n === 'MAX')
return { value: Math.max(...ns) }; return { value: ns.reduce((a, b) => a + b, 0) / ns.length }; }
function recalc(changed) { const impacted = new Set(); if (changed) {
const q = [changed];
while (q.length) {
const x = q.pop();
if (impacted.has(x))
continue;
impacted.add(x);
dependents.get(x)?.forEach(y => q.push(y));
}
}
else
raw.forEach((_v, k) => impacted.add(k)); const state = new Map(), stack = [], cycle = new Set(); const calc = (id) => { if (!valid(id))
return { value: '', error: '#REF!' }; if (!impacted.has(id))
return output.get(id) ?? { value: raw.get(id) ?? '' }; const st = state.get(id) || 0; if (st === 2)
return output.get(id); if (st === 1) {
const at = stack.indexOf(id);
stack.slice(at).forEach(x => cycle.add(x));
return { value: '', error: '#CYCLE!' };
} state.set(id, 1); stack.push(id); const text = raw.get(id) ?? ''; let v; if (!text.startsWith('='))
v = { value: text };
else {
const ast = asts.get(id);
if (!ast)
v = { value: '', error: '#ERR!' };
else
v = evalAst(ast, calc);
} stack.pop(); if (cycle.has(id))
v = { value: '', error: '#CYCLE!' }; output.set(id, v); state.set(id, 2); return v; }; impacted.forEach(calc); renderCells(); }
/* Grid UI and editing */
const table = document.querySelector('#sheetGrid'), formula = document.querySelector('#formulaInput'), refLabel = document.querySelector('#cellRef'), status = document.querySelector('#statusMessage');
let selected = 'A1', editor = null;
function makeGrid() { let html = '<thead><tr><th class="corner"></th>'; for (let c = 0; c < COLS; c++)
html += `<th data-col="${c}">${colName(c)}</th>`; html += '</tr></thead><tbody>'; for (let r = 0; r < ROWS; r++) {
html += `<tr><th class="row-head" data-row="${r}">${r + 1}</th>`;
for (let c = 0; c < COLS; c++)
html += `<td data-id="${idOf(r, c)}"></td>`;
html += '</tr>';
} table.innerHTML = html + '<tbody>'; }
function show(v) { return v.error ?? (v.value === undefined ? '' : String(v.value)); }
function renderCells() { table.querySelectorAll('td[data-id]').forEach(td => { if (td.classList.contains('editing'))
return; const id = td.dataset.id, v = output.get(id) ?? { value: raw.get(id) ?? '' }; td.textContent = show(v); td.className = ''; if (id === selected)
td.classList.add('selected'); if (v.error)
td.classList.add('error');
else if (typeof v.value === 'number' || (v.value !== '' && !Number.isNaN(Number(v.value))))
td.classList.add('number'); }); refLabel.textContent = selected; formula.value = raw.get(selected) ?? ''; const p = pos(selected); table.querySelectorAll('th.active-head').forEach(x => x.classList.remove('active-head')); table.querySelector(`th[data-col="${p.c}"]`)?.classList.add('active-head'); table.querySelector(`th[data-row="${p.r}"]`)?.classList.add('active-head'); }
function select(id) { if (!valid(id))
return; if (editor)
commit(); selected = id; renderCells(); }
function save() { localStorage.setItem(STORAGE_KEY, JSON.stringify([...raw])); status.textContent = 'Saved'; }
function commit(value) { if (!editor && value === undefined)
return; const v = value ?? editor.value; const old = raw.get(selected) ?? ''; if (v)
raw.set(selected, v);
else
raw.delete(selected); editor?.remove(); editor = null; rebuild(); recalc(selected); save(); if (old !== v)
status.textContent = 'Recalculated'; }
function beginEdit(seed) { const td = table.querySelector(`td[data-id="${selected}"]`); if (editor)
return; editor = document.createElement('input'); editor.value = seed ?? (raw.get(selected) ?? ''); td.textContent = ''; td.className = 'editing selected'; td.append(editor); editor.focus(); editor.setSelectionRange(editor.value.length, editor.value.length); editor.addEventListener('keydown', e => { if (e.key === 'Enter') {
e.preventDefault();
commit();
move(1, 0);
}
else if (e.key === 'Tab') {
e.preventDefault();
commit();
move(0, e.shiftKey ? -1 : 1);
}
else if (e.key === 'Escape') {
editor?.remove();
editor = null;
renderCells();
} }); editor.addEventListener('blur', () => { if (editor)
commit(); }); }
function move(dr, dc) { const p = pos(selected); select(idOf(Math.max(0, Math.min(ROWS - 1, p.r + dr)), Math.max(0, Math.min(COLS - 1, p.c + dc)))); }
table.addEventListener('mousedown', e => { const td = e.target.closest('td[data-id]'); if (td)
select(td.dataset.id); });
table.addEventListener('dblclick', e => { const td = e.target.closest('td[data-id]'); if (td) {
select(td.dataset.id);
beginEdit();
} });
document.addEventListener('keydown', e => { if (editor || document.activeElement === formula)
return; if (e.key === 'ArrowUp' || e.key === 'ArrowDown' || e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
e.preventDefault();
move(e.key === 'ArrowDown' ? 1 : e.key === 'ArrowUp' ? -1 : 0, e.key === 'ArrowRight' ? 1 : e.key === 'ArrowLeft' ? -1 : 0);
}
else if (e.key === 'Delete' || e.key === 'Backspace') {
e.preventDefault();
commit('');
}
else if (e.key.length === 1 && !e.ctrlKey && !e.metaKey)
beginEdit(e.key); });
formula.addEventListener('input', () => { raw.set(selected, formula.value); rebuild(); recalc(selected); save(); });
formula.addEventListener('keydown', e => { if (e.key === 'Enter') {
e.preventDefault();
formula.blur();
move(1, 0);
} });
/* Structural editing: rewrite A1 references in every formula */
function rewrite(axis, at, del) { const rewriteText = (s) => s.replace(/\b([A-Z])([1-9]\d*)\b/g, (_m, letter, num) => { let c = letter.charCodeAt(0) - 65, r = +num - 1, n = axis === 'row' ? r : c; if (del && n === at)
return '#REF!'; if (n > (at) || (!del && n >= at)) {
if (axis === 'row')
r += del ? -1 : 1;
else
c += del ? -1 : 1;
} return idOf(r, c); }); raw.forEach((v, k) => { if (v.startsWith('='))
raw.set(k, rewriteText(v)); }); rebuild(); recalc(); save(); status.textContent = `${del ? 'Deleted' : 'Inserted'} ${axis}`; }
document.querySelector('#insertRowBtn').addEventListener('click', () => rewrite('row', pos(selected).r, false));
document.querySelector('#deleteRowBtn').addEventListener('click', () => rewrite('row', pos(selected).r, true));
document.querySelector('#insertColBtn').addEventListener('click', () => rewrite('col', pos(selected).c, false));
document.querySelector('#deleteColBtn').addEventListener('click', () => rewrite('col', pos(selected).c, true));
/* Startup persistence */
try {
const saved = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
saved.forEach(([k, v]) => { if (valid(k) && typeof v === 'string')
raw.set(k, v); });
}
catch {
localStorage.removeItem(STORAGE_KEY);
}
makeGrid();
rebuild();
recalc();
};
// ── entry ──
__require("src/main.ts");
})();
</script>
</body>
</html>
<!-- agent-meta {"model":"openai/gpt-5.6-terra","provider":"openrouter","persona":"typescript","sessionId":"6960bdcf-4b2f-48f9-9c8f-12ac386b1b16","tokensIn":595463,"tokensOut":13338,"tokensTotal":608801,"turns":44,"toolCalls":49,"failedToolCalls":1,"timestamp":"2026-07-31T17:48:24.459Z"} -->