Themes
Luna's TUI uses a theme system for consistent visual styling across the chat interface, tool output, markdown rendering, and status bars. Themes are defined as Lua tables with color and attribute specifications.
Built-in Themes
Luna ships with 4 built-in themes:
| Theme | Description |
|---|---|
default | Classic dark theme with colored roles |
onedark | Subdued palette inspired by One Dark |
high-contrast | Light background for accessibility |
solarized-dark | Solarized color palette on dark background |
Selecting a Theme
In ~/.luna/config.json:
json
{
"theme": "onedark"
}Or at startup:
bash
luajit main.lua --theme solarized-darkTheme Structure
A theme is a Lua table with the following sections:
lua
{
name = "my-theme",
background = "black",
menubar_title = "cyan onblack bold",
scrollbar_bg = "black",
scrollbar = "white onblack",
roles = {
user = "green onblack bold",
assistant = "blue onblack bold",
system = "yellow onblack bold",
error = "red onblack bold",
tool = "magenta onblack",
tool_start = "cyan onblack bold",
tool_result = "white onblack dim",
dim = "white onblack dim",
thinking = "cyan onblack dim",
thinking_summary = "darkgrey onblack dim",
compact = "yellow onblack dim",
},
markdown = {
heading = "\27[1m\27[36m",
bold = "\27[1m\27[33m",
italic = "\27[3m\27[35m",
inline_code = "\27[32m",
code_block = "\27[90m",
code_border = "\27[90m",
code_bg = "\27[48;5;236m",
link = "\27[4m\27[34m",
strikethrough = "\27[2m",
quote_prefix = "\27[90m",
hr = "\27[90m",
table_header = "\27[1m\27[36m",
table_sep = "\27[90m",
},
spinner = { "blue" },
}Color Specification
Role Colors
Role colors use a descriptive string format:
<foreground> [on<background>] [<attribute>]Examples:
green onblack bold
cyan onblack dim
white onblack
red onwhite boldSupported Colors
| Color | ANSI Code |
|---|---|
black | 30 |
red | 31 |
green | 32 |
yellow | 33 |
blue | 34 |
magenta | 35 |
cyan | 36 |
white | 37 |
darkgrey | 90 |
Supported Attributes
| Attribute | ANSI Code |
|---|---|
bold | 1 |
dim | 2 |
italic | 3 |
underline | 4 |
reverse | 7 |
Markdown Colors
Markdown styling uses raw ANSI escape sequences for full control:
lua
heading = "\27[1m\27[36m" -- bold cyan
bold = "\27[1m\27[33m" -- bold yellow
link = "\27[4m\27[34m" -- underline blueThe M.ANSI table provides named constants:
lua
local theme = require("ui.theme")
theme.ANSI.bold -- "\27[1m"
theme.ANSI.red -- "\27[31m"
theme.ANSI.bg_darkgrey -- "\27[48;5;236m"Custom Theme
To create a custom theme, register it before the TUI initializes:
lua
local theme = require("ui.theme")
theme.themes["my-custom"] = {
name = "my-custom",
background = "black",
menubar_title = "yellow onblack bold",
scrollbar_bg = "black",
scrollbar = "darkgrey onblack",
roles = {
user = "green onblack bold",
assistant = "cyan onblack",
system = "yellow onblack bold",
error = "red onblack bold",
tool = "magenta onblack",
tool_start = "cyan onblack bold",
tool_result = "darkgrey onblack",
dim = "darkgrey onblack",
thinking = "blue onblack dim",
thinking_summary = "darkgrey onblack dim",
compact = "yellow onblack dim",
},
markdown = {
heading = theme.ANSI.bold .. theme.ANSI.green,
bold = theme.ANSI.bold .. theme.ANSI.yellow,
italic = theme.ANSI.italic .. theme.ANSI.magenta,
inline_code = theme.ANSI.cyan,
code_block = theme.ANSI.darkgrey,
code_border = theme.ANSI.darkgrey,
code_bg = theme.ANSI.bg_darkgrey,
link = theme.ANSI.underline .. theme.ANSI.cyan,
strikethrough = theme.ANSI.dim,
quote_prefix = theme.ANSI.darkgrey,
hr = theme.ANSI.darkgrey,
table_header = theme.ANSI.bold .. theme.ANSI.green,
table_sep = theme.ANSI.darkgrey,
},
spinner = { "green" },
}
theme.set_theme("my-custom")LTUI Rendering Engine
Luna uses LTUI (Lua TUI) built on ncurses for terminal rendering:
- 256-color support with automatic quantization via
quantize_256() - Color pair allocation managed by
alloc_pair()starting at pair 100 - Image pair caching for sixel/kitty image rendering (pairs 200+)
- Wide character support with proper wcwidth for CJK and emoji
- Dirty-region rendering using CSI 2026 for efficient screen updates
ANSI Helpers
The theme module provides text formatting utilities:
lua
local theme = require("ui.theme")
theme.color("error", "Something went wrong")
theme.color("heading", "Title")
theme.strip_ansi(text)
theme.visible_width(text)
theme.wrap(text, 80)
theme.pad(text, 40)Listing Available Themes
lua
local theme = require("ui.theme")
local names = theme.list_themes()
for _, name in ipairs(names) do
print(name)
end