# TUI Overview The Deft binary drives two TUI hosts: - **`retro`** — a panelised development environment (editor, git, file manager, terminal, AI chat, system monitor). Inspired by Emacs and Blender. Uses a native rendering backend (OpenGL where available, ANSI fallback otherwise). - **`retra`** — functionally identical to `retro` but pure ANSI, so it works over bare SSH. Both hosts run the same Deft apps, expose the same `tui/*` stdlib functions, and follow the same architectural model: each panel runs an app in its own OS thread with an isolated runtime. ## The Panel Model A TUI workspace is a tree of **panels** — rectangular regions of the screen, each running one app. The workspace holds the panels; the binary renders them. ``` Workspace ├── Panel 1 (editor) ─── editor.dft, its own runtime/thread ├── Panel 2 (files) ─── files.dft, its own runtime/thread └── Panel 3 (terminal) ─── shell.dft, its own runtime/thread ``` Each panel has: - A **numeric ID** (used by `tui/find-panel`, focus/resize, etc.). - An optional **string tag** (set via `[tui/tag ...]` or `[tui/auto-tag ...]`). - A **focused** state — one panel always has keyboard focus. - A **root widget tree** that handles events and draws. - An **AppThread** driving its event loop on a dedicated OS thread. ## Panel Management
 # Split the focused panel
tui/split :vertical                          # or :horizontal

# Focus by index, direction, or tag/id
tui/focus 2
tui/focus-right
tui/focus-id "editor"

# Find a panel by tag
set ed-id [tui/find-panel "editor"]

# Tag the calling panel
tui/tag "diff"
tui/auto-tag "editor"                        # auto-assigns editor, editor2, ...

# Close the focused panel
tui/close

# Resize
tui/resize 0.05                              # +/- a small amount 
## App Lifecycle Every panel runs an app that implements the `AppLifecycle` protocol:
 defimpl MyApp AppLifecycle {
    on-init {||
        @doc "Called once when the app starts. Set up state, subscribe to topics."
        # ...
    }

    on-suspend {||
        @doc "Called when the panel loses focus or the workspace backgrounds."
        # ...
    }

    on-resume {||
        @doc "Called when the panel regains focus."
        # ...
    }

    on-deinit {||
        @doc "Called once when the app is shutting down. Clean up resources."
        # ...
    }
} 
The framework calls these automatically; you implement whichever ones your app needs. ## The Widget Tree A panel's content is a tree of **widgets** — tagged maps implementing the relevant protocols. Most apps register a tree once on `on-init` and let the framework drive events:
 defimpl MyApp AppLifecycle {
    on-init {||
        set state [atom %{
            :input   [ui/text-input %{ :id "search" :placeholder "Search..." }]
            :results @{}
            :selected nil
        |}]

        tui/set-widget-tree $state             # opts into framework dispatch
    |
    }
} 
Once the widget tree is registered, the framework runs the target phase (focused widget gets key events; hit-test dispatches mouse events) before the panel's typed event methods. See [Widgets and Protocols](widgets-and-protocols) for the full widget pattern, and [Event Dispatch](event-dispatch) for how events flow through a panel. ## Drawing The framework calls a panel's `draw` method (from the Zig-registered `Panel` protocol) every frame. Within draw, use `draw/*` stdlib functions to render:
 defimpl MyApp Panel {
    draw {|ctx|
        @doc "Render the panel into the draw context."
        draw/fill-bg $ctx %{ :r 0 :g 0 :b 0 }
        draw/text $ctx 0 0 "Hello, TUI!"
    |
    }
} 
For pixel-precise graphics (canvas, images, 3D), see [Graphics](graphics). ## State and Atom Pattern Most apps hold their state in an atom at module scope:
 def state [atom %{
    :counter 0
    :items @{}
    :filter :all
|}]

def bump {||
    swap! $state { |s|
        assoc $s :counter ($s~counter + 1)
    |
    }
} 
The atom is shared across closures and the framework's protocol methods. Reads via `deref` (or `$state` shorthand); updates via `swap!` or `reset!`. See [Language: Stdlib](../01-Language/stdlib). ## Cross-Panel Communication Three mechanisms: - **`pub/*`** — broadcast events any panel can subscribe to. The default for loose coupling. See [Pubsub](../02-Platform/pubsub). - **`@rpc` + `rpc`** — direct call to a function exposed by another panel's runtime (addressed by the panel's app/tag name). Returns a Future. Runtime-level, so it works in every host. See [RPC](../02-Platform/rpc). - **`config/*`** — shared, persistent preferences. See [Config](../02-Platform/config). ## Common TUI Stdlib functions ### Workspace | Function | Purpose | |---|---| | `[tui/split :direction]` | Split focused panel | | `[tui/close]` / `[tui/close $id]` | Close a panel | | `[tui/focus $n]` / `[tui/focus-left]` / `[tui/focus-id $id]` | Move focus | | `[tui/resize $delta]` | Resize focused panel | | `[tui/zoom]` | Toggle zoom (maximise/restore) | | `[tui/panels]` / `[tui/groups]` | Enumerate layout | | `[tui/focused-panel]` | Focused panel's tag | | `[tui/self]` | Calling panel's own id | ### Tagging | Function | Purpose | |---|---| | `[tui/tag $tag]` | Set a string tag on calling panel | | `[tui/auto-tag $base]` | Auto-assign a unique instance tag | | `[tui/find-panel $tag]` | Look up panel id by tag | ### Panel content | Function | Purpose | |---|---| | `[tui/mount $panel-id $instance]` | Mount a Deft component | | `[tui/mount-widget $panel-id $widget]` | Mount a widget tree | | `[tui/set-widget-tree $atom]` | Register tree for framework dispatch | | `[tui/panel-label $id $text]` | Set a plain text label | | `[tui/panel-status-bar $id $left ?$right]` | Status bar | ### Focus | Function | Purpose | |---|---| | `[tui/focus-widget "id"]` | Set widget-level focus | | `[tui/focused-widget-id]` | Get focused widget id | | `[tui/cycle-focus :forward $tree]` | Tab-style cycling | | `[tui/clear-focus]` | Drop widget focus | ### Launching apps | Function | Purpose | |---|---| | `[tui/launch $panel-id $name $path]` | Launch an app in a panel | | `[tui/adopt $panel-id $name]` | Reuse a docked app instance | See `[info/sig ...]` or the `*-doc` text on each stdlib function for full argument lists. ## Lifecycle of a Panel App ``` 1. Workspace starts panel → AppThread spawns → New runtime created → File loaded → AppLifecycle/on-init called 2. Panel takes focus → AppLifecycle/on-resume called 3. User types → Framework dispatches to focused widget → Bubbles to panel's on-key (if not consumed) 4. User switches panels → AppLifecycle/on-suspend called on old → AppLifecycle/on-resume called on new 5. Panel closed → AppLifecycle/on-deinit called → AppThread exits, runtime destroyed ``` ## Where To Go Next - [Widgets and Protocols](widgets-and-protocols) — the widget pattern - [Event Dispatch](event-dispatch) — focus, target+bubble, key routing - [Dialogs](dialogs) — themed input/menu/file dialogs - [Graphics](graphics) — pixel-precise canvas, image, 3D