API del bridge JavaScript
Widget della schermata Home
Una piccola scheda Android nella schermata Home che la tua pagina riempie con un titolo, un valore e qualche riga - AppMint.widget.
AppMint.widget helper#
AppMint.widget.set(card) / update(partial) / clear() / placedCount() / requestPin()
Esempio
Riempie il widget della schermata Home della tua app: una scheda nativa con un titolo, un grande valore, una didascalia, fino a dodici righe (ciascuna con il proprio colore, indicatore e percorso di tocco, se vuoi), i tuoi colori e un percorso che si apre al tocco. Quando l'utente ingrandisce il widget, mostra più righe e con textSize: 'auto' il testo cresce per riempirlo, finché ogni riga ci sta ancora, con una riga lunga che occupa due righe invece di essere tagliata. Un widget non può mostrare HTML: gli invii il testo.
Restituisce: ogni metodo restituisce una Promise. set(card) → {ok: true, placed} (placed = quante copie ci sono nella schermata Home) oppure {ok: false, error}. clear() → undefined. placedCount() → un numero. requestPin() → true se la schermata «Aggiungi» del launcher si è aperta. Richiede: niente: ogni build ha il widget.
Imposta la scheda: una volta all'avvio e di nuovo ogni volta che cambiano i dati che mostra:
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);
}Un'agenda che riempie il widget: sfondo trasparente, testo che cresce quando il widget viene ridimensionato, righe colorate con indicatore, una riga in ritardo in rosso e un tocco su una riga che apre quel giorno:
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 riga è una stringa o { text, color?, marker?, bold?, wrap?, route? }; marker mette un ● colorato davanti al testo, wrap: true su una riga (o sulla scheda) permette a una riga lunga di occupare due righe a qualsiasi dimensione del testo; con textSize: 'auto' le righe lo fanno già , e wrap: false mantiene una riga su un'unica riga. Le prime sei righe si vedono sempre; le righe da sette a dodici compaiono quando il widget è abbastanza alto.
Widget più ricchi: un elenco, un grande numero con barra di avanzamento, un'immagine di sfondo e pulsanti. Una scheda viene disegnata così solo quando ha una di queste chiavi; una scheda senza di esse appare esattamente come prima:
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 keysIl tuo font: metti il file del font nella tua app (.ttf o .otf, ad esempio da «Download family» di Google Fonts) e indica il suo percorso, come faresti con un <img>; ogni testo della scheda viene disegnato con quel font:
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 NOME di font come 'RocknRoll One' non può funzionare: la schermata Home disegna i widget e ha solo i font del telefono stesso; set() risponde {ok: false, error} dicendolo. Queste famiglie integrate non richiedono alcun file: 'serif', 'monospace', 'sans-serif-condensed', 'sans-serif-light', 'sans-serif-medium', 'sans-serif-black', 'casual', 'cursive', 'sans-serif-smallcaps'. I file WOFF / WOFF2 vengono rifiutati (Android non può leggerli): usa il .ttf. Con un file di font la scheda viene disegnata come immagine alla dimensione del widget e ridisegnata quando viene ridimensionata; i tocchi su righe, voci e pulsanti e gli screen reader funzionano come su qualsiasi scheda.
Aggiornamento con l'app chiusa: indica un indirizzo https che restituisce lo stesso JSON; Android lo scarica ogni refreshMinutes (30 o più, predefinito 60) mentre un widget è nella schermata Home, anche dopo un riavvio:
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"}]}Immagini: un percorso nella tua app (risolto come un <img src> nella pagina), un indirizzo https o un URL data:image/…; vengono ridimensionate e conservate sul telefono. Nel JSON di aggiornamento un percorso relativo indica un file dell'app. refreshUrl: '' ferma l'aggiornamento; anche clear() lo ferma. Android decide il momento esatto di ogni aggiornamento (raggruppa il lavoro in background per risparmiare batteria).
Un pulsante «Aggiungi alla schermata Home»:
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.');
}Svuotalo (ad esempio dopo che l'utente si è disconnesso):
if (window.AppMint && AppMint.widget) AppMint.widget.clear(); // shows "Nothing to show yet"Un tocco sul widget apre l'app con appmint-shortcut://<package><route> come link di avvio: leggilo con WebToApk.getLaunchUrl() all'avvio e con l'evento appmint:deep-link mentre l'app è in esecuzione:
window.addEventListener('appmint:deep-link', function (e) {
var m = /^appmint-shortcut:\/\/[^\/?#]*([^#]*)/i.exec(e.detail.url);
if (m) goTo(m[1] || '/');
});Colori: #RRGGBB, #RGB, rgb(20, 38, 74), rgba(20, 38, 74, 0.6) (anche rgb(20 38 74 / 60%)), transparent e nomi semplici come red. L'esadecimale a otto cifre segue l'ordine di Android, con alpha PER PRIMO: #AARRGGBB. #9914264A è opaco al 60 %; il #14264A99 del CSS qui viene letto come alpha 14 (8 %), una scheda quasi invisibile. Per gli sfondi trasparenti rgba() evita la confusione. I colori delle righe (color, marker) accettano le stesse forme; un colore di riga non valido viene rifiutato con {ok: false, error: 'lines[2].color is not a colour'}, un colore di scheda non valido usa quello predefinito.
Note: Tutti i campi della scheda sono facoltativi: un title vuoto mostra il nome dell'app; value, caption o righe vuote vengono nascosti; le righe dopo la dodicesima vengono scartate. textSize e align si applicano a ogni testo della scheda; un valore sconosciuto viene rifiutato con {ok: false, error}. Una scheda senza le nuove chiavi (caption, textSize, align, wrap, righe con stile) appare esattamente come prima. Il widget conserva l'ultima scheda, anche dopo un riavvio, e cambia solo quando la tua pagina chiama di nuovo set(). requestPin() richiede Android 8+ e un launcher che lo supporti. Le app create con l'AI possono usare widget da @/lib/appmintNative. Le righe con stile, caption, textSize, align, wrap e rgba() richiedono un'app creata con AppMint 1.11.38 o più recente: ricrea l'app prima di inviare oggetti riga, perché una build più vecchia li mostrerebbe come testo grezzo. font richiede un'app creata con AppMint 1.11.41 o più recente (una build più vecchia lo ignora). App in modalità sito web: un tocco apre il percorso sul tuo sito: /#/today carica https://<your site>/#/today e getLaunchUrl() restituisce comunque il link appmint-shortcut://.
widgetClear bridge#
window.WebToApk.widgetClear()
Rimuove la scheda; i widget aggiunti tornano allo stato vuoto.
Esempio
Meccanismo interno dietro AppMint.widget.clear(): rimuove la scheda del widget; ogni widget aggiunto torna a «Niente da mostrare per ora».
Restituisce: niente. Richiede: niente.
Prima l'API pubblica:
async function onSignOut() {
if (window.AppMint && AppMint.widget) await AppMint.widget.clear();
}La chiamata grezza:
if (window.WebToApk && typeof window.WebToApk.widgetClear === 'function') {
window.WebToApk.widgetClear();
}Collega il widget a un interruttore «Mostra nella schermata Home» nelle tue impostazioni: spento lo svuota, acceso lo riempie di nuovo:
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();
}
};Dopo aver eliminato l'account, svuota il widget e avvisa l'utente se una copia è ancora nella sua schermata Home:
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.');
}
}Note: Il widget resta nella schermata Home; viene rimosso solo il suo contenuto. Chiama AppMint.widget.set() per riempirlo di nuovo.
widgetPlacedCount bridge#
window.WebToApk.widgetPlacedCount(): Int
Quante copie del widget l'utente ha aggiunto alla schermata Home.
Esempio
Meccanismo interno dietro AppMint.widget.placedCount(): quante copie del tuo widget l'utente ha messo nella schermata Home.
Restituisce: un numero, in modo sincrono (0 se nessuno). Richiede: niente.
Prima l'API pubblica: mostra un pulsante «Aggiungi widget» solo quando il widget non è ancora stato aggiunto:
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 chiamata grezza:
if (window.WebToApk && typeof window.WebToApk.widgetPlacedCount === 'function') {
var n = window.WebToApk.widgetPlacedCount();
console.log('Widgets on the home screen: ' + n);
}Controlla di nuovo ogni volta che l'utente torna dal launcher e ringrazialo quando il widget è presente:
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;
});Mostra lo stato del widget in una pagina delle impostazioni con la chiamata grezza sincrona:
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';
}Note: Controlla di nuovo quando l'app torna in primo piano (visibilitychange), perché l'utente aggiunge i widget dal launcher, fuori dalla tua app.
widgetRequestPin bridge#
window.WebToApk.widgetRequestPin(): Boolean
Apre la schermata «Aggiungi alla schermata Home» del launcher per il widget (Android 8+). Falso dove il launcher non può farlo.
Esempio
Meccanismo interno dietro AppMint.widget.requestPin(): apre la schermata «Aggiungi alla schermata Home» del launcher per il tuo widget.
Restituisce: true se la schermata si è aperta, false dove il launcher non può farlo (in modo sincrono). Richiede: Android 8+ e un launcher che supporti il fissaggio dei widget.
Prima l'API pubblica, da un tocco su un pulsante:
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 chiamata grezza:
if (window.WebToApk && typeof window.WebToApk.widgetRequestPin === 'function') {
if (!window.WebToApk.widgetRequestPin()) showManualSteps();
}Riempi prima la scheda, poi apri la schermata, così il widget mostra contenuti reali nel momento in cui arriva:
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
}
};Scopri se l'utente ha accettato: confronta il numero di widget aggiunti prima della schermata e dopo il suo ritorno:
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');
});
};Note: true significa solo che la schermata si è aperta: l'utente può ancora annullare. Usa AppMint.widget.placedCount() più tardi per vedere se è stato aggiunto. Riempi prima la scheda con AppMint.widget.set(), così il nuovo widget non è vuoto.
widgetSet bridge#
window.WebToApk.widgetSet(cardJson: String): String
Salva la scheda del widget [cardJson] `{title?, value?, lines?, route?, bg?, ink?, accent?}` e ridisegna ogni widget aggiunto. Restituisce `{ok, placed}`.
Esempio
Meccanismo interno dietro AppMint.widget.set(card): le pagine usano quell'helper per riempire il widget della schermata Home.
Restituisce: una stringa JSON {"ok":true,"placed":<number>} oppure {"ok":false,"error":"…"} (in modo sincrono). Richiede: niente: ogni build ha il widget.
Prima l'API pubblica:
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 chiamata grezza riceve la scheda come stringa JSON e restituisce testo 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);
}Mantieni il widget allineato a una lista di cose da fare: il numero di attivitĂ aperte come valore, le prime sei attivitĂ come righe e un tocco apre la schermata delle attivitĂ :
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);
}Scrivi l'ultimo stato quando l'utente lascia l'app; la chiamata grezza è sincrona, quindi termina prima che la pagina venga messa in pausa:
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'
}));
});Note: Chiavi della scheda: title, value, caption, lines (fino a dodici; una riga è una stringa o {text, color, marker, bold, wrap, route}), route (viene aggiunto un / se manca), bg, ink, accent, textSize, align, wrap, font (un file .ttf/.otf nella tua app o una famiglia di sistema come serif), più le chiavi del layout avanzato: vedi AppMint.widget. Colori: #RRGGBB, #AARRGGBB (alpha per primo), rgba(). Ogni widget aggiunto viene ridisegnato subito. placed è 0 quando l'utente non ha ancora aggiunto il widget: la scheda viene comunque salvata e compare quando lo aggiunge.
widgetUpdate bridge#
window.WebToApk.widgetUpdate(partialJson: String): String
Unisce [partialJson] alla scheda del widget salvata (le chiavi indicate sostituiscono, le altre restano) e ridisegna. Restituisce `{ok, placed}`.
Esempio
Modifica una parte del widget della schermata Home: le chiavi che passi sostituiscono quelle salvate, tutto il resto rimane. Le pagine normalmente chiamano AppMint.widget.update(partial).
Restituisce: una stringa JSON {"ok":true,"placed":<n>} oppure {"ok":false,"error":"…"}. Richiede: niente: ogni build ha il 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);
}Svuota la scheda alla disconnessione e segnala un errore da 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);
}Combinalo con il controllo di presenza: aggiorna la scheda solo mentre una copia è nella schermata Home.
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() });
}Note: Senza una scheda salvata, update() si comporta come set(). Vedi AppMint.widget per tutte le chiavi della scheda.