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.2.tar.gz (804.4 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.2-cp313-cp313-win_amd64.whl (827.5 kB view details)

Uploaded CPython 3.13Windows x86-64

opentui-0.1.2-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.2-cp313-cp313-manylinux_2_28_aarch64.whl (2.9 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.28+ ARM64

opentui-0.1.2-cp313-cp313-macosx_13_0_x86_64.whl (781.9 kB view details)

Uploaded CPython 3.13macOS 13.0+ x86-64

opentui-0.1.2-cp313-cp313-macosx_13_0_arm64.whl (756.6 kB view details)

Uploaded CPython 3.13macOS 13.0+ ARM64

opentui-0.1.2-cp312-cp312-win_amd64.whl (827.5 kB view details)

Uploaded CPython 3.12Windows x86-64

opentui-0.1.2-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.2-cp312-cp312-manylinux_2_28_aarch64.whl (2.9 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.28+ ARM64

opentui-0.1.2-cp312-cp312-macosx_13_0_x86_64.whl (782.0 kB view details)

Uploaded CPython 3.12macOS 13.0+ x86-64

opentui-0.1.2-cp312-cp312-macosx_13_0_arm64.whl (756.7 kB view details)

Uploaded CPython 3.12macOS 13.0+ ARM64

File details

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

File metadata

  • Download URL: opentui-0.1.2.tar.gz
  • Upload date:
  • Size: 804.4 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.2.tar.gz
Algorithm Hash digest
SHA256 7f3035dabf92b115c62b4a5e53806af1cf528a172406035c7344ab69656ed0ac
MD5 8b73eb50deecc4cb44e27461cf9df886
BLAKE2b-256 dc98c061d65ce35b48ce4e72a064bca29beb361a39d1a1dd47c56d8ef0297f1c

See more details on using hashes here.

Provenance

The following attestation bundles were made for opentui-0.1.2.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.2-cp313-cp313-win_amd64.whl.

File metadata

  • Download URL: opentui-0.1.2-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 827.5 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.2-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 c9ec59c0e8b0c49401b816f4fa7d9e9f6389f5c575b61643b01b98c013deb7a8
MD5 e805eae2ef79163e47b1ca2d239e399c
BLAKE2b-256 9a6d49d985d413b319e7fffcc655764f781c5701aaab5cd4be1f57cd29dcfd33

See more details on using hashes here.

Provenance

The following attestation bundles were made for opentui-0.1.2-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.2-cp313-cp313-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for opentui-0.1.2-cp313-cp313-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 b2b74edce084d0decd430f7c7ab3f0b8d2869ff2afe64ac177dc63daf91463ad
MD5 5e28b711ebd94f9142eb471b11b05b13
BLAKE2b-256 4455f31ec925663dcffc4c303acbd29c2ba80055f6870ec1b24091f00f08f336

See more details on using hashes here.

Provenance

The following attestation bundles were made for opentui-0.1.2-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.2-cp313-cp313-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for opentui-0.1.2-cp313-cp313-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 4a32829220e450316b3bfca2a08c5752c3d24fc02df8e2866e49bc6e2909eb74
MD5 e58019698aea7bd27412a99154afe864
BLAKE2b-256 b8001a22b74143f4a9475dffae35ffa3313ddaeefd0eecf33e7b22d07c375e1d

See more details on using hashes here.

Provenance

The following attestation bundles were made for opentui-0.1.2-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.2-cp313-cp313-macosx_13_0_x86_64.whl.

File metadata

File hashes

Hashes for opentui-0.1.2-cp313-cp313-macosx_13_0_x86_64.whl
Algorithm Hash digest
SHA256 b922add2291067eb9c6c08c1bfc05a1a86b93c7ff1de110cd4a56bed647410ce
MD5 1d2ae19e00d372d676f260084f22c20e
BLAKE2b-256 fb998a61e75fe193620d3eb7cfc47377f8c1392cc68e98720ccc3d8292a0f0a8

See more details on using hashes here.

Provenance

The following attestation bundles were made for opentui-0.1.2-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.2-cp313-cp313-macosx_13_0_arm64.whl.

File metadata

File hashes

Hashes for opentui-0.1.2-cp313-cp313-macosx_13_0_arm64.whl
Algorithm Hash digest
SHA256 769e92eba6d21dda77eb048577bd8a36882e4af090ae0bfeceda7b53cbbfbb04
MD5 39e641bf2d02f97797d6f7604d299e8e
BLAKE2b-256 ad6333c1ef02c01e0dd535644bdd503049c1d7d7015ace1fad0bbec434c6a30d

See more details on using hashes here.

Provenance

The following attestation bundles were made for opentui-0.1.2-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.2-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: opentui-0.1.2-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 827.5 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.2-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 40dbcf11a28ad40d8de69e98a1c6527b501e8f43d1e9a2dd14705b51bc0d94d1
MD5 15c1213b455c8fd11d12126772077aef
BLAKE2b-256 98c5691929f908dcc5e064c77779794c6afd2de2b43a88279db51bb6cadb39a5

See more details on using hashes here.

Provenance

The following attestation bundles were made for opentui-0.1.2-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.2-cp312-cp312-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for opentui-0.1.2-cp312-cp312-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 f844fe714a166a9c712634c1de363a69ffdf9a407890f7a5330dbc3bbaaaebda
MD5 eef05e2106f7d12c14f899aa5196a95e
BLAKE2b-256 0aea82ab48bd93250d5ee8266bac5f437dd4ad08f4e5b9230cb5321ab521563b

See more details on using hashes here.

Provenance

The following attestation bundles were made for opentui-0.1.2-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.2-cp312-cp312-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for opentui-0.1.2-cp312-cp312-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 24d726d0ba359ad95587519f02fccc33f8d1d376fac02741dd58f89f9e971d5a
MD5 27900996a9bc69c9f41f382c5c0c6bc1
BLAKE2b-256 0538f8a4f23d96bd4d5ecdc9fcfb904844e5c46950a6e2b388e1a5b210fd37d2

See more details on using hashes here.

Provenance

The following attestation bundles were made for opentui-0.1.2-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.2-cp312-cp312-macosx_13_0_x86_64.whl.

File metadata

File hashes

Hashes for opentui-0.1.2-cp312-cp312-macosx_13_0_x86_64.whl
Algorithm Hash digest
SHA256 ac96ed80ddcceff2d50c70096194df46c096837bcda84c31b2d8779259281842
MD5 1a9dcc1b6066118a645e1612b6b7b5c1
BLAKE2b-256 ab4016b06e058fd53e85921b5fa771bf354f507d24be31384ab0ce24541892c7

See more details on using hashes here.

Provenance

The following attestation bundles were made for opentui-0.1.2-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.2-cp312-cp312-macosx_13_0_arm64.whl.

File metadata

File hashes

Hashes for opentui-0.1.2-cp312-cp312-macosx_13_0_arm64.whl
Algorithm Hash digest
SHA256 a738a9b10b7842645daad32a4e0d60a3cda1aec9cbb4021f86c5a6e541b246c0
MD5 f4a2c461a0c88aaeeef6479d147c19b7
BLAKE2b-256 b511e79227d5dd7053e5a8c9948e43caedb7156471e84f27808fd97c38545754

See more details on using hashes here.

Provenance

The following attestation bundles were made for opentui-0.1.2-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