car-browser 0.7.0

Browser automation and perception pipeline for Common Agent Runtime
Documentation
//! UiMap formatting for LLM prompt consumption.
//!
//! Provides configurable formatters that serialize UiMap data into compact
//! text that fits within token budgets.

use super::ui_map::UiMap;

/// Configurable UiMap formatter.
pub struct UiMapFormatter {
    /// Maximum elements to include.
    pub max_elements: usize,
    /// Whether to include bounds coordinates.
    pub include_bounds: bool,
    /// Whether to include state flags.
    pub include_states: bool,
    /// Whether to only show interactive elements.
    pub interactive_only: bool,
    /// Maximum name length before truncation.
    pub max_name_length: usize,
}

impl UiMapFormatter {
    /// Default formatter.
    pub fn new() -> Self {
        Self {
            max_elements: 100,
            include_bounds: true,
            include_states: true,
            interactive_only: false,
            max_name_length: 50,
        }
    }

    /// Compact formatter for token-constrained contexts.
    pub fn compact() -> Self {
        Self {
            max_elements: 40,
            include_bounds: true,
            include_states: true,
            interactive_only: true,
            max_name_length: 30,
        }
    }

    /// Format a UiMap to text.
    pub fn format(&self, ui_map: &UiMap) -> String {
        // Delegate to UiMap's format_compact for now — more modes can be added
        // based on formatter configuration
        if self.interactive_only && ui_map.elements.len() > self.max_elements {
            let mut output = String::new();
            let elements = ui_map.interactive_elements();
            for element in elements.iter().take(self.max_elements) {
                use std::fmt::Write;
                let role_str = element.role.to_hash_string();
                let name_str = element
                    .name
                    .as_deref()
                    .map(|n| {
                        if n.len() > self.max_name_length {
                            let truncated: String = n.chars().take(self.max_name_length - 3).collect();
                            format!(" \"{}...\"", truncated)
                        } else {
                            format!(" \"{}\"", n)
                        }
                    })
                    .unwrap_or_default();
                let _ = writeln!(output, "[{}] {}{}", element.id, role_str, name_str);
            }
            output
        } else {
            ui_map.format_compact()
        }
    }
}

impl Default for UiMapFormatter {
    fn default() -> Self {
        Self::new()
    }
}