A terminal emulator in a native window. xterm.js renders the screen inside a frameless WebKitGTK window (pywebview, through wesktop); behind it one PTY and one WebSocket per tab, with the PTY side written against the Python standard library only.
uv tool install --python /usr/bin/python3 tinyterminal # or: pipx install tinyterminal
tinyterminalLinux with GTK 3 and WebKitGTK, and a Python that can import the
distribution's PyGObject. That is the system interpreter, so install with it
explicitly: uv prefers its own downloaded interpreters, which cannot import
the distribution's GTK bindings, while pipx uses the system one by default.
On Fedora the two packages are python3-gobject and webkit2gtk4.1; other
distributions name them differently.
| Key | Action |
|---|---|
| Ctrl+Shift+T | new tab |
| Ctrl+Shift+W | close tab |
| Ctrl+PageUp / Ctrl+PageDown | previous / next tab |
| Ctrl+Shift+C / Ctrl+Shift+V | copy selection / paste |
- Tabs, each its own login shell on its own PTY, titled from the OSC 0/2 title the shell sets and falling back to the shell's name. Closing the last tab closes the window.
- Light and dark, both faces of the GNOME palette. The theme button cycles
system, light and dark; on
systemthe window follows the desktop's setting live. - The fastest renderer that works. The page probes for a
webgl2context and loads xterm's WebGL addon, falls back to the canvas addon, and shows which one is live at the right of the tab bar. A WebGL context loss switches to canvas and relabels. - Flow control that reaches the shell. The server counts every byte sent as unacknowledged until xterm's parser has consumed it, and above 1 MiB it stops reading the PTY, so the kernel's PTY buffer applies back-pressure to the program producing the flood. Nothing is ever buffered unboundedly in the server.
- Its own window chrome. The window is frameless and transparent: the rounded card the page draws is the window silhouette, dragging goes to the compositor, and eight handles around the frame hand an interactive resize to it.
- Clipboard through
navigator.clipboard, falling back to the desktop's own clipboard through GDK when the web view refuses.
- Linux and GTK 3 only in practice. The resize handles, the clipboard fallback and unmaximizing all call the GTK toplevel directly. On another pywebview backend those do nothing; nothing else has been tried.
- No search, no per-tab titles you can edit, and no scrollbar styling beyond a thin native one.
allowProposedApi: trueis on, which xterm 6 requires forTerminal.unicode-- the Unicode 11 addon registers into it. No other proposed API is used.- Opened in a plain browser rather than its own window, the page cannot close anything when the last tab exits, and the clipboard has only its web API path.
- A face switch can lag in a screenshot. Late in a long scripted session a capture can show the previous face's top bar over an already-switched terminal. The page's computed style is correct, and no repaint the page can force changes it: WebKitGTK is holding that region's rasterisation.
uv run python -m tinyterminal # run it from the checkout
uv run pytest # the headless PTY + WebSocket suite
./scripts/fetch-assets.sh # re-vendor the pinned xterm.js build
timeout 120 uv run python scripts/screenshot-test.py # drives the real windowThe vendored xterm.js build is not committed. scripts/xterm-versions.txt
pins the versions, scripts/fetch-assets.sh downloads exactly those from the
npm registry into src/tinyterminal/static/vendor/ (curl and tar, no node),
and hatch_build.py runs that script during a build so the wheel carries the
engine.
src/tinyterminal/server.py PTY (openpty/fork/execvpe) + WebSocket bridge + flow control
src/tinyterminal/app.py routes, window chrome, the js_api the page drives the window with
src/tinyterminal/static/ the frame, tab bar, themes, renderer choice, keys, clipboard
scripts/fetch-assets.sh re-vendors the pinned engine
scripts/screenshot-test.py windowed scene runner, writes screenshots/
tests/ headless backend suite
bin/cli.js the npm shim, which runs the installed Python app
MIT