Skip to main content

guides/configuration.md

# Configuration

`kino_ex_ratatui` reads display options from three sources, in order:

```
per-instance opts (new/2 / frame/2)  >  configure/1  >  module defaults
```

Each key is resolved independently, so you can set a global font and theme via `configure/1` and still override just the theme on an individual cell.

## Per-instance opts

The most direct path. See the [Display options](`Kino.ExRatatui#module-display-options`) table for the full list.

```elixir
Kino.ExRatatui.new(MyApp,
  theme: %{background: "#0d1117", foreground: "#c9d1d9"},
  font_size: 14,
  height: "600px"
)
```

## Global defaults via `configure/1`

`Kino.ExRatatui.configure/1` writes to the `:kino_ex_ratatui` Application environment and applies to every subsequent `new/2` and `frame/2` call.

```elixir
# In a Livebook setup cell:
Kino.ExRatatui.configure(
  theme: :livebook,
  font_family: "JetBrains Mono, ui-monospace, monospace",
  font_size: 14
)
```

Calling `configure/1` again merges into the existing config rather than replacing it — handy for splitting concerns across cells:

```elixir
Kino.ExRatatui.configure(font_size: 14)         # font choice in one place
Kino.ExRatatui.configure(theme: :livebook)      # theme in another
# Both keys are now active.
```

The same values are available via `Application.get_all_env(:kino_ex_ratatui)`, so a release `config/runtime.exs` or `Config`-driven release config works equivalently:

```elixir
# config/runtime.exs
config :kino_ex_ratatui,
  theme: :livebook,
  font_size: 14
```

Validation runs immediately — `configure/1` raises `ArgumentError` on bad shapes the same way `new/2` does, so a mistyped key fails at the source rather than producing a working-but-wrong widget.

## Atom theme shorthands

The `:theme` option accepts a full xterm.js [`ITheme`](https://xtermjs.org/docs/api/terminal/interfaces/itheme/) map (the lowest-level form) or one of three atoms:

| Atom | What it does |
| ---- | ------------ |
| `:dark` | Bundled Catppuccin Mocha-flavored dark palette. Same colors as the no-opts default. |
| `:light` | Bundled Catppuccin Latte-flavored light palette. Pairs visually with `:dark`. |
| `:livebook` | Picks `:dark` or `:light` based on the user's OS-level `prefers-color-scheme`, and re-applies on the fly when that preference changes. |

The atoms are resolved in the JS hook, so `theme: :livebook` is a single source of truth for "match my Livebook" — no duplicate light/dark configs needed.

```elixir
# Reactively follows the user's color-scheme preference.
Kino.ExRatatui.configure(theme: :livebook)

# Or pick one explicitly.
Kino.ExRatatui.new(MyApp, theme: :light)
```

For finer control (selection backgrounds, the 16-color ANSI palette, cursor accents) supply a map directly — same vocabulary as xterm.js's `Terminal({theme: ...})`:

```elixir
Kino.ExRatatui.new(MyApp,
  theme: %{
    background: "#282c34",
    foreground: "#abb2bf",
    cursorAccent: "#282c34",
    selectionBackground: "#3e4451",
    red: "#e06c75",
    green: "#98c379",
    # …
  }
)
```

## Merge order in practice

```elixir
Kino.ExRatatui.configure(theme: :livebook, font_size: 14, height: "600px")

# This call uses:
#   - theme: %{background: "#000"}    (per-instance — overrides configure's :livebook)
#   - font_size: 14                   (configure)
#   - height: "600px"                 (configure)
#   - cursor_blink: true              (module default — neither set it)
Kino.ExRatatui.new(MyApp, theme: %{background: "#000"})
```

Each key is resolved independently — there's no "all-or-nothing" inheritance.

## When configure/1 is the wrong tool

If your TUI app needs runtime knowledge (current user, request id, …), thread that through `c:ExRatatui.App.mount/1` instead. `configure/1` is for cosmetics: theme, font, height, cursor, scrollback, stopped message — values that don't change between cells in the same notebook.