Skip to main content

Build terminal UIs with Python using React-like components and flexbox layout. A 1:1 port of Ink.

Project description

Ink logo

PyInk

PyPI Tests Python License: MIT

Build terminal UIs with Python using React-like components and flexbox layout.


A 1:1 Python port of Ink — the amazing React-based CLI framework by Vadim Demedes.

PyInk brings Ink's component model, hooks system, and flexbox layout engine to Python. Same architecture, same patterns, same terminal magic.

Install

pip install pyinklib

Or with uv:

uv add pyinklib

Usage

from pyink import component, render, Box, Text
from pyink.hooks import use_state, use_input, use_app

@component
def counter():
    count, set_count = use_state(0)
    app = use_app()

    def handle_input(input_str, key):
        if key.up_arrow:
            set_count(lambda c: c + 1)
        elif key.down_arrow:
            set_count(lambda c: max(0, c - 1))
        elif input_str == "q":
            app.exit()

    use_input(handle_input)

    return Box(
        Text(f"Counter: {count}", color="cyan", bold=True),
        Box(
            Text("Up/Down to change, q to quit", dim_color=True),
            margin_top=1,
        ),
        flex_direction="column",
        padding=1,
        border_style="round",
    )

render(counter())

Components

Box

Flexbox container, like <div>. Supports all flexbox props:

Box(
    *children,
    flex_direction="row",       # row | column | row-reverse | column-reverse
    justify_content="center",   # flex-start | center | flex-end | space-between | space-around | space-evenly
    align_items="stretch",      # flex-start | center | flex-end | stretch | baseline
    padding=1,                  # padding on all sides
    margin_top=1,               # individual margin
    border_style="round",       # single | double | round | bold | classic
    border_color="green",       # named color, hex, or rgb
    width=40,
    height=10,
    overflow="hidden",
)

Text

Text with styling:

Text("Hello", color="green", bold=True, italic=True, underline=True, strikethrough=True, dim_color=True, inverse=True)

Spacer

Fills available space (like flex: 1):

Box(Text("Left"), Spacer(), Text("Right"), flex_direction="row")

Static

Render items once (for logs, completed tasks):

Static(items=completed, render_item=lambda item, i: Text(f"Done: {item}"))

Transform

Transform text output per line:

Transform(Text("hello"), transform=lambda text, idx: text.upper())

Hooks

Hook Description
use_state(initial) Local state, returns (value, setter)
use_effect(fn, deps) Side effects with cleanup
use_input(handler) Keyboard input
use_app() App lifecycle (exit(), wait_until_render_flush())
use_focus() Tab-based focus
use_focus_manager() Programmatic focus control
use_animation(interval=100) Frame animation
use_window_size() Terminal dimensions
use_ref(initial) Mutable ref
use_memo(fn, deps) Memoized value
use_paste(handler) Paste events
use_stdout() / use_stderr() / use_stdin() Stream access
use_cursor() Cursor position
use_box_metrics(ref) Element measurements
use_is_screen_reader_enabled() Accessibility detection

Render Options

render(
    element,
    stdout=sys.stdout,              # output stream
    stdin=sys.stdin,                 # input stream
    stderr=sys.stderr,              # error stream
    exit_on_ctrl_c=True,            # exit on Ctrl+C
    use_alt_screen=False,           # alternate screen buffer (vim-like)
    max_fps=30,                     # max render frames per second
    debug=False,                    # each update as separate output
    interactive=None,               # override interactive mode detection
    incremental_rendering=False,    # only update changed lines
    patch_console=False,            # route print() through Ink output
    kitty_keyboard=None,            # kitty keyboard protocol options
    is_screen_reader_enabled=None,  # force screen reader mode
    on_render=None,                 # callback with render metrics
)

Examples

pip install pyinklib

python -m pyink.examples.counter            # Auto-incrementing counter
python -m pyink.examples.use_input          # Move a face with arrow keys
python -m pyink.examples.chat               # Type messages + Enter
python -m pyink.examples.select_input       # Arrow key selection list
python -m pyink.examples.dashboard          # Animated multi-panel dashboard
python -m pyink.examples.use_animation      # Unicorn animation
python -m pyink.examples.borders            # All 8 border styles
python -m pyink.examples.border_backgrounds # Per-edge border colors
python -m pyink.examples.box_backgrounds    # Background colors
python -m pyink.examples.use_focus          # Tab focus navigation
python -m pyink.examples.use_focus_with_id  # Programmatic focus by ID
python -m pyink.examples.focus              # Focus with visual indicators
python -m pyink.examples.table              # Data table with columns
python -m pyink.examples.justify_content    # All justify-content modes
python -m pyink.examples.terminal_resize    # Live terminal size display
python -m pyink.examples.use_stdout         # Terminal dimensions
python -m pyink.examples.alternate_screen   # Snake game (alt screen)
python -m pyink.examples.hello              # Hello World

Acknowledgements

PyInk is a 1:1 Python port of Ink by Vadim Demedes and the Ink contributors.

A huge thank you to the entire Ink team for creating such an incredible framework. The architecture, design, and attention to detail in Ink is what makes PyInk possible. Every component, hook, and rendering algorithm in PyInk is a direct port of Ink's TypeScript source code.

If you're building CLI tools in JavaScript/TypeScript, use Ink. If you're in Python, use PyInk.

License

MIT

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

pyinklib-1.1.6.tar.gz (108.2 kB view details)

Uploaded Source

Built Distribution

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

pyinklib-1.1.6-py3-none-any.whl (90.5 kB view details)

Uploaded Python 3

File details

Details for the file pyinklib-1.1.6.tar.gz.

File metadata

  • Download URL: pyinklib-1.1.6.tar.gz
  • Upload date:
  • Size: 108.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pyinklib-1.1.6.tar.gz
Algorithm Hash digest
SHA256 eacf14b4802c3f2f1aba4758cf79bbca2d33d330fc71aeff390579111c8eb958
MD5 f88e7a50e0ef0ccd0e77d3f2e4a46b9f
BLAKE2b-256 bb4929950a23c47329ee4e9440c426a389b3d76d5afa3feba1ec9ecb8c2b84cf

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyinklib-1.1.6.tar.gz:

Publisher: publish.yml on NicolaiLassen/pyink

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

File details

Details for the file pyinklib-1.1.6-py3-none-any.whl.

File metadata

  • Download URL: pyinklib-1.1.6-py3-none-any.whl
  • Upload date:
  • Size: 90.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pyinklib-1.1.6-py3-none-any.whl
Algorithm Hash digest
SHA256 5d206b26ef6e1655096d88ad0b4ab2474534dad2ff76e33f9cf2ad2328a406f2
MD5 da5e170ea9f7f9459d3e74cc2f364a0f
BLAKE2b-256 33efe74d70ff7bc6bbb1ebde0c5b7f4d4bcbf6b8eafffe502b69e8b14897ae0a

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyinklib-1.1.6-py3-none-any.whl:

Publisher: publish.yml on NicolaiLassen/pyink

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