Teclado, paneles, navegación, física e iconos Web
Disponible desde 0.1.0-rc.10. Las issues #366–#371 añaden seis capacidades al runtime y a los ámbitos Web. Importe /web durante SSR y cree createWebRuntime(document) después de montar los elementos; obtenga un ámbito con motion.scope().
Geometría del teclado
scope.onKeyboard(callback) y scope.followKeyboard(composer, { offset: 12 }) devuelven funciones de limpieza con pause/play, dispose y snapshot(). El estado incluye visibilidad, rectángulo, cobertura inferior en píxeles CSS, backend y precisión. VirtualKeyboard proporciona la geometría declarada; VisualViewport utiliza una estimación cuando hay un campo editable enfocado, sin zoom, y una reducción superior a 80 px. Los teclados físicos o flotantes y las barras del navegador limitan esa estimación. Un teclado flotante puede estar visible con cobertura inferior cero. Sin geometría, la precisión es unavailable.
La duración es siempre cero: el navegador no ofrece la curva nativa. Jolt no cambia overlaysContent ni descuenta dos veces un viewport ya reducido. Use un destino alineado a los ejes, sin ancestros transformados; el seguimiento añade una capa de traslación propia. offset es una separación no negativa.
Bottom sheet
scope.bottomSheet(panel, { host, handle, snap: [160, '75%'], scrollView: content }) requiere un panel hijo directo de un host posicionado y recortado, y un handle separado dentro del panel. Los snaps aceptan píxeles positivos, porcentajes o 'content' con un elemento medido aparte. El coordinador gestiona la transferencia entre arrastre y desplazamiento anidado, conservando la entrada de formularios. La aplicación controla la semántica modal y el bloqueo del fondo.
Use open, snapTo, expand, collapse, dismiss y remeasure. safeArea recibe píxeles CSS; keyboard: 'none' desactiva el seguimiento. El backdrop es un hermano del panel. onChange informa el destino y onDismiss se ejecuta una vez al terminar el cierre. dismissible: false impide el cierre del usuario. El handle ofrece semántica slider y flechas/Home/End/Escape; proporcione label localizado y, si hace falta, returnFocus.
Modal interactivo
scope.interactiveModal(dialog, { handle }) acepta un HTMLDialogElement montado e inicialmente cerrado. open() usa la capa modal, la inercia del fondo y el foco del navegador. canBegin puede vetar el arrastre; un arrastre corto o cancel() vuelve a la posición abierta. close(returnValue), Escape y el botón cierran. state/onState distinguen apertura, arrastre y cierre.
Se admiten dialog.close() externo y formularios method="dialog". Pausar congela el movimiento. Descartar deja un diálogo abierto bajo responsabilidad de la aplicación, que debe cerrarlo o eliminarlo al desmontar. No implementa delegates iOS ni predictive back Android.
Navegación de la aplicación
scope.sharedNavigation(routeHost) ofrece attach(card, 'cover') y navigate({ tag: 'cover', async navigate({ animated }) { ... } }). Mantenga el contenedor entre rutas. El callback se invoca una vez con animated: false; adjunte el destino antes de resolverlo. El router conserva historia, datos y control de solicitudes antiguas.
Una operación concurrente devuelve unavailable/busy sin ejecutar el callback. finished informa estado, motivo, navigated y error opcional; navigated significa callback invocado, no éxito confirmado. Cancelar elimina el efecto sin revertir la ruta. Rechazos devuelven failed; contenido no compatible y movimiento reducido mantienen la navegación sin vuelo.
navigationTimeout vale 3000 ms, máximo 10000. La ventana visual suma ese límite y layoutTimeout (500 por defecto), con máximo 10000 ms; excluye tiempo oculto. Se aplican los límites de copia del shared layout: HTML estático, sin formularios, vídeo, canvas, custom elements o SVG. No es una captura nativa ni un gesto de retorno interactivo.
Física circular
scope.physicsWorld(arena, [{ id: 'ball', view: ball, radius: 24, x: 80, y: 80 }]) acepta 1–16 hijos directos distintos. Radio 12–256 px CSS, masa 0,1–100, velocidad máxima 1200 px/s y área total hasta 40% del host. Cada wrapper mide el doble del radio; host y ancestros no deben transformarse. Los cuerpos iniciales deben caber sin superposición. Gravedad: componentes ±2000; restitución: 0–1; amortiguación: 0–20.
El motor TypeScript se compara con C/iOS y Java/Android en 52 casos. Integra a 240 Hz con hasta 24 pasos de recuperación por frame; snapshot().droppedMs informa el exceso. Web ejecuta la física en la thread principal. Admite arrastre directo o por resorte, attachment fijo o 1–8 snaps, y hasta 32 resortes entre cuerpos, cuatro por cuerpo. Los endpoints de resortes no admiten snaps; attachment y snaps son excluyentes.
impulse, attach, spring, snapTo, reset, pause/play y snapshot controlan el mundo. Las líneas son decorativas. El arrastre por resorte conserva el momento integrado al soltar. Redimensionar escala posiciones y borra velocidades; límites inválidos pausan hasta recuperarse. Movimiento reducido pausa salvo 'continue' explícito; el reposo elimina solicitudes de frames. Descartar libera guías, entrada, bindings y posicionamiento controlado.
Iconos propios
scope.symbolEffect(iconWrapper, 'bounce', { speed: 1 }) anima un wrapper HTML dedicado. Bounce, pulse y variableColor pueden repetirse; scale mantiene el tamaño ampliado hasta parar. variableColor usa ondas de opacidad en 1–32 capas [data-jolt-symbol-layer]; no carga SF Symbols.
{ replace: detachedIconElement } conserva el nuevo HTML/SVG decorativo tras cancelar o descartar. Actualice el nombre accesible y el estado de la aplicación. Los bucles y scale tienen duración infinita y no admiten seek. Los efectos finitos admiten progreso, pausa y resultado booleano. Movimiento reducido omite decoración (false), pero aplica reemplazo (true).
Ciclo de vida y validación
Todas las factories están en runtime y ámbitos, incluidos los de Vue. Suspensión/reanudación controla recursos; descarte y desconexión los liberan. El compositor puede conservar transform/opacity neutros serializados sin eliminar otras capas. Ejecute npm run web:platforms:typecheck, npm run web:platforms:test y python3 benchmarks/verify-circle-math.py. Las pruebas verifican Chromium, Firefox y WebKit. Los rectángulos de teclado inyectados pertenecen sólo a pruebas; los vídeos nunca fabrican un teclado móvil. Grabe con node benchmarks/web-platforms/record.mjs. La adopción en Mavis se realiza por separado.