gpynb 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gpynb-0.1.0/PKG-INFO +323 -0
- gpynb-0.1.0/README.md +309 -0
- gpynb-0.1.0/pyproject.toml +30 -0
- gpynb-0.1.0/pyproject.toml.orig +31 -0
- gpynb-0.1.0/src/gpython/__init__.py +56 -0
- gpynb-0.1.0/src/gpython/app.py +1146 -0
- gpynb-0.1.0/src/gpython/app.tcss +258 -0
- gpynb-0.1.0/src/gpython/bigtext.py +201 -0
- gpynb-0.1.0/src/gpython/cell.py +581 -0
- gpynb-0.1.0/src/gpython/config.py +62 -0
- gpynb-0.1.0/src/gpython/dom.py +16 -0
- gpynb-0.1.0/src/gpython/envpicker.py +463 -0
- gpynb-0.1.0/src/gpython/envs.py +370 -0
- gpynb-0.1.0/src/gpython/explorer.py +227 -0
- gpynb-0.1.0/src/gpython/finder.py +305 -0
- gpynb-0.1.0/src/gpython/graph.py +249 -0
- gpynb-0.1.0/src/gpython/help.py +43 -0
- gpynb-0.1.0/src/gpython/kernel.py +324 -0
- gpynb-0.1.0/src/gpython/keymap.py +137 -0
- gpynb-0.1.0/src/gpython/markdown.py +280 -0
- gpynb-0.1.0/src/gpython/notebook.py +560 -0
- gpynb-0.1.0/src/gpython/outputs.py +297 -0
- gpynb-0.1.0/src/gpython/pane.py +1020 -0
- gpynb-0.1.0/src/gpython/runnumber.py +145 -0
- gpynb-0.1.0/src/gpython/themepicker.py +144 -0
- gpynb-0.1.0/src/gpython/themes.py +441 -0
- gpynb-0.1.0/src/gpython/vim.py +1438 -0
- gpynb-0.1.0/src/gpython/windows.py +506 -0
gpynb-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: gpynb
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A reactive Python notebook in the terminal, built on Textual
|
|
5
|
+
Author: igaurab
|
|
6
|
+
Author-email: igaurab <dev@igaurab.com>
|
|
7
|
+
Requires-Dist: ipykernel>=7.4.0
|
|
8
|
+
Requires-Dist: jupyter-client>=8.10.0
|
|
9
|
+
Requires-Dist: pillow>=13.0.0
|
|
10
|
+
Requires-Dist: textual-image>=0.14.2
|
|
11
|
+
Requires-Dist: textual[syntax]>=8.2.8
|
|
12
|
+
Requires-Python: >=3.13
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# gpython
|
|
16
|
+
|
|
17
|
+
A Python notebook in the terminal, inspired by marimo: cells know which
|
|
18
|
+
variables they define and use, and runs follow that graph.
|
|
19
|
+
|
|
20
|
+
Running a cell first runs the stale cells it depends on (never run, edited
|
|
21
|
+
since, or their inputs changed), then the cell. What happens to the cells
|
|
22
|
+
*below* it, the ones that use its variables, is the **run mode** (`f3`, saved):
|
|
23
|
+
|
|
24
|
+
- **lazy** (the default): they are only marked stale. You run what you want,
|
|
25
|
+
when you want, without moving: `r 6` runs cell c6, `r r` this cell, `r C` the
|
|
26
|
+
cells down to this one, `r a` the cells after it, `r A` all, `r s` the stale
|
|
27
|
+
ones (`f4`); `r k` restarts the kernel, `r R` restarts and runs all, `r i`
|
|
28
|
+
interrupts (`space r …` inside an editor).
|
|
29
|
+
- **reactive**: they re-run automatically, like marimo.
|
|
30
|
+
|
|
31
|
+
<img width="1372" height="1034" alt="image" src="https://github.com/user-attachments/assets/c93ed22f-cf15-4a26-9322-c62c940febf5" />
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
uv tool install gpynb # installs the `gpython` command globally
|
|
36
|
+
uvx --from gpynb gpython # or run it once without installing
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
./gpython # empty untitled notebook
|
|
41
|
+
./gpython script.py # a plain script is split into cells automatically
|
|
42
|
+
./gpython a.py b.py # several files: one tab each (gt / gT switch)
|
|
43
|
+
./gpython notebooks/ # a directory: opens the file explorer there
|
|
44
|
+
./gpython --no-vim nb.py # plain editing, no modal keys
|
|
45
|
+
./gpython --theme nord nb.py # colour theme for this run (ctrl+t picks one and remembers it)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Files: `.py` notebooks (plain scripts, `# %%` cells, Jupytext markdown cells),
|
|
49
|
+
marimo notebooks (`@app.cell`) and `.ipynb`; see [Files](#files).
|
|
50
|
+
|
|
51
|
+
## Keys
|
|
52
|
+
|
|
53
|
+
Three modes, shown in the top bar: **INSERT** (typing), **NORMAL** (vim keys in
|
|
54
|
+
the editor) and **CELL** (Jupyter's command mode: the whole cell is selected and
|
|
55
|
+
keys act on cells). `escape` goes INSERT → NORMAL → CELL; `enter` or `i` goes back
|
|
56
|
+
into the editor. Clicking a cell's header selects it. `f1` (or `?` in cell mode)
|
|
57
|
+
shows every key; the list lives in `src/gpython/keymap.py`.
|
|
58
|
+
|
|
59
|
+
Everywhere:
|
|
60
|
+
|
|
61
|
+
| Key | Action |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `shift+enter` | Run cell (markdown: render it), then go to the cell below (a new cell only on the last one) |
|
|
64
|
+
| `ctrl+enter` / `ctrl+shift+enter` / `ctrl+e` | Run cell (markdown: render it), stay |
|
|
65
|
+
| `alt+enter` | Run cell and insert a new cell below |
|
|
66
|
+
| `f5` / `f4` | Run all / run stale cells |
|
|
67
|
+
| `f6` | Python environment: switch venv/interpreter, new uv or python venv, install packages |
|
|
68
|
+
| `ctrl+k` | Interrupt running code |
|
|
69
|
+
| `ctrl+n` / `ctrl+shift+n` | New cell below / above |
|
|
70
|
+
| `ctrl+d` | Delete cell (cells that used its variables are marked stale, or re-run when reactive; `u` in cell mode undoes) |
|
|
71
|
+
| `alt+↑/↓`, `ctrl+↑/↓` | Previous / next cell |
|
|
72
|
+
| `ctrl+home` / `ctrl+end` | First / last cell |
|
|
73
|
+
| `alt+shift+↑/↓` | Move the cell up / down |
|
|
74
|
+
| `ctrl+s` / `:w` | Save (asks for a name if untitled) |
|
|
75
|
+
| `ctrl+b` | Toggle the file explorer (see [Files, windows and tabs](#files-windows-and-tabs)) |
|
|
76
|
+
| `ctrl+w` … | Window commands: `s` split, `v` vsplit, `h j k l` move, `q` close, `o` only, `T` to tab |
|
|
77
|
+
| `f1` | Help |
|
|
78
|
+
| `f2` | Toggle variables panel (hidden at start) |
|
|
79
|
+
| `f3` | Toggle run mode, saved: lazy (default, cells that use the variables are only marked stale) / reactive (they re-run) |
|
|
80
|
+
| `ctrl+p` | Command palette: run all, restart kernel, … |
|
|
81
|
+
| `ctrl+q` / `:q` | Quit (asks when there are unsaved changes; `:q!` does not) |
|
|
82
|
+
|
|
83
|
+
Vim normal mode (in the editor):
|
|
84
|
+
|
|
85
|
+
| Key | Action |
|
|
86
|
+
|---|---|
|
|
87
|
+
| `escape` | Leave the editor: cell mode |
|
|
88
|
+
| `J` / `K` | Next / previous cell (`3J` jumps three) |
|
|
89
|
+
| `j` / `k` | Down / up a line; past the edge moves to the next cell |
|
|
90
|
+
| `gg` / `G` / `3G` | First / last / third line of this cell |
|
|
91
|
+
| `dd` | Delete line, like vim (`3dd` three lines; `u` undoes). To delete the cell: `esc`, then `dd` |
|
|
92
|
+
| `gJ` | Join lines (vim's `J`) |
|
|
93
|
+
| `/` | Jump: `/3` third cell (as numbered in the headers), `/text` next match after the cursor (smart case, highlighted) |
|
|
94
|
+
| `n` / `N` | Next / previous match of the last search, across cells (`3n` skips three) |
|
|
95
|
+
| `*` / `#` | Search forward / backward for the word under the cursor |
|
|
96
|
+
| `gt` / `gT` / `2gt` | Next / previous / second tab |
|
|
97
|
+
| `:` | Ex command |
|
|
98
|
+
|
|
99
|
+
Also `h j k l w b e 0 ^ $`, counts, `i a I A o O`, `x X s S dw d$ dj D cc cw C yy p P u ctrl+r`.
|
|
100
|
+
|
|
101
|
+
Cell mode. Whether you are in cell mode or in the editor is one choice for the whole
|
|
102
|
+
notebook: moving across a rendered markdown or hidden-code cell (which is always
|
|
103
|
+
selected as a cell), deleting, undoing, searching or switching tabs keeps it; `escape`,
|
|
104
|
+
`enter`/`i` and clicks change it.
|
|
105
|
+
|
|
106
|
+
| Key | Action |
|
|
107
|
+
|---|---|
|
|
108
|
+
| `j` / `k` (`3j`) | Select next / previous cell |
|
|
109
|
+
| `gg` / `G` / `3G` | Select first / last / third cell |
|
|
110
|
+
| `enter` / `i` | Edit the cell (normal / insert mode); on markdown, show its source |
|
|
111
|
+
| `a` / `b` | Insert a cell above / below |
|
|
112
|
+
| `dd` | Delete cell (`3dd` three; `u` brings them back) |
|
|
113
|
+
| `u` (or `z`) | Undo the last cell action: delete, move, insert, markdown/code switch |
|
|
114
|
+
| `yy` / `x` / `p` / `P` | Copy / cut / paste below / paste above |
|
|
115
|
+
| `J` / `K` | Move the cell down / up |
|
|
116
|
+
| `m` / `y` | Markdown / code cell |
|
|
117
|
+
| `h` | Hide / show the cell's code (marimo `hide_code`) |
|
|
118
|
+
| `o` / `O` | Collapse output / clear output |
|
|
119
|
+
| `l` | Toggle line numbers |
|
|
120
|
+
| `gt` / `gT` | Next / previous tab |
|
|
121
|
+
| `/` | Jump to `/3`, or select the next cell matching `/text` |
|
|
122
|
+
| `n` / `N` | Select the next / previous cell matching the last search |
|
|
123
|
+
| `escape` | Clear pending keys and the search highlight |
|
|
124
|
+
| `:`, `?` | Ex command, help |
|
|
125
|
+
|
|
126
|
+
Ex commands: `:w [file]`, `:wa`, `:q`, `:q!`, `:wq`, `:qa`, `:wqa`, `:e file`,
|
|
127
|
+
`:sp [file]`, `:vs [file]`, `:only`, `:tabnew [file]`, `:tabclose`, `:tabn`,
|
|
128
|
+
`:tabp`, `:ls`, `:noh`, `:Ex`, `:run`, `:runabove`, `:runbelow`, `:stale`, `:restart`,
|
|
129
|
+
`:clear`, `:set vim|novim|lazy|nolazy|nu|nonu`, `:vars`, `:md`, `:code`,
|
|
130
|
+
`:theme [name]`, `:N` (go to cell N), `:help`. `:set novim`, line numbers, the
|
|
131
|
+
run mode and the theme are remembered in `~/.config/gpython/config.toml`.
|
|
132
|
+
|
|
133
|
+
Cell headers read `✓ c3 [4] 0.42s defines x · uses y`: display position,
|
|
134
|
+
execution count (like Jupyter's `In [4]`) and run time.
|
|
135
|
+
|
|
136
|
+
Editing: Enter indents after `:` or an open bracket and dedents after
|
|
137
|
+
`return`/`pass`/…; backspace in leading whitespace removes one indent level;
|
|
138
|
+
Tab inserts four spaces.
|
|
139
|
+
|
|
140
|
+
`shift+enter` and `ctrl+enter` need a terminal that reports them (Ghostty,
|
|
141
|
+
Kitty, WezTerm, iTerm2). In Terminal.app use `ctrl+e` and `ctrl+n`.
|
|
142
|
+
|
|
143
|
+
## Markdown cells
|
|
144
|
+
|
|
145
|
+
A markdown cell shows rendered text (headings, lists, code, links) instead of an
|
|
146
|
+
editor, and it never runs.
|
|
147
|
+
|
|
148
|
+
- Make one: `ctrl+n` for a new cell, then `escape escape` (cell mode; one `escape` with `--no-vim`) and `m`. Or type
|
|
149
|
+
`:md` in any cell. `y` or `:code` turns it back into code. Turning a code cell
|
|
150
|
+
into markdown removes its variables, so the cells that used them are marked
|
|
151
|
+
stale (reactive mode re-runs them, and they fail).
|
|
152
|
+
- Edit: select the cell and press `enter` (or `i`), or double-click it.
|
|
153
|
+
- Render: `escape escape` (back to cell mode), `shift+enter` (render and go to
|
|
154
|
+
the next cell), `ctrl+enter` (render and stay), or just move to another cell.
|
|
155
|
+
- Files: in `.py` notebooks a markdown cell is `# %% [markdown]` followed by
|
|
156
|
+
lines starting with `# ` (the Jupytext format). In plain scripts without
|
|
157
|
+
markers, a top-level `"""text"""` string is a markdown cell. `.ipynb`
|
|
158
|
+
markdown cells load and save as markdown cells.
|
|
159
|
+
|
|
160
|
+
A code cell whose last line is a bare string (`"""note"""`) does not print
|
|
161
|
+
that string as its result.
|
|
162
|
+
|
|
163
|
+
## Files, windows and tabs
|
|
164
|
+
|
|
165
|
+
gpython opens several notebooks at once, the way vim opens several files:
|
|
166
|
+
each notebook is a *window*, windows can be split, and tabs group windows.
|
|
167
|
+
Every notebook has its own kernel (its own variables, its own `__file__`).
|
|
168
|
+
|
|
169
|
+
**File explorer** (`ctrl+b`, `:Ex`, `:e` with no name). A tree of the
|
|
170
|
+
notebook's folder on the left: folders and `.py`/`.ipynb` files, hidden files
|
|
171
|
+
and other extensions behind `.`. Keys inside it:
|
|
172
|
+
|
|
173
|
+
| Key | Action |
|
|
174
|
+
|---|---|
|
|
175
|
+
| `j` / `k`, `gg` / `G` | Move |
|
|
176
|
+
| `h` / `l` | Collapse (or go to the parent) / expand a folder; `l` on a file opens it |
|
|
177
|
+
| `enter` / `o` / `t` | Open the file in its own tab (a folder expands); a blank untitled window is reused |
|
|
178
|
+
| `e` | Open the file in the current window (like `:e`) |
|
|
179
|
+
| `s` / `v` | Open in a horizontal / vertical split |
|
|
180
|
+
| `-` | Go up: the parent folder becomes the root |
|
|
181
|
+
| `C` | Make the folder under the cursor the root |
|
|
182
|
+
| `%` | New notebook in the folder under the cursor |
|
|
183
|
+
| `/`, `n` | Search file names, repeat |
|
|
184
|
+
| `.` | Show hidden and non-notebook files |
|
|
185
|
+
| `R` | Reload |
|
|
186
|
+
| `q`, `escape`, `ctrl+b` | Hide the explorer |
|
|
187
|
+
|
|
188
|
+
`ctrl+b` toggles the explorer from anywhere. The explorer stays open after a
|
|
189
|
+
file is opened; `ctrl+w h` from the leftmost window (or `ctrl+w w`) goes back to
|
|
190
|
+
it. A file that is already open is shown (its tab focused) instead of being
|
|
191
|
+
opened twice. Clicking a file opens it too.
|
|
192
|
+
|
|
193
|
+
**Windows** (`ctrl+w` then a key, as in vim; not in insert mode, where
|
|
194
|
+
`ctrl+w` deletes a word):
|
|
195
|
+
|
|
196
|
+
| Key | Action |
|
|
197
|
+
|---|---|
|
|
198
|
+
| `ctrl+w s` / `:sp [file]` | Split: a new window below (an untitled notebook, or `file`) |
|
|
199
|
+
| `ctrl+w v` / `:vs [file]` | Vertical split: a new window to the right |
|
|
200
|
+
| `ctrl+w h` `j` `k` `l` | Focus the window to the left / below / above / right |
|
|
201
|
+
| `ctrl+w w` / `ctrl+w p` | Next / previous window (the explorer counts as the first) |
|
|
202
|
+
| `ctrl+w q` / `:q` | Close this window (asks if unsaved; closing the last one quits) |
|
|
203
|
+
| `ctrl+w o` / `:only` | Keep only this window |
|
|
204
|
+
| `ctrl+w T` | Move this window to a new tab |
|
|
205
|
+
| `ctrl+w =`, `+` `-` `<` `>` | Equalize / resize |
|
|
206
|
+
|
|
207
|
+
When a tab has several windows each shows a status line with its file name,
|
|
208
|
+
dirty mark and kernel state; the active window's line is highlighted.
|
|
209
|
+
|
|
210
|
+
**Tabs**: `:tabnew [file]`, `gt` / `gT` / `2gt` (normal and cell mode),
|
|
211
|
+
`:tabn`, `:tabp`, `:tabclose`, `:tabonly`. The tab line appears above the
|
|
212
|
+
notebook once there are two tabs (or a hidden buffer, see below); click a tab
|
|
213
|
+
to switch. `./gpython a.py b.py` opens one tab per file.
|
|
214
|
+
|
|
215
|
+
**Buffers** (as in vim): every opened notebook is a buffer with its own
|
|
216
|
+
kernel, numbered in opening order. Closing a window (`:q`, `ctrl+w q`,
|
|
217
|
+
`:tabclose`) only hides its notebook: variables and outputs stay, and the tab
|
|
218
|
+
line lists hidden buffers (click one to show it).
|
|
219
|
+
|
|
220
|
+
| Command | Action |
|
|
221
|
+
|---|---|
|
|
222
|
+
| `:ls` / `:buffers` | List buffers: `%a` current, `a` shown, `h` hidden, `+` unsaved |
|
|
223
|
+
| `:b N` / `:b name` | Show that buffer in the current window (a partial, unique name works) |
|
|
224
|
+
| `:bn` / `:bp` | Next / previous buffer in the current window |
|
|
225
|
+
| `:bd [N]` | Delete the buffer and stop its kernel (asks if unsaved; `:bd!` does not) |
|
|
226
|
+
| `:e file` | Open a file in the current window (refuses if unsaved; `:e! file` discards) |
|
|
227
|
+
|
|
228
|
+
`:w` saves the current notebook, `:wa` saves every buffer, `:q` on the last
|
|
229
|
+
window quits (asking about any unsaved buffer), `:qa` quits, `:wqa` saves
|
|
230
|
+
everything and quits. Hidden buffers keep their kernel process alive; `:bd`
|
|
231
|
+
frees it.
|
|
232
|
+
|
|
233
|
+
## Themes
|
|
234
|
+
|
|
235
|
+
`ctrl+t` opens a theme picker with live preview: move through the list and
|
|
236
|
+
the whole notebook (editor syntax colours included) changes as you go;
|
|
237
|
+
`enter` keeps the theme and writes it to `~/.config/gpython/config.toml`,
|
|
238
|
+
`escape` restores the one you had. Every later session starts in the saved
|
|
239
|
+
theme. `:theme name` does the same without the picker and is saved too;
|
|
240
|
+
`--theme name` applies to that run only; `:theme` alone shows the current one.
|
|
241
|
+
|
|
242
|
+
The default theme, `terminal` (also `:theme default`), uses your terminal's
|
|
243
|
+
own colours and background, so gpython looks like whatever colorscheme the
|
|
244
|
+
terminal has. Syntax colours are the terminal's 16 named colours.
|
|
245
|
+
|
|
246
|
+
Themes: terminal, catppuccin (mocha, macchiato, frappe, latte), gruvbox (dark, light),
|
|
247
|
+
material (oceanic, palenight, darker, lighter), nord, dracula, tokyo-night,
|
|
248
|
+
solarized (dark, light), monokai, plus Textual's built-in themes.
|
|
249
|
+
|
|
250
|
+
## Python environments and packages
|
|
251
|
+
|
|
252
|
+
Each notebook's kernel runs in a Python environment. The header chip shows
|
|
253
|
+
which one (`Python 3.12.4 · .venv ▾`); the bottom bar shows the kernel state
|
|
254
|
+
with a dot (green ready, yellow starting or installing, red failed) and the
|
|
255
|
+
live output of a running install.
|
|
256
|
+
|
|
257
|
+
At start a notebook uses the environment it last used; otherwise the nearest
|
|
258
|
+
`.venv` / `venv` (next to the file, up to the project root) that has
|
|
259
|
+
ipykernel; otherwise gpython's own interpreter. A project venv without
|
|
260
|
+
ipykernel is pointed out once.
|
|
261
|
+
|
|
262
|
+
`f6` (or a click on the header chip) opens the environment picker:
|
|
263
|
+
|
|
264
|
+
- **Switch**: every venv, uv env and conda env next to the notebook and in
|
|
265
|
+
`~/.virtualenvs`, `~/.venvs`, conda's `envs`, plus uv's managed Pythons and
|
|
266
|
+
`python3.x` on `PATH`. Type to filter, or type a path to a venv or a python.
|
|
267
|
+
Picking a venv without ipykernel installs it first. Picking a bare
|
|
268
|
+
interpreter offers to make a venv from it. Switching starts a fresh kernel
|
|
269
|
+
(cells become stale) and is remembered for that notebook.
|
|
270
|
+
- **New uv env**: `uv venv <folder> [--python 3.12]`, then ipykernel, then
|
|
271
|
+
switch. Any Python version uv can download works.
|
|
272
|
+
- **New venv**: the same with `python -m venv` and pip, no uv needed.
|
|
273
|
+
- **Install packages**: `uv pip install` into the kernel's environment (pip
|
|
274
|
+
without uv). New imports work without restarting the kernel.
|
|
275
|
+
- **Add to project**: `uv add`, writing to `pyproject.toml` and syncing into
|
|
276
|
+
the kernel's environment.
|
|
277
|
+
|
|
278
|
+
The same as commands: `:env` (picker) or `:env name|path`, `:venv [folder]
|
|
279
|
+
[python]`, `:uv venv [folder] [python]`, `:pip numpy pandas`, `:pip list`,
|
|
280
|
+
`:pip uninstall x`, `:uv add httpx`, `:uv <anything>` (runs in the project).
|
|
281
|
+
Inside cells, `%uv pip install x` and `%uv add x` target the kernel's own
|
|
282
|
+
interpreter, and `%pip` uses uv in environments that have no pip.
|
|
283
|
+
|
|
284
|
+
## Rules (same as marimo)
|
|
285
|
+
|
|
286
|
+
- A name may be defined in only one cell.
|
|
287
|
+
- Names starting with `_` are private to their cell.
|
|
288
|
+
- In-place changes (`xs.append(1)`, `d["k"] = v`) at a cell's top level are
|
|
289
|
+
tracked: readers below run after the mutator, and running the mutator first
|
|
290
|
+
re-runs the cell that created the object so it starts fresh.
|
|
291
|
+
|
|
292
|
+
## Files
|
|
293
|
+
|
|
294
|
+
Notebooks are plain `.py` files. `# %%` markers (VS Code / Jupytext) are
|
|
295
|
+
honoured; files without markers are split at top-level statements and saved
|
|
296
|
+
back without markers, so a script stays a script. `__file__` and the working
|
|
297
|
+
directory match the notebook, like running it as a script.
|
|
298
|
+
|
|
299
|
+
**marimo notebooks** (`import marimo as mo` … `@app.cell` functions) open as
|
|
300
|
+
cells and save back byte-identical when nothing changed: cell names, the
|
|
301
|
+
`hide_code=True` decorator argument (such cells open with their code hidden;
|
|
302
|
+
`h` in cell mode shows it) and `mo.md(...)` cells are kept. In the
|
|
303
|
+
kernel a small `marimo` shim provides `mo.md`, `mo.Html`, `mo.hstack`,
|
|
304
|
+
`mo.vstack` and `mo.ui` placeholders so such notebooks run. Not kept: other
|
|
305
|
+
decorator arguments and comments after a cell's `return`.
|
|
306
|
+
|
|
307
|
+
**Jupyter notebooks** (`.ipynb`) open with their code and markdown cells,
|
|
308
|
+
their stored outputs and execution counts, and save back as `.ipynb` with the
|
|
309
|
+
current outputs (notebook metadata preserved).
|
|
310
|
+
|
|
311
|
+
## Layout
|
|
312
|
+
|
|
313
|
+
`graph.py` dependency analysis · `kernel.py` IPython kernel in a subprocess
|
|
314
|
+
via jupyter_client · `notebook.py` file formats (`# %%`, scripts, marimo,
|
|
315
|
+
`.ipynb`) · `vim.py` modal editor and completion popup · `cell.py` one cell ·
|
|
316
|
+
`pane.py` one notebook window · `windows.py` tabs and split layout ·
|
|
317
|
+
`explorer.py` file tree · `themes.py` / `themepicker.py` / `config.py` ·
|
|
318
|
+
`outputs.py` rich outputs · `keymap.py` the key table (help and this README) ·
|
|
319
|
+
`app.py` + `app.tcss` the Textual app.
|
|
320
|
+
|
|
321
|
+
```sh
|
|
322
|
+
uv run pytest -q
|
|
323
|
+
```
|
gpynb-0.1.0/README.md
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# gpython
|
|
2
|
+
|
|
3
|
+
A Python notebook in the terminal, inspired by marimo: cells know which
|
|
4
|
+
variables they define and use, and runs follow that graph.
|
|
5
|
+
|
|
6
|
+
Running a cell first runs the stale cells it depends on (never run, edited
|
|
7
|
+
since, or their inputs changed), then the cell. What happens to the cells
|
|
8
|
+
*below* it, the ones that use its variables, is the **run mode** (`f3`, saved):
|
|
9
|
+
|
|
10
|
+
- **lazy** (the default): they are only marked stale. You run what you want,
|
|
11
|
+
when you want, without moving: `r 6` runs cell c6, `r r` this cell, `r C` the
|
|
12
|
+
cells down to this one, `r a` the cells after it, `r A` all, `r s` the stale
|
|
13
|
+
ones (`f4`); `r k` restarts the kernel, `r R` restarts and runs all, `r i`
|
|
14
|
+
interrupts (`space r …` inside an editor).
|
|
15
|
+
- **reactive**: they re-run automatically, like marimo.
|
|
16
|
+
|
|
17
|
+
<img width="1372" height="1034" alt="image" src="https://github.com/user-attachments/assets/c93ed22f-cf15-4a26-9322-c62c940febf5" />
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
uv tool install gpynb # installs the `gpython` command globally
|
|
22
|
+
uvx --from gpynb gpython # or run it once without installing
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
./gpython # empty untitled notebook
|
|
27
|
+
./gpython script.py # a plain script is split into cells automatically
|
|
28
|
+
./gpython a.py b.py # several files: one tab each (gt / gT switch)
|
|
29
|
+
./gpython notebooks/ # a directory: opens the file explorer there
|
|
30
|
+
./gpython --no-vim nb.py # plain editing, no modal keys
|
|
31
|
+
./gpython --theme nord nb.py # colour theme for this run (ctrl+t picks one and remembers it)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Files: `.py` notebooks (plain scripts, `# %%` cells, Jupytext markdown cells),
|
|
35
|
+
marimo notebooks (`@app.cell`) and `.ipynb`; see [Files](#files).
|
|
36
|
+
|
|
37
|
+
## Keys
|
|
38
|
+
|
|
39
|
+
Three modes, shown in the top bar: **INSERT** (typing), **NORMAL** (vim keys in
|
|
40
|
+
the editor) and **CELL** (Jupyter's command mode: the whole cell is selected and
|
|
41
|
+
keys act on cells). `escape` goes INSERT → NORMAL → CELL; `enter` or `i` goes back
|
|
42
|
+
into the editor. Clicking a cell's header selects it. `f1` (or `?` in cell mode)
|
|
43
|
+
shows every key; the list lives in `src/gpython/keymap.py`.
|
|
44
|
+
|
|
45
|
+
Everywhere:
|
|
46
|
+
|
|
47
|
+
| Key | Action |
|
|
48
|
+
|---|---|
|
|
49
|
+
| `shift+enter` | Run cell (markdown: render it), then go to the cell below (a new cell only on the last one) |
|
|
50
|
+
| `ctrl+enter` / `ctrl+shift+enter` / `ctrl+e` | Run cell (markdown: render it), stay |
|
|
51
|
+
| `alt+enter` | Run cell and insert a new cell below |
|
|
52
|
+
| `f5` / `f4` | Run all / run stale cells |
|
|
53
|
+
| `f6` | Python environment: switch venv/interpreter, new uv or python venv, install packages |
|
|
54
|
+
| `ctrl+k` | Interrupt running code |
|
|
55
|
+
| `ctrl+n` / `ctrl+shift+n` | New cell below / above |
|
|
56
|
+
| `ctrl+d` | Delete cell (cells that used its variables are marked stale, or re-run when reactive; `u` in cell mode undoes) |
|
|
57
|
+
| `alt+↑/↓`, `ctrl+↑/↓` | Previous / next cell |
|
|
58
|
+
| `ctrl+home` / `ctrl+end` | First / last cell |
|
|
59
|
+
| `alt+shift+↑/↓` | Move the cell up / down |
|
|
60
|
+
| `ctrl+s` / `:w` | Save (asks for a name if untitled) |
|
|
61
|
+
| `ctrl+b` | Toggle the file explorer (see [Files, windows and tabs](#files-windows-and-tabs)) |
|
|
62
|
+
| `ctrl+w` … | Window commands: `s` split, `v` vsplit, `h j k l` move, `q` close, `o` only, `T` to tab |
|
|
63
|
+
| `f1` | Help |
|
|
64
|
+
| `f2` | Toggle variables panel (hidden at start) |
|
|
65
|
+
| `f3` | Toggle run mode, saved: lazy (default, cells that use the variables are only marked stale) / reactive (they re-run) |
|
|
66
|
+
| `ctrl+p` | Command palette: run all, restart kernel, … |
|
|
67
|
+
| `ctrl+q` / `:q` | Quit (asks when there are unsaved changes; `:q!` does not) |
|
|
68
|
+
|
|
69
|
+
Vim normal mode (in the editor):
|
|
70
|
+
|
|
71
|
+
| Key | Action |
|
|
72
|
+
|---|---|
|
|
73
|
+
| `escape` | Leave the editor: cell mode |
|
|
74
|
+
| `J` / `K` | Next / previous cell (`3J` jumps three) |
|
|
75
|
+
| `j` / `k` | Down / up a line; past the edge moves to the next cell |
|
|
76
|
+
| `gg` / `G` / `3G` | First / last / third line of this cell |
|
|
77
|
+
| `dd` | Delete line, like vim (`3dd` three lines; `u` undoes). To delete the cell: `esc`, then `dd` |
|
|
78
|
+
| `gJ` | Join lines (vim's `J`) |
|
|
79
|
+
| `/` | Jump: `/3` third cell (as numbered in the headers), `/text` next match after the cursor (smart case, highlighted) |
|
|
80
|
+
| `n` / `N` | Next / previous match of the last search, across cells (`3n` skips three) |
|
|
81
|
+
| `*` / `#` | Search forward / backward for the word under the cursor |
|
|
82
|
+
| `gt` / `gT` / `2gt` | Next / previous / second tab |
|
|
83
|
+
| `:` | Ex command |
|
|
84
|
+
|
|
85
|
+
Also `h j k l w b e 0 ^ $`, counts, `i a I A o O`, `x X s S dw d$ dj D cc cw C yy p P u ctrl+r`.
|
|
86
|
+
|
|
87
|
+
Cell mode. Whether you are in cell mode or in the editor is one choice for the whole
|
|
88
|
+
notebook: moving across a rendered markdown or hidden-code cell (which is always
|
|
89
|
+
selected as a cell), deleting, undoing, searching or switching tabs keeps it; `escape`,
|
|
90
|
+
`enter`/`i` and clicks change it.
|
|
91
|
+
|
|
92
|
+
| Key | Action |
|
|
93
|
+
|---|---|
|
|
94
|
+
| `j` / `k` (`3j`) | Select next / previous cell |
|
|
95
|
+
| `gg` / `G` / `3G` | Select first / last / third cell |
|
|
96
|
+
| `enter` / `i` | Edit the cell (normal / insert mode); on markdown, show its source |
|
|
97
|
+
| `a` / `b` | Insert a cell above / below |
|
|
98
|
+
| `dd` | Delete cell (`3dd` three; `u` brings them back) |
|
|
99
|
+
| `u` (or `z`) | Undo the last cell action: delete, move, insert, markdown/code switch |
|
|
100
|
+
| `yy` / `x` / `p` / `P` | Copy / cut / paste below / paste above |
|
|
101
|
+
| `J` / `K` | Move the cell down / up |
|
|
102
|
+
| `m` / `y` | Markdown / code cell |
|
|
103
|
+
| `h` | Hide / show the cell's code (marimo `hide_code`) |
|
|
104
|
+
| `o` / `O` | Collapse output / clear output |
|
|
105
|
+
| `l` | Toggle line numbers |
|
|
106
|
+
| `gt` / `gT` | Next / previous tab |
|
|
107
|
+
| `/` | Jump to `/3`, or select the next cell matching `/text` |
|
|
108
|
+
| `n` / `N` | Select the next / previous cell matching the last search |
|
|
109
|
+
| `escape` | Clear pending keys and the search highlight |
|
|
110
|
+
| `:`, `?` | Ex command, help |
|
|
111
|
+
|
|
112
|
+
Ex commands: `:w [file]`, `:wa`, `:q`, `:q!`, `:wq`, `:qa`, `:wqa`, `:e file`,
|
|
113
|
+
`:sp [file]`, `:vs [file]`, `:only`, `:tabnew [file]`, `:tabclose`, `:tabn`,
|
|
114
|
+
`:tabp`, `:ls`, `:noh`, `:Ex`, `:run`, `:runabove`, `:runbelow`, `:stale`, `:restart`,
|
|
115
|
+
`:clear`, `:set vim|novim|lazy|nolazy|nu|nonu`, `:vars`, `:md`, `:code`,
|
|
116
|
+
`:theme [name]`, `:N` (go to cell N), `:help`. `:set novim`, line numbers, the
|
|
117
|
+
run mode and the theme are remembered in `~/.config/gpython/config.toml`.
|
|
118
|
+
|
|
119
|
+
Cell headers read `✓ c3 [4] 0.42s defines x · uses y`: display position,
|
|
120
|
+
execution count (like Jupyter's `In [4]`) and run time.
|
|
121
|
+
|
|
122
|
+
Editing: Enter indents after `:` or an open bracket and dedents after
|
|
123
|
+
`return`/`pass`/…; backspace in leading whitespace removes one indent level;
|
|
124
|
+
Tab inserts four spaces.
|
|
125
|
+
|
|
126
|
+
`shift+enter` and `ctrl+enter` need a terminal that reports them (Ghostty,
|
|
127
|
+
Kitty, WezTerm, iTerm2). In Terminal.app use `ctrl+e` and `ctrl+n`.
|
|
128
|
+
|
|
129
|
+
## Markdown cells
|
|
130
|
+
|
|
131
|
+
A markdown cell shows rendered text (headings, lists, code, links) instead of an
|
|
132
|
+
editor, and it never runs.
|
|
133
|
+
|
|
134
|
+
- Make one: `ctrl+n` for a new cell, then `escape escape` (cell mode; one `escape` with `--no-vim`) and `m`. Or type
|
|
135
|
+
`:md` in any cell. `y` or `:code` turns it back into code. Turning a code cell
|
|
136
|
+
into markdown removes its variables, so the cells that used them are marked
|
|
137
|
+
stale (reactive mode re-runs them, and they fail).
|
|
138
|
+
- Edit: select the cell and press `enter` (or `i`), or double-click it.
|
|
139
|
+
- Render: `escape escape` (back to cell mode), `shift+enter` (render and go to
|
|
140
|
+
the next cell), `ctrl+enter` (render and stay), or just move to another cell.
|
|
141
|
+
- Files: in `.py` notebooks a markdown cell is `# %% [markdown]` followed by
|
|
142
|
+
lines starting with `# ` (the Jupytext format). In plain scripts without
|
|
143
|
+
markers, a top-level `"""text"""` string is a markdown cell. `.ipynb`
|
|
144
|
+
markdown cells load and save as markdown cells.
|
|
145
|
+
|
|
146
|
+
A code cell whose last line is a bare string (`"""note"""`) does not print
|
|
147
|
+
that string as its result.
|
|
148
|
+
|
|
149
|
+
## Files, windows and tabs
|
|
150
|
+
|
|
151
|
+
gpython opens several notebooks at once, the way vim opens several files:
|
|
152
|
+
each notebook is a *window*, windows can be split, and tabs group windows.
|
|
153
|
+
Every notebook has its own kernel (its own variables, its own `__file__`).
|
|
154
|
+
|
|
155
|
+
**File explorer** (`ctrl+b`, `:Ex`, `:e` with no name). A tree of the
|
|
156
|
+
notebook's folder on the left: folders and `.py`/`.ipynb` files, hidden files
|
|
157
|
+
and other extensions behind `.`. Keys inside it:
|
|
158
|
+
|
|
159
|
+
| Key | Action |
|
|
160
|
+
|---|---|
|
|
161
|
+
| `j` / `k`, `gg` / `G` | Move |
|
|
162
|
+
| `h` / `l` | Collapse (or go to the parent) / expand a folder; `l` on a file opens it |
|
|
163
|
+
| `enter` / `o` / `t` | Open the file in its own tab (a folder expands); a blank untitled window is reused |
|
|
164
|
+
| `e` | Open the file in the current window (like `:e`) |
|
|
165
|
+
| `s` / `v` | Open in a horizontal / vertical split |
|
|
166
|
+
| `-` | Go up: the parent folder becomes the root |
|
|
167
|
+
| `C` | Make the folder under the cursor the root |
|
|
168
|
+
| `%` | New notebook in the folder under the cursor |
|
|
169
|
+
| `/`, `n` | Search file names, repeat |
|
|
170
|
+
| `.` | Show hidden and non-notebook files |
|
|
171
|
+
| `R` | Reload |
|
|
172
|
+
| `q`, `escape`, `ctrl+b` | Hide the explorer |
|
|
173
|
+
|
|
174
|
+
`ctrl+b` toggles the explorer from anywhere. The explorer stays open after a
|
|
175
|
+
file is opened; `ctrl+w h` from the leftmost window (or `ctrl+w w`) goes back to
|
|
176
|
+
it. A file that is already open is shown (its tab focused) instead of being
|
|
177
|
+
opened twice. Clicking a file opens it too.
|
|
178
|
+
|
|
179
|
+
**Windows** (`ctrl+w` then a key, as in vim; not in insert mode, where
|
|
180
|
+
`ctrl+w` deletes a word):
|
|
181
|
+
|
|
182
|
+
| Key | Action |
|
|
183
|
+
|---|---|
|
|
184
|
+
| `ctrl+w s` / `:sp [file]` | Split: a new window below (an untitled notebook, or `file`) |
|
|
185
|
+
| `ctrl+w v` / `:vs [file]` | Vertical split: a new window to the right |
|
|
186
|
+
| `ctrl+w h` `j` `k` `l` | Focus the window to the left / below / above / right |
|
|
187
|
+
| `ctrl+w w` / `ctrl+w p` | Next / previous window (the explorer counts as the first) |
|
|
188
|
+
| `ctrl+w q` / `:q` | Close this window (asks if unsaved; closing the last one quits) |
|
|
189
|
+
| `ctrl+w o` / `:only` | Keep only this window |
|
|
190
|
+
| `ctrl+w T` | Move this window to a new tab |
|
|
191
|
+
| `ctrl+w =`, `+` `-` `<` `>` | Equalize / resize |
|
|
192
|
+
|
|
193
|
+
When a tab has several windows each shows a status line with its file name,
|
|
194
|
+
dirty mark and kernel state; the active window's line is highlighted.
|
|
195
|
+
|
|
196
|
+
**Tabs**: `:tabnew [file]`, `gt` / `gT` / `2gt` (normal and cell mode),
|
|
197
|
+
`:tabn`, `:tabp`, `:tabclose`, `:tabonly`. The tab line appears above the
|
|
198
|
+
notebook once there are two tabs (or a hidden buffer, see below); click a tab
|
|
199
|
+
to switch. `./gpython a.py b.py` opens one tab per file.
|
|
200
|
+
|
|
201
|
+
**Buffers** (as in vim): every opened notebook is a buffer with its own
|
|
202
|
+
kernel, numbered in opening order. Closing a window (`:q`, `ctrl+w q`,
|
|
203
|
+
`:tabclose`) only hides its notebook: variables and outputs stay, and the tab
|
|
204
|
+
line lists hidden buffers (click one to show it).
|
|
205
|
+
|
|
206
|
+
| Command | Action |
|
|
207
|
+
|---|---|
|
|
208
|
+
| `:ls` / `:buffers` | List buffers: `%a` current, `a` shown, `h` hidden, `+` unsaved |
|
|
209
|
+
| `:b N` / `:b name` | Show that buffer in the current window (a partial, unique name works) |
|
|
210
|
+
| `:bn` / `:bp` | Next / previous buffer in the current window |
|
|
211
|
+
| `:bd [N]` | Delete the buffer and stop its kernel (asks if unsaved; `:bd!` does not) |
|
|
212
|
+
| `:e file` | Open a file in the current window (refuses if unsaved; `:e! file` discards) |
|
|
213
|
+
|
|
214
|
+
`:w` saves the current notebook, `:wa` saves every buffer, `:q` on the last
|
|
215
|
+
window quits (asking about any unsaved buffer), `:qa` quits, `:wqa` saves
|
|
216
|
+
everything and quits. Hidden buffers keep their kernel process alive; `:bd`
|
|
217
|
+
frees it.
|
|
218
|
+
|
|
219
|
+
## Themes
|
|
220
|
+
|
|
221
|
+
`ctrl+t` opens a theme picker with live preview: move through the list and
|
|
222
|
+
the whole notebook (editor syntax colours included) changes as you go;
|
|
223
|
+
`enter` keeps the theme and writes it to `~/.config/gpython/config.toml`,
|
|
224
|
+
`escape` restores the one you had. Every later session starts in the saved
|
|
225
|
+
theme. `:theme name` does the same without the picker and is saved too;
|
|
226
|
+
`--theme name` applies to that run only; `:theme` alone shows the current one.
|
|
227
|
+
|
|
228
|
+
The default theme, `terminal` (also `:theme default`), uses your terminal's
|
|
229
|
+
own colours and background, so gpython looks like whatever colorscheme the
|
|
230
|
+
terminal has. Syntax colours are the terminal's 16 named colours.
|
|
231
|
+
|
|
232
|
+
Themes: terminal, catppuccin (mocha, macchiato, frappe, latte), gruvbox (dark, light),
|
|
233
|
+
material (oceanic, palenight, darker, lighter), nord, dracula, tokyo-night,
|
|
234
|
+
solarized (dark, light), monokai, plus Textual's built-in themes.
|
|
235
|
+
|
|
236
|
+
## Python environments and packages
|
|
237
|
+
|
|
238
|
+
Each notebook's kernel runs in a Python environment. The header chip shows
|
|
239
|
+
which one (`Python 3.12.4 · .venv ▾`); the bottom bar shows the kernel state
|
|
240
|
+
with a dot (green ready, yellow starting or installing, red failed) and the
|
|
241
|
+
live output of a running install.
|
|
242
|
+
|
|
243
|
+
At start a notebook uses the environment it last used; otherwise the nearest
|
|
244
|
+
`.venv` / `venv` (next to the file, up to the project root) that has
|
|
245
|
+
ipykernel; otherwise gpython's own interpreter. A project venv without
|
|
246
|
+
ipykernel is pointed out once.
|
|
247
|
+
|
|
248
|
+
`f6` (or a click on the header chip) opens the environment picker:
|
|
249
|
+
|
|
250
|
+
- **Switch**: every venv, uv env and conda env next to the notebook and in
|
|
251
|
+
`~/.virtualenvs`, `~/.venvs`, conda's `envs`, plus uv's managed Pythons and
|
|
252
|
+
`python3.x` on `PATH`. Type to filter, or type a path to a venv or a python.
|
|
253
|
+
Picking a venv without ipykernel installs it first. Picking a bare
|
|
254
|
+
interpreter offers to make a venv from it. Switching starts a fresh kernel
|
|
255
|
+
(cells become stale) and is remembered for that notebook.
|
|
256
|
+
- **New uv env**: `uv venv <folder> [--python 3.12]`, then ipykernel, then
|
|
257
|
+
switch. Any Python version uv can download works.
|
|
258
|
+
- **New venv**: the same with `python -m venv` and pip, no uv needed.
|
|
259
|
+
- **Install packages**: `uv pip install` into the kernel's environment (pip
|
|
260
|
+
without uv). New imports work without restarting the kernel.
|
|
261
|
+
- **Add to project**: `uv add`, writing to `pyproject.toml` and syncing into
|
|
262
|
+
the kernel's environment.
|
|
263
|
+
|
|
264
|
+
The same as commands: `:env` (picker) or `:env name|path`, `:venv [folder]
|
|
265
|
+
[python]`, `:uv venv [folder] [python]`, `:pip numpy pandas`, `:pip list`,
|
|
266
|
+
`:pip uninstall x`, `:uv add httpx`, `:uv <anything>` (runs in the project).
|
|
267
|
+
Inside cells, `%uv pip install x` and `%uv add x` target the kernel's own
|
|
268
|
+
interpreter, and `%pip` uses uv in environments that have no pip.
|
|
269
|
+
|
|
270
|
+
## Rules (same as marimo)
|
|
271
|
+
|
|
272
|
+
- A name may be defined in only one cell.
|
|
273
|
+
- Names starting with `_` are private to their cell.
|
|
274
|
+
- In-place changes (`xs.append(1)`, `d["k"] = v`) at a cell's top level are
|
|
275
|
+
tracked: readers below run after the mutator, and running the mutator first
|
|
276
|
+
re-runs the cell that created the object so it starts fresh.
|
|
277
|
+
|
|
278
|
+
## Files
|
|
279
|
+
|
|
280
|
+
Notebooks are plain `.py` files. `# %%` markers (VS Code / Jupytext) are
|
|
281
|
+
honoured; files without markers are split at top-level statements and saved
|
|
282
|
+
back without markers, so a script stays a script. `__file__` and the working
|
|
283
|
+
directory match the notebook, like running it as a script.
|
|
284
|
+
|
|
285
|
+
**marimo notebooks** (`import marimo as mo` … `@app.cell` functions) open as
|
|
286
|
+
cells and save back byte-identical when nothing changed: cell names, the
|
|
287
|
+
`hide_code=True` decorator argument (such cells open with their code hidden;
|
|
288
|
+
`h` in cell mode shows it) and `mo.md(...)` cells are kept. In the
|
|
289
|
+
kernel a small `marimo` shim provides `mo.md`, `mo.Html`, `mo.hstack`,
|
|
290
|
+
`mo.vstack` and `mo.ui` placeholders so such notebooks run. Not kept: other
|
|
291
|
+
decorator arguments and comments after a cell's `return`.
|
|
292
|
+
|
|
293
|
+
**Jupyter notebooks** (`.ipynb`) open with their code and markdown cells,
|
|
294
|
+
their stored outputs and execution counts, and save back as `.ipynb` with the
|
|
295
|
+
current outputs (notebook metadata preserved).
|
|
296
|
+
|
|
297
|
+
## Layout
|
|
298
|
+
|
|
299
|
+
`graph.py` dependency analysis · `kernel.py` IPython kernel in a subprocess
|
|
300
|
+
via jupyter_client · `notebook.py` file formats (`# %%`, scripts, marimo,
|
|
301
|
+
`.ipynb`) · `vim.py` modal editor and completion popup · `cell.py` one cell ·
|
|
302
|
+
`pane.py` one notebook window · `windows.py` tabs and split layout ·
|
|
303
|
+
`explorer.py` file tree · `themes.py` / `themepicker.py` / `config.py` ·
|
|
304
|
+
`outputs.py` rich outputs · `keymap.py` the key table (help and this README) ·
|
|
305
|
+
`app.py` + `app.tcss` the Textual app.
|
|
306
|
+
|
|
307
|
+
```sh
|
|
308
|
+
uv run pytest -q
|
|
309
|
+
```
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "gpynb"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A reactive Python notebook in the terminal, built on Textual"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"ipykernel>=7.4.0",
|
|
9
|
+
"jupyter-client>=8.10.0",
|
|
10
|
+
"pillow>=13.0.0",
|
|
11
|
+
"textual-image>=0.14.2",
|
|
12
|
+
"textual[syntax]>=8.2.8",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[[project.authors]]
|
|
16
|
+
name = "igaurab"
|
|
17
|
+
email = "dev@igaurab.com"
|
|
18
|
+
|
|
19
|
+
[project.scripts]
|
|
20
|
+
gpython = "gpython:main"
|
|
21
|
+
|
|
22
|
+
[build-system]
|
|
23
|
+
requires = ["uv_build>=0.9.28,<0.10.0"]
|
|
24
|
+
build-backend = "uv_build"
|
|
25
|
+
|
|
26
|
+
[dependency-groups]
|
|
27
|
+
dev = ["pytest>=9.2.0"]
|
|
28
|
+
|
|
29
|
+
[tool.uv.build-backend]
|
|
30
|
+
module-name = "gpython"
|