Skip to main content

Python bindings for OpenTUI - Build terminal UIs in Python

Project description

OpenTUI Python

PyPI version Python License: MIT

A Pythonic port of OpenTUI — build rich terminal UIs in Python with reactive signals, flexbox layout, and a full component library.

OpenTUI is the rendering engine behind OpenCode. This package brings that same engine to Python: the native Zig core handles rendering and layout, while the Python layer provides an idiomatic API with signals, components, hooks, and async rendering.

Disclaimer: This is an independent community project. It is not affiliated with, endorsed by, or connected to OpenTUI, OpenCode, or Anomaly in any way. OpenTUI is developed by the anomalyco/opentui team and is used here under its MIT license.

Features

  • Reactive signals — fine-grained reactivity with Signal, computed, effect, and Expr operators
  • Flexbox layout — powered by Yoga via yoga-python
  • Rich componentsBox, Text, Input, Textarea, Select, ScrollBox, Markdown, Code, Diff, and more
  • Native performance — Zig core with nanobind C++ bindings for rendering-critical paths
  • Full input handling — keyboard, mouse, and paste events
  • Image support — Kitty and SIXEL graphics protocols
  • Syntax highlighting — Tree-sitter integration for code blocks
  • 4,700+ tests — comprehensive parity with the OpenTUI core test suite

Installation

pip install opentui

With optional extras:

pip install opentui[images]        # Pillow for image support
pip install opentui[highlighting]  # Tree-sitter syntax highlighting
pip install opentui[dev]           # pytest, ruff, ty

Quick Start

import asyncio
from opentui import render, Box, Text, Signal, component, use_keyboard, use_renderer

count = Signal(0, name="count")

@component
def App():
    return Box(
        Text(lambda: f"Count: {count()}"),
        Text("Press +/- to change, q to quit"),
        padding=2, border=True, gap=1,
    )

def on_key(event):
    if event.name == "q":
        use_renderer().stop()
    elif event.name in ("+", "="):
        count.add(1)
    elif event.name == "-":
        count.add(-1)

async def main():
    use_keyboard(on_key)
    await render(App)

asyncio.run(main())

Reactive Patterns

OpenTUI provides three tiers of reactivity — choose the simplest one that fits:

Direct signal prop — pass a Signal directly to any supported prop for zero-overhead updates:

color = Signal("red", name="color")
Text("Hello", fg=color)  # updates paint when color changes

Lambda / callable — use a lambda for computed or derived values:

count = Signal(0, name="count")
Text(lambda: f"Count: {count()}")

.map() transform — transform a signal's value without a full lambda:

Text(count.map(lambda v: f"Count: {v}"))

Expr operators — signals support arithmetic and comparison operators that return reactive expressions:

doubled = count * 2              # Expr: evaluates to count() * 2
is_high = count > 5              # Expr: evaluates to count() > 5
label = count.if_("yes", "no")   # Conditional: "yes" if truthy, "no" otherwise

Batch updates — group multiple signal writes into a single notification pass:

from opentui import Batch

with Batch():
    x.set(1)
    y.set(2)  # subscribers only fire once, after the block

Control Flow

Conditional and list rendering with Show, Switch, Match, and For:

from opentui import Show, Switch, Match, For, Signal

visible = Signal(True, name="visible")
mode = Signal("home", name="mode")
items = Signal(["a", "b", "c"], name="items")

# Conditional rendering
Show(Text("Visible!"), when=visible)
Show(Text("Visible!"), when=visible, fallback=Text("Hidden"))

# Multi-branch conditional
Switch(
    Match(HomePage(), when=mode.map(lambda m: m == "home")),
    Match(Settings(), when=mode.map(lambda m: m == "settings")),
    fallback=Text("Not found"),
)

# Signal-keyed switch (fast path — no re-subscription on change)
Switch(on=mode, cases={
    "home": HomePage(),
    "settings": Settings(),
})

# List rendering
For(lambda item, i: Text(f"{i}: {item}"), each=items)

Components

Use @component to define reusable components. Each invocation gets its own reactive scope:

from opentui import component, Signal, Box, Text

@component
def Counter(label: str = "Count"):
    count = Signal(0, name="count")
    return Box(
        Text(count.map(lambda v: f"{label}: {v}")),
        border=True,
    )

For lower-level control, use Mount directly:

from opentui import Mount, Signal, Text

counter = Mount(lambda: Text(Signal(0, name="n").map(str)))

Layout

Component Description
Box Flexbox container with border, padding, background
ScrollBox Scrollable container with mouse wheel support

Text

Component Description
Text Styled text with wrapping, selection, and inline modifiers
Bold, Italic, Underline Inline text style modifiers
Span Colored inline text spans
Link Clickable terminal hyperlinks

Input

Component Description
Input Single-line text input
Textarea Multi-line editor with native buffer, undo/redo, and syntax highlighting
Select Dropdown selection list

Advanced

Component Description
Code Syntax-highlighted code block via Tree-sitter
Diff Side-by-side and unified diff viewer
Markdown Rendered markdown with headings, lists, tables, code blocks
LineNumberRenderable Line number gutter (pairs with Code or Textarea)
Slider Numeric value slider
TabSelect Tab selection bar
TextTable Tabular text layout with borders

Control Flow

Component Description
For Keyed list rendering with efficient reconciliation
Show Conditional rendering
Switch / Match Multi-branch conditional rendering
Lazy Deferred child construction (built on first render)
Portal Render children into a different mount point
Dynamic / MemoBlock Dynamic node selection and memoized subtrees

Signals

from opentui import Signal, computed, effect

name = Signal("world", name="name")
greeting = computed(lambda: f"Hello, {name()}!")

effect(lambda: print(greeting()))  # prints "Hello, world!"
name.set("Python")                 # prints "Hello, Python!"

Hooks

from opentui import use_keyboard, use_mouse, use_paste, use_on_resize, use_timeline

use_keyboard(lambda event: print(event.name))
use_mouse(lambda event: print(event.type, event.x, event.y))
use_paste(lambda event: print(event.text))
use_on_resize(lambda cols, rows: print(f"{cols}x{rows}"))

timeline = use_timeline()
timeline.add(target, {"opacity": 1.0}, duration=300)

Development

git clone https://github.com/banditburai/opentui-python.git
cd opentui-python
uv sync --all-extras

# Run tests
uv run pytest tests/ -v

# Lint & type checking
uv run ruff check
uv run ruff format --check
uv run ty check

Test coverage

The test suite includes 4,700+ tests: a comprehensive 1:1 port of the OpenTUI core test suite plus Python-specific tests for signals, FFI bindings, and the reconciler.

Architecture

The package is a hybrid: the OpenTUI Zig core handles rendering, text buffers, and layout at native speed, while the Python layer implements the component model, signals runtime, reconciler, and public API. Performance-critical paths are additionally accelerated with nanobind C++ extensions.

OpenTUI Core (Zig) → libopentui.so/dylib
    ↓
nanobind C++ bindings (opentui_bindings)
    ↓
Python API (opentui)
    ├── signals       — Signal, computed, effect, Expr operators
    ├── components/   — Box, Text, Input, ScrollBox, Code, Diff, ...
    ├── hooks         — use_keyboard, use_mouse, use_paste, use_on_resize
    ├── renderer      — CliRenderer, Buffer, TerminalCapabilities
    ├── reconciler    — Component tree diffing (idiomorph-inspired)
    └── layout        — Yoga flexbox integration via yoga-python

What's native vs. Python:

  • Native (Zig via nanobind): text buffers, edit buffers, editor views, hit testing, graphics encoding, buffer rendering, syntax styling
  • C++ extensions: signal→prop bindings, reconciler patching, render tree dispatch
  • Python: signals runtime, component tree, event loop, input parsing, all public API

Pre-built wheels are provided for Linux (x86_64, aarch64), macOS (x86_64, arm64), and Windows (x64) on Python 3.12+.

License

MIT — see LICENSE.

OpenTUI core is also MIT licensed. yoga-python is MIT licensed.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

opentui-0.1.1.tar.gz (799.2 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

opentui-0.1.1-cp313-cp313-win_amd64.whl (471.4 kB view details)

Uploaded CPython 3.13Windows x86-64

opentui-0.1.1-cp313-cp313-manylinux_2_28_x86_64.whl (3.0 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.28+ x86-64

opentui-0.1.1-cp313-cp313-manylinux_2_28_aarch64.whl (2.9 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.28+ ARM64

opentui-0.1.1-cp313-cp313-macosx_13_0_x86_64.whl (777.7 kB view details)

Uploaded CPython 3.13macOS 13.0+ x86-64

opentui-0.1.1-cp313-cp313-macosx_13_0_arm64.whl (752.4 kB view details)

Uploaded CPython 3.13macOS 13.0+ ARM64

opentui-0.1.1-cp312-cp312-win_amd64.whl (471.4 kB view details)

Uploaded CPython 3.12Windows x86-64

opentui-0.1.1-cp312-cp312-manylinux_2_28_x86_64.whl (3.0 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.28+ x86-64

opentui-0.1.1-cp312-cp312-manylinux_2_28_aarch64.whl (2.9 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.28+ ARM64

opentui-0.1.1-cp312-cp312-macosx_13_0_x86_64.whl (777.8 kB view details)

Uploaded CPython 3.12macOS 13.0+ x86-64

opentui-0.1.1-cp312-cp312-macosx_13_0_arm64.whl (752.5 kB view details)

Uploaded CPython 3.12macOS 13.0+ ARM64

File details

Details for the file opentui-0.1.1.tar.gz.

File metadata

  • Download URL: opentui-0.1.1.tar.gz
  • Upload date:
  • Size: 799.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for opentui-0.1.1.tar.gz
Algorithm Hash digest
SHA256 4f8c9557ea58625d8a8567e2e4f9d123646e43b16ab7a2e1e3ecfda985f32179
MD5 41b78d1414ce6bb9864c3f07a14417d1
BLAKE2b-256 5c2bc014998001266c4f03c845058dc3e15b10bfda3560d0d7b859905ceb9955

See more details on using hashes here.

Provenance

The following attestation bundles were made for opentui-0.1.1.tar.gz:

Publisher: release.yml on banditburai/opentui-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file opentui-0.1.1-cp313-cp313-win_amd64.whl.

File metadata

  • Download URL: opentui-0.1.1-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 471.4 kB
  • Tags: CPython 3.13, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for opentui-0.1.1-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 6b6494fc8e0bc8fe698453da5f6734b94acab0d1655cee5ae73dd62718d8fe12
MD5 97dfb6f49cbb3b064c862d4137904eb9
BLAKE2b-256 1d6baaba1cca49f2b4454ccad75d86e89ab8679c790ea821d05c57a1c7243b54

See more details on using hashes here.

Provenance

The following attestation bundles were made for opentui-0.1.1-cp313-cp313-win_amd64.whl:

Publisher: release.yml on banditburai/opentui-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file opentui-0.1.1-cp313-cp313-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for opentui-0.1.1-cp313-cp313-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 ebc110502c12e3a1495186312f99401e98340ba3b1bc4657e3cc7961332c43c7
MD5 a26f3a5130bd94bc432f7eac9df6dc7f
BLAKE2b-256 4d2bee0bc45a8ad161291831c98535736137d4da7558546093cc61e1b8c2812d

See more details on using hashes here.

Provenance

The following attestation bundles were made for opentui-0.1.1-cp313-cp313-manylinux_2_28_x86_64.whl:

Publisher: release.yml on banditburai/opentui-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file opentui-0.1.1-cp313-cp313-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for opentui-0.1.1-cp313-cp313-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 76c202d9231accd46f9a24cd4325c378ede1bc7fa3d49a22383c70194f00cfc1
MD5 6314dd1bb398f07cf9fd241eda0b3deb
BLAKE2b-256 2c9723d6c5411895309802b2a191d3b05ced813fd31915613fee24d463606572

See more details on using hashes here.

Provenance

The following attestation bundles were made for opentui-0.1.1-cp313-cp313-manylinux_2_28_aarch64.whl:

Publisher: release.yml on banditburai/opentui-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file opentui-0.1.1-cp313-cp313-macosx_13_0_x86_64.whl.

File metadata

File hashes

Hashes for opentui-0.1.1-cp313-cp313-macosx_13_0_x86_64.whl
Algorithm Hash digest
SHA256 9f9e7cfcc61f1515b74ce6303761106e8008cabce874d076d84030be08747d98
MD5 bd0cdb84d96a88e2631925f3f510b9f2
BLAKE2b-256 10633ebc42230fca12d197390af50bfc6fd720ea0be65ceede7dcc837d6878fe

See more details on using hashes here.

Provenance

The following attestation bundles were made for opentui-0.1.1-cp313-cp313-macosx_13_0_x86_64.whl:

Publisher: release.yml on banditburai/opentui-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file opentui-0.1.1-cp313-cp313-macosx_13_0_arm64.whl.

File metadata

File hashes

Hashes for opentui-0.1.1-cp313-cp313-macosx_13_0_arm64.whl
Algorithm Hash digest
SHA256 82bbe3f819d822eb660bca2f94bf60b1d890c1de043a89b1624e0f4dde52b974
MD5 7b85a045c5b505662d63e2d8632d2c7f
BLAKE2b-256 e2998758c53021dfddf5335f7206fefce22d5b849a173658e6de8751c1416685

See more details on using hashes here.

Provenance

The following attestation bundles were made for opentui-0.1.1-cp313-cp313-macosx_13_0_arm64.whl:

Publisher: release.yml on banditburai/opentui-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file opentui-0.1.1-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: opentui-0.1.1-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 471.4 kB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for opentui-0.1.1-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 106a2454385b4effe92b07e882230ead47902df9dccb3ba97246796d7c3e49a4
MD5 fde354f0be3bd58648c17f4c6f770c42
BLAKE2b-256 2823b29cd3a159223dcb83a0a760ee1f7c517f8d210b48bbc5a05416e6551cda

See more details on using hashes here.

Provenance

The following attestation bundles were made for opentui-0.1.1-cp312-cp312-win_amd64.whl:

Publisher: release.yml on banditburai/opentui-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file opentui-0.1.1-cp312-cp312-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for opentui-0.1.1-cp312-cp312-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 5d6d1096ec7d59fa6eb81b92026f1cd36dca2c4e4d909d827dcd45cfef272c1a
MD5 c3d9017b743e3ba8755a9fc1473e7244
BLAKE2b-256 3f8eead40eb59533e49103742ddc62d05727ea95f0e4434cad60c46b7c876764

See more details on using hashes here.

Provenance

The following attestation bundles were made for opentui-0.1.1-cp312-cp312-manylinux_2_28_x86_64.whl:

Publisher: release.yml on banditburai/opentui-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file opentui-0.1.1-cp312-cp312-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for opentui-0.1.1-cp312-cp312-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 9c347821500cb16792d384057de057dc5f965e086ac6f7bd2be440eaedb47a6f
MD5 be88a8a0f1514ab0d26f73d939609b05
BLAKE2b-256 272f281142b013e2dfde23e6427efe4257ce109e4a7acddef8cc04247441d0a2

See more details on using hashes here.

Provenance

The following attestation bundles were made for opentui-0.1.1-cp312-cp312-manylinux_2_28_aarch64.whl:

Publisher: release.yml on banditburai/opentui-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file opentui-0.1.1-cp312-cp312-macosx_13_0_x86_64.whl.

File metadata

File hashes

Hashes for opentui-0.1.1-cp312-cp312-macosx_13_0_x86_64.whl
Algorithm Hash digest
SHA256 25673bec6f8a88bcb1e3721b5fd8d465546506f8cc6893022e8a1124b3adc25c
MD5 c4845d6b240dc8f9addd880875cd069c
BLAKE2b-256 1ddab7b92ceecf9440adfdb8904dbc5689ffd3016c61a98ebdbdfe92d775b66a

See more details on using hashes here.

Provenance

The following attestation bundles were made for opentui-0.1.1-cp312-cp312-macosx_13_0_x86_64.whl:

Publisher: release.yml on banditburai/opentui-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file opentui-0.1.1-cp312-cp312-macosx_13_0_arm64.whl.

File metadata

File hashes

Hashes for opentui-0.1.1-cp312-cp312-macosx_13_0_arm64.whl
Algorithm Hash digest
SHA256 62ae357039873021e750644b5fcb837925c93496429531b8417f14e4c1223b41
MD5 9f8e0efa2789efcb0028e2c3ebcc4fa8
BLAKE2b-256 cdeebd71eb5f307ccaa5eedb8ac771dbe3ed3e87c60c5608857cbd86238b413d

See more details on using hashes here.

Provenance

The following attestation bundles were made for opentui-0.1.1-cp312-cp312-macosx_13_0_arm64.whl:

Publisher: release.yml on banditburai/opentui-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page