Complete reference for all hypricer interfaces
Compile and apply a theme.
Usage:
hypricer build --profile <PROFILE_NAME>Arguments:
--profile, -p <NAME>: Profile to build (required)
Example:
hypricer build --profile seikiProcess:
- Load registry from
catalog/registry/*.toml - Load profile from
profiles/<NAME>.toml - Load theme referenced in profile
- Validate all dependencies (
checkcommands) - Generate daemon source code
- Copy to
generated/source/ - Theme is ready for manual compilation
Exit Codes:
0: Success1: Build error (missing file, TOML parse error, etc.)2: Validation error (missing dependency)
# List available themes
hypricer list themes
# List available profiles
hypricer list profiles
# Show daemon status
hypricer status
# View logs
hypricer logs [-f|--follow]
# Create new theme from template
hypricer new theme <NAME>
# Validate without building
hypricer check --profile <NAME>Registry files define available components. They live in catalog/registry/*.toml.
Purpose: Reference to a fixed configuration file.
Syntax:
[static.<ID>]
path = "relative/path/from/root.conf"
description = "Optional human-readable description"Example:
[static.apps_minimal]
path = "catalog/static/apps/minimal.conf"
description = "Terminal-only app suite"Fields:
path(required, string): Relative path from project rootdescription(optional, string): Documentation
Usage in themes:
[static]
apps = "apps_minimal" # References static.apps_minimalPurpose: Background threads that monitor system state and emit events.
Syntax:
[watcher.<ID>]
provider = "poll_cmd" | "stream_cmd" # Type of watcher
cmd = "shell command to execute"
interval = 5000 # Milliseconds (only for poll_cmd)
output = "string" | "json" # Optional, default: string
check = "command" | ["cmd1", "cmd2"] # Optional dependency checkProviders:
| Provider | Behavior | Use Case |
|---|---|---|
poll_cmd |
Runs command periodically, emits event if output changes | CPU usage, time, battery |
stream_cmd |
Runs command once, reads stdout line-by-line | playerctl --follow, log tailing |
Example (poll_cmd):
[watcher.cpu_usage]
provider = "poll_cmd"
cmd = "top -bn1 | grep 'Cpu(s)' | awk '{print $2}'"
interval = 2000
check = "which top"Example (stream_cmd):
[watcher.music_title]
provider = "stream_cmd"
cmd = "playerctl metadata --format '{{ title }}' --follow"
check = "which playerctl"Fields:
provider(required):"poll_cmd"or"stream_cmd"cmd(required, string): Shell command to executeinterval(required for poll_cmd, number): Milliseconds between pollsoutput(optional, string): Output format (currently unused)check(optional, string or array): Dependency validation command(s)
Generated Code:
async fn watch_<ID>(tx: mpsc::Sender<Event>) {
// For poll_cmd:
loop {
let output = Command::new("sh").arg("-c").arg(cmd).output();
if output_changed {
tx.send(Event { key: "<ID>", value: output }).await;
}
sleep(interval);
}
}Purpose: On-demand data fetchers called when events occur.
Syntax:
[provider.<ID>]
cmd = "shell command"
default = "fallback value if command fails"
check = "command" | ["cmd1", "cmd2"] # OptionalExample:
[provider.current_wallpaper]
cmd = "swww query | grep 'currently displaying' | cut -d' ' -f8"
default = "unknown.png"
check = "which swww"Fields:
cmd(required, string): Shell command to executedefault(required, string): Fallback value (used on timeout or error)check(optional, string or array): Dependency validation
Timeout: All providers have a 200ms timeout. If exceeded, default is used.
Generated Code:
async fn run_provider(cmd: &str, default_val: &str) -> String {
let timeout = Duration::from_millis(200);
match tokio::time::timeout(timeout, execute_cmd(cmd)).await {
Ok(output) => output,
Err(_) => default_val.to_string()
}
}Theme files define how components are wired together. They live in themes/<NAME>/theme.toml.
[meta]
name = "Display Name"
version = "1.0"
author = "Your Name" # Optional
template = "path/to/template.conf"
[inputs]
# (Currently unused, reserved for future)
[static]
tag_name = "registry_static_id"
[dynamic]
tag_name = "path/to/logic.rs"Fields:
name(required, string): Display nameversion(optional, string): Theme versionauthor(optional, string): Author nametemplate(required, string): Path to template file (relative to project root)
Example:
[meta]
name = "Cyber Punk 2077"
version = "2.1.0"
author = "Johnny Silverhand"
template = "themes/cyberpunk/template.conf"Maps template tags to registry static components.
Syntax:
[static]
<template_tag> = "<registry_static_id>"Example:
[static]
apps = "apps_minimal"
keybinds = "seiki_keybinds"In template:
{{ apps }}
{{ keybinds }}
Result:
source = /full/path/to/catalog/static/apps/minimal.conf
source = /full/path/to/themes/seiki/components/keybinds.conf
Maps template tags to Rust logic files.
Syntax:
[dynamic]
<template_tag> = "path/to/logic/file.rs"Example:
[dynamic]
window_style = "themes/mytheme/logic/window.rs"
bar_config = "themes/mytheme/logic/bar.rs"Logic file requirements:
- Must export a function:
pub fn resolve(ctx: &Context) -> String - Path is relative to project root
In template:
{{ window_style }}
{{ bar_config }}
At runtime:
The daemon calls logic::window::resolve(&ctx) and replaces {{ window_style }} with the returned string.
Your logic functions receive a Context containing all current system state.
Definition:
pub struct Context {
pub data: HashMap<String, String>,
}The data HashMap contains:
- All watcher values (key = watcher ID)
- All provider values (key = provider ID)
- All static component values (key = static tag from theme)
Example:
{
"cpu_usage": "45",
"time_part": "Day",
"current_window": "firefox",
"apps": "source = /path/to/apps.conf"
}Required:
use crate::Context;
pub fn resolve(ctx: &Context) -> String {
// Your logic here
"result".to_string()
}Parameters:
ctx: Reference toContextstruct
Returns:
String: The text to replace the template tag with
Example:
use crate::Context;
pub fn resolve(ctx: &Context) -> String {
// Get CPU usage (with fallback)
let cpu = ctx.data.get("cpu_usage")
.and_then(|s| s.parse::<i32>().ok())
.unwrap_or(0);
// Conditional logic
let color = if cpu > 70 {
"rgba(ff4444ee)"
} else {
"rgba(33ccffee)"
};
// Return config snippet
format!("general {{ col.active_border = {} }}", color)
}You can use any Rust std library:
use std::collections::HashMap;
use std::env;
use std::fs;
use std::path::Path;
pub fn resolve(ctx: &Context) -> String {
// File system access
if Path::new("/tmp/dark_mode").exists() {
return "dark".to_string();
}
// Environment variables
if let Ok(user) = env::var("USER") {
if user == "work" {
return "minimal".to_string();
}
}
// Complex data parsing
let values: Vec<i32> = ctx.data.values()
.filter_map(|s| s.parse().ok())
.collect();
format!("average = {}", values.iter().sum::<i32>() / values.len() as i32)
}Note: Logic runs inside the daemon, so:
- ✅ Fast (compiled code)
- ✅ Type-safe (won't compile if broken)
- ❌ Can't make network requests (no tokio in logic scope)
- ❌ Should be deterministic (avoid randomness)
You can unit test your logic:
// themes/mytheme/logic/window.rs
use crate::Context;
use std::collections::HashMap;
pub fn resolve(ctx: &Context) -> String {
// ... your logic
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_high_cpu() {
let mut data = HashMap::new();
data.insert("cpu_usage".to_string(), "80".to_string());
let ctx = Context { data };
let result = resolve(&ctx);
assert!(result.contains("ff4444")); // Red color
}
}Run tests:
cd generated/source
cargo testTemplates are standard Hyprland config files with variable substitution.
# Regular Hyprland config
monitor=,preferred,auto,1
input {
kb_layout = us
}
# Variable substitution
{{ tag_name }}
# Variables can be anywhere
$my_var = {{ some_value }}
bind = $mainMod, T, exec, {{ terminal_command }}
Tags are replaced based on theme definition:
Static tags:
# In theme.toml
[static]
apps = "apps_minimal"# In template.conf
{{ apps }}
# Becomes:
source = /full/path/to/catalog/static/apps/minimal.conf
Dynamic tags:
# In theme.toml
[dynamic]
window_style = "themes/mytheme/logic/window.rs"# In template.conf
{{ window_style }}
# Becomes (at runtime, based on logic function):
general { col.active_border = rgba(33ccffee) }
Provider/Watcher tags:
These are available in the Context passed to logic functions, not directly in templates.
Tags are replaced with single-line or multi-line content:
# Single-line replacement
$terminal = {{ terminal_app }}
# Multi-line replacement
{{ window_decorations }}
# Could become:
# decoration {
# rounding = 10
# blur {
# enabled = true
# }
# }
If you need literal {{ in your config:
# This will be replaced
{{ tag }}
# This will stay as-is (future feature for escaping)
\{{ not_a_tag }}
Current limitation: No escaping mechanism exists yet. Avoid {{ in comments or strings.
-
Use descriptive tag names:
# ❌ Unclear {{ s1 }} # ✅ Clear {{ window_decorations }} -
Group related tags:
# Startup {{ autostart_apps }} {{ daemon_services }} # Visuals {{ window_rules }} {{ decorations }} -
Comment your template:
# ================================================================= # WINDOW MANAGEMENT # ================================================================= {{ window_rules }} # ================================================================= # KEYBINDS # ================================================================= {{ keybinds }} -
Keep static content in template:
# Don't dynamically generate this monitor=,preferred,auto,1 # Do dynamically generate this {{ theme_colors }}
#[derive(Debug, Clone)]
pub struct Event {
pub key: String, // Watcher ID
pub value: String, // Command output
}pub struct Context {
pub data: HashMap<String, String>,
}Access patterns:
// Safe access with fallback
let cpu = ctx.data.get("cpu_usage").unwrap_or("0");
// Parse to number
let cpu_num: i32 = ctx.data.get("cpu_usage")
.and_then(|s| s.parse().ok())
.unwrap_or(0);
// Check existence
if ctx.data.contains_key("music_playing") {
// ...
}
// Iterate all values
for (key, value) in &ctx.data {
println!("{}: {}", key, value);
}| hypricer Version | Rust Version | Hyprland Version |
|---|---|---|
| 2.0.x | 1.70+ | 0.35+ |
| 2.1.x (planned) | 1.75+ | 0.36+ |
Breaking changes between versions will be documented in CHANGELOG.md.
- User Guide - Using hypricer
- Theme Developer Guide - Creating themes
- Registry Manual - Extending the registry
- Architecture - How it all works