How Theme Configuration Precedence Is Determined in Tuicr (Bundled vs Local)
Tuicr determines theme precedence through a five‑layer hierarchy: CLI flags override the theme config key, which overrides appearance‑based theme_dark/theme_light selection, with local theme files taking priority over built‑in bundled themes.
Tuicr, a terminal UI crate authored by agavra, implements a cascading resolution system for its colour palettes. Understanding this theme configuration precedence chain lets you predict exactly which palette renders when multiple settings conflict. The resolution logic spans src/main.rs, src/config/mod.rs, and src/theme/mod.rs, with clear rules governing bundled versus local theme sources.
The Five‑Layer Precedence Chain
Tuicr evaluates theme sources in strict priority order. Higher‑ranked sources completely override lower ones.
| Rank | Source | Implementation Location |
|---|---|---|
| 1 | --theme CLI flag |
src/main.rs |
| 2 | theme key in config |
src/config/mod.rs |
| 3 | Appearance‑aware selection (appearance, theme_dark, theme_light) |
src/config/mod.rs |
| 4 | Local theme files (~/.config/tuicr/themes/) |
src/theme/mod.rs (load_custom_theme) |
| 5 | Bundled theme constructors | src/theme/mod.rs (Theme::from_name) |
1. Command‑Line Flag (--theme / -t)
The highest‑priority source is the --theme argument parsed in src/main.rs. When present, the supplied name bypasses all configuration file settings.
tuicr --theme github-dark
This forces Theme::github_dark() regardless of any config.toml entries. The flag value is passed verbatim to the application initialization.
2. Explicit theme Config Key
After CLI processing, load_config() in src/config/mod.rs reads the theme key from $XDG_CONFIG_HOME/tuicr/config.toml (or %APPDATA%\tuicr\config.toml on Windows). If set, this key wins over the dark/light appearance keys.
theme = "tokyo-night-storm"
With this configuration, Tuicr skips appearance detection entirely and attempts to resolve "tokyo-night-storm".
3. Appearance‑Driven Selection (theme_dark / theme_light)
When no explicit theme key exists, Tuicr evaluates the appearance setting:
appearance = "system"— detects terminal background brightness and selectstheme_dark(dark background) ortheme_light(light background)appearance = "dark"— forcestheme_darkappearance = "light"— forcestheme_light
appearance = "system"
theme_dark = "gruvbox-dark"
theme_light = "gruvbox-light"
If terminal detection fails or keys are unset, the resolver defaults to "dark".
4. Local Theme Files
Once a theme name is determined, Tuicr checks themes_dir() (defined in src/config/mod.rs) for a matching file:
// src/theme/mod.rs — simplified local theme resolution
let path = themes_dir()?.join(&chosen_name).with_extension("toml");
if path.exists() {
return Theme::load_from_file(&path); // 4️⃣ local theme wins
}
Local definitions reside at ~/.config/tuicr/themes/<NAME>.toml and override bundled equivalents of the same name.
5. Bundled (Built‑in) Themes
Finally, Tuicr falls back to compiled‑in constructors via Theme::from_name() in src/theme/mod.rs:
// src/theme/mod.rs — bundled theme dispatch
match name {
"dark" => Theme::dark(),
"light" => Theme::light(),
"github-dark" => Theme::github_dark(),
"github-light" => Theme::github_light(),
"gruvbox-dark" => Theme::gruvbox_dark(),
"gruvbox-light" => Theme::gruvbox_light(),
"tokyo-night-storm" => Theme::tokyo_night_storm(),
_ => Theme::dark(), // default fallback
}
Unknown theme names resolve to Theme::dark().
Complete Resolution Flow in Code
The following simplified excerpt from src/app.rs demonstrates how the precedence chain combines:
// CLI flag extraction (src/main.rs)
let cli_theme = matches.value_of("theme");
// Config loading (src/config/mod.rs)
let cfg = load_config()?.config.unwrap_or_default();
// Final resolution (application entry point)
let chosen_name = match cli_theme {
Some(name) => name.to_string(), // 1️⃣ CLI flag
None => match cfg.theme {
Some(name) => name, // 2️⃣ config.theme
None => { // 3️⃣ appearance logic
match cfg.appearance.as_deref() {
Some("dark") => cfg.theme_dark.clone(),
Some("light") => cfg.theme_light.clone(),
Some("system") | None => {
if terminal_is_dark() { cfg.theme_dark.clone() }
else { cfg.theme_light.clone() }
}
_ => None,
}
}.unwrap_or_else(|| "dark".to_string())
}
};
// Local file check before bundled fallback
if let Ok(theme) = load_custom_theme(&chosen_name) {
theme // 4️⃣ local file
} else {
Theme::from_name(&chosen_name) // 5️⃣ bundled
}
Practical Configuration Examples
Override Everything with a CLI Flag
tuicr -t gruvbox-light
Runs with the Gruvbox Light palette even if config.toml specifies a dark theme.
System‑Aware with Local Customizations
# ~/.config/tuicr/config.toml
appearance = "system"
theme_dark = "custom-dark"
theme_light = "solarized-light"
Create ~/.config/tuicr/themes/custom-dark.toml to override the bundled definition; solarized-light will use the built‑in version unless a local file exists.
Explicit Theme Without Appearance Detection
theme = "tokyo-night-storm"
This disables dark/light switching entirely. The named theme resolves to local file first, bundled constructor second.
Key Source Files
src/main.rs— Parses--themeCLI argumentsrc/config/mod.rs— Loads TOML configuration, definesthemes_dir(), handlesappearance/theme/theme_dark/theme_lightkeyssrc/theme/mod.rs— ImplementsTheme::from_name()dispatcher, bundled constructors, andload_custom_theme()for local filesdocs/CONFIG.md— User‑facing documentation of precedence rulesREADME.md— Quick reference for bundled theme names
Summary
- CLI flag (
--theme) always wins when present themeconfig key overrides appearance‑based selectionappearance+theme_dark/theme_lightprovides automatic or forced light/dark handling- Local theme files (
~/.config/tuicr/themes/) shadow bundled themes of the same name - Bundled constructors provide guaranteed fallbacks via
Theme::from_name()
Frequently Asked Questions
What happens if I specify both --theme and a theme key in config?
The CLI flag takes precedence. According to src/main.rs, the --theme value is passed directly to the application and bypasses configuration file parsing for theme selection.
Can I override a bundled theme without modifying source code?
Yes. Create a file at $XDG_CONFIG_HOME/tuicr/themes/<NAME>.toml matching the bundled theme name. The load_custom_theme() function in src/theme/mod.rs checks local files before falling back to Theme::from_name(), so your local definition wins.
How does appearance = "system" detect terminal background?
Tuicr queries the terminal for background colour information. If detection succeeds and indicates a dark background, theme_dark is selected; otherwise theme_light applies. Detection failures default to dark mode.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →