superlighttui 0.12.11

Super Light TUI - A lightweight, ergonomic terminal UI library
# SLT Architecture

This document describes how the code is organized and how data flows through the system.
For design philosophy and conventions, see [DESIGN_PRINCIPLES.md](DESIGN_PRINCIPLES.md).

---

## Module Map

```
src/
├── lib.rs                      # Crate root
│   ├── Re-exports (public API surface)
│   ├── Backend trait
│   ├── AppState, RunConfig
│   └── run(), run_with(), run_inline(), run_async(), frame()
│
├── context.rs                  # The "UI handle" — passed to user closures
│   ├── Context struct (25+ fields: layout, focus, scroll, animation, hooks, theme, events, debug)
│   ├── ContainerBuilder        # Fluent builder for row/col/grid containers
│   ├── Response                # Widget interaction result { clicked, hovered, changed, focused, rect }
│   └── State<T> / use_state()  # Hook system for component-local state
│
├── context/
│   ├── widgets_display.rs      # impl Context: text, styled, button, tabs, modal, overlay, markdown, code_block...
│   ├── widgets_interactive.rs  # impl Context: list, table, select, radio, multi_select, tree, virtual_list...
│   ├── widgets_input.rs        # impl Context: text_input, textarea, form_field, validation
│   └── widgets_viz.rs          # impl Context: chart, bar_chart, sparkline, histogram, canvas, scatter, candlestick...
│
├── widgets.rs                  # State structs: TextInputState, TableState, ListState, SelectState, TabsState...
│                               # (30+ state types — one per interactive widget)
│
├── layout.rs                   # Layout engine
│   ├── Command enum            # Flat representation of UI calls
│   ├── LayoutNode              # Tree node with resolved children
│   ├── build_tree()            # Command list → LayoutNode tree
│   └── collect_all()           # Single DFS pass — collects focus, scroll, hits, animations, draws, modals, toasts
│
├── layout/
│   ├── flexbox.rs              # compute(), layout_row(), layout_column(), gap/grow/shrink resolution
│   └── render.rs               # render(), render_inner(), render_border(), clipping, viewport culling
│
├── style.rs                    # Style struct, Border, Padding, Margin, Constraints, Modifiers, Align, Justify
├── style/
│   ├── color.rs                # Color enum (Named, Indexed, Rgb), ColorDepth, color blending
│   └── theme.rs                # Theme struct, 7 presets (dark, light, dracula, catppuccin, nord, solarized, tokyo_night), ThemeBuilder
│
├── terminal.rs                 # Terminal backend
│   ├── Terminal                # Full-screen mode — alternate screen, raw mode, mouse capture
│   ├── InlineTerminal          # Inline mode — renders below cursor, no alternate screen
│   └── ANSI output, synchronized output (DECSET 2026), event polling
│
├── terminal/
│   └── selection.rs            # SelectionState, text selection overlay rendering
│
├── anim.rs                     # Animation primitives
│   ├── Tween                   # Linear interpolation with 9 easing functions
│   ├── Spring                  # Physics-based spring animation
│   ├── Keyframes               # Timeline with stops and loop modes
│   ├── Sequence                # Chained tween segments
│   └── Stagger                 # Delayed animation for list items
│
├── chart.rs                    # ChartBuilder, ChartConfig, Dataset, Marker
├── chart/
│   ├── render.rs               # Chart rendering, histogram
│   ├── axis.rs                 # Axis struct, label formatting
│   ├── bar.rs                  # Bar chart rendering
│   ├── grid.rs                 # Grid lines
│   └── braille.rs              # Braille dot patterns for line/scatter charts
│
├── buffer.rs                   # Double-buffer with clip stack and diff tracking
├── cell.rs                     # Cell = char + Style + optional URL
├── rect.rs                     # Rect struct, bounds checking, intersection
├── event.rs                    # Event, KeyCode, KeyModifiers, MouseEvent, MouseButton
├── halfblock.rs                # Half-block (▀▄) image rendering
├── keymap.rs                   # KeyMap, Binding structs
├── palette.rs                  # 256-color palette definitions
└── test_utils.rs               # TestBackend, EventBuilder for headless testing
```

---

## Frame Lifecycle

Every frame follows this exact sequence:

```
1. EVENT POLL
   └── Terminal polls for keyboard/mouse events (non-blocking)
   └── Events stored in Context for widget consumption

2. USER CLOSURE
   └── User's closure runs: ui.text(), ui.button(), ui.col(), etc.
   └── Each call pushes a Command to Context's internal command list
   └── No layout is computed yet — just recording intent

3. BUILD TREE — build_tree()
   └── Flat Command list → nested LayoutNode tree
   └── Parent-child relationships resolved from open/close markers

4. COLLECT ALL — collect_all()
   └── Single DFS traversal of the LayoutNode tree
   └── Collects: focus targets, scroll regions, hit areas,
       animation values, draw closures, modals, toasts
   └── This single pass replaced 7 separate traversals (v0.9)

5. FLEXBOX LAYOUT
   └── layout_row() / layout_column()
   └── Resolves: sizes, gaps, grow factors, min/max constraints
   └── Breakpoint-conditional styles evaluated against terminal width

6. RENDER
   └── render() → render_inner() → render_border()
   └── Writes Cell values to the back buffer
   └── Clip stack ensures children don't overflow parent bounds
   └── Viewport culling: nodes fully outside the viewport are skipped

7. DIFF + FLUSH
   └── Compare front buffer (previous frame) vs back buffer (current frame)
   └── apply_style_delta() — only emit ANSI attributes that changed
   └── Synchronized output (DECSET 2026) prevents tearing on supported terminals
   └── Swap front ↔ back buffers
```

---

## One-Frame Delay Feedback

Layout-computed data feeds back to the **next** frame via `prev_*` fields on Context:

```
Frame N:   closure runs → layout computed → focus_count, hit_areas, scroll_bounds stored
                                            Frame N+1: closure reads prev_focus_count, prev_hit_areas → makes decisions
```

This is an intentional design choice of immediate-mode UI:
- Widget positions are not known until layout runs (after the closure)
- So interaction checks (hover, click) use positions from the previous frame
- This introduces a one-frame delay that is imperceptible at 60 FPS

Interactive widgets depend on `prev_*` data for hit testing, scroll bounds, and focus count.

---

## Module Dependency Flow

```
lib.rs (entry point)
  ├── context.rs ←── context/widgets_*.rs (impl blocks on Context)
  │     ↑
  │     ├── widgets.rs (state types)
  │     ├── style.rs ←── style/color.rs, style/theme.rs
  │     ├── layout.rs ←── layout/flexbox.rs, layout/render.rs
  │     ├── buffer.rs ←── cell.rs
  │     ├── anim.rs
  │     ├── event.rs
  │     └── rect.rs
    ├── terminal.rs ←── terminal/selection.rs
  │     ↑
  │     └── buffer.rs, event.rs (for flush and polling)
    └── chart.rs ←── chart/render.rs, chart/axis.rs, chart/bar.rs, chart/grid.rs, chart/braille.rs
```

Key observations:
- `context.rs` is the central hub — it depends on almost everything
- `terminal.rs` is isolated — it only knows about `buffer` and `event`
- `style`, `layout`, `anim` are independent of each other
- Widget submodules (`context/widgets_*.rs`) add `impl Context` blocks — they don't define new types

---

## Visibility Rules

| Visibility | Use when | Example |
|------------|----------|---------|
| `pub` | Part of the user-facing API | `pub fn text()`, `pub struct Style` |
| `pub(crate)` | Shared across modules, not for users | `pub(crate) struct FrameData` |
| `pub(super)` | Shared with parent module's submodules only | `pub(super) fn render_border()` |
| Private (no modifier) | Implementation detail within a single file | Helper functions, internal state |

### Re-export Rule

**The public API is defined by `lib.rs` re-exports.** Users should never need deep imports like `slt::context::widgets_display::...`. If something is public, it must be re-exported from the crate root.

### Why This Matters for Semver

Every `pub use` in `lib.rs` is a semver commitment. Adding a re-export is non-breaking. Removing one is breaking. Be deliberate about what gets re-exported.

---

## File Conventions

- **Module pattern**: `module.rs` + `module/` directory (Rust 2018 style, NOT `mod.rs`)
- **Submodule imports**: `use super::*;` to access parent types
- **Splitting safety**: When splitting a file, keep `#[derive(...)]` and `#[cfg_attr(...)]` attached to their type definitions — they must not get separated by the split boundary