Build a spreadsheet app as one self-contained index.html (no external assets, no network).
UI: a visible grid with columns A-Z and rows 1-1000, a formula bar, click a cell to edit, Enter commits, arrow keys move. It should look like a tool someone would actually use.
Also expose this exact API on window.Sheet. It must stay in sync with the UI:
- set(ref, text): enter text into a cell, as if typed. Empty string clears it.
- get(ref): the computed value. A number for numeric results (JS number), a string for text and for errors, "" for empty cells.
- getRaw(ref): exactly what was entered.
- undo(), redo(): cover set, insertRow, deleteRow and fromCSV. A new edit clears the redo history.
- insertRow(n), deleteRow(n): 1-based. Rows shift, and formulas are rewritten as Excel does. Both relative and $absolute references move with their cells. A reference to a deleted single cell becomes #REF! in the formula text (for example "=#REF!"). A range that loses a row shrinks.
- toCSV(): RFC 4180 raw text (formulas as typed), rectangular up to the last non-empty row and column, rows joined by \n, no trailing newline.
- fromCSV(text): replaces the whole sheet, starting at A1. Handles quoted commas, doubled quotes and newlines inside quotes.
Formulas start with "=". Case-insensitive, whitespace ignored.
- Operators: + - * / ^ & (text concat), postfix %, parentheses. ^ is right-associative. Unary minus binds tighter than ^, as in Excel, so =-2^2 is 4.
- References: A1, $A$1, $A1, A$1. Ranges: A1:B3.
- Functions: SUM, AVERAGE, MIN, MAX, COUNT, IF(cond, a, b). Conditions use = <> < > <= >=.
- Text in double quotes.
- Empty cells are 0 in arithmetic and "" in concat. Range functions skip text and empty cells. Numbers concat in plain form (5 & "x" is "5x").
- Errors, exact strings: #DIV/0!, #REF!, #CYCLE!, #NAME? (unknown function), #VALUE! (text in arithmetic). Errors propagate, including through range functions. Every cell in a cycle, and every cell depending on one, shows #CYCLE!. Fixing the cycle recovers them.
- Cells recalculate automatically. With 10,000 filled cells and a SUM over them, editing one input must finish recalculating within 1 second. A dependency chain 1000 cells deep must work.
A numeric cell typed as 42 or 3.5 is a number. Anything else not starting with "=" is text.