feat: the grid, painting strings the server computed
Jspreadsheet CE 5.0.2 and jsuites 5.13.5, vendored with their licences and a README carrying the exact commands to reproduce them. jsuites is pinned to the newest 5.x rather than npm's latest: 6.x is out, and jspreadsheet declares ^5.8.5, so taking latest would silently cross a major it has never claimed to work with. parseFormulas is off. Our engine computes and the client paints; a client-side evaluator would disagree with the server on the first SUM(1,2,"3") — the divergence the corpus already documents — and the disagreement would look like a number in a cell either way. Cell data holds display strings and the raw input lives in a side map, so the grid shows 42 and the formula bar shows =6*7. Paints are discarded by version, not by arrival: without that, optimistic painting plus last-write-wins produces a visibly wrong grid under two editors, which is the exact bug the optimism is meant to hide. live.js closes the stream when the tab is hidden and opens a new one when it returns, and closes explicitly on the server's "bye" — without that, EventSource's own retry brings the browser straight back and the server's idle timeout is decoration. Coming back refetches before reopening, because the stream has no backlog. The JS line cap guard arrives with them, and it found its own bug first: it reused repo_test.go's skipDir, which skips any directory named "vendor" — right for Go dependency trees, exactly wrong for our vendored JavaScript. It walked past every file its exemption list named and passed. TestEveryJSExemptionIsLive now asserts the walk reaches each exemption, so an entry can never again be a comforting line that exempts nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018RN4HhHR4rHH3Fxh4LMds5
12 files changed,
+18065
−4
internal/guardstest/jscap_test.go+133 −0internal/sheets/handlers_sheets_test.go+54 −0internal/sheets/static/grid.js+206 −0internal/sheets/static/live.js+145 −0internal/sheets/static/vendor/LICENSE.jspreadsheet+21 −0internal/sheets/static/vendor/LICENSE.jsuites+21 −0internal/sheets/static/vendor/README.md+58 −0internal/sheets/static/vendor/jspreadsheet.css+716 −0internal/sheets/static/vendor/jspreadsheet.js+18 −0internal/sheets/static/vendor/jsuites.css+3068 −0internal/sheets/static/vendor/jsuites.js+13602 −0internal/sheets/templates/sheet.html+23 −4
internal/guardstest/jscap_test.goLoading diff…
internal/sheets/handlers_sheets_test.goLoading diff…
internal/sheets/static/grid.jsLoading diff…
internal/sheets/static/live.jsLoading diff…
internal/sheets/static/vendor/LICENSE.jspreadsheetLoading diff…
internal/sheets/static/vendor/LICENSE.jsuitesLoading diff…
internal/sheets/static/vendor/README.mdLoading diff…
internal/sheets/static/vendor/jspreadsheet.cssLoading diff…
internal/sheets/static/vendor/jspreadsheet.jsLoading diff…
internal/sheets/static/vendor/jsuites.cssLoading diff…
internal/sheets/static/vendor/jsuites.jsLoading diff…
internal/sheets/templates/sheet.htmlLoading diff…