tinyterminal
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.
Install
uv tool install --python /usr/bin/python3 tinyterminal # or: pipx install tinyterminal
tinyterminal
Linux 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.
Keys
| 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 |
What it does
- 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.
Known gaps
- 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.
Development
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 window
The 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.
Layout
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
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tinyterminal-0.1.0.tar.gz.
File metadata
- Download URL: tinyterminal-0.1.0.tar.gz
- Upload date:
- Size: 28.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dc9e495abc0af8e3de68d27e0c80a0f8265edecc7527919b269cdbcf64964b7f
|
|
| MD5 |
96f919d084e7d1a27a586e77bb8a44d8
|
|
| BLAKE2b-256 |
6959cf5e0874b4585802287d6e6429519a4cee49ba38d9c1d0aec55fa4e42c43
|
Provenance
The following attestation bundles were made for tinyterminal-0.1.0.tar.gz:
Publisher:
publish.yml on smm-h/tinyterminal
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tinyterminal-0.1.0.tar.gz -
Subject digest:
dc9e495abc0af8e3de68d27e0c80a0f8265edecc7527919b269cdbcf64964b7f - Sigstore transparency entry: 2795162538
- Sigstore integration time:
-
Permalink:
smm-h/tinyterminal@0ebb9a37ad0b7ecd571f02c9eae2762bcf3999e5 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/smm-h
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0ebb9a37ad0b7ecd571f02c9eae2762bcf3999e5 -
Trigger Event:
release
-
Statement type:
File details
Details for the file tinyterminal-0.1.0-py3-none-any.whl.
File metadata
- Download URL: tinyterminal-0.1.0-py3-none-any.whl
- Upload date:
- Size: 255.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1ac2eae5a7f727fa222599b3ef1da97d0419df1ab32763ad6dbcdb7744b18e54
|
|
| MD5 |
8159a381269a78b35344bdb7b2027fdc
|
|
| BLAKE2b-256 |
a3b205536a5413d5bbaaa51facdada6abd9111ad2b1b54f04a45d0d474a2938f
|
Provenance
The following attestation bundles were made for tinyterminal-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on smm-h/tinyterminal
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tinyterminal-0.1.0-py3-none-any.whl -
Subject digest:
1ac2eae5a7f727fa222599b3ef1da97d0419df1ab32763ad6dbcdb7744b18e54 - Sigstore transparency entry: 2795162595
- Sigstore integration time:
-
Permalink:
smm-h/tinyterminal@0ebb9a37ad0b7ecd571f02c9eae2762bcf3999e5 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/smm-h
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0ebb9a37ad0b7ecd571f02c9eae2762bcf3999e5 -
Trigger Event:
release
-
Statement type: