Crear APK gratis

API del puente de JavaScript

Widget de pantalla de inicio

Una pequeña tarjeta de Android en la pantalla de inicio que tu página llena con un título, un valor y algunas líneas - AppMint.widget.

AppMint.widget ayudante#

AppMint.widget.set(card) / update(partial) / clear() / placedCount() / requestPin()

Ejemplo

Llena el widget de pantalla de inicio de tu app: una tarjeta nativa con un título, un valor grande, un pie, hasta doce líneas (cada una con su propio color, marcador y ruta al tocar si quieres), tus colores y una ruta que se abre al tocar. Cuando el usuario agranda el widget, muestra más líneas, y con textSize: 'auto' el texto crece para llenarlo, mientras cada línea siga cabiendo; una línea larga ocupa dos filas en lugar de cortarse. Un widget no puede mostrar HTML: tú le envías el texto.

Devuelve: cada método devuelve una Promise. set(card) → {ok: true, placed} (placed = cuántas copias hay en la pantalla de inicio) o {ok: false, error}. clear() → undefined. placedCount() → un número. requestPin() → true si se abrió la hoja "Añadir" del launcher. Requiere: nada: todas las compilaciones incluyen el widget.

Define la tarjeta una vez al inicio, y de nuevo cada vez que cambien los datos que muestra:

async function updateWidget(events) {
  if (!(window.AppMint && AppMint.widget)) return;          // in a browser
  var r = await AppMint.widget.set({
    title: 'Today',
    value: events.length + ' events',
    lines: events.slice(0, 6).map(function (ev) { return ev.time + ' ' + ev.name; }),
    route: '/calendar',      // a tap opens the app at this route
    bg: '#0E7C86',           // background
    ink: '#FFFFFF',          // title and lines
    accent: '#FFE08A'        // the big value
  });
  if (!r.ok) console.log('Widget not updated: ' + r.error);
}

Una agenda que llena el widget: fondo transparente, texto que crece al cambiar el tamaño del widget, líneas de colores con marcador, una línea vencida en rojo y un toque en una línea que abre ese día:

AppMint.widget.set({
  title: 'Agenda',
  value: 'Sun 18 Oct',
  caption: '5 events, 1 overdue',          // small text under the value
  bg: 'rgba(20, 38, 74, 0.6)',             // 60 % opaque — the wallpaper shows through
  ink: '#FFFFFF', accent: '#FFE08A',
  textSize: 'auto',                        // 'auto' | 'small' | 'normal' | 'large' | 'xl' | a number 0.6–3
  align: 'start',                          // 'start' (left) | 'center' | 'end'
  lines: [
    { text: '09:00 Stand-up', marker: '#29B6F6', route: '/#/day/2026-10-18' },
    { text: '10:30 Dentist, Dr Rao', marker: '#43A047', route: '/#/day/2026-10-18' },
    { text: 'Overdue: renew the car insurance', color: '#FF5252', marker: '#FF5252', bold: true, route: '/#/tasks/overdue' },
    { text: 'Tue 20: Team lunch', route: '/#/day/2026-10-20' },
    'Wed 21: Gym'                           // a plain string is still a line
  ],
  route: '/#/today'                         // a tap anywhere else on the card
});

Una línea es una cadena o { text, color?, marker?, bold?, wrap?, route? }; marker pone un ● de color delante del texto, wrap: true en una línea (o en la tarjeta) permite que una línea larga ocupe dos filas con cualquier tamaño de texto; con textSize: 'auto' las líneas ya lo hacen, y wrap: false mantiene una línea en una sola fila. Siempre se muestran hasta seis líneas; las líneas de la siete a la doce se muestran cuando el widget es lo bastante alto.

Widgets más completos: una lista, un número grande con barra de progreso, una imagen de fondo y botones. Una tarjeta se dibuja así solo cuando tiene una de estas claves; una tarjeta sin ellas se ve exactamente como antes:

AppMint.widget.set({
  layout: 'list',                 // 'list' | 'progress' | 'card'
  title: 'Today', subtitle: 'Tue 30 Sep',
  items: [                         // up to 5 rows; a row tap opens its own route
    { text: '09:00 Dentist', secondary: 'Dr Rao, 2nd floor', image: 'img/tooth.png', route: '/event/41' },
    { text: '13:30 Lunch with Sam', image: 'https://example.com/sam.jpg', route: '/event/42' }
  ],
  buttons: [{ label: 'Add', route: '/new' }, { label: 'Week', route: '/week' }],   // up to 2
  bgImage: 'img/widget-bg.jpg', bg: '#101820', ink: '#FFFFFF', accent: '#FFB000'
});

AppMint.widget.set({
  layout: 'progress', title: 'Steps', value: '8 214',
  progress: { value: 8214, max: 10000, label: '82% of today's goal' }
});

AppMint.widget.update({ value: '8 950', progress: { value: 8950, max: 10000 } });   // change only these keys

Tu propia fuente: pon el archivo de la fuente en tu app (.ttf o .otf, p. ej. desde "Download family" de Google Fonts) e indica su ruta, como harías con un <img>; todo el texto de la tarjeta se dibuja con ella:

AppMint.widget.set({
  title: 'Score', value: '1 250',
  font: 'fonts/RocknRollOne-Regular.ttf',   // a file in your app, an https address, or a data: URL
  lines: ['Level 7', { text: 'New record!', bold: true }]
}).then(function (r) { if (!r.ok) console.log(r.error); });   // e.g. "font file not found in the app: …"

Un NOMBRE de fuente como 'RocknRoll One' no puede funcionar: la pantalla de inicio dibuja los widgets y solo tiene las fuentes propias del teléfono; set() responde {ok: false, error} indicándolo. Estas familias integradas no necesitan archivo: 'serif', 'monospace', 'sans-serif-condensed', 'sans-serif-light', 'sans-serif-medium', 'sans-serif-black', 'casual', 'cursive', 'sans-serif-smallcaps'. Los archivos WOFF / WOFF2 se rechazan (Android no puede leerlos): usa el .ttf. Con un archivo de fuente, la tarjeta se dibuja como una imagen al tamaño del widget y se redibuja al cambiar de tamaño; los toques en líneas, filas y botones y los lectores de pantalla funcionan como en cualquier tarjeta.

Actualizar con la app cerrada: indica una dirección https que devuelva el mismo JSON; Android la consulta cada refreshMinutes (30 o más, 60 por defecto) mientras haya un widget en la pantalla de inicio, incluso después de reiniciar:

AppMint.widget.set({
  title: 'Orders', value: '…',
  refreshUrl: 'https://api.example.com/widget.json?user=42',
  refreshMinutes: 30
}).then(function (r) { console.log('refreshing every ' + r.refreshMinutes + ' min'); });
// widget.json answers e.g. {"title":"Orders","value":"12 new","layout":"list","items":[{"text":"#1042 paid"}]}

Imágenes: una ruta dentro de tu app (resuelta como un <img src> en la página), una dirección https o una URL data:image/…; se reducen de tamaño y se guardan en el teléfono. En el JSON de actualización, una ruta relativa significa un archivo de la app. refreshUrl: '' detiene la actualización; clear() también la detiene. Android decide el momento exacto de cada actualización (agrupa el trabajo en segundo plano para ahorrar batería).

Un botón "Añadir a la pantalla de inicio":

async function addWidget() {
  if (!(window.AppMint && AppMint.widget)) return;
  if ((await AppMint.widget.placedCount()) > 0) { alert('The widget is already on your home screen'); return; }
  var opened = await AppMint.widget.requestPin();
  if (!opened) alert('Touch and hold your home screen, tap Widgets, and pick this app.');
}

Vacíalo (por ejemplo, cuando el usuario cierra sesión):

if (window.AppMint && AppMint.widget) AppMint.widget.clear();   // shows "Nothing to show yet"

Un toque en el widget abre la app con appmint-shortcut://<package><route> como enlace de inicio: léelo con WebToApk.getLaunchUrl() al arrancar y con el evento appmint:deep-link mientras se ejecuta:

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

Colores: #RRGGBB, #RGB, rgb(20, 38, 74), rgba(20, 38, 74, 0.6) (también rgb(20 38 74 / 60%)), transparent y nombres simples como red. El hexadecimal de ocho dígitos usa el orden de Android, con el alfa PRIMERO: #AARRGGBB. #9914264A es 60 % opaco; el #14264A99 de CSS se lee aquí como alfa 14 (8 %), una tarjeta casi invisible. Para fondos transparentes, rgba() evita la confusión. Los colores de línea (color, marker) admiten las mismas formas; un color de línea no válido se rechaza con {ok: false, error: 'lines[2].color is not a colour'}, y un color de tarjeta no válido usa el predeterminado.

Notas: Todos los campos de la tarjeta son opcionales: un title vacío muestra el nombre de la app; value, caption o líneas vacíos se ocultan; las líneas después de la duodécima se descartan. textSize y align se aplican a todo el texto de la tarjeta; un valor desconocido se rechaza con {ok: false, error}. Una tarjeta sin las claves nuevas (caption, textSize, align, wrap, líneas con estilo) se ve exactamente como antes. El widget conserva la última tarjeta, incluso después de reiniciar, y cambia solo cuando tu página vuelve a llamar a set(). requestPin() requiere Android 8+ y un launcher compatible. Las apps creadas con IA pueden usar widget de @/lib/appmintNative. Las líneas con estilo, caption, textSize, align, wrap y rgba() requieren una app compilada con AppMint 1.11.38 o posterior: vuelve a compilar la app antes de enviar objetos de línea, porque una compilación anterior los mostraría como texto sin procesar. font requiere una app compilada con AppMint 1.11.41 o posterior (una compilación anterior lo ignora). Apps en modo sitio web: un toque abre la ruta en tu sitio web: /#/today carga https://<your site>/#/today, y getLaunchUrl() sigue devolviendo el enlace appmint-shortcut://.

widgetClear puente#

window.WebToApk.widgetClear()

Elimina la tarjeta; los widgets colocados vuelven a su estado vacío.

Ejemplo

Mecanismo interno detrás de AppMint.widget.clear(): elimina la tarjeta del widget; cada widget colocado vuelve a "Nada que mostrar todavía".

Devuelve: nada. Requiere: nada.

Primero la API pública:

async function onSignOut() {
  if (window.AppMint && AppMint.widget) await AppMint.widget.clear();
}

La llamada en bruto:

if (window.WebToApk && typeof window.WebToApk.widgetClear === 'function') {
  window.WebToApk.widgetClear();
}

Vincula el widget a un interruptor "Mostrar en la pantalla de inicio" en tus ajustes: al apagarlo se vacía, al encenderlo se vuelve a llenar:

document.getElementById('widget-toggle').onchange = async function (e) {
  if (!(window.AppMint && AppMint.widget)) return;
  if (e.target.checked) {
    await AppMint.widget.set(buildWidgetCard()); // your own card builder
  } else {
    await AppMint.widget.clear();
  }
};

Después de eliminar la cuenta, vacía el widget e informa al usuario si todavía queda una copia en su pantalla de inicio:

function afterAccountDeleted() {
  var W = window.WebToApk;
  if (!W || typeof W.widgetClear !== 'function') return;
  W.widgetClear();
  if (W.widgetPlacedCount() > 0) {
    showMessage('Your widget is now empty. You can remove it from your home screen.');
  }
}

Notas: El widget permanece en la pantalla de inicio; solo se elimina su contenido. Llama a AppMint.widget.set() para volver a llenarlo.

widgetPlacedCount puente#

window.WebToApk.widgetPlacedCount(): Int

Cuántas copias del widget ha colocado el usuario en la pantalla de inicio.

Ejemplo

Mecanismo interno detrás de AppMint.widget.placedCount(): cuántas copias de tu widget ha puesto el usuario en la pantalla de inicio.

Devuelve: un número, de forma síncrona (0 si no hay ninguno). Requiere: nada.

Primero la API pública: muestra un botón "Añadir widget" solo cuando el widget aún no está colocado:

async function showAddWidgetButton() {
  var btn = document.getElementById('add-widget');
  if (!(window.AppMint && AppMint.widget)) { btn.hidden = true; return; }
  btn.hidden = (await AppMint.widget.placedCount()) > 0;
}

La llamada en bruto:

if (window.WebToApk && typeof window.WebToApk.widgetPlacedCount === 'function') {
  var n = window.WebToApk.widgetPlacedCount();
  console.log('Widgets on the home screen: ' + n);
}

Vuelve a comprobarlo cada vez que el usuario regrese del launcher y dale las gracias cuando el widget ya esté ahí:

document.addEventListener('visibilitychange', async function () {
  if (document.visibilityState !== 'visible') return;
  if (!(window.AppMint && AppMint.widget)) return;
  var n = await AppMint.widget.placedCount();
  document.getElementById('widget-promo').hidden = n > 0;
  if (n > 0) document.getElementById('widget-thanks').hidden = false;
});

Muestra el estado del widget en una página de ajustes con la llamada sin procesar síncrona:

var status = document.getElementById('widget-status');
if (window.WebToApk && typeof window.WebToApk.widgetPlacedCount === 'function') {
  var n = window.WebToApk.widgetPlacedCount();
  status.textContent = n === 0 ? 'Not on your home screen'
    : n === 1 ? 'On your home screen' : 'On your home screen ' + n + ' times';
} else {
  status.textContent = 'Available in the installed app';
}

Notas: Vuelve a comprobarlo cuando la app regrese al primer plano (visibilitychange), ya que el usuario añade widgets desde el launcher, fuera de tu app.

widgetRequestPin puente#

window.WebToApk.widgetRequestPin(): Boolean

Abre la hoja "Añadir a la pantalla de inicio" del launcher para el widget (Android 8+). Devuelve false donde el launcher no puede.

Ejemplo

Mecanismo interno detrás de AppMint.widget.requestPin(): abre la hoja "Añadir a la pantalla de inicio" del launcher para tu widget.

Devuelve: true si se abrió la hoja, false donde el launcher no puede (de forma síncrona). Requiere: Android 8+ y un launcher que admita fijar widgets.

Primero la API pública, desde un toque en un botón:

document.getElementById('add-widget').onclick = async function () {
  if (!(window.AppMint && AppMint.widget)) return;
  var opened = await AppMint.widget.requestPin();
  if (!opened) {
    alert('Touch and hold your home screen, tap Widgets, then pick this app.');
  }
};

La llamada en bruto:

if (window.WebToApk && typeof window.WebToApk.widgetRequestPin === 'function') {
  if (!window.WebToApk.widgetRequestPin()) showManualSteps();
}

Llena primero la tarjeta y luego abre la hoja, para que el widget muestre contenido real en cuanto aparezca:

document.getElementById('add-widget').onclick = async function () {
  if (!(window.AppMint && AppMint.widget)) return;
  await AppMint.widget.set({ title: 'Streak', value: '12 days', lines: ['Best 30'], route: '/streak' });
  if (!(await AppMint.widget.requestPin())) {
    document.getElementById('widget-help').hidden = false; // manual steps
  }
};

Averigua si el usuario aceptó: compara la cantidad de widgets colocados antes de abrir la hoja y después de que regrese:

document.getElementById('add-widget').onclick = function () {
  var W = window.WebToApk;
  if (!W || typeof W.widgetRequestPin !== 'function') return;
  var before = W.widgetPlacedCount();
  if (!W.widgetRequestPin()) { showManualSteps(); return; }
  document.addEventListener('visibilitychange', function check() {
    if (document.visibilityState !== 'visible') return;
    document.removeEventListener('visibilitychange', check);
    if (W.widgetPlacedCount() > before) showToast('Widget added');
  });
};

Notas: true solo significa que la hoja se abrió: el usuario aún puede cancelarla. Usa AppMint.widget.placedCount() más tarde para ver si se añadió. Llena primero la tarjeta con AppMint.widget.set(), para que el widget nuevo no esté vacío.

widgetSet puente#

window.WebToApk.widgetSet(cardJson: String): String

Guarda la tarjeta del widget [cardJson] `{title?, value?, lines?, route?, bg?, ink?, accent?}` y vuelve a dibujar cada widget colocado. Devuelve `{ok, placed}`.

Ejemplo

Mecanismo interno detrás de AppMint.widget.set(card): las páginas usan esa función para llenar el widget de pantalla de inicio.

Devuelve: una cadena JSON {"ok":true,"placed":<number>} o {"ok":false,"error":"…"} (de forma síncrona). Requiere: nada: todas las compilaciones incluyen el widget.

Primero la API pública:

if (window.AppMint && AppMint.widget) {
  AppMint.widget.set({ title: 'Steps', value: '8 214', lines: ['Goal 10 000'], route: '/' })
    .then(function (r) { if (!r.ok) console.log(r.error); });
}

La llamada sin procesar recibe la tarjeta como una cadena JSON y devuelve texto JSON:

if (window.WebToApk && typeof window.WebToApk.widgetSet === 'function') {
  var card = { title: 'Steps', value: '8 214', lines: ['Goal 10 000'], route: '/',
               bg: '#15161A', ink: '#FFFFFF', accent: '#22D3EE' };
  var r = JSON.parse(window.WebToApk.widgetSet(JSON.stringify(card)));
  console.log(r.ok ? 'on ' + r.placed + ' home screen(s)' : r.error);
}

Mantén el widget al día con una lista de tareas: el número de pendientes como valor, las primeras seis tareas como líneas, y un toque abre la pantalla de tareas:

async function syncTaskWidget(tasks) {
  if (!(window.AppMint && AppMint.widget)) return;
  var open = tasks.filter(function (t) { return !t.done; });
  var r = await AppMint.widget.set({
    title: 'To do',
    value: open.length ? open.length + ' left' : 'All done',
    lines: open.slice(0, 6).map(function (t) { return '• ' + t.title; }),
    route: '/tasks'
  });
  if (!r.ok) console.warn('Widget not updated: ' + r.error);
}

Escribe el último estado cuando el usuario salga de la app; la llamada sin procesar es síncrona, así que termina antes de que la página se pause:

document.addEventListener('visibilitychange', function () {
  if (document.visibilityState !== 'hidden') return;
  if (!window.WebToApk || typeof window.WebToApk.widgetSet !== 'function') return;
  var s = getTodayStats(); // your app's own numbers
  window.WebToApk.widgetSet(JSON.stringify({
    title: 'Today', value: s.spent + ' spent',
    lines: ['Budget ' + s.budget, 'Left ' + (s.budget - s.spent)], route: '/budget'
  }));
});

Notas: Claves de la tarjeta: title, value, caption, lines (hasta doce; una línea es una cadena o {text, color, marker, bold, wrap, route}), route (se añade un / si falta), bg, ink, accent, textSize, align, wrap, font (un archivo .ttf/.otf en tu app o una familia del sistema como serif), además de las claves del diseño enriquecido: consulta AppMint.widget. Colores: #RRGGBB, #AARRGGBB (alfa primero), rgba(). Todos los widgets colocados se redibujan a la vez. placed es 0 cuando el usuario aún no ha añadido el widget: la tarjeta se guarda igualmente y aparece cuando lo haga.

widgetUpdate puente#

window.WebToApk.widgetUpdate(partialJson: String): String

Combina [partialJson] con la tarjeta del widget guardada (las claves indicadas reemplazan, el resto se mantiene) y vuelve a dibujar. Devuelve `{ok, placed}`.

Ejemplo

Cambia una parte del widget de pantalla de inicio: las claves que pasas reemplazan a las guardadas, todo lo demás se mantiene. Las páginas normalmente llaman a AppMint.widget.update(partial).

Devuelve: una cadena JSON {"ok":true,"placed":<n>} o {"ok":false,"error":"…"}. Requiere: nada: todas las compilaciones incluyen el widget.

// Only the number and the progress bar change; title, rows and buttons stay as they were.
if (window.AppMint && AppMint.widget) {
  AppMint.widget.update({ value: '7 420', progress: { value: 7420, max: 10000, label: '74% of goal' } });
}

// Raw form:
if (window.WebToApk && typeof window.WebToApk.widgetUpdate === 'function') {
  var r = JSON.parse(window.WebToApk.widgetUpdate(JSON.stringify({ subtitle: 'Updated ' + new Date().toLocaleTimeString() })));
  if (!r.ok) console.warn(r.error);
}

Borra la tarjeta al cerrar sesión e informa de un error desde update:

async function onSignOut() {
  if (!(window.AppMint && AppMint.widget)) return;
  await AppMint.widget.clear();
}

async function bumpCount(n) {
  if (!(window.AppMint && AppMint.widget)) return;
  const r = await AppMint.widget.update({ value: String(n) });
  if (!r.ok) console.warn('Widget not updated: ' + r.error);
}

Combínalo con la comprobación de colocación: actualiza la tarjeta solo mientras haya una copia en la pantalla de inicio.

async function refreshWidget(stats) {
  if (!(window.AppMint && AppMint.widget)) return;
  if (await AppMint.widget.placedCount() === 0) return;
  AppMint.widget.update({ value: stats.total + ' today', subtitle: 'Updated ' + new Date().toLocaleTimeString() });
}

Notas: Sin una tarjeta guardada, update() se comporta como set(). Consulta AppMint.widget para ver todas las claves de la tarjeta.

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.