# llimphi-jaula La cáscara del lado **app**: corre el ciclo de Llimphi dentro de una jaula WASM y emite una [`EscenaWire`](../llimphi-wire-escena/LEEME.md) en vez de rasterizar. Es el gemelo de [`llimphi-wawa`](../llimphi-wawa/LEEME.md), del otro lado de la frontera: aquélla recibe y ejecuta, ésta compone y manda. W4 de [`03_ukupacha/wawa/PLAN-LLIMPHI.md`](../../../03_ukupacha/wawa/PLAN-LLIMPHI.md). ``` View → mount → layout → paint → EscenaWire → postcard → … …………………………… frontera WASM …………………………… … → shapear nativo → rasterizar ``` ## Lo que hay que entender de acá Las dos cáscaras corren el **mismo `llimphi-compositor`** — el mismo mount, el mismo layout de taffy, el mismo `render.rs` de 4.000 líneas. Lo único que cambia es el alias `Lienzo` y, con él, el `Typesetter`. Eso es el resultado de W4 que vale decir en voz alta: **no hay un compositor para apps y otro para el kernel.** Un widget escrito una vez sirve en Linux, adentro del kernel, y adentro de una jaula. ## Las dos cosas que ésta hace y la del kernel no ### Pregunta las medidas taffy no puede dimensionar una etiqueta sin saber cuánto mide, y medir es shapear — justo lo que no debe pasar adentro de la jaula: el 30,3x del intérprete se comería el 72% del fotograma (W4.1). Así que el medidor es un **argumento**: ```rust let mut jaula = Jaula::new(320.0, 72.0, |p: &Peticion| { // En wawa: un `sys_medir_texto` que cruza a parley del lado nativo. // En un test: un `Typesetter` de parley de verdad. medir_del_otro_lado(p) }); let montado = jaula.fotograma(vista()); let bytes = postcard::to_allocvec(jaula.escena())?; ``` Sin medidor no se devuelve cero — un cero silencioso colapsaría el layout entero y la pantalla saldría vacía «funcionando». Se estima grosero **y se cuenta**: `jaula.medidas_estimadas()` distinto de cero significa que ese layout es aproximado. En wawa, ese medidor ya viene hecho — es [`medidor::wawa`](src/medidor.rs), que envuelve el syscall `sys_medir_texto`: ```rust let mut jaula = Jaula::new(ancho, alto, llimphi_jaula::medidor::wawa); ``` `sys_medir_texto` **no está gateado por permiso**: medir texto no es un privilegio, es aritmética sobre una fuente, y toda app que dibuje la necesita. Lo que sí lleva es cuota, porque el shaping lo paga el kernel fuera del `fuel` de la app. Fuera de wasm el medidor devuelve `None` en vez de un número inventado. ### Se queda con el árbol El `Mounted` no cruza la frontera, así que el hit-test se resuelve de este lado: el kernel manda una coordenada y la app contesta qué se tocó. ```rust if let Some(msg) = jaula.click(&montado, x, y) { modelo = App::update(modelo, msg); } ``` Es lo contrario de la cáscara del kernel, que **no puede** hacer hit-test justamente porque el árbol se quedó acá. ## Por qué está fuera del workspace Enciende `llimphi-text/wire`, que es un backend excluyente de `vello` (el que usa todo el resto del repo por default). La unificación de features de cargo los juntaría y dispararía el `compile_error!` que `llimphi-text` tiene puesto para delatarlo — es la misma trampa que ya mordió con `llimphi-wawa`. Que las dos estén afuera no es una molestia: es el diseño. Son dos procesos en dos lados de una frontera. ```bash cargo test --manifest-path 02_ruway/llimphi/llimphi-jaula/Cargo.toml \ --no-default-features --features std cargo check --manifest-path 02_ruway/llimphi/llimphi-jaula/Cargo.toml \ --target wasm32-unknown-unknown --no-default-features --features libm ``` ## El fixture Como el encoder y el reproductor no pueden compilarse juntos, la costura del test es la misma que la de producción: **bytes**. ```bash cargo run --example emitir_fixture --no-default-features --features std ``` Compone una vista con el compositor de verdad y deja los bytes en `../llimphi-wawa/pruebas/fixture_jaula.postcard`, donde los levantan el test `la_escena_que_emitio_el_compositor_llega_a_pixeles` **y el arranque del kernel**. Si alguien cambia el formato del cable de un solo lado, esos dos lo dicen.