Skip to content

Repository files navigation

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 system the window follows the desktop's setting live.
  • The fastest renderer that works. The page probes for a webgl2 context 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: true is on, which xterm 6 requires for Terminal.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

About

Cross-platform terminal emulator built for those of us that live in the terminal

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages