Skip to content

Latest commit

 

History

History
171 lines (123 loc) · 4.74 KB

File metadata and controls

171 lines (123 loc) · 4.74 KB

hypricer: The Theme Developer's Guide

Welcome to hypricer. Unlike traditional "dotfile managers" that just copy files, hypricer allows you to build Reactive Themes that change instantly based on system events (Music, Battery, Time, Weather, etc.).

As a "Ricer" (Theme Developer), you are not just writing config files; you are effectively building a small, specialized Rust program that manages the desktop state.


1. The Core Concept

A hypricer theme consists of three parts:

  1. The Manifest (theme.toml): The wiring. It tells the system "I need to listen to Music and Battery."
  2. The Brain (logic/*.rs): The logic. "If Music is Metal AND Battery > 20%, use Neon borders."
  3. The Body (template.conf): The skeleton. Standard Hyprland config with {{ placeholders }}.

2. Creating a New Theme

Create a new directory in themes/. The folder name (e.g., cyber_punk) will be your theme ID.

mkdir -p themes/cyber_punk/logic
touch themes/cyber_punk/theme.toml
touch themes/cyber_punk/template.conf

Step 1: The Manifest (theme.toml)

This file defines what your theme needs to "see" (inputs) and how it decides "what to show" (dynamic parts).

[meta]
name = "Cyber Punk 2077"
version = "1.0"
author = "YourName"
template = "themes/cyber_punk/template.conf"

# 1. INPUTS: What data does your logic need?
# These keys must exist in 'catalog/registry/*.toml'
inputs = ["music_genre", "battery_level", "active_window"]

# 2. DYNAMIC: Which Rust function resolves this tag?
[dynamic]
# When template asks for {{ window_style }}, run logic/window.rs
window_style = "themes/cyber_punk/logic/window.rs"

# 3. STATIC: Simple re-usable components
[static]
apps = "apps_standard"

Step 2: The Logic (logic/*.rs)

This is where the magic happens. You write standard Rust code to decide which components to load.

File: themes/cyber_punk/logic/window.rs

// The function signature must always be:
// pub fn resolve(ctx: &Context) -> Vec<String>

pub fn resolve(ctx: &Context) -> Vec<String> {
    // 1. Access Data safely
    // (Keys match the 'inputs' list in theme.toml)
    let genre = ctx.data.get("music_genre").unwrap_or("unknown");
    let battery = ctx.data.get("battery_level").unwrap_or("100");
    let batt_int: i32 = battery.parse().unwrap_or(100);

    // 2. The Decision Logic
    if batt_int < 20 {
        // Low power mode: Use minimal borders
        return vec!["win_minimal".to_string()];
    }

    if genre.contains("Synthwave") {
        // High energy mode: Use Neon borders
        return vec!["win_neon".to_string()];
    }

    // Default
    return vec!["win_glass".to_string()];
}

Step 3: The Template (template.conf)

This is your Hyprland configuration file. Instead of hardcoding values, use the tags you defined.

# Auto-generated by hypricer

# Load Static Apps
{{ apps }}

# Load Dynamic Window Style (Changes instantly!)
{{ window_style }}

# Regular Hyprland Config
monitor=,preferred,auto,1

3. Adding New Capabilities (Registry)

If you need a Watcher or Component that doesn't exist yet, you must add it to the Registry.

Adding a New Watcher (Trigger)

Create or edit a file in catalog/registry/.

Example: You want to change themes based on Wi-Fi Status. Create catalog/registry/network.toml:

[watcher.wifi_ssid]
provider = "poll_cmd"
cmd = "iwgetid -r"
interval = 5000 # Check every 5 seconds
output = "string"

Now you can use inputs = ["wifi_ssid"] in your theme!

Adding a New Component

If you created a new Window Style (e.g., "Glassy"), add it to the registry so it can be resolved. Edit catalog/registry/styles.toml:

[tunable.win_glass]
path = "catalog/tunable/window_styles/glass.conf"

4. Derived State (Advanced)

Sometimes raw data is annoying to parse in every file. You can create a Global Setup Hook to clean up data before your components see it.

File: themes/cyber_punk/logic/derived.rs

use std::collections::HashMap;

pub fn calculate(raw_data: &HashMap<String, String>) -> HashMap<String, String> {
    let mut derived = raw_data.clone();

    // Simplify the time
    if let Some(time) = raw_data.get("time_hour") {
        let hour: i32 = time.parse().unwrap_or(12);
        if hour > 20 || hour < 6 {
            derived.insert("is_night".to_string(), "true".to_string());
        } else {
            derived.insert("is_night".to_string(), "false".to_string());
        }
    }
    
    return derived;
}

5. Build & Test

To compile your theme into a running daemon:

# 1. Select your profile (which points to your theme)
hypricer build --profile my_dev_profile

# 2. Watch the logs (Located in the 'live' folder)
tail -f ~/.config/hypr/hypricer/live/daemon.log