Jupyter notebooks in the terminal. Real vim. Live Python kernel.
turbobook notebook.tb
No browser. No Electron. A 30ms keystroke-to-render notebook that works like an editor.
Web notebooks are slow and mouse-dependent. Most "vim mode" add-ons are keybinding shims — they don't compose operators with motions. Jupyter's wire protocol is overkill for local work.
turbobook is a terminal-first notebook editor built from scratch:
- Vim that actually composes.
d2w,c$,yip,ct)— operators + motions work the way vim users expect, with correct inclusive/exclusive/linewise semantics. - Persistent Python kernel. A subprocess connected over a Unix socket keeps all your globals alive across cell executions.
- Git-friendly format.
.tbfiles are versioned JSON with stable UUID cell IDs (not execution counts). They diff cleanly. - Execute anywhere.
Ctrl+Enterruns the focused cell from normal, insert, or visual mode — no mode switching required.
Requires Go 1.21+ and Python 3.10+.
git clone https://github.com/fntune/turbobook
cd turbobook
go build -o turbobook .
sudo mv turbobook /usr/local/bin/ # optionalturbobook notebook.tb # open an existing notebook
turbobook # splash screen with recent files| Key | Action |
|---|---|
j / k |
Move focus between cells |
Ctrl+Enter |
Execute focused cell |
o / O |
Insert cell below / above |
dd |
Delete cell |
gg / G |
First / last cell |
: |
Command mode |
Full vim editing — all motions, operators, and text objects work:
| Pattern | Examples |
|---|---|
| Motions | w b e B E 0 ^ $ gg G { } |
| Operators | d c y with any motion (d2w, c$, yip) |
| Find/till | f F t T + char, then ; / , to repeat |
| Visual | v (char), V (line) — then any operator |
| Replace | r (single char), ~ (toggle case) |
| Search | / forward, ? backward, n / N to cycle |
| Join | J joins next line |
| Command | Action |
|---|---|
:w |
Save (source only) |
:w! |
Save with outputs |
:q |
Quit |
Notebooks are stored as .tb files — plain JSON, designed to diff cleanly:
{
"version": "1",
"cells": [
{
"id": "3f2a1c8b-...",
"type": "code",
"source": "import pandas as pd\ndf = pd.read_csv('data.csv')",
"outputs": [],
"meta": { "exec_count": 1, "exec_ms": 42, "collapsed": false }
}
]
}Cell IDs are UUIDs — reordering cells doesn't rewrite the whole file.
turbobook (Go binary)
├── bubbletea TUI event loop, terminal rendering
├── vim layer (pure functions) VimState × Buffer → VimState × Buffer × Action
│ ├── normal mode operators, motions, counts, registers
│ ├── insert mode text input, ctrl sequences
│ └── visual mode inclusive/exclusive selection
├── kernel client Unix socket IPC to Python subprocess
│ ├── execute / interrupt cell execution, cancellation
│ └── status / ready kernel lifecycle events
└── .tb format versioned JSON, UUID cells, MIME outputs
Python runtime (subprocess)
└── Unix socket server JSON-RPC, Content-Length framing
├── execute handler eval in shared namespace, captures stdout/stderr
└── ready / shutdown lifecycle handshake
The vim layer is entirely pure: every key event is a function from (VimState, Buffer, key) to (VimState, Buffer, Action). No side effects, no global state — the UI drives side effects based on the returned Action.
Working alpha. Core vim editing, kernel execution, cell management, and .tb format are complete. Planned: LSP code completion, SQL cells with remote DB connections, AI generation and fix commands.