textual-wasm¶
Ship one Textual application as both a terminal TUI and a client-side web page — same source, no server-side Python process, and no fork of Textual.
$ textual-wasm build myapp.main:App myapp -o dist/
built dist - 9 files, 240 KiB, 11 requirement(s)
$ textual-wasm dev dist/
serving dist on http://127.0.0.1:8000
The output is static files. Copy dist/ to any static host and it works: there is no server,
no build toolchain at the far end, and nothing for the person deploying it to install. Pyodide
and xterm.js are fetched from a pinned CDN; your application is written into the page as
source.
This site is built with textual-wasm
Every demo in the examples gallery is a real build of a real Textual app, running in your browser on this GitHub Pages site. GitHub Pages serves static files and nothing else.
Why this works¶
Textual’s driver is an abstraction with an environment-variable hook —
TEXTUAL_DRIVER=module:Symbol — that exists for out-of-tree drivers. Every
syscall lives in textual/drivers/, the entry point is fully awaitable, and the input parser
is pure synchronous Python. So a driver whose sink is an xterm.js terminal instead of a file
descriptor is all it takes.
Nothing here patches Textual. The feasibility study is the audit that established that, and §11–§14 are what the implementation measured against it.
Start here¶
A working web build of a new app in about five minutes.
What to change, in the order you will hit it.
Custom pages, and Textual inside Svelte, Vue or plain HTML.
Live values between the app and the page, in both directions.
What does not work, and whose constraint each one is.
Measured across Chromium, Firefox and WebKit — and what that measurement does not cover.
Commands¶
Command |
|
|---|---|
|
What will break, with a |
|
A static site. |
|
Serve it locally. Standard library only — no Node. |
|
Run the app on every runtime available and compare them. |
check is the unusual one. It runs your application natively on CPython, under Pyodide in
Node, in a real Chrome, and on a real pty through tmux — then compares the Python runtimes
check by check and the real terminal against the browser cell by cell. A runtime this
machine cannot reach is reported as skipped with the command that would enable it.