Metadata-Version: 2.4
Name: tuix-core
Version: 0.6
Summary: A lightweight and modular terminal UI engine. Designed to make building interactive terminal interfaces simple and structured.
Author-email: custosh <contact@custosh.dev>
License-Expression: MIT
Project-URL: Homepage, https://github.com/custosh/tuix-core
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# 🧱 TUIX Core 0.6

---

## 🧩 Overview

**TUIX** is a modular terminal UI engine inspired by web technologies.
It introduces a **DOM-like component system**, a **nested layout model**, a **viewport-aware input/rendering pipeline**, and a **buffer-based renderer** for building structured and styled terminal interfaces.

v0.6 ships a compiled **C + Cython** core focused on API consistency, safer native lifecycles, namespaced builder registration, command/event bridge support, and Unicode rendering foundations. It keeps Python close to the native surface while preserving practical compatibility aliases for existing Python callers.

## 🆕 What's New In 0.6

- Breaking native API cleanup: C functions, headers, modules, and build units now follow consistent `tuix_system_action` naming.
- Vocabulary normalization across renderer/compositor/input/layout APIs: `renderer`, `compose`, `hit_map`, `sub_cycle`, `progress_bar`, `list_view`, `split_pane`, `text_area`, `foreground`, and `background`.
- Namespaced builder registry: built-in builders register under the `tuix` namespace, descriptors carry ABI metadata, and runtime builder entries track ownership, generation, unloadability, and live instances.
- Object creation and layout child helpers now use explicit builder namespace/name at the native layer. Python keeps compatibility aliases for practical callers.
- Shared `TUIX_RC_*` return-code system replaces ambiguous status values across public native APIs.
- Thread-safe event shim and command-buffer bridge for frame-boundary commits and C-to-runtime event flushing.
- Expanded Python command-buffer coverage for hot-path scene, buffer, widget, layout, viewport, canvas, and dialog controls.
- Core-loop and registry lifecycle hardening, including stale scene-cache invalidation across scene free, registry init, and registry destroy.
- Unicode foundation work: interned UTF-8 symbols, safer wide-glyph handling, grapheme-cluster-aware measuring/drawing helpers, and full UTF-8 keyboard text payloads.
- Rendering robustness fixes for REP output, half-block bounds, transparent composition, terminal control-byte sanitization, and text style diff runs.
- Build scripts and docs were updated for renamed modules, namespaced builders, and current verification flows.

**Core vs Framework:** TUIX provides a low-level engine core - not a high-level application framework. The public API intentionally exposes low-level primitives and is not designed to be "simple" by itself.

---

## 🚀 Installation

```bash
pip install tuix-core
```

> Requires Python ≥ 3.10. Pre-built wheels are provided for Windows/Linux/macOS (AMD64, x86, ARM).  
> Building from source also requires **Cython ≥ 0.29** and a C compiler.

---

## ⚡ Quick Start

```python
from tuix.core import core, scene, buffer, content_builder, input
from tuix.core import object as objects

core.init()
content_builder.register_standard()

scene.init_scene(b"Main")
scene.select_scene(b"Main")
input.listen()

# create a progressbar and animate it
uid = objects.create_object(content_builder.PROGRESS_BAR, b"Main", 0.7, 0.08, 0.45, 0.15)
obj = objects.get_object_by_uid(uid)
snap = buffer.get_buffer_snapshot(b"Main", uid)

if obj and snap:
    for i in range(101):
        objects.tuix_progressbar_set_value(obj, i / 100.0)
        core.main_loop()

buffer.free_buffer(b"Main", uid)
input.stop()
core.shutdown()
```

---

## 🧩 Widgets

| Builder constant | What it renders |
|---|---|
| `content_builder.PROGRESS_BAR` / `PROGRESSBAR` | Horizontal fill bar with customisable chars and colours |
| `content_builder.CHOICE` | Keyboard-navigable list menu |
| `content_builder.INPUT` | Single-line text input with placeholder support |
| `content_builder.CANVAS` | Free-draw surface - pixels, lines, rects, circles, text, sprites |
| `content_builder.TEXT` | Inline text content with runtime fg/bg updates |
| `content_builder.BOX` | Framed container with title and color controls |
| `content_builder.DIVIDER` | Horizontal/vertical divider with custom symbol/color |
| `content_builder.BADGE` | Compact label with fg/bg palette |
| `content_builder.BUTTON` | Clickable/keyboard-activatable button state |
| `content_builder.TAG` | Chip-style label with configurable brackets |
| `content_builder.STATUS` | IDLE/OK/WARN/ERROR status display widget |
| `content_builder.MENU` | Interactive menu with keyboard/mouse selection |
| `content_builder.SCROLL_CONTAINER` | Viewport-backed scroll container with content-space children |
| `content_builder.ROW` | Horizontal stack/flex-style layout parent |
| `content_builder.COLUMN` | Vertical stack/flex-style layout parent |
| `content_builder.SPLIT_PANE` / `SPLITPANE` | Two-pane layout with draggable/keyboard-resizable divider |
| `content_builder.GRID` | Grid layout with fixed and weighted row/column tracks |
| `content_builder.CHECKBOX` | Toggle with label, checked/disabled state, mouse and keyboard input |
| `content_builder.LIST_VIEW` / `LISTVIEW` | Viewport-backed virtual list with selection and activation |
| `content_builder.TEXT_AREA` / `TEXTAREA` | Multiline viewport-backed editor with cursor/scroll/editing APIs |
| `content_builder.DIALOG` | Modal dialog with backdrop, focus trapping, and close handling |

### Progressbar

```python
objects.tuix_progressbar_set_value(obj, 0.75)          # 0.0 – 1.0
objects.tuix_progressbar_set_style(obj,
    ord('#'), ord('-'),          # fill char, empty char
    120, 220, 80,                # fill RGB
    50,  50,  50)                # empty RGB
```

### Choice

```python
objects.tuix_choice_set_options(obj, [b"Yes", b"No", b"Maybe"])
# no manual feed_input needed (handled by engine/main loop)

if objects.tuix_choice_is_confirmed(obj):
    idx = int(objects.tuix_choice_get_result(obj))
```

### Input

```python
objects.tuix_input_set_placeholder(obj, b"Type here...")
# no manual feed_input needed (handled by engine/main loop)

if objects.tuix_input_is_submitted(obj):
    text = objects.tuix_input_get_result(obj)   # bytes
```

### Canvas

```python
objects.tuix_canvas_draw_rect  (obj, x, y, w, h, b'#', filled=1, r, g, b, br, bg, bb)
objects.tuix_canvas_draw_circle(obj, cx, cy, radius, b'O', filled=0, r, g, b, br, bg, bb)
objects.tuix_canvas_draw_line  (obj, x0, y0, x1, y1, b'/', r, g, b, br, bg, bb)
objects.tuix_canvas_draw_text  (obj, x, y, b"hello", r, g, b, br, bg, bb)
objects.tuix_canvas_set_pixel  (obj, x, y, b'*', r, g, b, br, bg, bb)
```

### Layout Builders

```python
row_uid = objects.create_object(content_builder.ROW, b"Main", 0.9, 0.25, 0.1, 0.05)
row = objects.get_object_by_uid(row_uid)

objects.tuix_stack_set_gap(row, 2)
objects.tuix_stack_set_padding(row, 1, 1, 1, 1)
objects.tuix_stack_set_justify(row, content_builder.JUSTIFY_SPACE_BETWEEN)
objects.tuix_stack_set_align(row, content_builder.ALIGN_STRETCH)

label_uid = objects.tuix_stack_add_object(row, b"Main", content_builder.TEXT, 1.0, 1.0)
buffer.set_buffer_layout_slot_by_uid(label_uid, grow=1.0, min_w=12)
```

```python
grid_uid = objects.create_object(content_builder.GRID, b"Main", 0.9, 0.5, 0.35, 0.05)
grid = objects.get_object_by_uid(grid_uid)

objects.tuix_grid_set_columns(grid, [
    (content_builder.GRID_TRACK_WEIGHT, 1),
    (content_builder.GRID_TRACK_FIXED, 24),
])
objects.tuix_grid_set_rows(grid, [
    (content_builder.GRID_TRACK_FIXED, 3),
    (content_builder.GRID_TRACK_WEIGHT, 1),
])
objects.tuix_grid_set_gaps(grid, 1, 1)

child_uid = objects.tuix_grid_add_object(grid, b"Main", content_builder.TEXT, 1.0, 1.0)
buffer.set_buffer_grid_placement_by_uid(child_uid, row=1, col=0, row_span=1, col_span=2)
```

### Viewport And Modal Widgets

```python
list_uid = objects.create_object(content_builder.LIST_VIEW, b"Main", 0.4, 0.4, 0.1, 0.05)
listview = objects.get_object_by_uid(list_uid)
objects.tuix_listview_set_title(listview, b"Items")
objects.tuix_listview_set_items(listview, [b"Alpha", b"Beta", b"Gamma"])

textarea_uid = objects.create_object(content_builder.TEXT_AREA, b"Main", 0.5, 0.4, 0.1, 0.5)
textarea = objects.get_object_by_uid(textarea_uid)
objects.tuix_textarea_set_title(textarea, b"Notes")
objects.tuix_textarea_set_placeholder(textarea, b"Type here...")

dialog_uid = objects.create_object(content_builder.DIALOG, b"Main", 0.9, 0.7, 0.05, 0.05)
dialog = objects.get_object_by_uid(dialog_uid)
objects.tuix_dialog_set_title(dialog, b"Confirm")
objects.tuix_dialog_activate(dialog, b"Main")
```

---

## 📁 Examples

Runnable examples are in the `examples/` directory:

| File | Description |
|---|---|
| `examples/widgets/progressbar_dual.py` | Two bars filling at different speeds |
| `examples/widgets/choice_palette.py` | Colour palette menu, prints chosen RGB to stdout |
| `examples/widgets/canvas_bounce.py` | Animated bouncing ball with colour cycling and FPS counter |
| `examples/widgets/text_and_box_demo.py` | Text and box builder styling demo |
| `examples/widgets/button_and_badge_demo.py` | Button and badge interaction demo |
| `examples/widgets/menu_and_tags_demo.py` | Menu and tag widgets together |
| `examples/widgets/scroll_container_demo.py` | Scroll container viewport demo |
| `examples/widgets/viewport_modal_demo.py` | ListView, TextArea, Checkbox, Button, and Dialog demo |
| `examples/multimodal/focus_routing.py` | Focus routing between Choice, ListView, and TextArea |
| `examples/showcase/sequential_journey.py` | Full sequential demo (progressbar → choice → input → canvas) |
| `examples/showcase/layout_demo.py` | Row, SplitPane, Grid, layout slots, and grid placement demo |
| `examples/showcase/buffer_hierarchy_demo.py` | Parent-child buffer hierarchy and z-index layering |
| `examples/showcase/scene_stats_demo.py` | Scene stats snapshots and compaction APIs |
| `examples/showcase/command_buffer_batch.py` | Scoped command-buffer batching and frame-boundary commits |

```bash
python examples/widgets/canvas_bounce.py
```

---

## 📚 Documentation

Full documentation is available at: https://docs.custosh.dev/docs/tuix-core

---

## 🧪 Tests

```bash
pip install pytest
pytest
```

---

176 tests cover engine lifecycle, scene management, registry, modular package exports, multimodal routing APIs, widget builders, nested layout helpers, viewport widgets, modal state, snapshots/hierarchy APIs, scene compaction/stats, canvas draw calls, examples layout integrity, and Python-to-native wrapper contracts. The wrapper contract checks verify direct native call arity and require missing optional native lookups to be explicitly allowlisted.

---

## 📊 Benchmarks

Benchmarked against **blessed, terminal-kit, Ink, ReziTUI, Bubble Tea, Ratatui, OpenTUI.Core, OpenTUI.React, TUIX.Renderer, TUIX.Core, TUIX.Python, Rich, Urwid, PromptToolkit**.

Latest run metadata (from [BENCHMARKS.md](BENCHMARKS.md)):
- Date: 2026-09-08T16:18:41.125Z
- Runtime/OS: Node v22.19.0 on Windows_NT 10.0.26200 (win32 x64)
- CPU/RAM: 12th Gen Intel(R) Core(TM) i5-12450HX (12 cores), RAM 24352MB

The README summary keeps `Mean`, `ops/s`, and `Peak RSS`. The full report also includes `Runs`, `Run CV`, `Mean CI95`, `Wall`, `RSS Growth`, and two byte metrics:
- `Bytes(local)`: bytes reported by each framework counter.
- `Bytes(pty)`: observed PTY bytes (cross-framework comparable).

### Startup

| Framework | Mean | ops/s | Peak RSS |
|---|---:|---:|---:|
| TUIX.Python | 140µs | 7.2K | 107.0 MB |
| Ratatui | 165µs | 6.1K | 30.0 KB |
| TUIX.Renderer | 212µs | 4.7K | 4.1 MB |
| TUIX.Core | 260µs | 3.8K | 4.3 MB |
| ReziTUI | 274µs | 3.6K | 81.4 MB |
| OpenTUI.React | 437µs | 2.3K | 36.2 MB |
| Bubble Tea | 8.84ms | 113 | 15.4 MB |

### Tree Construction (1,000 items)

| Framework | Mean | ops/s | Peak RSS |
|---|---:|---:|---:|
| TUIX.Python | 70µs | 14.3K | 108.4 MB |
| TUIX.Renderer | 102µs | 9.8K | 4.2 MB |
| OpenTUI.Core | 120µs | 8.3K | 35.4 MB |
| TUIX.Core | 137µs | 7.3K | 4.5 MB |
| Bubble Tea | 148µs | 6.7K | 15.9 MB |
| OpenTUI.React | 296µs | 3.4K | 38.6 MB |
| ReziTUI | 523µs | 1.9K | 178.9 MB |
| Rich | 533µs | 1.9K | 109.4 MB |

### Re-render

| Framework | Mean | ops/s | Peak RSS |
|---|---:|---:|---:|
| Bubble Tea | 5µs | 207.6K | 16.2 MB |
| TUIX.Renderer | 18µs | 56.6K | 4.2 MB |
| Blessed | 37µs | 26.7K | 511.6 MB |
| TUIX.Core | 38µs | 26.3K | 4.5 MB |
| Ratatui | 57µs | 17.5K | 30.0 KB |
| TUIX.Python | 59µs | 16.9K | 108.4 MB |
| OpenTUI.React | 59µs | 17.0K | 38.6 MB |
| ReziTUI | 65µs | 15.4K | 184.8 MB |

### Layout Stress (4 cols x 10 rows)

| Framework | Mean | ops/s | Peak RSS |
|---|---:|---:|---:|
| Bubble Tea | 19µs | 53.4K | 16.2 MB |
| TUIX.Python | 54µs | 18.4K | 108.4 MB |
| OpenTUI.React | 62µs | 16.1K | 39.4 MB |
| Ratatui | 74µs | 13.4K | 30.0 KB |
| TUIX.Core | 79µs | 12.7K | 4.5 MB |
| TUIX.Renderer | 85µs | 11.7K | 4.2 MB |
| OpenTUI.Core | 123µs | 8.1K | 35.4 MB |

### Scroll Stress (2,000 items)

| Framework | Mean | ops/s | Peak RSS |
|---|---:|---:|---:|
| Bubble Tea | 8µs | 121.5K | 16.2 MB |
| OpenTUI.React | 43µs | 23.3K | 39.4 MB |
| Ratatui | 73µs | 13.7K | 30.0 KB |
| ReziTUI | 80µs | 12.6K | 181.5 MB |
| OpenTUI.Core | 132µs | 7.5K | 35.4 MB |
| TUIX.Renderer | 165µs | 6.1K | 4.2 MB |
| TUIX.Python | 217µs | 4.6K | 114.6 MB |
| TUIX.Core | 236µs | 4.2K | 4.5 MB |

### Virtual List (100,000 items, viewport 40)

| Framework | Mean | ops/s | Peak RSS |
|---|---:|---:|---:|
| Bubble Tea | 16µs | 62.5K | 16.2 MB |
| OpenTUI.React | 46µs | 21.8K | 39.4 MB |
| TUIX.Renderer | 58µs | 17.3K | 4.2 MB |
| Ratatui | 76µs | 13.1K | 30.0 KB |
| TUIX.Core | 76µs | 13.1K | 4.5 MB |
| Blessed | 116µs | 8.6K | 513.8 MB |
| OpenTUI.Core | 131µs | 7.7K | 35.4 MB |
| ReziTUI | 141µs | 7.1K | 183.3 MB |
| TUIX.Python | 163µs | 6.2K | 198.1 MB |

### Terminal Full UI (120x40, 24 services)

| Framework | Mean | ops/s | Peak RSS |
|---|---:|---:|---:|
| Bubble Tea | 76µs | 13.1K | 20.5 MB |
| TUIX.Renderer | 156µs | 6.4K | 4.7 MB |
| Ratatui | 190µs | 5.3K | 30.0 KB |
| TUIX.Core | 260µs | 3.8K | 5.1 MB |
| OpenTUI.React | 355µs | 2.8K | 39.7 MB |
| TUIX.Python | 428µs | 2.3K | 377.7 MB |
| OpenTUI.Core | 428µs | 2.3K | 36.7 MB |

In this run, TUIX.Python leads startup (140µs) and 1,000-item tree construction (70µs), Bubble Tea and TUIX.Renderer lead low-latency re-render, and TUIX variants consistently maintain high throughput across layout, scroll, and virtual-list workloads while keeping a minimal C core memory footprint (Peak RSS ~4.2–5.1 MB for TUIX.Core and TUIX.Renderer).

Full tables (including CI95, CV, wall time, RSS growth, `Bytes(local)`, and `Bytes(pty)`) are in [BENCHMARKS.md](BENCHMARKS.md).

---

## ⚙️ Architecture

```
tuix.core
├── core/           init / shutdown / main_loop / stats / mouse capture / registry proxy
├── scene/          scene lifecycle / focus / modal / transactions / compaction
├── content_builder/ builder constants + namespaced registry helpers + builder modules
├── object/         create/get object + widget runtime APIs + compatibility aliases
├── buffer/         snapshots + hierarchy + z-index + layout/grid overrides
├── input/          listen / stop / snapshots / key injection
├── renderer/       native buffer rendering, patch rendering, invalidation, stats
├── compositor/     scene composition, geometry resolution, hit_map invalidation
├── command_buffer/ frame-boundary command-buffer helpers
├── event/          C-to-runtime event bridge helpers
├── tuix/           terminal profile and symbol/pixel helpers
├── viewport/       viewport capability helpers
├── _structs.py     ctypes mirror of C structs
└── _tuix_cy.pyx    Cython extension - all C calls go through here
```

The C core handles builder registration, layout geometry, viewport clipping, hit_map generation, compositing, diffing, command/event bridging, and terminal rendering. Python drives scene/object lifecycle, widget state, nested layout placement, and safe state inspection through thin wrappers and snapshots.

---

## ⚙️ Future Plans

- Optional runtime assertions around buffer bounds and ownership
- Debug mode for buffer allocation/free and ownership-transition logs
- Theme / style system
- Async-friendly loop integration
- External/WASM builder owners on top of the namespaced builder registry

---

## Support / Donate

If you enjoy using TUIX, you can support its development:

- [💖 GitHub Sponsors](https://github.com/sponsors/custosh)
- [💰 PayPal](https://www.paypal.com/donate/?hosted_button_id=R7W3Z65XMVLBN)

---

## 📜 License

MIT License © 2026 custosh

```text
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
