rae 0.1.2

Renderer-neutral widget, layout, state, and GLSL shader primitives for agentic Rust desktop tools.
Documentation
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum AgentMode {
    #[default]
    Build,
    Plan,
}

impl AgentMode {
    pub fn label(self) -> &'static str {
        match self {
            Self::Build => "Build",
            Self::Plan => "Plan",
        }
    }

    pub fn prompt_prefix(self) -> &'static str {
        match self {
            Self::Build => "build> ",
            Self::Plan => "plan> ",
        }
    }

    pub fn toggle(self) -> Self {
        match self {
            Self::Build => Self::Plan,
            Self::Plan => Self::Build,
        }
    }

    pub fn contract(self) -> &'static str {
        match self {
            Self::Build => "inspect, patch small, validate, repair",
            Self::Plan => "read-only planning; no file mutation",
        }
    }
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum ThinkingMode {
    Off,
    Low,
    #[default]
    Medium,
    High,
    XHigh,
}

impl ThinkingMode {
    pub fn label(self) -> &'static str {
        match self {
            Self::Off => "none",
            Self::Low => "low",
            Self::Medium => "medium",
            Self::High => "high",
            Self::XHigh => "xhigh",
        }
    }

    pub fn next(self) -> Self {
        match self {
            Self::Off => Self::Low,
            Self::Low => Self::Medium,
            Self::Medium => Self::High,
            Self::High => Self::XHigh,
            Self::XHigh => Self::Off,
        }
    }

    pub fn previous(self) -> Self {
        match self {
            Self::Off => Self::XHigh,
            Self::Low => Self::Off,
            Self::Medium => Self::Low,
            Self::High => Self::Medium,
            Self::XHigh => Self::High,
        }
    }
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum PaneId {
    #[default]
    Transcript,
    Sidebar,
    Prompt,
}

impl PaneId {
    pub fn next(self) -> Self {
        match self {
            Self::Transcript => Self::Sidebar,
            Self::Sidebar => Self::Prompt,
            Self::Prompt => Self::Transcript,
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum OverlayKind {
    Help,
    CommandPalette,
    ContextPicker,
    AgentFlow,
    ProviderSetup,
    ThemeEditor,
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum FlowLane {
    #[default]
    Overview,
    Context,
    Kanban,
    Runs,
}

impl FlowLane {
    pub fn label(self) -> &'static str {
        match self {
            Self::Overview => "Overview",
            Self::Context => "Context",
            Self::Kanban => "Kanban",
            Self::Runs => "Runs",
        }
    }

    pub fn next(self) -> Self {
        match self {
            Self::Overview => Self::Context,
            Self::Context => Self::Kanban,
            Self::Kanban => Self::Runs,
            Self::Runs => Self::Overview,
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum TaskStatus {
    Todo,
    Doing,
    Done,
    Blocked,
}

impl TaskStatus {
    pub fn label(self) -> &'static str {
        match self {
            Self::Todo => "todo",
            Self::Doing => "doing",
            Self::Done => "done",
            Self::Blocked => "blocked",
        }
    }
}

#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ResourceSnapshot {
    pub session_messages: usize,
    pub prompt_history: usize,
    pub run_history: usize,
    pub kanban_tasks: usize,
    pub active_model_task: bool,
    pub active_validation_task: bool,
}

impl ResourceSnapshot {
    pub const MAX_SESSION_MESSAGES: usize = 800;
    pub const MAX_PROMPT_HISTORY: usize = 100;
    pub const MAX_RUN_HISTORY: usize = 100;
    pub const MAX_KANBAN_TASKS: usize = 200;

    pub fn compact_lines(&self) -> [String; 4] {
        [
            usage_line(
                "messages",
                self.session_messages,
                Self::MAX_SESSION_MESSAGES,
            ),
            usage_line("prompts", self.prompt_history, Self::MAX_PROMPT_HISTORY),
            usage_line("runs", self.run_history, Self::MAX_RUN_HISTORY),
            usage_line("tasks", self.kanban_tasks, Self::MAX_KANBAN_TASKS),
        ]
    }
}

fn usage_line(label: &str, count: usize, limit: usize) -> String {
    let percent = if limit == 0 {
        0
    } else {
        count.saturating_mul(100) / limit
    };
    format!("{label}: {count}/{limit} {percent}%")
}