[package] name = "llimphi-ui" version.workspace = true edition.workspace = true license.workspace = true authors.workspace = true publish.workspace = true description = "Native Rust UI framework: retained-mode View Elm loop over vello + wgpu + taffy + parley." repository.workspace = true [dependencies] # El trait de despacho. `Handle` lo implementa para que animar —y cualquier otra # cosa que sólo quiera mandar un `Msg`— deje de arrastrar el bucle de escritorio # entero. Ver `llimphi-despacho`. llimphi-despacho = { path = "../llimphi-despacho", version = "0.1.0" } llimphi-hal = { path = "../llimphi-hal", version = "0.1.0" } llimphi-layout = { path = "../llimphi-layout", version = "0.1.0" } llimphi-raster = { path = "../llimphi-raster", version = "0.1.0" } # `default-features = false` en los dos que conmutan de backend: sus defaults # encienden el perfil `vello`, que es EXCLUYENTE del `cpu` que pide la feature # `hybrid`. Las features del perfil normal se listan explícitas abajo. llimphi-text = { path = "../llimphi-text", version = "0.1.0", default-features = false } # El compositor declarativo (winit-free): View, mount, paint, hit-test. llimphi-compositor = { path = "../llimphi-compositor", version = "0.1.0", default-features = false } pollster = { workspace = true } # Árbol de accesibilidad por frame (NVDA/VoiceOver/Orca/TalkBack). Lo # alimentamos desde `View::semantics` + el árbol Mounted. `accesskit_winit` # es el adapter que conecta el árbol a la API nativa del SO vía winit. accesskit = { workspace = true } accesskit_winit = { workspace = true } # Reloj monotónico portable. En cualquier target NATIVO `web_time::Instant` es # literalmente un re-export de `std::time::Instant` — coste cero, mismo tipo, # mismo comportamiento. En wasm32 se apoya en `performance.now()`, donde # `std::time::Instant::now()` PANICA («time not implemented on this platform»). # Es además el tipo con el que winit tipa `ControlFlow::WaitUntil` en web, así # que unificarlo acá arregla el reloj y el borde con winit de una vez. # Reexportado como `llimphi_ui::Instant`; dentro del crate se usa `crate::Instant`. web-time = "1" # Portapapeles del sistema para copiar texto seleccionado fuera del editor # (Ctrl/Cmd+C). Best-effort: si no hay backend (headless) degrada a no-op sin # panicar. Feature `clipboard` (default) para que builds sin display puedan # apagarlo con --no-default-features. En Android y en wasm arboard no existe: # la dep va target-gated y `copy_to_clipboard` es no-op aunque la feature esté # activa (en el navegador el portapapeles es `navigator.clipboard`, asíncrono y # detrás de un gesto del usuario — otro camino, no arboard). [target.'cfg(all(not(target_os = "android"), not(target_arch = "wasm32")))'.dependencies] arboard = { workspace = true, optional = true } # Pista `wp_content_type_v1` (ver `src/content_type.rs`): winit no habla ese # protocolo, así que se monta una conexión wayland-client sobre el MISMO # `wl_display` que ya abrió winit (`Backend::from_foreign_display`) y se envuelve # su `wl_surface` por `ObjectId::from_ptr`. Es el cruce winit↔wayland-client # ajeno que validó el spike `llimphi-video-plane`. Sólo Linux: en macOS/Windows # no hay Wayland y el módulo cae a un no-op. [target.'cfg(not(target_arch = "wasm32"))'.dependencies] # Sólo con la feature `config-wawa` (ver [features]). Fuera de wasm: en el # navegador no hay bus de config del SO que leer. [target.'cfg(unix)'.dependencies] # El protocolo de dormir (ver `src/dormir.rs`): un socket Unix por el que mirada # le pide a la app que guarde su estado y se vaya. Sólo donde hay sockets Unix. llimphi-dormir = { workspace = true } [target.'cfg(target_os = "linux")'.dependencies] # `pipe2` + `poll`: el hilo del puente de soltar-archivos duerme sobre DOS fds # (el socket de wayland y un despertador) para que se lo pueda apagar antes de # que winit cierre el display. Ver `src/file_drop.rs`. libc = { workspace = true } wayland-client = "0.31" wayland-backend = { version = "0.3", features = ["client_system"] } # `staging`: content-type-v1 todavía vive en staging/ del repo de protocolos. wayland-protocols = { version = "0.32", features = ["client", "staging"] } [target.'cfg(target_os = "android")'.dependencies] # Logging a logcat: en Android eprintln! va a /dev/null; los errores del # runtime (event loop, GPU) salen por `log` (la app instala android_logger). log = "0.4" [target.'cfg(target_arch = "wasm32")'.dependencies] # El runtime web (`run_web`). `spawn_local` lanza el arranque asíncrono del # `Hal`; `web-sys` da el `` del documento y la consola (en wasm # `eprintln!` no va a ningún lado); `wasm-bindgen` da el `JsCast` para bajar de # `Element` a `HtmlCanvasElement`. wasm-bindgen = "0.2" wasm-bindgen-futures = "0.4" web-sys = { version = "0.3", features = [ "console", "Document", "Element", "HtmlCanvasElement", "Window", ] } [features] default = ["clipboard", "vello"] clipboard = ["dep:arboard"] # El perfil normal: rasterizado con vello y todo lo que cuelga de él (fuentes # del sistema, GPU directa). Es el default; `hybrid` es su alternativa. vello = ["llimphi-text/vello", "llimphi-text/system", "llimphi-compositor/vello", "llimphi-compositor/system", "llimphi-compositor/gpu"] # Rasteriza con vello_hybrid (CPU+GPU, SIN compute shaders) en vez de vello. # # **No es una preferencia de rendimiento: es el único camino a píxeles en GPUs # cuyo compilador de shaders no digiere el compute de vello.** El Mali-G57 del # Redmi de pruebas pierde el device en el primer cuadro por Vulkan y aborta por # GLES; con este perfil y `Backends::GL` pinta a 119 fps. La matriz completa # está en `02_ruway/llimphi/android/DIAGNOSTICO.md`. # # Enciende el perfil `cpu` de text y compositor —los que pintan en una `Escena` # grabada en vez de en una `vello::Scene`— y el backend hybrid del rasterizador. # Los tres tienen que ir juntos: son el mismo tipo de escena visto desde tres # crates, y `llimphi-text` lleva un `compile_error!` que delata la mezcla. # # Cuesta el hook de GPU directa (`View::gpu_paint_with`): vive en la feature # `gpu` del compositor, que implica el backend vello porque encima una pasada # de wgpu sobre la `vello::Scene`. Una app que lo use no puede ir por acá. hybrid = [ "llimphi-raster/hybrid", "llimphi-text/cpu", "llimphi-text/std", "llimphi-text/system", "llimphi-compositor/cpu", "llimphi-compositor/std", "llimphi-compositor/system", ] # Instala `alloc::ContadorAlloc` como `#[global_allocator]` del binario para # que `LLIMPHI_PERF` reporte asignaciones/bytes por frame (Bloque 21). OFF por # defecto: cuenta con dos `fetch_add` relajados por asignación, y un runtime no # le impone el allocator a nadie sin que lo pidan. perf-alloc = [] # Lee el **sentido de la rueda** (ratón / touchpad, por separado) del bus de # config del SO wawa y lo fija al arrancar, con watcher para el cambio en # caliente. OFF por defecto porque `llimphi-ui` es toolkit y `wawa-config` es el # bus de un sistema operativo concreto: el toolkit publicable no puede depender # de él. En tawasuyu la enciende el `[workspace.dependencies]` de la raíz, así # que TODAS las apps del monorepo obedecen la misma config sin tocar una línea. config-wawa = [] [[example]] name = "content_type" path = "examples/content_type.rs" [[example]] name = "counter" path = "examples/counter.rs" # El mismo counter, en el navegador. `cdylib` porque es lo que wasm-bindgen # necesita; en targets nativos el archivo entero está bajo # `#![cfg(target_arch = "wasm32")]`, así que queda una biblioteca vacía y # `cargo check --workspace --examples` lo cubre sin ruido. [[example]] name = "counter_web" path = "examples/counter_web.rs" crate-type = ["cdylib"] [[example]] name = "editor" path = "examples/editor.rs" [[example]] name = "gpu_paint_demo" path = "examples/gpu_paint_demo.rs" [[example]] name = "gestos" path = "examples/gestos.rs" # Instrumento del auto-repeat: `view()` duerme a propósito para que un toque de # tecla caiga entero dentro de un cuadro. Ver el encabezado del archivo. [[example]] name = "melaza_teclas" path = "examples/melaza_teclas.rs" [[example]] name = "selectable_text" path = "examples/selectable_text.rs" [dev-dependencies] # El consumidor de AccessKit — lo que corre del OTRO lado del adapter cuando un # lector de pantalla se activa. Está acá sólo para el test: es el único oráculo # que dice si el árbol que publicamos se puede publicar de verdad. Sin él, un # `TreeUpdate` inválido no se descubre hasta que alguien enciende Orca. accesskit_consumer = "0.36"