JavaScript bridge API
Launch, shortcuts and the back button
How the app was opened, long-press shortcuts on the icon, the in-app review prompt, and telling the shell the page wants the back press.
AppMint.setBackHandler helper#
AppMint.setBackHandler(handler)
Example
Gives your page the first say when the user presses the phone's Back button (or uses the back gesture). Needed by apps that change screens by showing and hiding elements, where Android's history is empty and Back would exit the app.
Returns: nothing. Your handler must return true (handled, stay here) or false (let the app go back / exit). Needs: nothing. Installed at document start, so you can register it in your first 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);The appmint:back event - cancelable. Call e.preventDefault() to consume the press. It runs before the handler, and any number of listeners can use it:
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
}
});Notes: The answer must be synchronous: returning a Promise counts as "not handled". Answer quickly - if the page does not answer within about 350 ms, the shell goes back by itself. Order on each press: an open side menu (the app's own drawer) closes first and the page is not asked; then appmint:back listeners → your handler → the shell's built-in dialog closer (it closes an open <dialog>, ARIA dialog or full-screen modal) → history back / exit confirmation / exit. Apps that use history.pushState for every screen (React Router and similar) do not need this. AI-built apps can use onBackGesture from @/lib/appmintNative.
__pullRefreshEligible bridge#
window.WebToApk.__pullRefreshEligible(eligible: Boolean)
See [pullPageEligible] / [pullProbeJs]. Called on every touchstart.
Example
Internal plumbing behind pull-to-refresh: the shell's own touch probe calls it on every touch to say whether a downward pull there should refresh the page. Pages never call it - they control pull-to-refresh with standard CSS and touch events.
Returns: nothing. Needs: Enable Pull-to-Refresh switched on in Step 3 (Integrate), under Display Options, when you build (without it the probe is not installed).
The public way to stop a pull from refreshing in part of your page - claim the gesture:
<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>Or claim it from a touch handler, the standard way:
document.getElementById('canvas').addEventListener('touchstart', function (e) {
e.preventDefault(); // the page took this gesture: no pull-to-refresh
}, { passive: false });Notes: A pull only refreshes when the nearest scrollable box under the finger (or the page itself) is scrolled to the top, only one finger is down, and neither of the claims above applies. A touch inside a cross-origin iframe never refreshes. Do not call this method yourself.
getLaunchUrl bridge#
window.WebToApk.getLaunchUrl(): String
The deep link that launched or resumed this app, or "" if it was opened normally. Read it whenever your app mounts and route on it yourself: const u = window.WebToApk?.getLaunchUrl?.(); if (u) router.navigate(new URL(u).pathname); The value is KEPT, not consumed, so a late-mounting app still sees it; a later link arriving while the app runs replaces it and also fires 'appmint:deep-link'.
Example
The deep link that opened (or reopened) the app, as a string, or "" when the app was opened normally.
Returns: a string, synchronously. Needs: nothing. The value is kept, not consumed, so it is safe to read whenever your app mounts.
A link can arrive in three shapes: https://yoursite.com/path, the shell's own appmint-shortcut://<package>/<route> (launcher shortcuts, the home-screen widget), or your custom scheme myapp://segment/path. This helper turns all three into a route:
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 }
});The same announcement is also passed to a global hook, if you prefer one:
window.onAppMintDeepLink = function (detail) { goTo(routeOf(detail.url)); };Notes: appmint:deep-link is also fired after every page load while a link is kept, so the same url can arrive more than once. Ignore a link you already handled.
Website-mode apps are different: the shell itself loads an incoming https:// link (on a cold start and while running), and a custom-scheme link myapp://product/42 is first rewritten onto your site's address (https://yoursite.com/product/42), so getLaunchUrl() returns the https:// form and your page usually has nothing to route. A launcher shortcut or widget tap does not reach a Website-mode page at all (see setAppShortcuts). In HTML, ZIP and AI-built apps (served from inside the app) the shell never navigates: your page gets the link and routes on it. A custom-scheme link stays as it arrived there, because there is no site address to map it onto. AI-built apps can use launchUrl() from @/lib/appmintNative.
rateApp bridge#
window.WebToApk.rateApp()
Opens the Play listing's rating flow. `rateApp()` already existed but only the native drawer entry could reach it, so an app that draws its own UI - every AI build, where the drawer is off - had no way to ask for a rating. It is the one store action a page genuinely cannot perform for itself: `market://` is an intent, not a navigable URL. Gated on the creator's own "Rate App Button" switch, exactly like the drawer entry and the bottom-nav Share item: turning Rate off must mean no rating surface anywhere, or the toggle looks broken.
Example
Opens the app's Google Play page so the user can rate it.
Returns: nothing. Needs: the Rate App Button switch in Step 5 (Menu) when you build (it is on by default; it also adds a "Rate App" item to the side menu). With it off, the call does nothing.
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
}Notes: It opens the Play Store listing (market://details?id=<package>), or the Play website when the Play Store app is missing. It is not the in-app review card. A page cannot open market:// itself - it is an Android intent, not a web address. Only ask after the user did something they liked, and from a tap.
setAppShortcuts bridge#
window.WebToApk.setAppShortcuts(itemsJson: String)
Launcher shortcuts (long-press the app icon). Up to four entries: `[{id, title, route}]`, where `route` is a path in the app ('/new-note'). A tap opens the app with that route as its launch deep link (`appmint-shortcut://<package><route>`), which the page reads through `getLaunchUrl()` and routes on exactly like any other deep link. In a Website-mode app the shell itself opens the route on the website (`/new-note` → `https://<site>/new-note`). Pass `[]` to remove them. Android 7.1+; a no-op below that.
Example
Sets the launcher shortcuts that appear when the user touches and holds the app icon.
Returns: nothing. Needs: nothing. Android 7.1+ (a no-op below that).
itemsJson is a JSON string: an array of up to four {id, title, route} objects. route is a path inside your app. A tap opens the app with appmint-shortcut://<package><route> as its launch link:
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' }
]));
}Read the tap the same way as any other deep link, on start and while running:
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);
});Remove all shortcuts:
if (window.WebToApk && window.WebToApk.setAppShortcuts) window.WebToApk.setAppShortcuts('[]');Notes: Each call replaces the whole list. Entries without a title are skipped; the label is cut to 25 characters (50 for the long label). id defaults to appmint-<index>. A route without a leading / gets one. The icon is the app icon. A malformed list is ignored (logged), never thrown. AI-built apps can use shortcuts from @/lib/appmintNative. Website-mode apps: a tap opens the route on your website - /new-note loads https://<your site>/new-note - and getLaunchUrl() still returns the appmint-shortcut:// link, so a page that routes itself keeps working.