GitHub Copilot 4d3a13f6ef fix: resolve terminal lifecycle race conditions
1. Lock pyte screen initialization in open() to prevent races with
   concurrent _update_screen() calls

2. Reorder session registration: call open() BEFORE adding to
   sessions/routes dicts, so sessions are fully initialized before
   other code can access them

3. Add clarifying comment that PTY resize completes before pyte resize

These fixes prevent dimension mismatches between PTY and pyte screen
that could cause content wrapping in screenshots.
2026-01-24 11:17:18 +00:00
2026-01-21 23:53:57 +00:00
2026-01-21 23:53:57 +00:00
2026-01-21 23:53:57 +00:00
2026-01-21 23:53:57 +00:00
2026-01-21 23:53:57 +00:00
2026-01-21 23:53:57 +00:00
2026-01-21 23:53:57 +00:00
2026-01-21 23:53:57 +00:00
2026-01-21 23:53:57 +00:00

textual-webterm

Icon

Serve terminal sessions and Textual apps over the web with a simple CLI command.

This is heavily based on textual-web, but specifically focused on serving a persistent terminal session in a way that you can host behind a reverse proxy (and some form of authentication).

Built on top of textual-serve, this package provides an easy way to expose terminal sessions and Textual applications via HTTP/WebSocket with automatic reconnection support.

Features

  • 🖥️ Web-based terminal - Access your terminal from any browser
  • 🐍 Textual app support - Serve Textual apps directly from Python modules
  • 🔄 Session reconnection - Refresh the page and reconnect to the same session
  • 🎨 Full terminal emulation - Colors, cursor, and ANSI codes work correctly
  • 📐 Auto-sizing - Terminal automatically resizes to fit the browser window
  • 🚀 Simple CLI - One command to start serving

Non-Features

  • No Authentication - this is meant to be used inside a dedicated container, and you should set up an authenticating reverse proxy like authelia
  • No Encryption (TLS/HTTPS) - again, this is meant to be fronted by something like traefik or caddy

Quick Start

Serve a Terminal

Serve your default shell:

textual-webterm

Serve a specific command:

textual-webterm htop

Serve a Textual App

Serve a Textual app from an installed module:

textual-webterm --app mypackage.mymodule:MyApp

Serve a Textual app from a Python file:

textual-webterm --app ./calculator.py:CalculatorApp

Options

Specify host and port:

textual-webterm --host 0.0.0.0 --port 8080 bash

Then open http://localhost:8080 in your browser.

Landing pages

You can serve a landing page with multiple terminal tiles driven by a YAML manifest:

- name: My Service
  slug: my-service
  command: docker logs -f my-service

Run with:

textual-webterm --landing-manifest landing.yaml

You can also point to a docker-compose file; services with the label webterm-command become tiles. For example:

services:
  db:
    image: postgres
    labels:
      webterm-command: docker exec -it db psql

Start with:

textual-webterm --compose-manifest compose.yaml

When a landing manifest is provided, the root (/) shows the grid; clicking a tile opens a dedicated terminal session in a new tab. Without a manifest, the server operates in single-terminal mode.

CLI Reference

Usage: textual-webterm [OPTIONS] [COMMAND]

  Serve a terminal or Textual app over HTTP/WebSocket.

  COMMAND: Shell command to run in terminal (default: $SHELL)

Options:
  -H, --host TEXT               Host to bind to [default: 0.0.0.0]
  -p, --port INTEGER            Port to bind to [default: 8080]
  -a, --app TEXT                Load a Textual app from module:ClassName
                                Examples: 'mymodule:MyApp' or './app.py:MyApp'
  -L, --landing-manifest PATH   YAML manifest describing landing page tiles
                                (slug/name/command).
  -C, --compose-manifest PATH   Docker compose YAML; services with label
                                "webterm-command" become landing tiles.
  --version                     Show the version and exit.
  --help                        Show this message and exit.

Development

Setup (Makefile-first)

git clone https://github.com/rcarmo/textual-webterm.git
cd textual-webterm

# Install with dev dependencies via Makefile
make install-dev

Common tasks (use Makefile)

  • Lint: make lint
  • Format: make format
  • Tests: make test
  • Coverage (fail_under=80): make coverage
  • Full check (lint + coverage): make check

Notes

  • WebSocket protocol (browser <-> server) is JSON: ["stdin", data], ["resize", {"width": w, "height": h}], ["ping", data].
  • Static assets are provided by textual-serve; this project does not add custom static files.
  • /screenshot.svg replays the terminal buffer to SVG via Rich; width can be set with ?width=120 and is clamped for safety.

Requirements

  • Python 3.9+
  • Linux or macOS

License

MIT License - see LICENSE for details.

  • Textual - TUI framework for Python
  • textual-serve - Serve Textual apps on the web
  • Rich - Rich text formatting for terminals
S
Description
No description provided
Readme 122 MiB
Languages
Go 51.5%
TypeScript 31%
JavaScript 14.8%
Shell 1.2%
Makefile 0.8%
Other 0.7%