Reusable runner for interactive widget demos.
Handles terminal setup/cleanup, input reading, and the render loop so demo modules can focus purely on widget logic.
Uses OTP 28's :shell.start_interactive({:noshell, :raw}) to switch
the terminal to raw mode and read input via IO.getn/2.
Supports Kitty keyboard protocol auto-detection with modifyOtherKeys fallback. Detects synchronized output (mode 2026) support and wraps frames in BSU/ESU when available to eliminate flicker.
Demo Module Protocol
A demo module must implement:
init(cols, rows)— returns initial statehandle_event(event, state)— returns{:cont, new_state}or:quitrender(state)— returns an Element treefocused_id(state)— returns the focused widget id or nil
Summary
Functions
Run a demo module. Blocks until the demo exits or times out.