Notebooks
The notebook puts written text, code and its results in one document, for the weekly reports, investigations and worked analyses that someone runs again and again. Markdown cells hold the explanation; JavaScript cells run in the browser, inside a worker in a sandboxed frame that can reach neither the page nor the network, and keep their variables from cell to cell. A cell's results show beneath it as text, tables, charts, Markdown, pictures or JSON. The notebook knows which cells read from which, so it can run a cell with the cells it needs, mark the ones a change has put out of date, and run them again.
SQL cells are handed to your page's own run function, with their {{name}} placeholders
passed as parameters rather than written into the text, and the rows that come back are kept for the next
cells. A parameters cell holds the values a run can be given. Notebooks open from and save to .ipynb files, the
format other notebook tools read, and go out as a web page of their own or a PDF with the charts drawn in.
Try it with your own notebooks in confidence: they are opened and run by your browser, on your device. Nothing is uploaded, no copy reaches us or anyone else, and nothing is kept once you close this tab.
A bakery's week, in one notebook
Ashgrove Bakehouse, a made-up bakery with five shops in five made-up towns, looks at each week's sales on a Monday morning. The notebook below makes up a week of sales for six of its lines - the same numbers for the same week - and works out the takings by shop and by day, the best sellers and the shops that threw away more than the waste target allows. Run it, run one cell with only what it needs, tighten the target, and take the week away as a web page or a PDF.
// Node 20 or later, from the Gold package: run the weekly notebook for a given week, keep the run and print it.
import { readFileSync, writeFileSync } from 'node:fs';
import { readIpynb, runNotebook, writeIpynb, notebookToPdf } from './dist/node/bmx-notebook.mjs';
const week = Number(process.argv[2]);
const nb = readIpynb(readFileSync('ashgrove-weekly.ipynb', 'utf8'));
const run = await runNotebook(nb, { parameters: { week, wasteTarget: 10 } });
if (!run.ok) throw new Error(`Cell ${run.failedCell} failed`);
const over = run.variables.overTarget;
console.log(`Week ${week}: ${over.length} shops over the waste target`, over.map(s => `${s.shop} ${s.waste}%`));
writeFileSync(`ashgrove-week-${week}.ipynb`, writeIpynb(run.notebook));
writeFileSync(`ashgrove-week-${week}.pdf`, await notebookToPdf(run.notebook, { code: false }));
SQL cells, answered by the page
The same made-up bakery bakes for cafés, schools and a care home too. Its wholesale order book is a notebook
file this page writes for itself - a plain .ipynb, with its SQL cells marked by a %%sql line -
and opens below. The SQL goes to the page's own run function, which answers three SELECT
statements from a small invented table of shops and another of orders kept in the page, and refuses anything
else. Change the parameters, ask it something it does not know, and save the run and read it back in.