← Spreadsheet results

Spreadsheet

lfm2 24b a2b · basic

Initial view of Spreadsheet

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 15,862 bytes · SHA-256 1e77bb054ba3
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Spreadsheet App</title>
<style>
  :root {--cols:26;--rows:100;--cell-w:8.5rem;--cell-h:38px;--bg:#f9fafb;--header-bg:#e0e7f9;--header-text:#1a202d;--selected:#90caf9;--formula-bg:#fff;--formula-bar:#e0e7f9;--text:#111;--error:red;--border:1px solid #d1e8ff;}
  * {box-sizing:border-box;}
  body {margin:0;font-family:system-ui, -apple-system, Segoe UI, Roboto, sans-serif; background:#f0f4f8;color:#111;}
  .container {max-width:1200px;margin:0 auto;padding:1rem;}
  .formula-bar {background:#e0e7f9;border:1px solid #d1e8ff;padding:0.5rem 1rem;font-family:system-ui;font-size:0.95rem;user-select:none;margin-bottom:0.5rem;}
  table {width:100%;border-collapse:collapse;background:#fff;border:var(--border);}
  th, td {text-align:left;vertical-align:top;border:1px solid #d1e8ff;padding:0.25rem 0.5rem;}
  th {background:#e0e7f9;font-weight:600;color:#1a202d;}
  td {min-height:38px;vertical-align:top;border:var(--border);}
  .selected {background:#90caf9;color:#fff;border:none;outline:none;z-index:2;}
  .error {color:#c00;font-weight:600;}
  .toolbar {display:flex;gap:0.5rem;margin-bottom:0.5rem;}
  button {background:#e0e7f9;border:1px solid #d1e8ff;color:#111;padding:0.25rem 0.75rem;border-radius:4px;cursor:pointer;font-size:0.9rem;}
  button:hover {background:#d1e8ff;border:1px solid #b3c3ff;}
  .colheader, .rowheader {white-space:nowrap;}
  .rowheader {position:sticky;top:0;z-index:1;background:#f0f4f8;border-bottom:1px solid #d1e8ff;}
  .colheader {font-weight:600;color:#1a202d;}
  .formula-edit {width:100%;padding:0.25rem;border:none;border-bottom:2px dashed #d1e8ff;font-family:system-ui;font-size:0.9rem;}
  .formula-bar-edit {width:100%;padding:0.25rem;border:none;border-bottom:2px dashed #d1e8ff;font-family:system-ui;font-size:0.9rem;background:#f0f4f8;}
</style>
</head>
<body>
<div class="container">
  <div class="formula-bar" id="formulaBar"></div>
  <table id="sheet">
    <thead>
      <tr>
        <th class="colheader">A</th>
        <!-- Generated dynamically -->
      </tr>
    </thead>
    <tbody></tbody>
  </table>
  <div class="toolbar">
    <button id="addCol">⇧⇒</button>
    <button id="delCol">✕</button>
    <button id="addRow">⇧⇒</button>
    <button id="delRow">✕</button>
  </div>
</div>

<script>
/* === CONFIG === */
const GRID_SIZE = {cols:26, rows:100};
const CELL_WIDTH = 8.5;
const CELL_HEIGHT = 38;
const COLOR_BG = "#f9fafb";
const COLOR_HEADER = "#e0e7f9";
const COLOR_SELECTED = "#90caf9";
const COLOR_FORMULA = "#e0e7f9";
const COLOR_TEXT = "#111";
const COLOR_ERROR = "#c00";

/* === STATE === */
let state = {
  values: Array.from({length:GRID_SIZE.rows}, ()=>Array(GRID_SIZE.cols).fill(null)),
  formulas: Array.from({length:GRID_SIZE.rows}, ()=>Array(GRID_SIZE.cols).fill(null)),
  selectedRow:0, selectedCol:0,
  history:[],
  cols: Array.from({length:GRID_SIZE.cols}, (_,i)=>String.fromCharCode(65+i)),
  rows: Array.from({length:GRID_SIZE.rows}, (_,r)=>r),
  activeCell: null,
  errorCells: new Set(),
  dependencyMap: new Map(), // cell -> [dependents]
  reverseDeps: new Map(), // cell -> [dependents]
  formulaBarEl: null,
  sheetEl: null
};

/* === UTILS === */
function getCell(row, col) { return state.values[row][col]; }
function setCell(row, col, value) {
  if (value===undefined) value = null;
  state.values[row][col] = value;
  updateDependencies(row, col);
}
function getFormula(row, col) { return state.formulas[row][col]; }
function setFormula(row, col, f) { state.formulas[row][col]=f; updateDependencies(row, col); }
function getDependencies(row, col) {
  let deps=[];
  const f = getFormula(row, col);
  if (!f) return deps;
  // simple regex for refs and ranges
  const matches = f.match(/([A-Za-z]+[0-9]+(?:,[0-9]+)*)|[A-Za-z]+|\d+/g);
  if (matches) {
    for (let m of matches) {
      if (m[0]==='=') continue;
      if (/^[A-Za-z]+$/.test(m)) deps.push({type:'ref',val:m});
      else if (/^\d+$/.test(m)) deps.push({type:'num',val:Number(m)});
      else if (m.match(/^[A-Za-z]+$/) && m.length===2 && /^[A-Za-z]$/.test(m[0])) {
        // range
        const rg = m.slice(1, -1).split(':');
        if (rg[0]&&rg[1]) {
          let r1 = parseInt(rg[0],10), c1=rg[0].charCodeAt(0)-65, r2=parseInt(rg[1],10),c2=rg[1].charCodeAt(0)-65;
          for (let r=r1;r<=r2;r++) for (let c=c1;c<=c2;c++) deps.push({type:'cell',val:`${String.fromCharCode(r+65)}${c+1}`});
        } else if (m.length===2) {
          // single ref
          let r1 = parseInt(m[0],10), c1=m[0].charCodeAt(0)-65, r2=r1, c2=c1;
          deps.push({type:'cell',val:`${String.fromCharCode(r1+65)}${c1}`});
        }
      }
    }
  }
  return deps;
}
function isFormula(cell) { return typeof cell==='string' && cell&&cell[0]==='='; }

/* === DEPENDENCY GRAPH === */
function buildDependencyMap() {
  state.dependencyMap.clear(); state.reverseDeps.clear();
  for (let r=0;r<GRID_SIZE.rows;r++) {
    for (let c=0;c<GRID_SIZE.cols;c++) {
      const f = getFormula(r,c);
      if (!f) continue;
      const deps = getDependencies(r,c);
      state.dependencyMap.set(`${r},${c}`, deps);
      for (let dep of deps) {
        if (dep.type==='cell') {
          let depKey = `${dep.val},${r},${c}`;
          if (!state.reverseDeps.has(depKey)) state.reverseDeps.set(depKey, []);
          state.reverseDeps.get(depKey).push({from:r,c: c});
        }
      }
    }
  }
}
function updateDependencies(row, col) {
  buildDependencyMap();
  // topological sort and recalc
  const visited = new Set(), stack=[];
  function dfs(r,c) {
    if (visited.has(`${r},${c}`)) return;
    visited.add(`${r},${c}`);
    const deps = state.dependencyMap.get(`${r},${c}`)|| [];
    for (let dep of deps) {
      if (dep.type==='cell') {
        let [r2,c2]=dep.val.split(',').map(x=>parseInt(x,10));
        dfs(r2,c2);
      }
    }
    stack.push({r,c});
  }
  for (let r=0;r<GRID_SIZE.rows;r++) for (let c=0;c<GRID_SIZE.cols;c++) dfs(r,c);
  // recalc in reverse order
  for (let i=stack.length-1;i>=0;i--) {
    const {r,c}=stack[i];
    let f = getFormula(r,c);
    if (!f) { setCell(r,c,null); continue; }
    try {
      // simple eval not allowed; use recursive descent
      let val = parseExpression(f, r, c);
      setCell(r,c,val);
    } catch(e) {
      setCell(r,c,null);
      state.errorCells.add(`${r},${c}`);
    }
  }
  // detect cycles
  const inCycle = new Set();
  function detectCycle(startR,startC) {
    let visited=[], stack=[];
    function dfs(r,c) {
      if (visited.has(`${r},${c}`)) return true;
      visited.push(`${r},${c}`);
      stack.push(`${r},${c}`);
      const deps = state.dependencyMap.get(`${r},${c}`)|| [];
      for (let dep of deps) {
        if (dep.type==='cell') {
          let [r2,c2]=dep.val.split(',').map(x=>parseInt(x,10));
          if (stack.includes(`${r2},${c2}`)) return true;
          if (detectCycle(r2,c2)) return true;
        }
      }
      stack.pop();
      return false;
    }
    return dfs(startR,startC);
  }
  for (let r=0;r<GRID_SIZE.rows;r++) {
    for (let c=0;c<GRID_SIZE.cols;c++) {
      if (state.errorCells.has(`${r},${c}`)) continue;
      if (detectCycle(r,c)) {
        state.errorCells.add(`${r},${c}`);
        setCell(r,c,null);
      }
    }
  }
}
function parseExpression(expr, r, c) {
  // tokenize
  const tokens = expr.match(/([A-Za-z]+[0-9]*|[A-Za-z]+|\d+|[()+\-*/])/g) || [];
  if (!tokens.length) throw new Error('empty');
  // shunting yard
  let output=[], ops=[];

  const precedence = {
    '+':1, '-':1, '*':2, '/':2, '^':3,
    '(':0, ')':-1
  };
  function apply(op) {
    if (op==='(') return;
    if (op===')') { while(ops[ops.length-1]!=='(') output.push(ops.pop()); }
    else {
      while (ops.length && precedence[ops[ops.length-1]]>=precedence[op]) output.push(ops.pop());
      ops.push(op);
    }
  }
  for (let t of tokens) {
    if (/^[A-Za-z]+$/.test(t)) output.push(t);
    else if (!isNaN(Number(t))) output.push(Number(t));
    else if (t==='(') ops.push(t);
    else if (t===')') { while(ops[ops.length-1]!=='(') output.push(ops.pop()); ops.pop(); }
    else if (['+','-','*','/','^'].includes(t)) {
      while (ops.length && ops[ops.length-1]!=='(' && precedence[ops[ops.length-1]]>=precedence[t]) apply(ops.pop());
      ops.push(t);
    }
  }
  while (ops.length) output.push(ops.pop());

  // eval
  let stack=[];
  for (let tok of output) {
    if (typeof tok==='number') stack.push(tok);
    else if ('ABCDEFGHIJKLMNOPQRSTUVWXYZ'.includes(tok)) {
      stack.push({type:'cell',val:tok});
    } else if (typeof tok==='string' && /^[A-Za-z]+$/.test(tok)) {
      let r=r,c=c;
      stack.push({type:'ref',val:tok});
    } else if (tok==='(') continue;
    else if (tok===')') continue;
    else if (['+','-','*','/'].includes(tok)) {
      let b=stack.pop(), a=stack.pop();
      switch(tok) {
        case '+': stack.push(a+b); break;
        case '-': stack.push(a-b); break;
        case '*': stack.push(a*b); break;
        case '/':
          if (b===0) throw new Error('#DIV/0!');
          stack.push(a/b);
          break;
      }
    } else if (tok==='^') {
      let b=stack.pop(); let a=stack.pop();
      stack.push(Math.pow(a,b));
    }
  }
  return stack.pop();
}

/* === UI === */
function render() {
  state.sheetEl = document.getElementById('sheet');
  state.formulaBarEl = document.getElementById('formulaBar');
  // header
  let header = document.createElement('tr');
  header.innerHTML = `<th class="colheader">A</th>`;
  for (let i=1;i<GRID_SIZE.cols;i++) header.innerHTML += `<th class="colheader">${String.fromCharCode(65+i)}</th>`;
  state.sheetEl.appendChild(header);
  // rows
  let tbody = document.querySelector('tbody');
  tbody.innerHTML='';
  for (let r=0;r<GRID_SIZE.rows;r++) {
    const tr = document.createElement('tr');
    // row header
    let th = document.createElement('th');
    th.className='rowheader';
    th.textContent = String.fromCharCode(65+r);
    tr.appendChild(th);
    for (let c=0;c<GRID_SIZE.cols;c++) {
      const cell = document.createElement('td');
      cell.className = 'cell';
      let val = getCell(r,c);
      let f = getFormula(r,c);
      if (val!==null) {
        cell.textContent = val;
        if (typeof val==='number') cell.style.color='#111'; else cell.style.font-weight=0;
      } else if (f!==null) {
        cell.textContent = f;
        cell.style.color=cell.errorCells.has(`${r},${c}`)?COLOR_ERROR:COLOR_TEXT;
        cell.style.font-weight=0;
      } else {
        cell.textContent = '';
        cell.style.color=cell.errorCells.has(`${r},${c}`)?COLOR_ERROR:COLOR_TEXT;
        cell.style.font-weight=0;
      }
      // selection
      if (r===state.selectedRow && c===state.selectedCol) cell.classList.add('selected');
      cell.onclick = ()=>selectCell(r,c);
      cell.oncontextmenu = e=>{e.preventDefault(); showContextMenu(e, r,c);};
      tr.appendChild(cell);
    }
    tbody.appendChild(tr);
  }
  // formula bar
  state.formulaBarEl.innerHTML = state.formulas[state.selectedRow][state.selectedCol]||'';
  document.getElementById('addCol').onclick = ()=>addColumn();
  document.getElementById('delCol').onclick = ()=>delColumn();
  document.getElementById('addRow').onclick = ()=>addRow();
  document.getElementById('delRow').onclick = ()=>delRow();
  // set initial selection
  state.selectedRow=0; state.selectedCol=0;
  render();
}

function selectCell(r,c) {
  state.selectedRow=r; state.selectedCol=c;
  render();
}
function showContextMenu(e, r, c) {
  let menu = document.createElement('ul');
  menu.style.position='absolute';menu.style.left=e.clientX+'px';menu.style.top=e.clientY+'px';menu.style.zIndex=2;menu.style.background=COLOR_BG;menu.style.border=`1px solid #d1e8ff`;menu.style.padding=0.25rem;
  ['Delete','Insert Row','Insert Column'].forEach(t=>{
    let li=document.createElement('li'); li.textContent=t; li.onclick=()=>{ e.preventDefault(); if (t==='Delete') delRow(r); else if(t==='Insert Row') addRow(r+1); else if(t==='Insert Column') insertColumn(c+1); };
    menu.appendChild(li);
  });
  document.body.appendChild(menu);
  menu.onclick=e=>{document.body.removeChild(menu);};
}

/* === INPUT HANDLERS === */
function handleInput(e, r,c) {
  if (e.key==='Enter'||e.key==='Tab') {
    e.preventDefault();
    let nextR = r+1, nextC = c;
    if (e.key==='Tab') { nextR=r; nextC=(c%GRID_SIZE.cols)+1; }
    if (nextR>=GRID_SIZE.rows) { nextR=0; nextC=c+1; if(nextC>=GRID_SIZE.cols) { alert('Column limit'); return; }
    }
    setCell(nextR,nextC,null); // clear cell
    setCell(r,c,null);
    state.selectedRow=nextR; state.selectedCol=nextC;
    render();
  } else if (e.key==='Escape') {
    let v=getCell(r,c); setCell(r,c,v); render();
  } else if (e.key==='Backspace'||e.key==='Delete') {
    let v = getCell(r,c);
    if (v===null) return;
    let prev = getCell(r,c-1), prevVal = prev !== null ? prev : '';
    if (prevVal==='') prevVal = null;
    setCell(r,c,prev);
    render();
  } else if (e.key==='ArrowDown' || e.key==='ArrowUp') {
    let dr=e.key==='ArrowDown'?1:-1, nc=c; nc=(c+dr+GRID_SIZE.cols)%GRID_SIZE.cols;
    setCell(r+dr,nc,null); setCell(r,c,null); state.selectedRow=r+dr; state.selectedCol=nc; render();
  } else if (e.key==='ArrowRight' || e.key==='Tab') {
    let nr=r, nc=(c+1)%GRID_SIZE.cols;
    setCell(nr,nc,null); setCell(r,c,null); state.selectedRow=nr; state.selectedCol=nc; render();
  } else if (e.key==='ArrowLeft') {
    let nr=r, nc=(c-1+GRID_SIZE.cols)%GRID_SIZE.cols;
    setCell(nr,nc,null); setCell(r,c,null); state.selectedRow=nr; state.selectedCol=nc; render();
  } else if (e.key==='ControlOrMeta' && e.key==='z') {
    let v = getCell(r,c); setCell(r,c,null); render();
  }
  // handle formula bar
  if (e.target.id==='formulaBarInput') {
    let v = e.target.value;
    let f = e.target.value;
    if (f&&f[0]==='=') {
      let newF = v.slice(1).trim();
      setFormula(r,c,newF);
      e.target.value=newF;
    } else {
      let nv = e.target.value;
      if (nv===undefined||nv=='') setFormula(r,c,null);
      else setFormula(r,c,nv);
      e.target.value=nv;
    }
  }
}
document.getElementById('formulaBar').addEventListener('input',e=>{
  if (e.target.id==='formulaBarInput') {
    let v = e.target.value;
    if (v&&v[0]==='=') setFormula(state.selectedRow,state.selectedCol,v.slice(1).trim());
  }
});

/* === INIT === */
render();
window.addEventListener('load',()=>{
  // init
  buildDependencyMap();
  // load from localStorage if any
  try {
    let s = localStorage.getItem('spreadsheet');
    if (s) {
      let data=JSON.parse(s);
      for (let r=0;r<GRID_SIZE.rows;r++) {
        for (left of data[r] || []) {
          let v=r, c=data[r].indexOf(left);
          if (v!==undefined&&c!==undefined) setCell(v,c,left);
        }
      }
    }
  } catch(e){}
  // save on change
  function save() {
    let data=[];
    for (let r=0;r<GRID_SIZE.rows;r++) {
      let row=[];
      for (let c=0;c<GRID_SIZE.cols;c++) {
        let v = getCell(r,c);
        let f = getFormula(r,c);
        if (v===null) row.push(null); else row.push(JSON.stringify({val:v,formula:f}));
      }
      data[r]=row;
    }
    localStorage.setItem('spreadsheet',JSON.stringify(data));
  }
  // bind
  document.getElementById('formulaBar').oninput = e=>handleInput(e,state.selectedRow,state.selectedCol);
  window.addEventListener('keydown',e=>handleInput(e,state.selectedRow,state.selectedCol));
  window.addEventListener('keyup',e=>handleInput(e,state.selectedRow,state.selectedCol));
  window.addEventListener('blur',(()=>save()));
});
</script>
</body>
</html>
<!-- agent-meta {"model":"liquid/lfm2-24b-a2b","provider":"lmstudio","persona":"basic","sessionId":"31065c4c-aa12-4b4b-8e7a-da0cd8b4487b","tokensIn":1229,"tokensOut":5418,"tokensTotal":6647,"cost":0,"turns":1,"toolCalls":0,"failedToolCalls":0,"timestamp":"2026-08-01T04:49:18.324Z"} -->