Private
Public Access
0
0
Files
manual_slop/docs/guide_themes.md
T

7.2 KiB

Themes — Authoring Guide

File Layout

  • Global themes: themes/<name>.toml — one file per theme, in a directory at the project root.
  • Project-specific overrides: <project>/project_themes.toml — a single bundled TOML file with one [themes.<name>] table per theme.
  • Override the global path via the SLOP_GLOBAL_THEMES environment variable (must be a directory).

Both layouts are scanned and merged; project themes with the same name as a global theme override it.

Schema

# human-readable label (optional)
description = "Solarized Dark by Ethan Schoonover"

# one of: dark | light | mariana | retro_blue
# selects which built-in imgui_color_text_edit palette to apply
# to code blocks in markdown viewers
syntax_palette = "dark"

[colors]
# RGB triples, 0-255
window_bg    = [  0,  43,  54]
text         = [147, 161, 161]
button_hovered = [ 38, 139, 210]
# ... any imgui.Col_ key is accepted
  • syntax_palette is required for TOML-defined themes. Unknown values fall back to "dark".
  • [colors] is required. Missing it is a hard error (logged to stderr, theme skipped).
  • Color keys are imgui Col_ enum members in snake_case. The loader does best-effort mapping; unknown keys are silently ignored.

Common Color Keys

Key ImGui Col_ Use
window_bg WindowBg Panel/window background
child_bg ChildBg Nested child regions
popup_bg PopupBg Modal/popup backdrop
border Border Separator/border
frame_bg FrameBg Input field background
title_bg TitleBg Window title bar
menu_bar_bg MenuBarBg Top menu strip
scrollbar_bg ScrollbarBg Scrollbar track
button Button Standard button
header Header Collapsible section header
separator Separator Divider line
tab Tab Tab bar item
text Text Default text
text_disabled TextDisabled Greyed-out text
check_mark CheckMark Checkbox/radio check
slider_grab SliderGrab Slider thumb
table_header_bg TableHeaderBg Table column headers
status_info (semantic) Informational accent
status_success (semantic) Success/positive accent
status_warning (semantic) Warning accent
status_error (semantic) Error/negative accent

The status_* keys are semantic — they map to the theme's accent colors and are used by the C_* color helpers in src/gui_2.py:80-92.

The 4-Syntax-Palette Upstream Limit

imgui-bundle ships four built-in imgui_color_text_edit palettes and exposes no API to define new ones:

Palette Style
dark Default dark; balanced contrast
light Default light; balanced contrast
mariana VS Code Mariana-inspired; muted blues
retro_blue High-contrast blue-on-black retro CRT

You select which one your theme uses by setting the syntax_palette field. The system picks the closest match for you when you omit the field (built-in non-TOML themes get dark by default). To get a different palette, set the field explicitly.

This is a hard upstream limit; there is no way to define a 5th palette without forking imgui-bundle. If you find yourself wanting to, the answer is to pick the closest of the four and adjust your UI theme's colors to harmonize.

Public API (src/theme_2.py)

The system exposes three functions for runtime use:

Function Purpose
load_themes_from_disk() -> None Re-scan the global themes directory and <project>/project_themes.toml, re-parse, and refresh the palette registry. Call this after dropping a new .toml file into themes/.
get_syntax_palette_for_theme(theme_name: str) -> str Return the syntax palette name (dark/light/mariana/retro_blue) associated with a UI theme. Returns "dark" for unknown themes.
apply_syntax_palette(palette_name: str) -> None Set the active imgui_color_text_edit default palette. No-op for unknown names.

The MarkdownRenderer.__init__ (src/markdown_helper.py) automatically calls apply_syntax_palette(get_syntax_palette_for_theme(get_current_palette())), so code blocks in markdown viewers track the active theme. When the user switches themes, new TextEditor instances pick up the new palette; cached editors keep their previous palette until the next block renders.

Usage Examples

from src import theme_2 as theme

# Re-scan disk (e.g. after dropping a new theme file)
theme.load_themes_from_disk()

# Look up the syntax palette for a UI theme name
syntax = theme.get_syntax_palette_for_theme("solarized_dark")  # "dark"

# Force a specific syntax palette (e.g. for a one-off preview)
theme.apply_syntax_palette("mariana")

The C_* Color-Callable Convention

src/gui_2.py:80-92 defines 13 module-level getter functions for semantic colors used throughout the GUI:

def C_LBL() -> imgui.ImVec4: return theme.get_color("text_disabled")
def C_VAL() -> imgui.ImVec4: return theme.get_color("text")
def C_OUT() -> imgui.ImVec4: return theme.get_color("status_info")
def C_IN()  -> imgui.ImVec4: return theme.get_color("status_success")
# ... and 9 more (C_REQ, C_RES, C_TC, C_TR, C_TRS, C_KEY, C_NUM, C_TRM, C_SUB)

These are callables, not ImVec4 values. They resample the current theme's color on each call, so theme switches take effect on the next render frame.

Correct usage — call the function at the use site:

imgui.text_colored(C_LBL(), "Completed:")
imgui.text_colored(C_VAL(), str(value))

Common bug — storing the function in a dict keyed by name, then passing the function (not its result) to imgui:

DIR_COLORS = {"request": C_OUT, "response": C_IN}
d_col_fn = DIR_COLORS.get(direction, C_VAL)  # stores the function
imgui.text_colored(d_col_fn(), direction)     # CORRECT: call it

The bug shipped in commit 7ea52cbb (multi-themes track) at src/gui_2.py:3705-3707 and was fixed in 1469ecac. When writing tests that assert theme color usage, patch src.theme_2.imgui so theme.get_color() returns the mock's ImVec4, and assert with C_LBL() (called), not C_LBL (the function).

Hot Reload

Theme TOMLs are loaded once at module init and can be reloaded on demand via theme.load_themes_from_disk(). The function is safe to call from the GUI thread; it mutates the global registry atomically.

Typical workflow when authoring a new theme:

  1. Drop a new file into themes/.
  2. From the AI Settings panel's theme dropdown, the new theme is not yet visible — the registry is cached.
  3. To see it without restarting, call theme.load_themes_from_disk() from a Python console hooked into the running process, OR add a "Refresh Themes" button that calls it, OR restart the app.

project_themes.toml is scanned for every project load, so changes there are picked up automatically when you switch projects.

Cross-References

  • guide_gui_2.md — The C_* callables in detail; the DIR_COLORS bug history.
  • guide_testing.md — How to test theme color usage without crashing imgui.color().
  • conductor/tracks.md — The multi_themes_20260604 track entry (the 8 shipped themes and the API design).