https://github.com/jstrieb/code-grid
Spreadsheet for programmers where XSS is a feature, not a bug
https://github.com/jstrieb/code-grid
runes spreadsheet svelte svelte-5 svelte-reactivity svelte-runes svelte5 sveltejs xss
Last synced: 2 months ago
JSON representation
Spreadsheet for programmers where XSS is a feature, not a bug
- Host: GitHub
- URL: https://github.com/jstrieb/code-grid
- Owner: jstrieb
- License: gpl-3.0
- Created: 2024-11-04T14:52:53.000Z (6 months ago)
- Default Branch: master
- Last Pushed: 2025-02-17T06:14:56.000Z (3 months ago)
- Last Synced: 2025-02-17T06:23:10.522Z (3 months ago)
- Topics: runes, spreadsheet, svelte, svelte-5, svelte-reactivity, svelte-runes, svelte5, sveltejs, xss
- Language: Svelte
- Homepage: https://jstrieb.github.io/code-grid/
- Size: 845 KB
- Stars: 2
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
Code Grid
Spreadsheet for programmers where XSS is a feature, not a bug.
> [!WARNING]
>
> This is alpha-quality code. It is likely to have bugs. The APIs are not yet
> stable, so things like spreadsheet links may break as the code is updated.# About
Code Grid is a web-based spreadsheet that is designed to be easy to extend.
- Write new formula functions using JavaScript
- Create HTML elements inside cells
- Navigate with Vim keybindings
- Build small applications that are easy to share and modify
- Never send data to a back end – Code Grid runs fully client-sideCross-Site Scripting (XSS) is a class of web security vulnerabilities in which
users can execute their own (potentially malicious) code on someone else's
website. Usually it is considered a very big problem for the website owner. In
this case, I am deliberately letting you execute your code on my website to make
Code Grid do whatever you want.# Examples
- [Make charts and graphs](https://jstrieb.github.io/code-grid/#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)
- [Create interactive applications](https://jstrieb.github.io/code-grid/#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)
- [Write formulas using Python (via Pyodide)](https://jstrieb.github.io/code-grid/#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)
- [Split receipts using OpenCV](https://jstrieb.github.io/code-grid/#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)
- [Write custom parsers with ease](https://jstrieb.github.io/code-grid/#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)
- [Constrained optimization and mixed-integer linear programming using SCIP](https://jstrieb.github.io/code-grid/#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)
- TODO simple spreadsheet
- TODO LaTeX
- TODO Keystone and unicorn# API Documentation
Use the help menu within Code Grid to access a tutorial.
## Formulas
Formulas begin with an equals sign (`=`), and can contain:
- Numbers such as `123` and `-3.21`
- Strings such as `"asdf"` and `"multi\nline"`
- Singleton references in R1C1 notation such as `R10C3` (zero-indexed) for
absolute references, `R[-1]c[2]` for relative references, and `RC` for
self-references
- Negative absolute references start from the end of a row or column, such as
`R-1C-1` to select the cell in the bottom right corner of the sheet, and
`R1C0:R1C-1` to select all of row 1
- Ranges such as `R[-3]C:R[-1]C`
- References and ranges across sheets like `S1!R1C1` and `S[1]!R2C2:R2C-1` and
`S-1R2C3` (the exclamation point is optional)
- Function calls (case insensitive) containing expressions as arguments such as
`sum(RC0:RC[-1])`, `sLiDeR(0, 10, 1)`, and `DOLLARS(PRODUCT(1 * 2 + 3, 4, 3,
R[-1]C))`
- Optionally parenthesized binary operations combining any of the expressions
above such as `(RC[-2] + RC[-3]) * 100` and `1 + -2 + 3 ** 5`## Writing Formula Functions
The formula language above can be extended by adding new formula functions.
Formula functions are written in JavaScript from the Code Grid user interface,
and saved alongside sheet data.### Simple Formula Functions
To register formula functions, add them to the `functions` object.
``` javascript
functions.digits = (n) => {
// Compute the number of digits that n has
let result = 0;
while (n > 0) {
result++;
n = Math.floor(n / 10);
}
return result;
}
```Registered functions will become available within formulas. In this example,
`=DIGITS(1234)` will put the value 4 in the cell.### Advanced Formula Functions
Click to read about advanced formula functions
Formula functions can be `async`. They will be awaited automatically by the Code
Grid runtime. Cells that depend on async formulas will only update when the
dependencies' promises resolve.``` javascript
functions.crypto = async (ticker) => {
return await fetch("https://api.gemini.com/v1/pricefeed", { cache: "force-cache" })
.then((r) => r.json())
.then((l) => Number(
l.filter((o) => o.pair === ticker.toUpperCase() + "USD")[0].price,
));
}
```Formula functions declared using `function() { /* */ }` syntax are passed a
`this` object that enables advanced functionality.**JavaScript arrow functions (such as `x => x + 1`) are not passed a `this`
object!** This is inherent to JavaScript. To use `this` in formula functions,
the functions must be declared using `function(){}` syntax. To quote
[MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions):> An arrow function expression is a compact alternative to a traditional
> function expression, with some semantic differences and deliberate limitations
> in usage:
>
> - Arrow functions don't have their own bindings to `this`, `arguments`, or
> `super`, and should not be used as methods.The `this` object passed to formula functions when they execute contains:
- `this.row` and `this.col` – the current row and column, respectively
- `this.set` – a function that sets the cell's value to whatever parameter it is passed
- Useful for updating the cell value asynchronously or in callbacks
- `this.update` – a function that takes a callback, where the callback takes the
previous cell value and returns the value to set the cell to
- Useful for updating the cell value based on the previous one
- `this.element` – an HTML element that will be put in the cell if defined
- `this.style` – the style attribute passed to the cell's `` elementThe `element` can be set to add custom displays or interfaces to a sheet. For
example, to add marquees:``` javascript
functions.marquee = function(x) {
// Wrap whatever element was set before
const oldElement = this.element;
this.element = document.createElement("marquee");
this.element.appendChild(
oldElement ?? document.createTextNode(x)
);
// Return the input value so this cell can still be used in formulas
return x;
}
```The following advanced example adds a formula function for interactive
checkboxes. The output value of the formula is the checked state of the box. Note
the use of `this.set` in the callback to update the cell's value upon
interaction, and `this.update` to set the initial value of the checkbox. Setting
the initial value means the checkbox state is saved and loaded from the URL.``` javascript
functions.checkbox = function (label) {
let value;
this.update((previous) => {
value = previous;
return previous;
});
this.element = Object.assign(document.createElement("label"), {
innerText: label,
style: "display: flex; align-items: center; gap: 1ch; margin: 0 0.5em;",
});
this.element.appendChild(
Object.assign(document.createElement("input"), {
type: "checkbox",
style: "appearance: auto;",
checked: value,
oninput: (e) => this.set(e.target.checked),
}),
);
return value;
};
```The `style` can be set to alter the display of the cell. For example, to make
the cell's text centered:``` javascript
functions.center = function(x) {
this.style += "text-align: center;"
return x;
}
```The `set` function is useful in interactive element callbacks, as demonstrated
above. It is also useful for functions that run on a timeout or interval. For
example:``` javascript
functions.sleep = async function(ms) {
// Will say "sleeping" until complete
this.set("Sleeping...");
await new Promise(r => setTimeout(r, ms));
return "Complete!";
}functions.time = function() {
// Will auto-update once per second with the Unix time
setInterval(() => this.set(Date.now()), 1000);
return Date.now();
}
```# How Code Grid Works
## Code Table of Contents
The links below are listed in the order the code should be read to understand
the application from the highest to lowest level.- [`Makefile`](Makefile) – build and run the application locally
- [`index.html`](index.html) and [`src/App.svelte`](src/App.svelte) – entrypoint
to the main, high-level application (the index page mounts the App)
- [`src/Table.svelte`](src/Table.svelte) and
[`src/Cell.svelte`](src/Cell.svelte) – interactive spreadsheet UI code
- [`src/classes.svelte.js`](src/classes.svelte.js) – classes that manage state
throughout the application
- `Sheet.newCell` is responsible for reactively rederiving the store that
computes a cell's value; it is run whenever the cell's value changes
- [`src/store.js`](src/store.js) – implementation of "rederivable" stores that
can change their derived dependencies without invalidating their object
reference
- Every cell's value is a rederivable store that is rederived when its formula
changes, and updated whenever any of its dependencies' values changes
- [`src/formula.js`](src/formula.js) – formula parsing logic
- [`src/parsers.js`](src/parsers.js) – parser combinator library used for formula parsing
- [`src/keyboard.js`](src/keyboard.js) – mapping of keyboard shortcuts to handlers
- [`src/*.svelte`](src/) – UI components
- [`src/formula-functions.js`](src/formula-functions.js) – "standard library"
formula functions available in every spreadsheet
- Includes functionality to `eval` user code and add functions to the formula
function object
- [`src/global.css`](src/global.css) and [`public/*`](public/) – global
stylesheet, favicons, etc.
- [`src/compress.js`](src/compress.js) – compress and decompress text using PNGs
- [`test/*`](test/) – test suite and related functions## Cool Code Highlights
- Spreadsheet formulas are built on a custom Svelte store that is
"[rederivable](https://github.com/jstrieb/code-grid/blob/05a1545730555a671186717950bb758148161bab/src/store.js)."
It functions like a Svelte derived store, except it can add or remove
dependencies it is derived from without changing its object reference.
- The [menu
implementation](https://github.com/jstrieb/code-grid/blob/05a1545730555a671186717950bb758148161bab/src/ShyMenu.svelte)
(and the [right click
menu](https://github.com/jstrieb/code-grid/blob/05a1545730555a671186717950bb758148161bab/src/ContextMenu.svelte))
features advanced usage of the new Svelte snippets feature, and would have
been much harder to build (maybe even impossible) using slots in Svelte 4.
- [Formulas are
parsed](https://github.com/jstrieb/code-grid/blob/05a1545730555a671186717950bb758148161bab/src/formula.js)
using a custom [parser combinator
implementation](https://github.com/jstrieb/code-grid/blob/05a1545730555a671186717950bb758148161bab/src/parsers.js).
- The [SVG favicon uses
CSS](https://github.com/jstrieb/code-grid/blob/05a1545730555a671186717950bb758148161bab/public/favicon.svg?short_path=04e3b01#L2)
to invert its own colors based on user light/dark-mode preferences.
- All spreadsheet data is saved to the URL, so sheets can be shared without
using a storage back end or database.
- Spreadsheet URLs are
[compressed](https://github.com/jstrieb/code-grid/blob/7f6431624c703d61e794a7dd6f83f3baba459cd3/src/compress.js#L17-L35)
by setting `` element pixel data to the serialized sheet bytes, and
having the browser convert the canvas to a PNG (since pixel data is ZLIB
compressed in PNG files).## Running the Code
[Install NodeJS](https://nodejs.org/en/download) if you do not already have it.
Once NodeJS is installed, to run Code Grid locally, you only need to run the
following inside the repository:``` bash
make dev
```To build a static version of the code to host elsewhere, run:
``` bash
make build
```The static, compiled files for Code Grid will be generated in the `dist/`
subdirectory.# Warning
Clicking Code Grid links can be risky, because the links can run arbitrary code.
Only click links from those you trust. Links running code in your browser means
that those links:- Can impersonate my website
- Can redirect to malicious pages
- Can steal locally stored data about other Code Grid spreadsheets
- Can make requests to other websites# Known Issues
See the list of bugs at the bottom of [`todo.md`](todo.md).
# Project Status & Contributing
Code Grid is under active development.
Bug reports and feature requests via [GitHub
Issues](https://github.com/jstrieb/code-grid/issues) are encouraged. Pull
requests with more than 20 lines of code are unlikely to be merged quickly,
unless attached to prior discussion or accompanied by substantial, explanatory,
English prose. In other words, pull requests containing code without context may
be merged after much delay, or may not be merged at all.Since Code Grid is a fully static web application with no server-side
processing, it is extremely scalable, and has a very low maintenance burden. As
such, even if something were to happen to me, and I could not continue to work
on the project, the [public version](https://jstrieb.github.io/code-grid) should
continue to remain functional and available online as long as my GitHub account
is open, and [jstrieb.github.io](https://jstrieb.github.io) domain is active.## Support the Project
The best ways to support the project are to:
- Share the project on sites like Twitter, Reddit, and Hacker News
- Report any bugs, glitches, errors, or shortcomings that you find
- Star the repository and follow me on GitHub
- Host a version of the code translated into another languageIf you insist on spending money to show your support, please do so in a way
that benefits as many people as possible. In particular, donations to the
following organizations help me, as well as the general, Internet-using public:- [Electronic Frontier Foundation](https://supporters.eff.org/donate/)
- [The Internet Archive](https://archive.org/donate/index.php)
- [Signal Foundation](https://signal.org/donate/)
- [Mozilla](https://donate.mozilla.org/en-US/)# Acknowledgments & Greetz
Thanks to [Logan Snow](https://github.com/lsnow99) for consulting on all manner
of web esoterica, and for testing many versions of Code Grid. Check out
[Listable](https://getlistable.app/) for a different take on achieving
tranquility through web-based tables.Thanks to [Amy Liu](https://www.youtube.com/watch?v=7gJX3lvi5OY) for feedback on
early versions of Code Grid, and for continued demonstration of great patience
and tolerance, despite my incurable penchant for mischief.Shout out to [Ella Liu](https://www.instagram.com/ella.liu/) for doing a full
financial model in a *very* early version of Code Grid. It's not easy for
[Excel-loving bankers](https://www.instagram.com/ellassaddesksalads/) to use
other spreadsheet software; her struggle drove feature development that has
benefited us all.