Crear APK gratis

API del puente de JavaScript

Inicio, atajos y el botón Atrás

Cómo se abrió la app, atajos de pulsación larga en el icono, el aviso de valoración dentro de la app y cómo decirle al shell que la página quiere el botón Atrás.

AppMint.requestReview ayudante#

AppMint.requestReview(options)

Ejemplo

Pide a Google Play que muestre su propia hoja de valoración dentro de la app (Play In-App Review) y te dice lo que ocurrió.

Devuelve: una Promise que siempre se resuelve {result, openedStore, message?}. result es 'shown' (Play mostró su hoja; nunca indica si el usuario valoró), 'not_from_play' (esta instalación no vino de Google Play: un APK instalado manualmente o una instalación de prueba, donde Play nunca muestra la hoja), 'quota' (Play decidió no mostrarla ahora: su cuota para este usuario, o ya dejó una reseña) o 'error' (no se pudo consultar a Play: no hay Play Store, sin conexión). Con {openStore: true} se abre la ficha de la app en Play cuando la hoja no se mostró, y openedStore es true. Requiere: nada que activar.

Cualquier código:

// A "Rate us" button: the sheet when Play shows it, the Play listing otherwise.
document.getElementById('rate-btn').onclick = async function () {
  if (!(window.AppMint && AppMint.requestReview)) { location.href = 'https://play.google.com/store/apps/details?id=com.example.app'; return; }
  var r = await AppMint.requestReview({ openStore: true });
  if (r.result === 'shown' || r.openedStore) showToast('Thank you!');
};

Pregunta tras un momento de éxito en lugar de desde un botón, sin salir nunca de la app:

async function onOrderDelivered(count) {
  if (count !== 3 || !(window.AppMint && AppMint.requestReview)) return;
  var r = await AppMint.requestReview();      // no openStore: nothing opens if Play says no
  if (r.result === 'not_from_play') console.log('Test install: Play only shows the sheet for Play installs');
}

Cuando el creador activó Pedir a los usuarios que valoren la app (paso Pantalla), la app pregunta por sí misma en un momento tranquilo y le dice a la página lo que ocurrió:

window.addEventListener('appmint:review', function (e) {
  // e.detail = { result, openedStore: false, source: 'auto' }
  analytics.track('rate_prompt', { result: e.detail.result });
});

Notas: Google Play limita la frecuencia de la hoja y decide por sí mismo si aparece; nunca recompenses una reseña ni prometas la hoja en un botón. Pruébalo con una instalación desde la pista de pruebas internas de Play: un APK instalado manualmente siempre responde not_from_play.

Sin código: el enlace appmint://review. Un botón o enlace con esta dirección pide la misma hoja (y abre la ficha de Play cuando Play no la muestra, porque el usuario pidió valorar). En un mensaje dentro de la app, ponlo en un botón del centro de envíos (la plantilla Valóranos lo hace); en una página, úsalo como un enlace normal:

<a href="appmint://review" class="rate-link">Rate us on Google Play</a>

Desde un script, por ejemplo tras el tercer pedido terminado:

function maybeAskForRating(ordersDone) {
  if (ordersDone === 3) location.href = 'appmint://review';   // the app runs it; nothing is loaded
}

Entérate de lo que eligió el usuario en un mensaje dentro de la app de "Valóranos" antes de que se abra la hoja:

window.addEventListener('appmint:inapp', function (e) {
  var d = e.detail;
  if (d.type === 'answered' && d.question === 'stars' && d.value <= 3) showFeedbackForm();
  if (d.type === 'clicked' && d.link === 'appmint://review') console.log('Opening the Play review sheet');
});

AppMint.setBackHandler ayudante#

AppMint.setBackHandler(handler)

Ejemplo

Da a tu página la primera palabra cuando el usuario pulsa el botón Atrás del teléfono (o usa el gesto de volver). Lo necesitan las apps que cambian de pantalla mostrando y ocultando elementos, donde el historial de Android está vacío y Atrás saldría de la app.

Devuelve: nada. Tu handler debe devolver true (gestionado, quédate aquí) o false (deja que la app retroceda / salga). Necesita: nada. Se instala al inicio del documento, así que puedes registrarlo en tu primer script.

if (window.AppMint && AppMint.setBackHandler) {
  AppMint.setBackHandler(function () {
    if (isMenuOpen)               { closeMenu();  return true; }   // consumed
    if (currentScreen !== 'home') { goTo('home'); return true; }
    return false;   // nothing to undo: normal Back (history back, exit dialog, exit)
  });
}

// Remove it again, for example when the screen that needed it closes:
// AppMint.setBackHandler(null);

El evento appmint:back - cancelable. Llama a e.preventDefault() para consumir la pulsación. Se ejecuta antes del gestor, y cualquier número de listeners puede usarlo:

window.addEventListener('appmint:back', function (e) {
  var sheet = document.querySelector('.bottom-sheet.open');
  if (sheet) {
    sheet.classList.remove('open');
    e.preventDefault();   // consumed: the handler and the shell do nothing more
  }
});

Evita que se pierda una nota a medio escribir: Atrás muestra tu propia barra de "¿Descartar cambios?" en lugar de salir (un confirm() bloqueante perdería la ventana de respuesta de 350 ms, así que el shell retrocedería igualmente):

if (window.AppMint && AppMint.setBackHandler) {
  var noteDirty = false;
  document.getElementById('note').addEventListener('input', function () { noteDirty = true; });

  AppMint.setBackHandler(function () {
    if (!noteDirty) return false;        // nothing to lose: normal Back
    showDiscardBar();                    // your in-page "Discard changes? / Keep editing" bar
    return true;                         // stay here while the user decides
  });

  document.getElementById('discard-btn').onclick = function () {
    noteDirty = false;                   // the next Back press passes straight through
    hideDiscardBar();
    closeEditor();
  };
}

Notas: La respuesta debe ser síncrona: devolver una Promesa cuenta como "no gestionado". Responde rápido: si la página no responde en unos 350 ms, el shell retrocede por sí mismo. Orden en cada pulsación: primero se cierra un menú lateral abierto (el panel propio de la app) y no se pregunta a la página; luego los listeners de appmint:back → tu gestor → el cierre de diálogos integrado del shell (cierra un <dialog> abierto, un diálogo ARIA o un modal a pantalla completa) → atrás en el historial / confirmación de salida / salir. Las apps que usan history.pushState para cada pantalla (React Router y similares) no necesitan esto. Las apps creadas con IA pueden usar onBackGesture desde @/lib/appmintNative.

__pullRefreshEligible puente#

window.WebToApk.__pullRefreshEligible(eligible: Boolean)

Consulta [pullPageEligible] / [pullProbeJs]. Se llama en cada touchstart.

Ejemplo

Tubería interna detrás de pull-to-refresh: la sonda táctil propia del shell la llama en cada toque para decir si un arrastre hacia abajo ahí debe actualizar la página. Las páginas nunca la llaman: controlan pull-to-refresh con CSS estándar y eventos táctiles.

Devuelve: nada. Necesita: Activar Pull-to-Refresh activado en el Paso 3 (Integrar), en Opciones de pantalla, al compilar (sin ello la sonda no se instala).

La forma pública de evitar que un gesto de arrastre actualice una parte de tu página: reclama el gesto:

<style>
  /* A map, drawing canvas or slider: this element owns vertical drags. */
  #map, #canvas { touch-action: none; }
  /* A horizontal carousel: pan-x also tells the shell "not a refresh". */
  .carousel { touch-action: pan-x; }
</style>

O reclámalo desde un gestor de toque, la forma estándar:

document.getElementById('canvas').addEventListener('touchstart', function (e) {
  e.preventDefault();   // the page took this gesture: no pull-to-refresh
}, { passive: false });

Evita que una superficie de desplazamiento y zoom actualice la página reclamando los arrastres verticales solo con CSS:

<div id="board" style="touch-action: none; height: 60vh"></div>
<script>
  // Dragging on #board moves the board; a pull from the top of the rest of the page still refreshes.
  document.getElementById('board').addEventListener('pointermove', function (e) { moveBoard(e); });
</script>

Notas: Un arrastre solo actualiza cuando la caja desplazable más cercana bajo el dedo (o la propia página) está en la parte superior, solo hay un dedo en la pantalla y no se aplica ninguna de las reclamaciones anteriores. Un toque dentro de un iframe de otro origen nunca actualiza. No llames tú mismo a este método.

getAppIcon puente#

window.WebToApk.getAppIcon(): String

El icono del launcher en uso: "default" o uno de los nombres de getAvailableIcons().

Ejemplo

El icono del launcher en uso: 'default' o uno de los nombres de getAvailableIcons().

Devuelve: una cadena. 'default' en una compilación sin iconos alternativos.

if (window.WebToApk && WebToApk.getAppIcon) {
  const current = WebToApk.getAppIcon();          // 'default', 'Dark', …
  document.querySelectorAll('[data-icon]').forEach(el => {
    el.classList.toggle('selected', el.dataset.icon === current);
  });
}

Cambia al siguiente icono desde una pantalla de ajustes:

function nextIcon() {
  if (!(window.WebToApk && WebToApk.getAvailableIcons && WebToApk.setAppIcon)) return;
  const names = JSON.parse(WebToApk.getAvailableIcons());
  if (names.length < 2) return;                                  // no alternate icons in this build
  const next = names[(names.indexOf(WebToApk.getAppIcon()) + 1) % names.length];
  if (!WebToApk.setAppIcon(next)) alert('Could not change the icon.');
}

Muestra el nombre del icono en una etiqueta, u ocúltalo fuera de la app:

const label = document.getElementById('icon-label');
if (window.WebToApk && WebToApk.getAppIcon) {
  const name = WebToApk.getAppIcon();
  label.textContent = name === 'default' ? 'Classic icon' : name + ' icon';
} else {
  label.hidden = true;           // in a browser there is no launcher icon
}

getAvailableIcons puente#

window.WebToApk.getAvailableIcons(): String

Arreglo JSON de los nombres de icono que setAppIcon acepta: primero "default", luego los alternativos de la compilación.

Ejemplo

Los nombres de icono que acepta setAppIcon, como una cadena de arreglo JSON: primero 'default', luego los iconos alternativos del asistente de compilación en el orden en que los añadiste.

Devuelve: '["default","Dark","Gold"]', o '["default"]' en una compilación sin iconos alternativos.

Crea un selector de iconos:

function iconPicker(container) {
  if (!(window.WebToApk && WebToApk.getAvailableIcons)) return;
  const names = JSON.parse(WebToApk.getAvailableIcons());
  if (names.length < 2) { container.hidden = true; return; }   // nothing to choose
  const current = WebToApk.getAppIcon();
  container.innerHTML = '';
  names.forEach(name => {
    const b = document.createElement('button');
    b.textContent = name === 'default' ? 'Classic' : name;
    b.className = name === current ? 'selected' : '';
    b.onclick = () => { if (WebToApk.setAppIcon(name)) iconPicker(container); };
    container.appendChild(b);
  });
}
iconPicker(document.querySelector('#iconPicker'));

Oculta el selector en una compilación sin iconos alternativos:

const picker = document.getElementById('iconPicker');
let names = ['default'];
if (window.WebToApk && WebToApk.getAvailableIcons) names = JSON.parse(WebToApk.getAvailableIcons());
picker.hidden = names.length < 2;      // only 'default' means there is nothing to choose

Cambia el icono con el tema:

function matchIconToTheme(dark) {
  if (!(window.WebToApk && WebToApk.getAvailableIcons && WebToApk.setAppIcon)) return;
  const names = JSON.parse(WebToApk.getAvailableIcons());
  const wanted = dark ? 'Dark' : 'default';
  if (names.includes(wanted) && WebToApk.getAppIcon() !== wanted) WebToApk.setAppIcon(wanted);
}
matchIconToTheme(matchMedia('(prefers-color-scheme: dark)').matches);

getLaunchUrl puente#

window.WebToApk.getLaunchUrl(): String

El enlace profundo que lanzó o reanudó esta app, o "" si se abrió normalmente. Léelo cuando tu app se monte y enruta tú mismo según él: const u = window.WebToApk?.getLaunchUrl?.(); if (u) router.navigate(new URL(u).pathname); El valor se CONSERVA, no se consume, así que una app que se monta tarde aún lo ve; un enlace posterior que llegue mientras la app se ejecuta lo reemplaza y también dispara 'appmint:deep-link'.

Ejemplo

El enlace profundo que abrió (o reabrió) la app, como cadena, o "" cuando la app se abrió normalmente.

Devuelve: una cadena, de forma síncrona. Necesita: nada. El valor se conserva, no se consume, así que es seguro leerlo cuando tu app se monte.

Un enlace puede llegar de tres formas: https://yoursite.com/path, el appmint-shortcut://<package>/<route> propio del shell (atajos del launcher, el widget de la pantalla de inicio) o tu esquema personalizado myapp://segment/path. Este ayudante convierte las tres en una ruta:

function routeOf(link) {
  var m = /^([a-z][a-z0-9+.-]*):\/\/([^\/?#]*)([^#]*)/i.exec(link || '');
  if (!m) return '/';
  var scheme = m[1].toLowerCase(), host = m[2], rest = m[3] || '';
  if (scheme === 'http' || scheme === 'https' || scheme === 'appmint-shortcut') return rest || '/';
  return '/' + host + rest;               // myapp://product/42 -> /product/42
}

// 1. On start: the link that launched the app.
if (window.WebToApk && typeof window.WebToApk.getLaunchUrl === 'function') {
  var link = window.WebToApk.getLaunchUrl();
  if (link) goTo(routeOf(link));
}

// 2. While running: a new link replaces the old one and fires 'appmint:deep-link'.
window.addEventListener('appmint:deep-link', function (e) {
  goTo(routeOf(e.detail.url));            // detail: { url }
});

El mismo anuncio también se pasa a un hook global, si prefieres uno:

window.onAppMintDeepLink = function (detail) { goTo(routeOf(detail.url)); };

Lee un código de referido de un enlace como https://yoursite.com/invite?code=AB12 o myapp://invite?code=AB12, una vez por enlace (usa routeOf de arriba):

function applyInvite(link) {
  if (!link) return;
  var done = '';
  try { done = sessionStorage.getItem('handledLink') || ''; } catch (e) {}
  if (link === done) return;                      // the same url is announced again after a reload
  try { sessionStorage.setItem('handledLink', link); } catch (e) {}
  var code = null;
  try { code = new URL(link).searchParams.get('code'); } catch (e) { return; }
  if (code && routeOf(link).indexOf('/invite') === 0) showInviteBanner(code);
}

if (window.WebToApk && typeof window.WebToApk.getLaunchUrl === 'function') {
  applyInvite(window.WebToApk.getLaunchUrl());
  window.addEventListener('appmint:deep-link', function (e) { applyInvite(e.detail.url); });
}

Notas: appmint:deep-link también se dispara después de cada carga de página mientras se conserva un enlace, así que el mismo url puede llegar más de una vez. Ignora un enlace que ya procesaste.

Las apps en modo Sitio web son distintas: el propio shell carga un enlace https:// entrante (en un arranque en frío y mientras se ejecuta), y un enlace de esquema personalizado myapp://product/42 se reescribe primero sobre la dirección de tu sitio (https://yoursite.com/product/42), así que getLaunchUrl() devuelve la forma https:// y tu página normalmente no tiene nada que enrutar. Un atajo del launcher o un toque en un widget abre su ruta en tu sitio (/today carga https://yoursite.com/today), y getLaunchUrl() sigue devolviendo la forma appmint-shortcut:// (consulta setAppShortcuts). En apps HTML, ZIP y creadas con IA (servidas desde dentro de la app) el shell nunca navega: tu página recibe el enlace y enruta según él. Un enlace de esquema personalizado se queda tal como llegó, porque no hay dirección de sitio a la que asignarlo. Las apps creadas con IA pueden usar launchUrl() desde @/lib/appmintNative.

rateApp puente#

window.WebToApk.rateApp(): String

Abre el flujo de valoración de la ficha de Play. `rateApp()` ya existía pero solo la entrada del panel nativo podía alcanzarlo, así que una app que dibuja su propia interfaz (todas las compilaciones con IA, donde el panel está desactivado) no tenía forma de pedir una valoración. Es la única acción de tienda que una página realmente no puede hacer por sí misma: `market://` es un intent, no una URL navegable. Depende del interruptor "Botón Valorar la app" del propio creador, igual que la entrada del panel y el elemento Compartir de la navegación inferior: desactivar Valorar debe significar ninguna superficie de valoración en ningún sitio, o el interruptor parece roto.

Ejemplo

Abre la página de la app en Google Play para que el usuario pueda valorarla.

Devuelve: 'opened' cuando se abrió la ficha, 'off' cuando el creador desactivó Valorar la app (entonces no pasa nada). Necesita: el interruptor Botón Valorar la app en el Paso 5 (Menú) al compilar (está activado por defecto; también añade un elemento "Valorar la app" al menú lateral). Con él desactivado, la llamada no hace nada y devuelve 'off'. Para la hoja de valoración dentro de la app de Google Play usa AppMint.requestReview().

var rateBtn = document.getElementById('rate-btn');
if (window.WebToApk && typeof window.WebToApk.rateApp === 'function') {
  rateBtn.onclick = function () { window.WebToApk.rateApp(); };
} else {
  rateBtn.hidden = true;   // in a browser
}

Ofrece la valoración una vez, después de que el usuario termine cinco entrenamientos, desde una tarjeta que toque:

function maybeOfferRating(completedCount) {
  if (!window.WebToApk || typeof window.WebToApk.rateApp !== 'function') return;
  var asked = false;
  try { asked = localStorage.getItem('rateAsked') === '1'; } catch (e) {}
  if (asked || completedCount < 5) return;

  var card = document.getElementById('rate-card');   // "Enjoying the app? Rate it on Google Play"
  card.hidden = false;
  card.querySelector('.rate-yes').onclick = function () {
    window.WebToApk.rateApp();                         // opens the Play listing
    try { localStorage.setItem('rateAsked', '1'); } catch (e) {}
    card.hidden = true;
  };
  card.querySelector('.rate-later').onclick = function () { card.hidden = true; };
}

Una fila "Valóranos" en tu pantalla de ajustes que da las gracias cuando el usuario vuelve de Google Play (el regreso de la página a primer plano es la única señal de que el usuario volvió):

document.getElementById('settings-rate').onclick = function () {
  if (!window.WebToApk || typeof window.WebToApk.rateApp !== 'function') return;
  window.WebToApk.rateApp();
  document.addEventListener('visibilitychange', function onBack() {
    if (document.visibilityState !== 'visible') return;
    document.removeEventListener('visibilitychange', onBack);
    showToast('Thanks for taking a look!');           // your own toast; the user may not have rated
  });
};

Notas: Abre la ficha de Play Store (market://details?id=<package>), o el sitio web de Play cuando falta la app de Play Store. No es la tarjeta de reseña dentro de la app. Una página no puede abrir market:// por sí misma: es un intent de Android, no una dirección web. Pide la reseña solo después de que el usuario haya hecho algo que le gustó, y desde un toque.

requestReview puente#

window.WebToApk.requestReview(optionsJson: String, callbackId: String)

Pide a Play su hoja de reseñas; [optionsJson] `{openStore?}`. Resuelve AppMint.requestReview [callbackId].

Ejemplo

Tubería interna detrás de AppMint.requestReview(): las páginas usan ese ayudante con Promesa, que pide a Google Play su hoja de reseña dentro de la app y resuelve con lo que ocurrió.

Devuelve: nada directamente; el resultado resuelve la promesa de AppMint.requestReview(). Necesita: nada que activar. La hoja solo aparece para una app instalada desde Google Play.

La API pública:

async function askForRating() {
  if (!(window.AppMint && AppMint.requestReview)) return;          // a browser: no store here
  var r = await AppMint.requestReview();
  console.log(r.result);   // 'shown' | 'not_from_play' | 'quota' | 'error'
}

Pregunta tras un buen momento, y pide la página de la tienda cuando la hoja no aparezca:

async function afterHappyMoment() {
  if (!(window.AppMint && AppMint.requestReview)) return;
  const r = await AppMint.requestReview({ openStore: true });   // {result, openedStore, message?}
  if (r.result === 'shown') thankUser();
  else if (r.openedStore) console.log('Opened the Play listing instead');
}

Escucha el aviso automático e informa de un error:

window.addEventListener('appmint:review', (e) => {
  if (e.detail.source === 'auto') console.log('Rate prompt result:', e.detail.result);
});

AppMint.requestReview().then((r) => {
  if (r.result === 'error') console.warn(r.message);
  if (r.result === 'not_from_play') showMessage('Rating is available once the app is installed from Google Play.');
});

setAppIcon puente#

window.WebToApk.setAppIcon(name: String): Boolean

Cambia el icono del launcher de la app a uno de los iconos alternativos añadidos en el asistente de compilación, por el nombre dado allí ("default" = el icono normal). Devuelve false para un nombre que esta compilación no tiene. El launcher se redibuja a su ritmo (unos segundos en algunos teléfonos), y algunos launchers cierran la app o mueven su icono de la pantalla de inicio cuando cambia: cambia desde una pantalla de ajustes, no en medio de una tarea.

Ejemplo

Cambia el icono del launcher de la app a uno de los iconos alternativos que añadiste en el asistente de compilación (paso Básico, "Iconos alternativos de la app"), por el nombre que le diste allí. 'default' es tu icono normal.

Devuelve: true cuando se hizo el cambio (o ya es ese icono), false para un nombre que esta compilación no tiene. Necesita: iconos alternativos añadidos en el asistente (hasta 4). Algunos launchers tardan unos segundos en redibujar el icono, y unos pocos cierran la app o mueven su acceso directo de la pantalla de inicio cuando cambia: cambia desde una pantalla de ajustes, no en medio de una tarea. Tus atajos del launcher (setAppShortcuts) se republican automáticamente con el nuevo icono.

function useIcon(name) {
  if (!(window.WebToApk && window.WebToApk.setAppIcon)) return false;
  const ok = WebToApk.setAppIcon(name);
  if (!ok) console.warn('no icon called', name);
  return ok;
}

document.querySelector('#darkIcon').addEventListener('click', () => useIcon('Dark'));
document.querySelector('#normalIcon').addEventListener('click', () => useIcon('default'));

Crea el selector a partir de los iconos que tiene esta compilación:

if (window.WebToApk && WebToApk.getAvailableIcons) {
  const names = JSON.parse(WebToApk.getAvailableIcons());   // 'default' first, then your alternates
  const current = WebToApk.getAppIcon();
  names.forEach((n) => addRadio('icon', n, n === current, () => WebToApk.setAppIcon(n)));
}

Comprueba la respuesta para un nombre que la compilación no tiene:

function pickIcon(name) {
  if (!(window.WebToApk && WebToApk.setAppIcon)) return showMessage('Icons can be changed in the app only');
  if (!WebToApk.setAppIcon(name)) {
    showMessage('Unknown icon "' + name + '". Choose one of: ' + JSON.parse(WebToApk.getAvailableIcons()).join(', '));
  } else {
    showMessage('Done. Your launcher may take a few seconds to redraw the icon.');
  }
}

Notas: la primera actualización que AÑADE iconos alternativos mueve la entrada del launcher a un componente nuevo, así que un acceso directo de la pantalla de inicio creado antes de esa actualización puede tener que volver a colocarse.

setAppShortcuts puente#

window.WebToApk.setAppShortcuts(itemsJson: String)

Atajos del launcher (pulsación larga en el icono de la app). Hasta cuatro entradas: `[{id, title, route}]`, donde `route` es una ruta dentro de la app ('/new-note'). Un toque abre la app con esa ruta como su enlace profundo de inicio (`appmint-shortcut://<package><route>`), que la página lee mediante `getLaunchUrl()` y enruta exactamente igual que cualquier otro enlace profundo. En una app en modo Sitio web el propio shell abre la ruta en el sitio (`/new-note` → `https://<site>/new-note`). Pasa `[]` para quitarlos. Android 7.1+; no hace nada en versiones anteriores.

Ejemplo

Establece los atajos del launcher que aparecen cuando el usuario mantiene pulsado el icono de la app.

Devuelve: nada. Necesita: nada. Android 7.1+ (no hace nada en versiones anteriores).

itemsJson es una cadena JSON: un arreglo de hasta cuatro objetos {id, title, route}. route es una ruta dentro de tu app. Un toque abre la app con appmint-shortcut://<package><route> como su enlace de inicio:

if (window.WebToApk && typeof window.WebToApk.setAppShortcuts === 'function') {
  window.WebToApk.setAppShortcuts(JSON.stringify([
    { id: 'new-note', title: 'New note', route: '/new-note' },
    { id: 'search',   title: 'Search',   route: '/search' }
  ]));
}

Lee el toque igual que cualquier otro enlace profundo, al iniciar y mientras se ejecuta:

function shortcutRoute(link) {
  var m = /^appmint-shortcut:\/\/[^\/?#]*([^#]*)/i.exec(link || '');
  return m ? (m[1] || '/') : null;
}
if (window.WebToApk && window.WebToApk.getLaunchUrl) {
  var r = shortcutRoute(window.WebToApk.getLaunchUrl());
  if (r) goTo(r);
}
window.addEventListener('appmint:deep-link', function (e) {
  var r = shortcutRoute(e.detail.url);
  if (r) goTo(r);
});

Quita todos los atajos:

if (window.WebToApk && window.WebToApk.setAppShortcuts) window.WebToApk.setAppShortcuts('[]');

Notas: Cada llamada reemplaza toda la lista. Las entradas sin title se omiten; la etiqueta se corta a 25 caracteres (50 para la etiqueta larga). id es appmint-<index> por defecto. A una ruta sin / inicial se le añade una. El icono es el icono de la app. Una lista mal formada se ignora (se registra), nunca lanza un error. Las apps creadas con IA pueden usar shortcuts desde @/lib/appmintNative. Apps en modo sitio web: un toque abre la ruta en tu sitio web (/new-note carga https://<your site>/new-note) y getLaunchUrl() sigue devolviendo el enlace appmint-shortcut://, así que una página que gestiona sus propias rutas sigue funcionando.

Generado a partir del runtime de la app y sus archivos de ejemplo en cada compilación de la documentación. Léelo como Markdown · Todas las familias.

Comprobado con el puente publicado el 2026-10-22.