A cell can hold Python instead of a value or a formula. Running it executes
the code on a Jupyter kernel; its result becomes the cell's value, which
formulas read like any other; its rich outputs (a figure, a table, HTML)
show in a frame beside the cell. The grid, the model and the file format
know code cells; running them and drawing their outputs is
@datalayer/cells/jupyter, the one entry that imports
@datalayer/jupyter-react (an optional peer), which a host loads when it
wants code to run.
A code cell is SET_CODE with a CodeCell (codeCell(source) makes one),
or Make the cell a code cell (cells.code.insert) in the context menu.
The canvas paints a badge, the result (or the source's first line before a
run), #BUSY! while it runs and a corner mark when what it reads changed.
A cell with outputs paints a preview of them too: a PNG's thumbnail scaled
to the cell's height, or the first line of a text output, and an output
glyph in the corner; hovering the cell (300 ms) or Alt+Down shows the
outputs in a preview at the cell, drawn by the contributed renderer, up to
480 × 360. Dragging the preview by its strip pins it where it is dropped:
the drop writes the cell's frame (its size, and dx, dy, the offset in
px at zoom 1 from its place beside the cell), one undo step. A cell whose
frame is set (or unset, the default 360 × 240) shows its outputs in that
pinned frame instead, and never a preview too: the frame's strip drags it
(the offset again, one undo step) and its × unpins it (frame: null: the
preview only). A host whose bundler is not
webpack defines __webpack_public_path__ ('') before loading ./jupyter,
as the examples' Vite config and page do: JupyterLab's packages read it.
| Keys, on a code cell | Command | What it does |
|---|---|---|
| Shift+Enter | cells.code.run |
Run it on the sheet's kernel |
| Enter, F2, F4, a double-click | cells.code.openEditor |
An overlay at the cell with the source in Cell; Escape, a click outside or Ctrl+S save and close; Shift+Enter there runs |
| Alt+Down | cells.code.preview |
Its outputs in a preview at the cell (a hover of 300 ms too), unless pinned in a frame; Escape closes |
cells.code.goToOutput |
Focus its output frame (Escape returns) | |
cells.code.runAll |
Every code cell of the sheet, in dependency order | |
cells.code.interrupt |
Stop the run in flight |
The code reads the sheet through cells (from datalayer_cells import cells, the workbook the kernel is bound to): cells["A1:B10"] is the
range's values, a pandas DataFrame when pandas is there (the first row its
header when it is text over values), rows otherwise, and a single cell its
value. Values, never formulas. The ranges a run read are reported back to
the grid and become the cell's precedents. The
last expression's value is the result: a number, a string, a boolean or
None; anything else (a DataFrame, a figure, an Image) is displayed in
the frame and its kind is the result. An exception is #ERROR!, with the
traceback in the frame. Writing the sheet is cells["C2"] = 42 and
cells["E1:F3"] = df (a DataFrame spilled from E1, refused with #SPILL!
over a cell that holds something): collaboration patches attributed to the
code cell, through the document the kernel holds (below).
A run is one undo step on the page: the result and the outputs. A
kernel's writes are another peer's edits: undone by running the cell
again, or by Undo the cell's writes.
The ranges a cell reads are compared after every change: a manual cell
(the default) is marked stale; an auto cell runs again after 300 ms, one
at a time, in dependency order. Nothing runs when a workbook opens; the
first run of a session is always a person's gesture.
Runs go to the first datalayer.cells.kernel contribution (a
KernelProvider: { id, label, connect() }). @datalayer/cells/jupyter
offers liteKernel() (JupyterLite's Pyodide, in the page, no server) and
serviceManagerKernel(manager) (a Jupyter server or a Datalayer runtime),
and CellsJupyterPlugin(provider) contributes one with jupyter-react's
Output and Cell as the code renderer, each under jupyter-react's theme
(JupyterReactTheme: the JupyterLab CSS and variables) in the host's colour
mode, Primer's resolved one or the document's data-color-mode.
KernelStatus is jupyter-react's KernelIndicator on the runner's kernel
with Interrupt and Restart: not started before the first connect, then
idle, busy, starting, dead… as the kernel says; the editor overlay's header
shows it, a page can too. The overlay never waits on the kernel: Cell
needs a connected one to mount, so while the kernel connects (the browser
kernel downloads Pyodide on its first run), and when it fails or takes over
20 s (said in a line), SourceCell stands in: JupyterLab's own code cell
and CodeMirror with no kernel, in the same theme and colour mode, editable
and saved the same way, with the cell's last outputs under it; Cell starts
from what was typed there. The overlay moves by a drag of its header. On the canvas, a running cell
paints a dot in the kernel's state colour beside #BUSY!. With no kernel, a run says No
kernel. A kernel that is not Python gets the program too: programParts
gives it the source and the values read, and it answers with a
display_data output under RESULT_MIME (the plugin guide shows one).
The kernel holds a live copy of the workbook, the collaboration document
(the Loro document of the collab entry), bound to
datalayer_cells.LiveWorkbook through loro-py (pip install datalayer-cells[live]), over one of two transports the page chooses:
- a room joined over the rooms' websocket with the page's token
(
datalayer_cells.join(url, room, token)): a server kernel, a second peer beside the page, its writes merged like another person's; - the
datalayer.cellsJupyter comm the page opens on the kernel it runs code on (openCellsCommin./jupyter; a kernel with abindingopens it on the first run): the page sends the document's snapshot, then every update, and takes the kernel's updates back, the same Loro bytes the websocket carries. Where loro-py cannot run (the browser's Pyodide has no wheel for it) the comm carries the workbook as JSON instead and the kernel's writes come back asSET_CELLS, one undo step named after the cell: the samecellsAPI, one format less faithful.
from datalayer_cells import cells is the workbook the kernel is bound to;
a run records the ranges it reads and answers them to the grid, which makes
them the cell's precedents (no parsing of the source). A kernel without the
package gets a snapshot of the values the source names instead.
CellsWorkbook JSON keeps code cells under sheets[].code["B4"]: the
source, the outputs (nbformat's), the result, the execution count, when it
last ran, run, the frame's size and offset, and the spill's. XLSX has no place for
code: the sources and their settings go into customXml/cells-code.xml,
the result is the cell's cached value, and the outputs are dropped, which
the import report says (report.code). The ipynb exporter writes the
code cells as a notebook, one code cell each in reading order with its
outputs, the sheet and cell in its metadata. CSV writes the result.
- Code runs only on a kernel the host contributed, never on open, and first on a person's gesture. The kernel is the host's: the browser's Pyodide runs in the page, a server runs what the server allows.
- Outputs from a file, a paste or another person are untrusted until
the cell runs here: the frame shows their text and images only
(
safeOutputs:text/plain,image/png,image/jpeg,image/gif,text/latex); HTML, SVG, scripts and widgets are replaced by a line saying what was held. A run in this session trusts the cell's outputs (model.codeTrusted), and loading another workbook forgets the trust. The renderer receivestrustedand the filtered outputs; jupyter-react's own sanitiser applies on top. - A host turns code cells off with
codeCells: false: their sources, results and outputs are shown as they are, andSET_CODEand every run are refused (CodeCellsOff, Code cells are off here). - A code cell reads values, never formulas; its writes are collaboration patches attributed to it, so undo and collaboration stay cell-shaped.
- Outputs travel with the workbook through collaboration like any content; keep a workbook's outputs small (frames over 200 kB render on demand).