Notebook Basics
Cell types
There are four kinds:
Code cells evaluate Julia. The last expression's value renders below, along with stdout and any rich display (images, ECharts, tables, LaTeX).
Markdown cells render GitHub-flavored markdown with LaTeX math and double-brace interpolation of Julia values. GFM tables work, as do
!!! note "Title"admonitions; the category is free-form, so a notebook can coin its own.Web cells hold HTML, CSS and JS in their own panes, for building an interface that talks to Julia. See Front-end Extensions.
Tool cells hold an
@tool name(...)call, defining a tool the agent can call. They are never swept up by an automatic run, so reopening a notebook does not re-fire one.
The cell header shows a button for each kind the cell is not ({·} code, </> web, ⌁ tool, M↓ markdown). In command mode, y / m / w convert to code, markdown and web.
A code cell shows an always-on editor with tree-based Julia highlighting (CodeMirror 6 + the Lezer Julia grammar), the run button, timing, and state badge:

Command mode vs. edit mode
KaimonSlate uses a Jupyter-style two-mode model:
Command mode — the cell has an accent ring and single keys act on it.
Edit mode — focus is inside the editor (green ring);
Escreturns to command mode.
Every key in the tables below is the default and every one of them can be changed — see Keyboard shortcuts at the end of this page.
| Key | Action |
|---|---|
↑/k, ↓/j | move selection |
⇧↑ / ⇧↓ (also ⇧K / ⇧J) | extend the selection to the cell above / below |
⌥↑ / ⌥↓ | move the cell up/down |
Esc | collapse a multi-selection back to one cell |
⏎ | enter edit mode |
a / b | add cell above / below |
c / x / v | copy / cut / paste cell(s) |
m / y / w | to markdown / to code / to web |
dd | delete cell |
⇧M | merge with the cell below |
Select several cells with shift-click (a range) or ⌘/Ctrl-click (toggle one), as well as the ⇧-arrow keys. Delete, cut, copy and the type-toggle keys then act on the whole selection, and a floating chip shows how many are selected. The cell clipboard is shared across notebook tabs.
You can also reorder with the mouse: drag the ⠿ grip in a cell's header and a drop line shows where it will land, or use the header's ↑/↓ buttons to move it one place.
In edit mode:
| Key | Action |
|---|---|
⇧⏎ | run (or commit a markdown/bind cell) |
⌘⇧⏎ / Ctrl⇧⏎ | run and open a fresh cell below |
⌘⇧- / Ctrl⇧- | split the cell at the cursor |
⇥ | completion (Julia REPL completions + cell-local bindings) |
⌘⌥↑ / ⌘⌥↓ | add a caret on the line above / below |
⌥-click | add a caret at the click |
⌘D / CtrlD | select the word under the caret, then each next occurrence |
Extra carets type together. Esc drops back to a single caret, or clears a selection, and a second Esc returns to command mode. That holds under every keymap. Vim reaches it one rung later: Esc leaves insert or visual mode first, then collapses the carets, and only once there is a single bare caret does it hand the cell back to command mode.
Selecting a word also tints its other occurrences in that cell. Turn it off, or recolour it, under Settings → Highlight matching words and Match highlight.
Notebook-wide:
| Key | Action |
|---|---|
⌘↵ | run stale cells |
⌘F / ⌘⌥F | find across every cell / find and replace |
⌘G / ⇧⌘G | next / previous match |
⌘K / ⌘⇧K | command palette / docs search |
⌘⇧A | agent panel |
⌘⇧F | controls palette |
⌘⇧L | table of contents |
⌘⇧G | dependency graph |
⌘⇧S | scratchpad |
⌘Z / ⌘⇧Z | undo / redo structural changes |
Keyboard shortcuts
Every shortcut above is a command with a stable name, and the chord that runs it is yours to change. Settings → Keyboard → Customise… opens the full list: search it, click a chord to re-record it, ✕ to remove one, + to add a second, ↺ to put a row back the way it came.
A whole keymap preset switches the lot at once, in the same Settings section:
| Preset | What it follows |
|---|---|
| Slate | the defaults above — Jupyter-style command mode, ⌘-chords for the panels |
| VS Code | ⌘⇧P palette, ⌘⇧O outline, ⌃↵ run cell, ⌥↵ run and insert below, ⌘⇧E files |
| Jupyter / Colab | classic command mode verbatim — ⌃↵ run, ⇧↵ run and advance, z undo, ii interrupt, 00 restart |
| Vim-flavoured | vim motions over the cells — j/k, gg/⇧G, i/o/⇧O, dd/yy/p, u and ⌃R |
A preset is a starting point, not a lock: anything you rebind on top of it is kept separately, so switching preset moves everything you haven't touched and leaves what you have.
Three things the panel does that are worth knowing about:
Sequences. A binding can be more than one keystroke —
dd,gg,⌘K Z. Press the strokes in turn while recording and pause to save. When you type the first stroke of one, a chip at the bottom of the window shows what is armed.Reserved chords. A browser keeps a handful of combinations for itself — new tab, close window, the devtools — and a page never sees them. Those are refused outright rather than accepted and quietly broken. Ones the browser will give up (⌘S, ⌘P, ⌘F) are allowed with a note saying what they shadow. None of the four presets ships a chord from either group.
Clashes are a question, not an outcome. Record a chord something else already uses and nothing is written: the panel names the command holding it and offers Reassign… (take it, then record a replacement for the one you displaced, right there), Take it (leave the other unbound), Keep both (flagged as a conflict on both rows), or Cancel — which changes nothing at all. Only a clash in a context the two actually share counts, so
bin command mode andbin the editor are left alone.
Shortcuts are stored per person rather than per notebook, in keymap.json under your Slate config directory — so they follow you between browsers, and you can read, hand-edit or check in the file. Export / Import move a keymap as JSON.
Text editing inside a cell — word motion, indentation, bracket matching, ⌘/ for comments — is a separate choice: Settings → Editing → Editor keymap, which is default, vim or emacs. Those bring complete binding sets of their own, and where one of them claims a chord Slate also uses, the editor keymap wins inside the editor. Consequently a row in the panel's Editor group adds a chord next to whatever that keymap already binds for the same action, rather than replacing it.
An extension that contributes a palette command gets a row here too, badged with the package that owns it, so its shortcut is as changeable as the built-in ones.
Finding across the notebook
⌘F opens one find bar for the whole document rather than a panel inside the focused cell — in a notebook what you are looking for is usually in a different cell. It walks every cell in order, counts the hits and steps between them across cell boundaries with ⏎ or ⌘G. ⌘⌥F opens the replace row as well; ⌘⏎ there replaces every match in the notebook.
The three switches on the bar are match case (Aa), whole word (ab) and regular expression (.*). Whole word is decided by character category rather than \b, so it behaves sensibly for the Unicode names Julia encourages: searching α matches a standalone α and not the α inside αβ. In regex mode $1…$9, $& and $$ expand in the replacement; in plain mode a $ stays a $.
Cells that have not been scrolled to yet still contribute to the count — their text comes from the last saved source. Stepping onto a match in a markdown or @bind cell opens that cell's source so the match can be shown.
Replace All is a single action. It rewrites every match across the notebook in one step, which means one ⌘Z puts it all back — the toast names what it undid — and the timeline gets one checkpoint labelled with the search and replacement, so you can also restore it later from ☰ → 🕘 History. The rewritten cells are left stale rather than re-run: a text substitution decides what your code says, not when it should run.
Inside the Files tab's whole-file editor, ⌘F keeps CodeMirror's own single-document find panel, scoped to that one file.
Running cells
Run a single cell with ⇧⏎, or press ⌘⏎ (Ctrl⏎) to recompute every stale cell. The same action is in the command palette as Run stale cells. The kernel dot in the top bar breathes while a computation is in flight.
Stopping a run
A run in flight can be interrupted. That leaves the namespace intact, so everything computed so far is still there. ⟲ Restart worker is the heavier option: it discards the run and the namespace with it.
When a run gets stuck
A supervisor sweeps every few seconds and raises a badge in the top bar when something looks wrong: amber for a warning, red for something critical. Clicking it opens a panel naming each alert, a stalled kernel or a runaway loop, with the recovery action for that alert (stop the run, or restart the worker).
The scratchpad
☰ → 🧪 Scratchpad (⌘⇧S) opens a panel where you can run Julia in the notebook's own kernel without creating a cell or touching the .jl. Results stream in with a timestamp, 🧹 clears them, and the 🧪 pill in the top bar lights up while something is running there.
It is the place for a quick check that is not part of the document. The agent's slate_eval runs in the same scratchpad.
Opening a cold notebook
The first open of a notebook whose packages are not yet precompiled shows a banner reading Precompiling k/N · <package>, with elapsed time and a collapsible build log. This is one-time; later opens are fast, and you can keep editing while it runs.
Installing a package mid-session is different: that shows a blocking overlay, because the notebook is paused while its environment resolves and precompiles.
A notebook can also open inactive, showing its stored results with no worker behind it. See the front page.
Quiet cells
A code cell whose last non-comment line ends in ; suppresses display of the value (stdout and explicit display() still show) — handy for setup cells.
big = rand(1000, 1000); # no 1000×1000 dumpMarkdown interpolation
Inside a markdown cell, a double-brace expr block splices a Julia value into the rendered output:
Mean: {{ round(mean(data); digits=2) }}
{{ echart(spec) }} <!-- an interactive chart -->
{{ slate_table(df) }} <!-- an interactive table -->Scalars render inline; images, charts, and tables render as blocks. Interpolation works inside math ($…$) too.

Images and clips
An image alone in its paragraph is treated as a figure and centred; one sitting inside a run of prose stays inline where you put it. An attribute block directly after the image (no space, as in Pandoc/Quarto) overrides how it is drawn:
{width=300}
{width=40% align=right}
{.zoomable #fig-detail title="click to enlarge"}width/height take a bare number as pixels (300 → 300px) or any CSS length or percentage; align is left, right, or center; .name adds a class, #name sets an id. The same block works on a <video> or <audio> you dropped into the cell, and on an interpolated image. Sizes carry into the HTML export and the PDF; unrecognised keys are dropped rather than emitted.
Completion
Tab-completion uses Julia's REPL completions against the live kernel plus the cell's own local bindings (assignments, loop/comprehension variables, function parameters) so names complete even before the cell has run. The popup carries a docstring preview, and method completions insert tab-through argument placeholders. LaTeX/emoji shortcuts work — type \pi⇥ → π.

Renaming cells
Cell ids are header-safe labels. Double-click a cell's id to rename it; renames are tracked as renames in history, and dependencies (which are by id) are rebuilt.