Herramientas como apps instalables — cómo está hecho
Las herramientas de la Oficina se instalan como apps de escritorio sin dejar de ser web estática: son PWA (aplicaciones web progresivas). No hay Electron ni Tauri ni firma de Apple; el usuario abre la web y el navegador la instala. La visión de usuario está en instalar-las-herramientas-como-app. Este documento cuenta el cómo, de dentro afuera.
Repo: webs/oficina. Estado y decisiones vivas: docs/estado/apps-instalables-pwa.md de ese repo. Firma: Karb and Sil.
Qué hace instalable a una web
Tres piezas por herramienta:
- **
manifest.webmanifest** — declara nombre, iconos, color de marca (#8a5a2c) ydisplay: standalone(ventana propia). Es lo que el navegador lee para ofrecer «Instalar». - **
sw.js(service worker) — hace que arranque sin conexión**. Estrategia stale-while-revalidate en los ficheros propios: se sirven de la caché al instante y se refrescan en segundo plano, así un redepliegue se recoge en la siguiente apertura sin quedarse pegado. Lo de fuera (CDN de la casa, contador dejuntos) va a la red y, si no hay, se deja pasar sin romper. - Iconos (192, 512 y uno maskable) — se generan del glifo del favicon de cada herramienta (el mismo de
herramientas.wordkers.com), no del bocadillo genérico de Wordkers (que confundiría unas con otras en el Dock). Rasterizados del SVG conqlmanage -t -s 512; esquinas transparentes en elany(máscara redondeada con PIL) y a sangre en elmaskable.
En el <head> se enlaza el manifest, el theme-color, el apple-touch-icon y se registra el service worker.
Abrir ficheros (asociación de tipo)
Dos caminos:
- Arrastrar a la ventana: JavaScript puro (
dragover/drop), no necesita nada del sistema. Se vuelca el fichero en el editor (#src+ eventoinput) o, en apps con lógica propia, en su importador. - Doble clic / «abrir con»:
file_handlersen el manifest +launchQueueen la página (recibe el fichero que el sistema pasa al abrir la app). Es de Chromium; Safari no lo tiene.
Reparto de tipos (para que no se peleen varias apps por el mismo):
- md es la dueña de
.txt(además de.md/.markdown). - mapas:
.wkmapa(formato propio, JSON dentro). - html:
.html/.htm. json:.json. - El resto no registra
.txt(si lo hicieran, media docena de apps competirían por cualquier.txt). Se quedan instalables + arrastrar.
Dos topologías: subdominio vs ruta
- Español: cada herramienta en su subdominio (
md.wordkers.com,json.wordkers.com…). Cada subdominio es un origen aparte → PWA conscope: "/", se instala como app independiente sin más. - Inglés (
toolbox.wordkers.com/<herramienta>): todas en un dominio con rutas. Para que cada una se instale por separado se usan manifests con ámbito por ruta (scope/start_url/id=/<herramienta>/) y unsw.jsscopeado a esa ruta. Es el patrón estándar para sitios multi-app en un dominio; funciona en Chrome/Edge. Es un pelín menos redondo que un subdominio propio: si una inglesa se quisiera «de primera», se le daría su subdominio.
El botón «Instalar»
Cada herramienta lleva un botón flotante «⤓ Instalar app» (snippet autónomo, sin dependencias):
- En Chromium usa el evento
beforeinstallprompt(píldora abajo a la derecha, con una × que recuerda el descarte enlocalStorage). - Se oculta si la app ya está instalada (
display-mode: standalone) o tras instalar. - En Safari (sin ese evento) enlaza a la guía de instalación.
El formato .wkmapa y MindNode
mapas guarda su formato propio **.wkmapa** (es el mismo JSON que ya usaba, con extensión propia para poder asociarlo sin pisar al tool json). Sigue importando .json antiguos y mapas de MindNode. Los ficheros de MindNode son plist binario de Apple; la app trae su propio lector. Para convertir en bloque hay un script Node reutilizable, mapas/tools/mn2wkmapa.cjs, que reutiliza ese mismo lector. Al abrir un mapa, la vista se auto-centra (recenter(): repite render+fit tras requestAnimationFrame y fonts.ready, porque el fit inmediato corre antes de medir los nodos).
Reproducirlo / añadir una herramienta nueva
En webs/oficina/tools/ hay generadores idempotentes:
gen_pwa.py <raíz>— aplica manifest + sw + iconos + head + abrir-fichero a las herramientas españolas (una carpeta por subdominio).gen_pwa_en.py <toolbox>— igual pero con scope por ruta para las inglesas.add_install.py <raíz>— inyecta el botón «Instalar» (marcawk.install, no duplica).
md y mapas llevan lógica propia (multi-documento; mapa mental) y se editaron a mano con el mismo patrón. Los landings (herramientas y toolbox) se regeneran con su generar.cjs en cada deploy: los enlaces del landing van ahí, no en el index.html (que es un subproducto). Las tools sí son fuente directa.
Límites conocidos
file_handlers(doble clic) es solo Chromium; en Safari, instalar + arrastrar.- Firefox no instala PWA en Mac.
- Una app ya instalada guarda el icono del momento: para ver un icono nuevo hay que desinstalar y reinstalar.
- Antes de desplegar, el clon local debe estar en el último
main(si no, se sube código viejo).