@dotrino/install
v0.3.3
Published
Botón de "Instalar app" (PWA) unificado del ecosistema Dotrino: Web Component <dotrino-install> que captura beforeinstallprompt temprano, maneja iOS con un modal de instrucciones (sin alert) y se oculta si ya está instalada. Vue o vanilla. Sin JS de terce
Maintainers
Readme
@dotrino/install
Parte del ecosistema Dotrino. Dotrino es un ecosistema de aplicaciones centradas en la privacidad de los datos: tu información es tuya, y las decisiones sobre ella también — qué compartes, con quién, cuándo y por qué. Sin anuncios, sin cookies, sin rastreo de datos, sin vender tu identidad a nadie.
Botón de "Instalar app" (PWA) unificado para todo el ecosistema Dotrino.
Este repo tiene DOS cosas, y conviene no confundirlas:
- el paquete npm
@dotrino/install(src/) — el botón de instalar la PWA;- el instalador universal del ecosistema (
web/), servido en install.dotrino.com — elcurl … | shque pone a andar cualquier herramienta de Dotrino (el vault, el agente de la terminal, el túnel).Las dos son «instalar», por eso viven juntas: una instala una app en tu teléfono y la otra una herramienta en tu computadora. Estuvo en
dotrino.com/install.shhasta el 2026-07-27; se movió a su propio subdominio para que el dominio del ecosistema no sea también un servidor de scripts.
Resuelve la fragmentación de tener el mismo flujo beforeinstallprompt copiado a
mano en cada app (Vue y vanilla), donde cada copia divergió y arrastra los mismos
bugs sutiles. Un solo Web Component, testeado, igual en todas las apps.
Sin JS de terceros, sin cookies, autohosteado (Shadow DOM). Bilingüe es/en.
Por qué un paquete y no copiar el snippet
El botón es trivial; lo que no lo es —y por eso se centraliza— son tres detalles que casi todas las copias hacían mal:
beforeinstallpromptse dispara muy pronto, a veces antes de montar el componente. Si lo escuchás enonMountedlo perdés y el botón nunca aparece. Aquí se captura a nivel de módulo, en elimport.- iOS/Safari no soporta
beforeinstallpromptni API de instalación: la única vía es Compartir → Añadir a pantalla de inicio. Sin esto, en iPhone la app simplemente no se puede instalar. Lo resolvemos con un modal de instrucciones propio (noalert(), prohibido en el ecosistema). - No reaparecer cuando la app ya corre instalada (
display-mode: standalone) ni trasappinstalled. — ni dentro de la app nativa de Dotrino (iOS o Android), que se anuncia conwindow.DotrinoNative: ahí la página ya es la app y no hay nada que instalar (≥ 0.3.3).
Uso — Web Component (recomendado)
// Vue: importa el paquete una vez (p. ej. en main.js) y usa el tag.
import '@dotrino/install'<header class="topbar">
<dotrino-install></dotrino-install>
</header><!-- vanilla -->
<script type="module" src=".../@dotrino/install/src/index.js"></script>
<dotrino-install lang="es"></dotrino-install>El elemento se oculta solo (no ocupa espacio) cuando no hay forma de instalar o la app ya está instalada. En Chromium muestra el botón cuando llega el prompt y lo dispara al hacer click. En iOS muestra el botón siempre (hasta que se instale) y al hacer click abre el modal con las instrucciones de Compartir.
Atributos
| Atributo | Valores | Default |
|---|---|---|
| lang | es | en | <html lang> / navegador |
| label | texto del botón | Instalar / Install |
| icon | false para ocultar el icono | icono visible |
| android-apk | URL del APK (release) o de Play de la TWA | — |
| android-package | id del paquete (com.dotrino.<app>) | — |
| android-label | texto del botón en modo Android | Instalar app Android |
Preferir la TWA en Android
Si pasás android-apk, en Android el botón prefiere la app nativa (TWA) sobre
el PWA: al tocarlo descarga/abre ese APK (o la ficha de Play). Con android-package +
related_applications en tu manifest, usa getInstalledRelatedApps() para ocultarse
si la app ya está instalada. En desktop/iOS se comporta como siempre (prompt PWA /
instrucciones). Ver la receta para generar la TWA en TWA.md del ecosistema.
<dotrino-install
android-apk="https://github.com/imdotrino/dotrino-wallet/releases/latest/download/wallet.apk"
android-package="com.dotrino.wallet">
</dotrino-install>Estilo (custom properties)
--cc-install-color, --cc-install-bg, --cc-install-bg-hover,
--cc-install-radius, --cc-install-pad, --cc-install-gap,
--cc-install-font-size, --cc-install-icon, --cc-install-focus,
--cc-install-accent (acento del modal), --cc-install-modal-bg,
--cc-install-modal-color.
Parts: button, icon, label, modal, modal-card.
Eventos
cc-install— cancelable, antes de actuar (preventDefault()para hacer lo tuyo).cc-install-result—detail.outcome:accepted|dismissed|instructions|installed.
Uso programático
Para apps que quieren su propio botón con la lógica compartida:
import { canInstall, promptInstall, onInstallStateChange, isIOS } from '@dotrino/install'
const unsub = onInstallStateChange(() => { miBoton.hidden = !canInstall() })
miBoton.onclick = async () => {
const outcome = await promptInstall() // 'accepted' | 'dismissed' | 'instructions' | 'installed'
if (outcome === 'instructions') mostrarMisInstrucciones() // iOS / navegador sin soporte
}API: isAppInstalled(), isIOS(), canInstall(), hasNativePrompt(),
promptInstall(), onInstallStateChange(fn) → unsub.
Composable Vue 3
import { useInstall } from '@dotrino/install/vue'
const { canInstall, isInstalled, install } = useInstall()<button v-if="canInstall" @click="install">Instalar</button>Para el botón ya hecho (con modal iOS incluido) usá el Web Component; el composable es solo para botones a medida.
El instalador universal (web/install.sh, web/install.ps1)
Servido en install.dotrino.com. Pone a andar cualquier pieza del ecosistema publicada en npm:
curl -fsSL https://install.dotrino.com/install.sh | sh -s -- @dotrino/inspector
# …y a partir de ahí, desde cualquier terminal:
dotrino-inspectorInstala de verdad (desde 2026-08-24; antes solo hacía npx y no dejaba comando, así
que había que repetir el curl entero cada vez):
npm install -gcon el prefijo en~/.dotrino/npm, sin root;- enlaces en
~/.dotrino/bin, que es lo único que se mete alPATH: así la línea del rc no cambia aunque cambie lo de dentro. Si hubo que bajar Node, sus enlaces van ahí también — si no, el#!/usr/bin/env nodedel comando se queda sin intérprete; - una línea idempotente en
~/.bashrc/~/.zshrc/~/.profilecon marcadores# >>> dotrino >>>, y se dice qué archivo se tocó. En Windows, elPATHdel usuario (sin administrador); - el nombre del comando sale del
bindelpackage.jsondel paquete: vale para cualquier pieza sin cablear nada; - actualizar es volver a correr el mismo comando;
- sin argumentos NO arranca la herramienta: instala, dice el comando y sale. Muchas
piezas se quedan corriendo (un servidor, un agente, una UI) y arrancarlas sin que nadie
lo pidiera deja la terminal parada, que se lee como un cuelgue. Con argumentos sí la
arranca, porque ahí se pidió una acción concreta (
… -- @dotrino/terminal-agent enroll); - la tty se le pasa a cada comando, nunca al shell.
exec < /dev/ttyparece inofensivo y no lo es: concurl | shel stdin del shell es el propio script, así que reemplazarlo tira lo que quedaba por leer y el shell se pone a leer del teclado — parado y mudo. Estuvo años sin morder porque el script era corto y esa línea caía casi al final; al crecer se comió medio instalador (2026-08-24). Si vuelves a tocar esto: se prueba bajo una terminal de verdad (script -qec "curl … | sh -s -- …" /dev/null), porque con la salida capturada[ -t 1 ]es falso y el fallo no aparece; - dice en qué paso está mientras lo hace, con barra de progreso al bajar Node y sin silenciar a npm. Un instalador no parece colgado por tardar: parece colgado por tardar sin decir en qué está.
Banderas (van antes del paquete):
| | |
|---|---|
| --run-once | no instala: corre el paquete una vez con npx, como antes |
| --no-path | no toca el rc; imprime la línea para pegarla |
| --ignore-scripts | no ejecuta los scripts de instalación de las dependencias |
Por qué los scripts de instalación van activados, al revés que en los repos del ecosistema (CONVENCIONES §1.1): allí se monta un árbol de dependencias para desarrollar; aquí se instala un programa para usarlo, y alguno trae binario nativo que se elige en su
postinstall(el PTY de@dotrino/terminal-agent). Con--ignore-scriptsquedaría instalado y roto, que es peor que no instalarlo. Quien quiera la versión estricta tiene la bandera.
Desinstalar: borrar ~/.dotrino y quitar el bloque # >>> dotrino >>> del rc.
Test
npm test # Playwright contra Chromium: prompt nativo, appinstalled y rama iOSLicencia
MIT
