aura-gui 0.3.5

UI nativa acelerada por GPU para escritorio, Android e iOS, en un solo crate: estilo tipo Tailwind, componentes, sombras, animaciones y agentes de IA. Reposo a cero. Licencia AGPL-3.0 o comercial.
//! Portapapeles del sistema (arboard) como servicio del `EntityMap`.

use crate::core::ClipboardProvider;

pub struct SystemClipboard {
    clipboard: Option<arboard::Clipboard>,
}

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

impl SystemClipboard {
    pub fn new() -> Self {
        let clipboard = arboard::Clipboard::new()
            .map_err(|error| crate::app::console::warn(format_args!("portapapeles no disponible: {error}")))
            .ok();
        Self { clipboard }
    }
}

impl ClipboardProvider for SystemClipboard {
    fn read(&mut self) -> Option<String> {
        self.clipboard.as_mut()?.get_text().ok()
    }

    fn read_image(&mut self) -> Result<Option<Vec<u8>>, String> {
        let Some(clipboard) = self.clipboard.as_mut() else { return Ok(None) };
        let image = match clipboard.get_image() {
            Ok(image) => image,
            Err(arboard::Error::ContentNotAvailable) => return Ok(None),
            Err(error) => return Err(error.to_string()),
        };
        let width = u32::try_from(image.width).map_err(|_| "Imagen demasiado ancha")?;
        let height = u32::try_from(image.height).map_err(|_| "Imagen demasiado alta")?;
        let length = image.width.checked_mul(image.height).and_then(|pixels| pixels.checked_mul(4));
        if width == 0 || height == 0 || length != Some(image.bytes.len()) || image.bytes.len() > 32 * 1024 * 1024 {
            return Err("La imagen supera el límite de 32 MB o tiene dimensiones inválidas".into());
        }
        let mut bytes = Vec::new();
        {
            let mut encoder = png::Encoder::new(&mut bytes, width, height);
            encoder.set_color(png::ColorType::Rgba);
            encoder.set_depth(png::BitDepth::Eight);
            let mut writer = encoder.write_header().map_err(|error| error.to_string())?;
            writer.write_image_data(&image.bytes).map_err(|error| error.to_string())?;
        }
        Ok(Some(bytes))
    }

    fn write(&mut self, text: String) -> Result<(), String> {
        match self.clipboard.as_mut() {
            Some(clipboard) => clipboard.set_text(text).map_err(|error| error.to_string()),
            None => Err("portapapeles no disponible".into()),
        }
    }
}