API پل جاوااسکریپت
ویجت صفحه اصلی
یک کارت کوچک Android روی صفحه اصلی که صفحه شما آن را با عنوان، مقدار و چند خط پر میکند - AppMint.widget.
AppMint.widget کمککننده#
AppMint.widget.set(card) / update(partial) / clear() / placedCount() / requestPin()
نمونه
ویجت صفحه اصلی اپلیکیشن شما را پر میکند: یک کارت بومی با عنوان، یک مقدار بزرگ، یک شرح، تا دوازده خط (هر کدام در صورت تمایل با رنگ، نشانگر و مسیر ضربه خودش)، رنگهای شما، و مسیری که ضربه باز میکند. وقتی کاربر ویجت را بزرگتر میکند خطهای بیشتری نشان میدهد، و با textSize: 'auto' متن بزرگ میشود تا آن را پر کند - تا جایی که همه خطها جا شوند و خط بلند بهجای بریده شدن دو ردیف بگیرد. ویجت نمیتواند HTML نشان دهد - شما متن را برایش میفرستید.
مقدار بازگشتی: هر متد یک Promise برمیگرداند. set(card) ← {ok: true, placed} (placed = تعداد نسخههای روی صفحه اصلی) یا {ok: false, error}. clear() ← undefined. placedCount() ← یک عدد. requestPin() ← اگر برگه «افزودن» لانچر باز شد true. نیازمند: هیچ - هر ساختی ویجت را دارد.
کارت را تنظیم کنید - یک بار در شروع، و دوباره هر بار که دادههای نمایشدادهشده تغییر کند:
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);
}برنامهای که ویجت را پر میکند - پسزمینه شفاف، متنی که با تغییر اندازه ویجت بزرگ میشود، خطهای رنگی با نشانگر، یک خط عقبافتاده به رنگ قرمز، و ضربه روی یک خط که همان روز را باز میکند:
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
});هر خط یک رشته یا { text, color?, marker?, bold?, wrap?, route? } است؛ marker یک ● رنگی جلوی متن میگذارد، wrap: true روی یک خط (یا روی کارت) به خط بلند اجازه میدهد در هر اندازه متن دو ردیف بگیرد؛ با textSize: 'auto' خطها خودشان اینطور هستند، و wrap: false خط را روی یک ردیف نگه میدارد. تا شش خط همیشه نمایش داده میشود؛ خطهای هفت تا دوازده وقتی ویجت به اندازه کافی بلند باشد نمایش داده میشوند.
ویجتهای غنیتر - فهرست، عدد بزرگ با نوار پیشرفت، تصویر پسزمینه و دکمهها. کارت فقط وقتی به این شکل رسم میشود که یکی از این کلیدها را داشته باشد؛ کارت بدون آنها دقیقاً مثل قبل است:
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قلم خودتان - فایل قلم را در اپلیکیشن بگذارید (.ttf یا .otf، مثلاً از «Download family» در Google Fonts) و مسیرش را بدهید، مثل یک <img>؛ همه متنهای روی کارت با آن رسم میشوند:
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: …"نام قلم مثل 'RocknRoll One' کار نمیکند: صفحه اصلی ویجتها را رسم میکند و فقط قلمهای خود گوشی را دارد - set() با {ok: false, error} همین را میگوید. این خانوادههای داخلی به فایل نیاز ندارند: 'serif'، 'monospace'، 'sans-serif-condensed'، 'sans-serif-light'، 'sans-serif-medium'، 'sans-serif-black'، 'casual'، 'cursive'، 'sans-serif-smallcaps'. فایلهای WOFF / WOFF2 رد میشوند (Android نمیتواند آنها را بخواند) - از .ttf استفاده کنید. با فایل قلم، کارت بهصورت تصویر در اندازه ویجت رسم میشود و با تغییر اندازه دوباره رسم میشود؛ ضربه روی خطها، ردیفها و دکمهها و صفحهخوانها مثل هر کارت دیگری کار میکنند.
تازهسازی با اپلیکیشن بسته - یک آدرس https بدهید که همان JSON را برگرداند؛ Android تا وقتی ویجتی روی صفحه اصلی هست، حتی بعد از راهاندازی مجدد، آن را هر refreshMinutes دریافت میکند (۳۰ یا بیشتر، پیشفرض ۶۰):
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"}]}تصاویر: مسیری در اپلیکیشن شما (مثل <img src> در صفحه حل میشود)، یک آدرس https، یا یک URL از نوع data:image/…؛ کوچک میشوند و روی گوشی نگه داشته میشوند. در JSON تازهسازی، مسیر نسبی یعنی فایلی از اپلیکیشن. refreshUrl: '' تازهسازی را متوقف میکند؛ clear() هم آن را متوقف میکند. Android لحظه دقیق هر تازهسازی را تعیین میکند (کارهای پسزمینه را برای صرفهجویی در باتری دستهای اجرا میکند).
دکمه «افزودن به صفحه اصلی»:
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.');
}خالیاش کنید (مثلاً بعد از خروج کاربر از حساب):
if (window.AppMint && AppMint.widget) AppMint.widget.clear(); // shows "Nothing to show yet"ضربه روی ویجت اپلیکیشن را با appmint-shortcut://<package><route> بهعنوان پیوند اجرا باز میکند - آن را در شروع با WebToApk.getLaunchUrl() و هنگام اجرا با رویداد appmint:deep-link بخوانید:
window.addEventListener('appmint:deep-link', function (e) {
var m = /^appmint-shortcut:\/\/[^\/?#]*([^#]*)/i.exec(e.detail.url);
if (m) goTo(m[1] || '/');
});رنگها: #RRGGBB، #RGB، rgb(20, 38, 74)، rgba(20, 38, 74, 0.6) (همچنین rgb(20 38 74 / 60%))، transparent و نامهای ساده مثل red. هگز هشت رقمی به ترتیب Android است، آلفا اول: #AARRGGBB. #9914264A ۶۰ درصد مات است؛ #14264A99 در CSS اینجا بهصورت آلفای 14 (۸ درصد) خوانده میشود، یعنی کارتی تقریباً نامرئی. برای پسزمینههای شفاف rgba() از این سردرگمی جلوگیری میکند. رنگ خطها (color، marker) همین شکلها را میپذیرد؛ رنگ خط نامعتبر با {ok: false, error: 'lines[2].color is not a colour'} رد میشود، رنگ کارت نامعتبر از پیشفرض استفاده میکند.
نکات: همه فیلدهای کارت اختیاریاند: title خالی نام اپلیکیشن را نشان میدهد؛ value، caption یا خطهای خالی پنهان میشوند؛ خطهای بعد از دوازدهم حذف میشوند. textSize و align روی همه متنهای کارت اعمال میشوند؛ مقدار ناشناخته با {ok: false, error} رد میشود. کارت بدون کلیدهای جدید (caption، textSize، align، wrap، خطهای استایلدار) دقیقاً مثل قبل است. ویجت آخرین کارت را حتی بعد از راهاندازی مجدد نگه میدارد و فقط وقتی تغییر میکند که صفحه شما دوباره set() را فراخوانی کند. requestPin() به Android 8 به بالا و لانچری که از آن پشتیبانی کند نیاز دارد. اپلیکیشنهای ساختهشده با هوش مصنوعی میتوانند از widget در @/lib/appmintNative استفاده کنند. خطهای استایلدار، caption، textSize، align، wrap و rgba() به اپلیکیشنی نیاز دارند که با AppMint نسخه 1.11.38 یا جدیدتر ساخته شده باشد - پیش از فرستادن اشیای خط، اپلیکیشن را دوباره بسازید، چون ساخت قدیمیتر آنها را بهصورت متن خام نشان میدهد. font به اپلیکیشنی نیاز دارد که با AppMint نسخه 1.11.41 یا جدیدتر ساخته شده باشد (ساخت قدیمیتر آن را نادیده میگیرد). اپلیکیشنهای حالت وبسایت: ضربه مسیر را در وبسایت شما باز میکند - /#/today آدرس https://<your site>/#/today را بارگذاری میکند - و getLaunchUrl() همچنان پیوند appmint-shortcut:// را برمیگرداند.
widgetClear پل#
window.WebToApk.widgetClear()
کارت را حذف میکند؛ ویجتهای گذاشتهشده به حالت خالی برمیگردند.
نمونه
زیرساخت داخلی پشت AppMint.widget.clear() - کارت ویجت را حذف میکند؛ هر ویجت گذاشتهشده به «هنوز چیزی برای نمایش نیست» برمیگردد.
مقدار بازگشتی: هیچ. نیازها: هیچ.
ابتدا API عمومی:
async function onSignOut() {
if (window.AppMint && AppMint.widget) await AppMint.widget.clear();
}فراخوانی خام:
if (window.WebToApk && typeof window.WebToApk.widgetClear === 'function') {
window.WebToApk.widgetClear();
}ویجت را به یک کلید «نمایش روی صفحه اصلی» در تنظیمات خود وصل کنید - خاموش آن را خالی میکند، روشن دوباره پر میکند:
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();
}
};بعد از حذف حساب، ویجت را خالی کنید و اگر هنوز نسخهای روی صفحه اصلی کاربر هست به او بگویید:
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.');
}
}نکات: ویجت روی صفحه اصلی میماند؛ فقط محتوای آن حذف میشود. برای پر کردن دوباره آن AppMint.widget.set() را فراخوانی کنید.
widgetPlacedCount پل#
window.WebToApk.widgetPlacedCount(): Int
کاربر چند نسخه از ویجت را روی صفحه اصلی گذاشته است.
نمونه
زیرساخت داخلی پشت AppMint.widget.placedCount() - اینکه کاربر چند نسخه از ویجت شما را روی صفحه اصلی گذاشته است.
مقدار بازگشتی: یک عدد، همزمان (اگر هیچ نبود 0). نیازمند: هیچ.
ابتدا API عمومی - دکمه «افزودن ویجت» را فقط وقتی نشان دهید که ویجت هنوز گذاشته نشده است:
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;
}فراخوانی خام:
if (window.WebToApk && typeof window.WebToApk.widgetPlacedCount === 'function') {
var n = window.WebToApk.widgetPlacedCount();
console.log('Widgets on the home screen: ' + n);
}هر بار که کاربر از لانچر برمیگردد دوباره بررسی کنید و وقتی ویجت آنجا بود از او تشکر کنید:
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;
});وضعیت ویجت را در صفحه تنظیمات با فراخوانی خام همزمان نشان دهید:
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';
}نکات: وقتی اپلیکیشن دوباره به پیشزمینه برمیگردد (visibilitychange) دوباره بررسی کنید، چون کاربر ویجتها را از لانچر و بیرون از اپلیکیشن شما اضافه میکند.
widgetRequestPin پل#
window.WebToApk.widgetRequestPin(): Boolean
برگه «افزودن به صفحه اصلی» لانچر را برای ویجت باز میکند (Android 8 به بالا). جایی که لانچر نتواند، false.
نمونه
زیرساخت داخلی پشت AppMint.widget.requestPin() - برگه «افزودن به صفحه اصلی» لانچر را برای ویجت شما باز میکند.
مقدار بازگشتی: اگر برگه باز شد true، جایی که لانچر نتواند false (همزمان). نیازمند: Android 8 به بالا و لانچری که از سنجاق کردن ویجت پشتیبانی کند.
ابتدا API عمومی، از ضربه روی یک دکمه:
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.');
}
};فراخوانی خام:
if (window.WebToApk && typeof window.WebToApk.widgetRequestPin === 'function') {
if (!window.WebToApk.widgetRequestPin()) showManualSteps();
}ابتدا کارت را پر کنید، بعد برگه را باز کنید، تا ویجت همان لحظه که قرار میگیرد محتوای واقعی نشان دهد:
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
}
};بفهمید کاربر پذیرفته یا نه: تعداد ویجتهای گذاشتهشده را قبل از برگه و بعد از برگشتن او مقایسه کنید:
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');
});
};نکات: true فقط یعنی برگه باز شد - کاربر هنوز میتواند آن را لغو کند. بعداً با AppMint.widget.placedCount() ببینید اضافه شده یا نه. ابتدا کارت را با AppMint.widget.set() پر کنید تا ویجت جدید خالی نباشد.
widgetSet پل#
window.WebToApk.widgetSet(cardJson: String): String
کارت ویجت [cardJson] به شکل `{title?, value?, lines?, route?, bg?, ink?, accent?}` را ذخیره میکند و همه ویجتهای گذاشتهشده را دوباره رسم میکند. `{ok, placed}` برمیگرداند.
نمونه
زیرساخت داخلی پشت AppMint.widget.set(card) - صفحهها از آن کمکی برای پر کردن ویجت صفحه اصلی استفاده میکنند.
مقدار بازگشتی: یک رشته JSON به شکل {"ok":true,"placed":<number>} یا {"ok":false,"error":"…"} (همزمان). نیازمند: هیچ - هر ساختی ویجت را دارد.
ابتدا API عمومی:
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); });
}فراخوانی خام کارت را بهصورت رشته JSON میگیرد و متن 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);
}ویجت را با فهرست کارها هماهنگ نگه دارید - تعداد کارهای باز بهعنوان مقدار، شش کار اول بهعنوان خطها، و ضربه صفحه کارها را باز میکند:
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);
}وقتی کاربر از اپلیکیشن خارج میشود آخرین وضعیت را بنویسید؛ فراخوانی خام همزمان است، پس پیش از متوقف شدن صفحه تمام میشود:
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'
}));
});نکات: کلیدهای کارت: title، value، caption، lines (تا دوازده؛ هر خط یک رشته یا {text, color, marker, bold, wrap, route} است)، route (اگر نباشد / اضافه میشود)، bg، ink، accent، textSize، align، wrap، font (فایل .ttf/.otf در اپلیکیشن شما یا یک خانواده سیستمی مثل serif)، بهاضافه کلیدهای چیدمان غنی - ببینید AppMint.widget. رنگها: #RRGGBB، #AARRGGBB (آلفا اول)، rgba(). هر ویجت گذاشتهشده فوراً دوباره رسم میشود. وقتی کاربر هنوز ویجت را اضافه نکرده باشد placed برابر 0 است - کارت باز هم ذخیره میشود و وقتی اضافه کرد ظاهر میشود.
widgetUpdate پل#
window.WebToApk.widgetUpdate(partialJson: String): String
[partialJson] را در کارت ذخیرهشده ویجت ادغام میکند (کلیدهای نامبرده جایگزین میشوند، بقیه میمانند) و دوباره رسم میکند. `{ok, placed}` برمیگرداند.
نمونه
بخشی از ویجت صفحه اصلی را تغییر میدهد: کلیدهایی که میدهید جای کلیدهای ذخیرهشده را میگیرند و بقیه میمانند. صفحهها معمولاً فراخوانی میکنند AppMint.widget.update(partial).
مقدار بازگشتی: یک رشته JSON به شکل {"ok":true,"placed":<n>} یا {"ok":false,"error":"…"}. نیازمند: هیچ - هر ساختی ویجت را دارد.
// 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);
}کارت را هنگام خروج از حساب پاک کنید و خطا را از 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);
}آن را با بررسی گذاشتهشدن ترکیب کنید: کارت را فقط وقتی تازه کنید که یک نسخه روی صفحه اصلی باشد.
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() });
}نکات: وقتی کارت ذخیرهشدهای نیست، update() مثل set() رفتار میکند. برای همه کلیدهای کارت AppMint.widget را ببینید.