Skip to content

Land rtables: 8 lanes - #208

Merged
XrxcGH merged 117 commits into
betafrom
train-rtables
Nov 12, 2026
Merged

XrxcGH merged 117 commits into
betafrom
train-rtables

Conversation

@XrxcGH

@XrxcGH XrxcGH commented Nov 12, 2026

Copy link
Copy Markdown
Owner

Lands eight Excel-level table lanes in one merge train off beta.

Lanes

Order Branch PR
1 b10-tables-fn-logic-info #196
2 b10-tables-fn-lookup #197
3 b10-tables-toolbar #195
4 b10-tables-keys #182
5 b10-tables-doc-exports none (#0)
6 b11-tables-fn-arrays #198
7 b11-tables-conditional none (#0)
8 b11-tables-fn-stats none (#0)

Then origin/beta (the Home tab, #205) was merged in, so the train is current.

Conflicts (31) and how they were resolved

  • app/bundle-budget.json (5 merges): union of keys, the larger of each limit.
  • Formula engine plumbing (fn-lookup): calls.ts, context.ts, fn/types.ts, graph/engine.ts. Both lanes' hooks kept (refArgs and wantsCaller, formulaAt and cellFacts).
  • gridcraftDiff.test.ts (4 merges): every lane's fixture and skip set kept.
  • fixtures/fn/DIFFERENCES.md (3 merges): rows unioned.
  • fn-stats: the lexer's wider dotted-name rule (reads T.DIST.2T) with its test; fn/index.test.ts per-family counts; smart/attach.ts imports.
  • keys: README tables line, page/index.ts exports, tables/register.ts, featureCommands.ts imports.
  • doc-exports: features/tables/index.ts exports. conditional: sheet/registry.test.ts filters.
  • beta merge: featureCommands.ts keeps the cell, sheet, and Home tab text.
  • docs/tools/registry.json and docs/FEATURE-LIST.md: regenerated with npm run docs:features, not hand-merged.

Fixes on the merged result (one commit each)

  • The print test bundle failed to build: the table export pulled in a lazy chunk, the worker, and React. The export reads ui/color/parse; the print harness maps the table sheet loader to a light module.
  • The table export imported the tables feature at the top of the pages export, so every pages and accounts entry carried it (+160 KB gzipped). The export now loads the sheet reader on demand. The Markdown, web page, and print paths load it first.
  • The logic test now counts the lambda helpers as logical, as the design table lists them.
  • ResizeObserver and matchMedia are checked before use, so test documents do not throw.
  • The spreadsheet key tests find the palette by its placeholder (the Home tab added comboboxes).
  • Bundle budgets: raised by the lanes' combined growth. Each lane raised its own, and the merge kept the largest. Start-up goes from 595 to 612 KB: spreadsheet keys, cell commands, and function help text. Entries that share the formula engine grow 26 to 35 KB, and the pages entries grow 4 KB.

Cross-lane hand-over wired

  • Conditional formatting: Cell: Conditional formatting rules opens the rule manager in a window from the palette and the table's context menu. Each change is one undo step. The new-rule editor is not built yet.
  • The recalculation worker waits for the statistics chunk (fn-stats hand-over).

Docs

README feature list and status line, docs/FEATURES.md, the tables help page, and the feature list name the new features.

Gates run locally

Typecheck, lint, format check, unit, component, npm run checks, the checks/design/lint-rule/matrix/extension tests, bundle budget, cargo fmt, clippy, and test for opennote-interop, and the Playwright suite.

🤖 Generated with Claude Code

XrxcGH and others added 30 commits November 7, 2026 02:30
A table uses spreadsheet keys or document keys. The choice is a new key of
the sheet's table object (spec R-SHT-10), so the sheet model and the
reference reader accept it. A table that stores nothing keeps document
keys when it holds only text, and uses spreadsheet keys when a cell
holds a number or a formula (PLAN.md decision 4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pure functions for Ctrl+arrow jumps to the data's edges, the last used
cell, the current region, Enter and Tab cycling inside a range, and
cell addresses. Screen readers hear the address, the column header,
the shown value, and the formula read as words. The sheetKeys strings
hold every sentence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A plugin, added first through the table extra, makes a cell selection
Grid mode and the caret in a cell Edit mode. Typing replaces the active
cell, Esc puts it back, F2 edits, Enter finishes and moves down, and
Enter and Tab go through a range. Changeable chords are commands in the
tableGrid, tableEdit, and editor.table scopes, so rebinding works and
Ctrl+K stays the palette. Insert table stores the new-table setting.
The shortcut list gains a Table keys section. Behind tables.sheetWrites,
so Stable keeps today's table keys.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Grid mode's commands come before the page's, the page's chord applies in
Edit mode, Ctrl+K stays the palette, and a changed chord moves with the
command.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drives a smart table in the browser: data edge jumps, typing over and
Esc, F2 and Enter, Alt+Enter and document keys, Ctrl+D and Ctrl+;, the
screen reader announcement, and Ctrl+K in Grid mode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ADR 0063 gains implementation notes. The generated feature registry and
list gain the sheet commands, each with a sentence. The README's tables
line names the new keys.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Settings, then Tables: choose spreadsheet or document keys for new tables.
- A table with spreadsheet keys is an ARIA grid, so aria-selected is allowed.
  No cell stays selected while focus is outside the editor.
- Typing maps the cell numbers instead of rebuilding them for every cell.
- The key scope fixture lists the real sheet commands and chords.
- ADR 0063 is accepted: announcements replace roving focus.
- A long run of spaces in a cell no longer slows the number check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Cells are placed by the table's map, so a merged cell no longer shifts
  the column numbers and addresses of the cells after it. A table with a
  merged cell uses document keys until the merge goes. Tab in the last
  cell adds a row as wide as the table.
- An older table that switches to spreadsheet keys shows a one-time tip
  that names the Document keys command.
- In Edit mode an arrow at the table's edge goes on to the block beside
  the table. The help says how Esc leaves.
- Esc puts the text back after a click into a cell and typing.
- No cell is marked selected before the editor has focus.
- The UI spec waits up to 20 seconds for the keys to load.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new table uses spreadsheet keys, so its cells are grid cells.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…dget

The table cells now carry grid roles, so the listed holder overlaps use the new selectors. The keys code adds about 4 KB to start-up.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
CELL, ISFORMULA, ISREF, SHEET and SHEETS need the reference itself, not
its values. A function with refArgs gets references as references and a
probe of the page; the engine answers from the table as it is now, so an
edit never leaves a stale answer. The sheet lexer keeps a dot between
letters inside a name (ERROR.TYPE, STDEV.S).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
IFS, SWITCH, XOR, IFNA, TRUE, FALSE, LET and LAMBDA metadata, AND and OR
over arrays; CELL, INFO (offline-safe types only), ERROR.TYPE, the IS
functions, N, NA, SHEET, SHEETS, TYPE, ISEMAIL, ISURL, ISDATE. Cases come
from a fixture of documented and hand-worked values. The registry and
differential tests no longer pin the old function count.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…gets

The tip toast queues ahead of the toasts the tests wait for. The budgets grew by under 4 KB for the keys code.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
FIELD maps to 13 and CYCLE falls back to the #N/A code. Text inside an array constant no longer reads as a boolean. The engine's host facts move to graph/facts.ts.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Excel lets an argument be left empty, as in UNIQUE(A1:A9,,TRUE), and the
dynamic array functions document that form. The sheet dialect with Excel's
rules now parses an empty slot as a name with no text. The compiler reads
it as empty, and a library call gives the parameter's default instead.
Textbook formulas keep refusing it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An array past the 1,000,000-item cap gives #NUM!, and the design says the
explanation reads "Too many values". A FormulaError may now carry a
reason. The call records it in the error origin, and originSentence gives
the 'tooMany' sentence key for it, so the formula bar can show the words.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Matrix and arrays row of the function library and the LAMBDA helpers
(DESIGN.md section 7): FILTER, SORT, SORTBY, UNIQUE, TAKE, DROP, VSTACK,
HSTACK, TOCOL, TOROW, WRAPROWS, WRAPCOLS, EXPAND, CHOOSEROWS, CHOOSECOLS,
TRIMRANGE, MMULT, MINVERSE, MDETERM, MUNIT, TRANSPOSE, SEQUENCE,
RANDARRAY, MAP, REDUCE, SCAN, BYROW, BYCOL, MAKEARRAY, and Sheets'
FLATTEN, SORTN, and ARRAYFORMULA. Each follows its documented rules.

Every array result is capped at 1,000,000 items. A larger request gives
#NUM! with "Too many values" before anything is allocated. MINVERSE and
MDETERM reuse the LU decomposition in core/num and stop at order 500.

Tests run each formula through the engine and read the spill. Expected
values are worked by hand or come from published examples. The registry
test now allows new names, the differential test leaves array results to
these tests, and the engine-state test names a stand-in that stays
unknown. DIFFERENCES.md lists the choices no reference pins yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bold, Italic, size, alignment, Text color, and Clear formatting ask the format target seam first. With whole cells selected, the new target writes a cell style into data.sheet as one patch and takes the matching marks off the cell's runs. Selected text inside a cell still gets marks.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Pure functions: the number format menu codes, more and fewer decimals, the thousands separator, and the sixteen border presets as steps over a box. Known answers are worked by hand from the format language and the preset sides.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A second row of the table toolbar sets font, size, bold, italic, underline, strikethrough, text and fill colors from the shared palette, borders, alignment, wrap, rotation, number formats, and Clear formats. Each tool is a command in the one registry (palette: Cell:, context menu entries). The bar wraps at 400 percent zoom and moves less used tools into More when narrow. The help page and the README line name it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
XrxcGH and others added 28 commits November 12, 2026 06:17
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
# Conflicts:
#	app/bundle-budget.json
#	app/src/features/tables/engine/gridcraftDiff.test.ts
# Conflicts:
#	app/src/features/tables/engine/formula/calls.ts
#	app/src/features/tables/engine/formula/context.ts
#	app/src/features/tables/engine/formula/fn/types.ts
#	app/src/features/tables/engine/graph/engine.ts
#	app/src/features/tables/engine/gridcraftDiff.test.ts
#	app/src/features/tables/fixtures/fn/DIFFERENCES.md
# Conflicts:
#	app/bundle-budget.json
# Conflicts:
#	README.md
#	app/bundle-budget.json
#	app/src/features/page/index.ts
#	app/src/features/tables/register.ts
#	docs/FEATURE-LIST.md
#	docs/tools/featureCommands.ts
#	docs/tools/registry.json
# Conflicts:
#	app/src/features/tables/index.ts
# Conflicts:
#	app/src/features/tables/engine/gridcraftDiff.test.ts
#	app/src/features/tables/fixtures/fn/DIFFERENCES.md
# Conflicts:
#	app/src/features/tables/sheet/registry.test.ts
# Conflicts:
#	app/bundle-budget.json
#	app/src/core/expr/lexer.test.ts
#	app/src/core/expr/lexer.ts
#	app/src/features/tables/engine/formula/fn/index.test.ts
#	app/src/features/tables/engine/gridcraftDiff.test.ts
#	app/src/features/tables/fixtures/fn/DIFFERENCES.md
#	app/src/features/tables/smart/attach.ts
A statistical function that ran in the worker before its lazy chunk loaded
gave #BUSY! for good: the worker has no page to recalculate when the chunk
lands. The worker now waits for the chunk and answers again. This is the
fn-stats hand-over for the worker; the sheet grid already listens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README, FEATURES.md, the tables help page, and the feature list now name the
cell format bar, spreadsheet keys, conditional formatting, styled table
exports, and the logic, lookup, array, and statistics functions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop over.txt, a bundle-size scratch list a lane committed by mistake, and
give the worker's statistics test its own describe block so the main block
stays under the length limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…print bundle builds

The table export read the sheet model through the tables index and the color through the ui/color index. Those pull in the cell format bar's lazy chunk, the recalculation worker, and React, so the one-file print test bundle failed to build and the PDF and print tests broke. The export now reads the parse module, and the tables index passes on a light export view that the print test bundle uses directly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The arrays lane added BYCOL, BYROW, MAKEARRAY, MAP, REDUCE, and SCAN to the logical family, as the design table lists them. The logic test now expects them and checks their text in the array strings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The column width field opens the table toolbar, whose placement watched its size with ResizeObserver. A document without it threw, so the table block test failed. The toolbar now checks for it, as the other placement code does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… context menu

The conditional lane built the rule manager and left its command to the cell commands. Cell: Conditional formatting rules now opens it in a window for the current table. It lists the rules of the selection, the table, or the page, and each raise, lower, stop, or delete is one patch of the table the rule belongs to. The feature list, FEATURES.md, and the help page say so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The table toolbar test now attaches the smart extra, and the sheet render plugin asked window.matchMedia for the color scheme. The test document has none, so the unit run reported uncaught errors. The plugin now checks for it, as the ink code does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The table export imported the tables feature at the top of the pages export, so the pages chunks carried the whole tables feature (about 160 KB gzipped on each pages and accounts entry). The export now loads the sheet reader on demand: the Markdown, web page, and print paths load it before they write a page with a sheet, and until it has loaded a table exports plain, as before the sheet lane. The tables index passes on named sheet model parts instead of the whole model, so a feature that imports it carries only what it uses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each lane raised the budgets by its own growth, and the merge kept the largest of each, so the train is over by the rest. Measured against beta: start-up +17 KB (spreadsheet keys, cell commands, and the function help text), the formula engine chunk +26 to +35 KB on the math, statistics, and kit entries (the logic, lookup, array, and statistics functions), the pages entries +4 KB (styled table export), and the new table chunks. Each limit is the measured size plus 2 KB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Home tab from beta added font and size boxes, which are comboboxes before the palette's field, so the first combobox was not the palette any more. The tests now find the palette by its placeholder, as the other palette tests do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The merge put the row under the REGEX guard list, where it read as part of a bullet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s audit findings as intermittent

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@XrxcGH
XrxcGH merged commit d112216 into beta Nov 12, 2026
62 of 64 checks passed
@XrxcGH
XrxcGH deleted the train-rtables branch November 12, 2026 23:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant