← Spreadsheet results

Spreadsheet

nex agi Nex N2.5 mini GGUF · typescript

Initial view of Spreadsheet

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 33,677 bytes · SHA-256 331ca01c7c5f
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Spreadsheet</title>
<style>
/* ── css: app ── */
:root {
  --gridline: #d7dce2;
  --gridline-strong: #b8c0ca;
  --header-bg: #f7f8fa;
  --header-text: #3c4043;
  --blue: #1a73e8;
  --blue-soft: #e8f0fe;
  --text: #202124;
  --muted: #5f6368;
  --danger: #c5221f;
  --shadow: 0 1px 2px rgba(60,64,67,.18), 0 1px 3px rgba(60,64,67,.12);
}

* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body { font-family: Arial, Helvetica, sans-serif; color: var(--text); background: #fff; overflow: hidden; }
#app { display: flex; flex-direction: column; height: 100%; min-width: 760px; }

.topbar { height: 48px; display: flex; align-items: center; gap: 12px; padding: 0 14px; border-bottom: 1px solid #e2e5e9; background: #fff; }
.brand { font-size: 15px; font-weight: 700; letter-spacing: .02em; }
.brand span { color: var(--blue); }
.toolbar { display: flex; align-items: center; gap: 6px; margin-left: 12px; flex: 1; }
button { height: 30px; border: 1px solid #d2d7dd; background: #fff; color: var(--text); border-radius: 4px; padding: 0 10px; font-size: 12px; cursor: pointer; }
button:hover { background: #f5f8ff; border-color: #aebbdd; }
button:active { background: var(--blue-soft); }
button.primary { background: var(--blue); border-color: var(--blue); color: #fff; }
button.primary:hover { background: #1765cc; }
button:disabled { opacity: .45; cursor: default; }
.toolbar-separator { width: 1px; height: 20px; background: #e2e5e9; margin: 0 2px; }
.status { font-size: 12px; color: var(--muted); white-space: nowrap; }

.formula-bar { height: 42px; display: flex; align-items: center; gap: 10px; padding: 6px 12px; border-bottom: 1px solid #e2e5e9; background: #fbfcfd; }
.name-box { width: 66px; height: 30px; border: 1px solid transparent; background: transparent; font-size: 13px; font-weight: 700; color: var(--text); outline: none; }
.name-box:focus { border-color: var(--blue); background: #fff; }
.formula-separator { width: 1px; height: 24px; background: #d8dde3; }
.formula-input { flex: 1; height: 32px; border: 1px solid #d2d7dd; border-radius: 4px; padding: 0 10px; font-size: 13px; color: var(--text); outline: none; background: #fff; }
.formula-input:focus { border-color: var(--blue); box-shadow: 0 0 0 2px rgba(26,115,232,.12); }
.mini-label { font-size: 12px; color: var(--muted); }

.sheet-shell { flex: 1; min-height: 0; overflow: auto; position: relative; background: #fff; }
#grid-table { border-collapse: separate; border-spacing: 0; table-layout: fixed; font-size: 13px; background: #fff; }
#grid-table th, #grid-table td { border-right: 1px solid var(--gridline); border-bottom: 1px solid var(--gridline); padding: 0; height: 28px; min-width: 112px; }
#grid-table th { position: sticky; z-index: 3; background: var(--header-bg); color: var(--header-text); font-weight: 700; text-align: center; font-size: 12px; user-select: none; }
#grid-table th.row-header { position: sticky; left: 0; z-index: 4; width: 48px; min-width: 48px; border-left: 1px solid var(--gridline); }
#grid-table thead th { top: 0; z-index: 5; height: 30px; }
#grid-table thead th.row-header { z-index: 8; }
#grid-table td { position: relative; background: #fff; cursor: default; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; padding: 0 8px; }
#grid-table td.active { background: #fff; }
#grid-table td.active::after { content: ""; position: absolute; inset: -1px; border: 2px solid var(--blue); pointer-events: none; z-index: 2; }
#grid-table td.active .cell-content { color: #0b57d0; }
#grid-table td.error { color: var(--danger); font-weight: 700; }
#grid-table td.number { text-align: right; }
#grid-table td.text { text-align: left; }
#grid-table td.formula { color: #202124; }
#grid-table .cell-content { display: block; height: 28px; line-height: 28px; }
.cell-edit { position: absolute; z-index: 6; left: 2px; top: 2px; width: calc(100% - 4px); height: 26px; border: 1px solid var(--blue); border-radius: 1px; padding: 0 6px; font: inherit; outline: none; box-shadow: 0 0 0 1px rgba(26,115,232,.12); }
.context-menu { position: fixed; z-index: 30; min-width: 180px; padding: 5px 0; border: 1px solid #d2d7dd; border-radius: 4px; background: #fff; box-shadow: var(--shadow); }
.context-menu button { display: block; width: 100%; height: 30px; text-align: left; border: 0; border-radius: 0; padding: 0 12px; background: #fff; font-size: 12px; }
.context-menu button:hover { background: var(--blue-soft); }
.context-menu .menu-title { padding: 6px 12px; color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .06em; }
#context-menu[hidden] { display: none; }

.sheet-footer { height: 26px; border-top: 1px solid #e2e5e9; background: #fbfcfd; display: flex; align-items: center; justify-content: space-between; padding: 0 12px; color: var(--muted); font-size: 12px; }
.hint { color: #7b828a; }
</style>
</head>
<body>
<header class="topbar">
  <div class="brand"><span>◆</span> SheetForge</div>
  <div class="toolbar">
    <button id="insert-row" type="button">Insert row</button>
    <button id="delete-row" type="button">Delete row</button>
    <span class="toolbar-separator"></span>
    <button id="insert-col" type="button">Insert column</button>
    <button id="delete-col" type="button">Delete column</button>
    <span class="toolbar-separator"></span>
    <button id="clear-cell" type="button">Clear cell</button>
    <span class="toolbar-separator"></span>
    <span class="mini-label" id="operation-label">Ready</span>
  </div>
  <div class="status" id="status">26 columns × 100 rows</div>
</header>
<section class="formula-bar">
  <input id="name-box" class="name-box" spellcheck="false" aria-label="Selected cell">
  <span class="formula-separator"></span>
  <input id="formula-input" class="formula-input" spellcheck="false" aria-label="Formula bar">
  <span class="mini-label">Show: computed value · Edit: raw formula</span>
</section>
<main id="sheet-shell" class="sheet-shell">
  <table id="grid-table" aria-label="Spreadsheet grid"></table>
  <div id="context-menu" class="context-menu" hidden=""></div>
</main>
<footer class="sheet-footer">
  <span id="selection-status">Ready</span>
  <span class="hint">Enter commits down · Tab commits right · Delete clears · Double-click edits</span>
</footer>
<script>
(function () {
'use strict';
var __mods = {};
var __cache = {};
var __map = {"src/main.ts":{"./constants":"src/constants.ts","./formula":"src/formula.ts","./spreadsheet":"src/spreadsheet.ts"},"src/spreadsheet.ts":{"./constants":"src/constants.ts","./formula":"src/formula.ts"}};
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 });
const spreadsheet_1 = require("./spreadsheet");
const app = document.querySelector('#app');
const table = document.querySelector('#grid-table');
const shell = document.querySelector('#sheet-shell');
const nameBox = document.querySelector('#name-box');
const formulaInput = document.querySelector('#formula-input');
const contextMenu = document.querySelector('#context-menu');
const status = document.querySelector('#status');
const selectionStatus = document.querySelector('#selection-status');
const operationLabel = document.querySelector('#operation-label');
const sheet = new spreadsheet_1.Spreadsheet();
window.sheetApp = sheet;
let selectedRow = 0;
};

// ── module: src/constants.ts ──
__mods["src/constants.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.COLOR = exports.STORAGE_KEY = exports.ROW_HEADER_WIDTH = exports.HEADER_HEIGHT = exports.CELL_HEIGHT = exports.CELL_WIDTH = exports.GRID_ROWS = exports.GRID_COLUMNS = void 0;
exports.columnName = columnName;
exports.columnFromName = columnFromName;
exports.rowFromName = rowFromName;
exports.cellAddress = cellAddress;
exports.splitAddress = splitAddress;
exports.isInside = isInside;
exports.GRID_COLUMNS = 26;
exports.GRID_ROWS = 100;
exports.CELL_WIDTH = 112;
exports.CELL_HEIGHT = 28;
exports.HEADER_HEIGHT = 30;
exports.ROW_HEADER_WIDTH = 48;
exports.STORAGE_KEY = 'sheetforge.spreadsheet.raw.v1';
exports.COLOR = { blue: '#1a73e8', gridline: '#d7dce2', gridlineStrong: '#b8c0ca', error: '#c5221f' };
function columnName(index0) { let n = index0 + 1; let result = ''; while (n > 0) {
    n -= 1;
    result = String.fromCharCode(65 + (n % 26)) + result;
    n = Math.floor(n / 26);
} return result; }
function columnFromName(name) { let result = 0; for (const char of name.toUpperCase()) {
    if (char < 'A' || char > 'Z')
        return null;
    result = result * 26 + char.charCodeAt(0) - 64;
} return result === null || result < 1 || result > exports.GRID_COLUMNS ? null : result - 1; }
function rowFromName(name) { if (!/^[1-9][0-9]*$/.test(name))
    return null; const row = Number(name); return row < 1 || row > exports.GRID_ROWS ? null : row - 1; }
function cellAddress(row, col) { return `${columnName(col)}${row + 1}`; }
function splitAddress(address) { const match = address.match(/^([A-Z]{1,3})([1-9][0-9]*)$/i); if (!match)
    return null; const col = columnFromName(match[1]); const row = rowFromName(match[2]); return col === null || row === null ? null : { row, col }; }
function isInside(row, col) { return row >= 0 && row < exports.GRID_ROWS && col >= 0 && col < exports.GRID_COLUMNS; }
};

// ── module: src/formula.ts ──
__mods["src/formula.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ERR_CYCLE = exports.ERR_PARSE = exports.ERR_DIV = exports.ERR_REF = void 0;
exports.tokenizeFormula = tokenizeFormula;
exports.parseFormula = parseFormula;
exports.formulaDependencies = formulaDependencies;
exports.evaluateFormula = evaluateFormula;
exports.evaluateNode = evaluateNode;
exports.formatValue = formatValue;
exports.ERR_REF = '#REF!';
exports.ERR_DIV = '#DIV/0!';
exports.ERR_PARSE = '#ERR!';
exports.ERR_CYCLE = '#CYCLE!';
const PRECEDENCE = { '+': 1, '-': 1, '*': 2, '/': 2, '^': 3 };
const MAX_COLUMN = 26;
const MAX_ROW = 100;
function fail() { throw new Error('parse'); }
function isLetter(char) { return char >= 'A' && char <= 'Z'; }
function tokenizeFormula(input) {
    const tokens = [];
    let i = 0;
    while (i < input.length) {
        const char = input[i];
        if (/\s/.test(char)) {
            i += 1;
            continue;
        }
        if (char === '"' || char === "'") {
            const quote = char;
            let j = i + 1;
            let value = '';
            while (j < input.length) {
                if (input[j] === quote) {
                    if (input[j + 1] === quote) {
                        value += quote;
                        j += 2;
                        continue;
                    }
                    j += 1;
                    break;
                }
                value += input[j];
                j += 1;
            }
            if (j > input.length || input[j - 1] !== quote)
                fail();
            tokens.push({ kind: 'text', text: input.slice(i, j), value, start: i, end: j });
            i = j;
            continue;
        }
        if (/\d/.test(char) || (char === '.' && /\d/.test(input[i + 1] || ''))) {
            let j = i;
            let sawDigit = false;
            while (j < input.length && /\d/.test(input[j])) {
                sawDigit = true;
                j += 1;
            }
            if (input[j] === '.') {
                j += 1;
                while (j < input.length && /\d/.test(input[j])) {
                    sawDigit = true;
                    j += 1;
                }
            }
            if (!sawDigit)
                fail();
            const raw = input.slice(i, j);
            tokens.push({ kind: 'number', text: raw, value: Number(raw), start: i, end: j });
            i = j;
            continue;
        }
        if (/[A-Za-z]/.test(char)) {
            let j = i + 1;
            while (j < input.length && /[A-Za-z]/.test(input[j]))
                j += 1;
            if (/\d/.test(input[j] || '')) {
                let k = j;
                while (k < input.length && /\d/.test(input[k]))
                    k += 1;
                const raw = input.slice(i, k);
                const parsed = parseAddressText(raw);
                tokens.push({ kind: 'ref', text: raw, row: parsed?.row ?? -1, col: parsed?.col ?? -1, invalid: !parsed, start: i, end: k });
                i = k;
                continue;
            }
            tokens.push({ kind: 'ident', text: input.slice(i, j), start: i, end: j });
            i = j;
            continue;
        }
        if (char === ':') {
            tokens.push({ kind: 'colon', text: ':', start: i, end: i + 1 });
            i += 1;
            continue;
        }
        if (char === '(' || char === ')' || char === ',') {
            const kind = char === '(' ? 'lp' : char === ')' ? 'rp' : 'comma';
            tokens.push({ kind, text: char, start: i, end: i + 1 });
            i += 1;
            continue;
        }
        if ('+-*/^'.includes(char)) {
            tokens.push({ kind: 'op', text: char, start: i, end: i + 1 });
            i += 1;
            continue;
        }
        fail();
    }
    tokens.push({ kind: 'eof', text: '', start: input.length, end: input.length });
    return tokens;
}
function parseAddressText(address) {
    const match = address.toUpperCase().match(/^([A-Z]{1,3})([0-9]+)$/);
    if (!match)
        return null;
    let col = 0;
    for (const char of match[1]) {
        if (!isLetter(char))
            return null;
        col = col * 26 + char.charCodeAt(0) - 64;
    }
    if (col < 1 || col > MAX_COLUMN)
        return null;
    const row = Number(match[2]);
    if (row < 1 || row > MAX_ROW)
        return null;
    return { row, col };
}
class FormulaParser {
    constructor(input) {
        this.input = input;
        this.index = 0;
        this.tokens = tokenizeFormula(input);
    }
    peek(offset = 0) { return this.tokens[Math.min(this.index + offset, this.tokens.length - 1)]; }
    atKind(kind) { return this.peek().kind === kind; }
    consumeKind(kind) { const token = this.peek(); if (token.kind !== kind)
        fail(); this.index += 1; return token; }
    acceptKind(kind) { return this.atKind(kind) ? this.consumeKind(kind) : null; }
    parse() { const expression = this.parseExpression(); if (this.peek().kind !== 'eof')
        fail(); return expression; }
    parseExpression(minPrecedence = 0) {
        let left = this.parseUnary();
        while (true) {
            if (this.atKind('colon')) {
                this.consumeKind('colon');
                const end = this.parsePrimary();
                if (end.kind !== 'ref')
                    fail();
                left = { kind: 'range', start: left, end };
                continue;
            }
            if (!this.atKind('op'))
                break;
            const op = this.consumeKind('op').text;
            const precedence = PRECEDENCE[op] ?? -1;
            const right = this.parseExpression(precedence + 1);
            left = { kind: 'binary', op, left, right };
        }
        return left;
    }
    parseUnary() {
        if (this.atKind('op') && (this.peek().text === '+' || this.peek().text === '-')) {
            const op = this.consumeKind('op').text;
            return { kind: 'unary', op, operand: this.parseUnary() };
        }
        return this.parsePrimary();
    }
    parsePrimary() {
        const token = this.peek();
        if (token.kind === 'number') {
            this.index += 1;
            return { kind: 'number', value: token.value };
        }
        if (token.kind === 'text') {
            this.index += 1;
            return { kind: 'text', value: token.value };
        }
        if (token.kind === 'ref') {
            this.index += 1;
            return { kind: 'ref', row: token.row, col: token.col, invalid: token.invalid ?? false };
        }
        if (token.kind === 'lp') {
            this.consumeKind('lp');
            const expression = this.parseExpression();
            this.consumeKind('rp');
            return expression;
        }
        if (token.kind === 'ident') {
            const nameToken = this.consumeKind('ident');
            if (!this.atKind('lp'))
                fail();
            this.consumeKind('lp');
            const args = [];
            if (!this.atKind('rp')) {
                args.push(this.parseExpression());
                while (this.acceptKind('comma')) {
                    if (this.atKind('rp'))
                        fail();
                    args.push(this.parseExpression());
                }
            }
            this.consumeKind('rp');
            return { kind: 'func', name: nameToken.text, args };
        }
        fail();
    }
}
function parseFormula(raw) {
    try {
        const normalized = raw.trim();
        if (normalized[0] !== '=')
            return null;
        return new FormulaParser(normalized.slice(1)).parse();
    }
    catch {
        return null;
    }
}
function formulaDependencies(raw) {
    const normalized = raw.trim();
    if (!normalized.startsWith('='))
        return new Set();
    const ast = parseFormula(normalized);
    if (!ast)
        return new Set();
    const result = new Set();
    const visit = (node) => {
        if (node.kind === 'ref') {
            if (!node.invalid)
                result.add(`${node.row}-${node.col}`);
            return;
        }
        if (node.kind === 'range') {
            const startRow = Math.min(node.start.row, node.end.row), endRow = Math.max(node.start.row, node.end.row);
            const startCol = Math.min(node.start.col, node.end.col), endCol = Math.max(node.start.col, node.end.col);
            for (let row = startRow; row <= endRow; row += 1)
                for (let col = startCol; col <= endCol; col += 1)
                    result.add(`${row}-${col}`);
            return;
        }
        if (node.kind === 'unary')
            visit(node.operand);
        if (node.kind === 'binary') {
            visit(node.left);
            visit(node.right);
        }
        if (node.kind === 'func')
            for (const arg of node.args)
                visit(arg);
    };
    visit(ast);
    return result;
}
function evaluateFormula(raw, getCell) {
    const ast = parseFormula(raw);
    if (!ast)
        return { __formulaError: true, code: exports.ERR_PARSE };
    try {
        return evaluateNode(ast, getCell);
    }
    catch {
        return { __formulaError: true, code: exports.ERR_PARSE };
    }
}
function isFormulaError(value) { return typeof value === 'object' && value !== null && '__formulaError' in value; }
function rangeValues(value) { return value && typeof value === 'object' && '__range' in value ? value.values : [value]; }
function evaluateNode(node, getCell) {
    if (node.kind === 'number')
        return node.value;
    if (node.kind === 'text')
        return node.value;
    if (node.kind === 'ref') {
        if (node.invalid)
            return { __formulaError: true, code: exports.ERR_REF };
        return getCell(`${node.row}-${node.col}`);
    }
    if (node.kind === 'range') {
        const startRow = Math.min(node.start.row, node.end.row), endRow = Math.max(node.start.row, node.end.row);
        const startCol = Math.min(node.start.col, node.end.col), endCol = Math.max(node.start.col, node.end.col);
        const values = [];
        for (let row = startRow; row <= endRow; row += 1)
            for (let col = startCol; col <= endCol; col += 1)
                values.push(getCell(`${row}-${col}`));
        return { __range: true, values };
    }
    if (node.kind === 'unary') {
        const value = evaluateNode(node.operand, getCell);
        if (isFormulaError(value))
            return value;
        const number = coerceNumber(value);
        if (number === undefined)
            return { __formulaError: true, code: exports.ERR_PARSE };
        return node.op === '-' ? -number : number;
    }
    if (node.kind === 'binary') {
        const left = evaluateNode(node.left, getCell), right = evaluateNode(node.right, getCell);
        if (isFormulaError(left))
            return left;
        if (isFormulaError(right))
            return right;
        const leftNumber = coerceNumber(left), rightNumber = coerceNumber(right);
        if (leftNumber === undefined || rightNumber === undefined)
            return { __formulaError: true, code: exports.ERR_PARSE };
        if (node.op === '+')
            return leftNumber + rightNumber;
        if (node.op === '-')
            return leftNumber - rightNumber;
        if (node.op === '*')
            return leftNumber * rightNumber;
        if (node.op === '/')
            return rightNumber === 0 ? { __formulaError: true, code: exports.ERR_DIV } : leftNumber / rightNumber;
        if (node.op === '^')
            return Math.pow(leftNumber, rightNumber);
    }
    if (node.kind === 'func') {
        const args = node.args.flatMap(arg => rangeValues(evaluateNode(arg, getCell)));
        const firstError = args.find(isFormulaError);
        if (firstError)
            return firstError;
        const scalars = args.filter((value) => !isFormulaError(value) && typeof value !== 'object');
        const numeric = scalars.map(value => coerceNumber(value)).filter((value) => value !== undefined);
        const name = node.name.toUpperCase();
        if (name === 'SUM' || name === 'AVG' || name === 'AVERAGE' || name === 'MIN' || name === 'MAX' || name === 'COUNT') {
            if (name === 'COUNT')
                return numeric.length;
            if (numeric.length === 0)
                return name === 'MIN' || name === 'MAX' ? 0 : { __formulaError: true, code: exports.ERR_DIV };
            if (name === 'SUM')
                return numeric.reduce((sum, value) => sum + value, 0);
            if (name === 'MIN')
                return Math.min(...numeric);
            if (name === 'MAX')
                return Math.max(...numeric);
            return numeric.reduce((sum, value) => sum + value, 0) / numeric.length;
        }
        return { __formulaError: true, code: exports.ERR_PARSE };
    }
    return { __formulaError: true, code: exports.ERR_PARSE };
}
function coerceNumber(value) {
    if (typeof value === 'number')
        return Number.isFinite(value) ? value : undefined;
    if (typeof value !== 'string')
        return undefined;
    const trimmed = value.trim();
    if (trimmed === '')
        return 0;
    const parsed = Number(trimmed);
    return Number.isFinite(parsed) ? parsed : undefined;
}
function formatValue(value) {
    if (isFormulaError(value))
        return value.code;
    if (value && typeof value === 'object' && '__range' in value)
        return value.values.map(formatValue).join(', ');
    if (typeof value === 'boolean')
        return value ? 'TRUE' : 'FALSE';
    if (typeof value === 'number')
        return Object.is(value, -0) ? '0' : String(value);
    return String(value);
}
};

// ── module: src/spreadsheet.ts ──
__mods["src/spreadsheet.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Spreadsheet = void 0;
const constants_1 = require("./constants");
const formula_1 = require("./formula");
function keyForRowCol(row, col) { return `${row}-${col}`; }
function parseAddress(address) {
    const match = address.match(/^([A-Za-z]{1,3})([1-9][0-9]*)$/i);
    if (!match)
        return null;
    const col = (0, constants_1.columnFromName)(match[1]);
    const row = (0, constants_1.rowFromName)(match[2]);
    if (col === null || row === null)
        return null;
    return { row: row - 1, col: col - 1 };
}
function dependenciesFor(raw) {
    const ast = (0, formula_1.parseFormula)(raw);
    const result = new Set();
    if (!ast)
        return result;
    const visit = (node) => {
        if (node.kind === 'ref') {
            if (!node.invalid)
                result.add(`${node.row}-${node.col}`);
            return;
        }
        if (node.kind === 'range') {
            for (let row = Math.min(node.start.row, node.end.row); row <= Math.max(node.start.row, node.end.row); row += 1)
                for (let col = Math.min(node.start.col, node.end.col); col <= Math.max(node.start.col, node.end.col); col += 1)
                    result.add(`${row}-${col}`);
            return;
        }
        if (node.kind === 'unary')
            return visit(node.operand);
        if (node.kind === 'binary') {
            visit(node.left);
            return visit(node.right);
        }
        if (node.kind === 'func')
            for (const arg of node.args)
                visit(arg);
    };
    visit(ast);
    return result;
}
class Spreadsheet {
    constructor() {
        this.raw = new Map();
        this.computed = new Map();
        this.dependents = new Map();
        this.errors = new Map();
        this.cycles = new Set();
        this.rowCount = constants_1.GRID_ROWS;
        this.colCount = constants_1.GRID_COLUMNS;
        this.pending = [];
        this.changedKeys = new Set();
        this.load();
    }
    load() { try {
        const stored = JSON.parse(localStorage.getItem(constants_1.STORAGE_KEY) ?? 'null');
        if (!stored || stored.versions !== 1 || typeof stored.cells !== 'object')
            return;
        for (const [address, value] of Object.entries(stored.cells)) {
            const parsed = parseAddress(address);
            if (parsed && (0, constants_1.isInside)(parsed.row, parsed.col))
                this.raw.set(address.toUpperCase(), String(value));
        }
    }
    catch { /* ignore corrupt storage */ } this.recalculate(); }
    getRaw(row, col) { return this.raw.get(keyForRowCol(row, col)) ?? ''; }
    getComputed(row, col) { return this.computed.get(keyForRowCol(row, col)) ?? ''; }
    getAddress(row, col) { return keyForRowCol(row, col); }
    setCell(row, col, raw) { if (!(0, constants_1.isInside)(row, col))
        return null; const address = keyForRowCol(row, col); if (this.getRaw(row, col) === raw)
        return null; this.raw.set(address, raw); this.recalculate(); return { changed: true, affected: this.changedKeys }; }
    clearCell(row, col) { if (!(0, constants_1.isInside)(row, col))
        return null; const address = keyForRowCol(row, col); if (!this.raw.has(address))
        return null; this.raw.delete(address); this.recalculate(); return { changed: true, affected: this.changedKeys }; }
    insertRow(index) { if (index < 0 || index > this.rowCount || this.rowCount >= constants_1.GRID_ROWS)
        return null; const affected = this.collectRawCellsForRow(index); for (const [address, raw] of this.raw) {
        const parsed = parseAddress(address);
        if (!parsed || parsed.row < index)
            continue;
        this.raw.delete(address);
        this.raw.set(keyForRowCol(parsed.row + 1, parsed.col), raw);
    } this.rowCount += 1; this.recalculate(); return { changed: true, affected, inserted: 'row', index }; }
    deleteRow(index) { if (index < 0 || index >= this.rowCount)
        return null; const affected = this.collectRawCellsForRow(index); if (affected.size === 0)
        return null; for (const [address, raw] of this.raw) {
        const parsed = parseAddress(address);
        if (!parsed || parsed.row < index)
            continue;
        this.raw.delete(address);
        this.raw.set(keyForRowCol(parsed.row - 1, parsed.col), raw);
    } this.rowCount -= 1; this.recalculate(); return { changed: true, affected, inserted: 'row', index }; }
    insertColumn(index) { if (index < 0 || index >= this.colCount || this.colCount >= constants_1.GRID_COLUMNS)
        return null; const affected = this.collectRawCellsForColumn(index); for (const [address, raw] of this.raw) {
        const parsed = parseAddress(address);
        if (!parsed || parsed.col < index)
            continue;
        this.raw.delete(address);
        this.raw.set(keyForRowCol(parsed.row, parsed.col + 1), raw);
    } this.colCount += 1; this.recalculate(); return { changed: true, affected, inserted: 'column', index }; }
    deleteColumn(index) { if (index < 0 || index >= this.colCount)
        return null; const affected = this.collectRawCellsForColumn(index); if (affected.size === 0)
        return null; for (const [address, raw] of this.raw) {
        const parsed = parseAddress(address);
        if (!parsed || parsed.col < index)
            continue;
        this.raw.delete(address);
        this.raw.set(keyForRowCol(parsed.row, parsed.col - 1), raw);
    } this.colCount -= 1; this.recalculate(); return { changed: true, affected, inserted: 'column', index }; }
    collectRawCellsForRow(row) { const result = new Set(); for (let col = 0; col < this.colCount; col += 1) {
        const address = keyForRowCol(row, col);
        if (this.raw.has(address))
            result.add(address);
    } return result; }
    collectRawCellsForColumn(col) { const result = new Set(); for (let row = 0; row < this.rowCount; row += 1) {
        const address = keyForRowCol(row, col);
        if (this.raw.has(address))
            result.add(address);
    } return result; }
    buildGraph() { this.pending = Array.from(this.raw.entries()).map(([address, raw]) => { const parsed = parseAddress(address); return parsed ? { row: parsed.row, col: parsed.col, raw, dependencies: dependenciesFor(raw) } : { row: 0, col: 0, raw, dependencies: new Set() }; }); this.dependents.clear(); for (const pending of this.pending)
        for (const dependency of pending.dependencies) {
            const set = this.dependents.get(dependency) ?? new Set();
            set.add(keyForRowCol(pending.row, pending.col));
            this.dependents.set(dependency, set);
        } }
    recalculate() {
        this.buildGraph();
        const states = new Map();
        const visiting = new Set();
        const cycleNodes = new Set();
        const visit = (address) => {
            const state = states.get(address) ?? 0;
            if (state === 2)
                return 1;
            if (state === 1)
                return 0;
            states.set(address, 1);
            visiting.add(address);
            const pending = this.pending.find(item => address === keyForRowCol(item.row, item.col));
            if (!pending) {
                visiting.delete(address);
                states.set(address, 2);
                return 1;
            }
            for (const dependency of pending.dependencies) {
                if (this.pending.some(item => keyForRowCol(item.row, item.col) === dependency) && visit(dependency) === 0) {
                    cycleNodes.add(address);
                    visiting.delete(address);
                    states.set(address, 3);
                    return 0;
                }
            }
            visiting.delete(address);
            states.set(address, 2);
            return 1;
        };
        for (const pending of this.pending)
            visit(keyForRowCol(pending.row, pending.col));
        this.cycles = cycleNodes;
        const evaluateValue = (address) => {
            if (this.cycles.has(address))
                return { __formulaError: true, code: '#CYCLE!' };
            const pending = this.pending.find(item => address === keyForRowCol(item.row, item.col));
            if (!pending)
                return 0;
            if (!pending.raw.startsWith('='))
                return pending.raw;
            const ast = (0, formula_1.parseFormula)(pending.raw);
            if (!ast)
                return { __formulaError: true, code: '#ERR!' };
            try {
                return (0, formula_1.evaluateNode)(ast, dependency => evaluateValue(dependency));
            }
            catch {
                return { __formulaError: true, code: '#ERR!' };
            }
            ;
        };
        for (const pending of this.pending) {
            const address = keyForRowCol(pending.row, pending.col);
            this.computed.set(address, evaluateValue(address));
        }
        this.changedKeys = new Set(this.raw.keys());
        this.persist();
    }
    persist() { const cells = {}; for (const [address, raw] of this.raw) {
        const parsed = parseAddress(address);
        if (parsed)
            cells[(0, constants_1.cellAddress)(parsed.row, parsed.col)] = raw;
    } try {
        localStorage.setItem(constants_1.STORAGE_KEY, JSON.stringify({ versions: 1, cells }));
    }
    catch { /* ignore quota */ } }
}
exports.Spreadsheet = Spreadsheet;
};

// ── entry ──
__require("src/main.ts");
})();
</script>
</body>
</html>
<!-- agent-meta {"model":"bartowski/nex-agi_Nex-N2.5-mini-GGUF","provider":"brigid","persona":"typescript","sessionId":"72532635-2472-4d5b-8bb7-64dc7705c460","tokensIn":76368379,"tokensOut":198407,"tokensTotal":76566786,"cost":0,"turns":517,"toolCalls":517,"failedToolCalls":27,"timestamp":"2026-09-14T16:55:08.472Z"} -->