synaps-tui 0.12.0

Terminal UI layer — ratatui, crossterm, syntect, tachyonfx
Documentation
//! Static list of settings exposed in the /settings menu.
//!
//! The `ALL_SETTINGS` array is generated by the `define_settings!` macro in
//! `defs.rs` — that's the single source of truth for both schema and apply
//! handler. Do not add settings here; add them in `defs.rs`.

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) enum Category {
    Model,
    Providers,
    Agent,
    Context,
    Budgets,
    ToolLimits,
    Appearance,
    Plugins,
    Startup,
    Daemon,
}

impl Category {
    pub fn label(&self) -> &'static str {
        match self {
            Category::Model => "Model",
            Category::Providers => "Providers",
            Category::Agent => "Agent",
            Category::Context => "Context & Cache",
            Category::Budgets => "Turn Budgets",
            Category::ToolLimits => "Tool Limits",
            Category::Appearance => "Appearance",
            Category::Plugins => "Plugins",
            Category::Startup => "Startup",
            Category::Daemon => "Daemon",
        }
    }
}

pub(crate) const CATEGORIES: [Category; 10] = [
    Category::Model,
    Category::Providers,
    Category::Agent,
    Category::Context,
    Category::Budgets,
    Category::ToolLimits,
    Category::Appearance,
    Category::Plugins,
    Category::Startup,
    Category::Daemon,
];

/// The built-in categories shown in the modal, in order. Plugin
/// categories follow them.
pub(crate) fn visible_categories() -> &'static [Category] {
    &CATEGORIES
}

pub(crate) enum EditorKind {
    Cycler(&'static [&'static str]),
    /// Options computed at interaction time from the RuntimeSnapshot.
    DynamicCycler,
    /// Read-only derived value — never editable, never persisted.
    Display,
    ModelPicker,
    ThemePicker,
    Text {
        numeric: bool,
    },
}

pub(crate) struct SettingDef {
    pub key: &'static str,
    pub label: &'static str,
    pub category: Category,
    pub editor: EditorKind,
    /// Kept on each setting so the settings modal can render an inline
    /// help line later without reshuffling the schema. Referenced by
    /// drafts of the per-setting hint UI; retain even while unused.
    #[allow(dead_code)]
    pub help: &'static str,
}

// Re-export the macro-generated schema so existing call sites keep working.
pub(crate) use super::defs::ALL_SETTINGS;

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn every_setting_belongs_to_known_category() {
        for def in ALL_SETTINGS {
            assert!(CATEGORIES.contains(&def.category));
        }
    }

    #[test]
    fn new_categories_are_present_and_populated() {
        for cat in [
            Category::Context,
            Category::Budgets,
        ] {
            assert!(CATEGORIES.contains(&cat), "{cat:?} missing from CATEGORIES");
            assert!(
                ALL_SETTINGS.iter().any(|d| d.category == cat),
                "{cat:?} has no settings"
            );
        }
        assert_eq!(Category::Context.label(), "Context & Cache");
        assert_eq!(Category::Budgets.label(), "Turn Budgets");
    }

    #[test]
    fn plugins_category_is_present() {
        assert!(CATEGORIES.contains(&Category::Plugins));
    }

    #[test]
    fn plugins_category_label() {
        assert_eq!(Category::Plugins.label(), "Plugins");
    }

    #[test]
    fn visible_categories_are_the_built_ins_without_a_sidecar_page() {
        let v = visible_categories();
        assert_eq!(v, CATEGORIES.to_vec());
        assert_eq!(v.len(), 10);
        assert!(v.iter().all(|c| c.label() != "Sidecar"));
        assert_eq!(v[0], Category::Model);
        assert_eq!(*v.last().unwrap(), Category::Daemon);
    }
}