aura-gui 0.3.0

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.
//! Todo lo que una aplicación de Aura necesita para tener una ventana, eficiente por defecto:
//!
//! - **Reposo a cero**: el bucle duerme (`Wait`) y solo se despierta por entrada, por un hilo de trabajo
//!   (inbox), por un temporizador (`WaitUntil` hasta el más próximo) o mientras haya una animación.
//! - **Sin pintar lo que no se ve**: tapada, minimizada o con tamaño cero, la ventana no dibuja y suelta las
//!   cachés de glifos y de GPU; al volver se redimensiona y repinta.
//! - **GPU de bajo consumo**: adaptador `LowPower`, sincronía vertical y un solo frame en cola (`aura_gpu`).
//! - **Sin GPU también**: si no hay una utilizable (equipos viejos, sin controladores, emulada por software) pinta la
//!   CPU con la misma escena; `AURA_RENDERER=cpu` lo fuerza.
//! - **Memoria devuelta**: tras un rato ocioso, las páginas libres del asignador vuelven al sistema (macOS).
//! - **Accesibilidad sin coste**: el árbol de AccessKit solo se construye mientras haya un lector de pantalla.
//! - **Pruebas visuales**: `AURA_SNAPSHOT=salida.png` guarda un frame, con teclas y clics simulados.
//!
//! Lo mínimo es [`run`] con una vista raíz; un programa con varias ventanas o reglas propias (el editor) usa
//! [`Surface`] y las piezas sueltas.

#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub mod clipboard;
mod closure;
pub mod console;
pub mod input;
#[cfg(target_os = "ios")]
mod ios_scene;
pub mod memory;
mod options;
mod renderer_choice;
mod runner;
pub mod safe_area;
pub mod snapshot;
mod surface;
pub mod touch;
pub mod visibility;
pub mod wake;

#[cfg(target_os = "android")]
pub use runner::AndroidApp;
pub use runner::{AppOptions, run};
pub use surface::{Surface, SurfaceOptions};
pub use wake::{ProxyWaker, Wake};

/// Punto de entrada de una app escrita como closure: `run_app("Contador", |ui| ...)`.
///
/// La closure se vuelve a llamar en cada frame y lee su estado con `ui.state("clave", valor)`. No hay `impl`,
/// ni `AppOptions`, ni `set_root` en el código de la app.
///
/// ```no_run
/// use aura_ui::prelude::*;
///
/// fn main() {
///     run_app("Contador", |ui| {
///         let clics = ui.state("clics", 0u32);
///         ui.page().center().gap_4()
///             .child(ui.card().child(format!("Clics: {}", clics.get())))
///             .child(ui.button("Sumar").on_click(clics.update(|n| *n += 1)))
///     });
/// }
/// ```
pub fn run_app<F, R>(title: impl Into<String>, build: F)
where
    F: FnMut(&mut crate::view::ui::Ui) -> R + 'static,
    R: crate::view::element::IntoElement + 'static,
{
    if let Err(error) = AppOptions::new(title).run_ui(build) {
        eprintln!("{error}");
        std::process::exit(1);
    }
}