أنشئ APK مجانًا

واجهة JavaScript bridge API

ملحقات Capacitor

ملحقات Capacitor التي يستضيفها التطبيق. تصل إليها الصفحة المكتوبة يدويًا عبر window.Capacitor.Plugins.<Name>؛ ويستخدم التطبيق المبني بالذكاء الاصطناعي الغلاف @/lib/appmintNative الذي يذكره كل مثال.

Capacitor.ActionSheet Capacitor#

Capacitor.Plugins.ActionSheet — showActions({ title, message, options })

مثال

يعرض ورقة سفلية أصلية بقائمة خيارات ويخبرك أيّها نُقر عليه (@capacitor/action-sheet 8.1.1).

القيم المُعادة: Promise { index, canceled } - index هو الخيار الذي نُقر عليه؛ وعند إغلاق الورقة يكون canceled هو true ويكون index خارج القائمة (-1). المتطلبات: لا شيء لتفعيله، فكل تطبيق يتضمن الملحق. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

كل خيار هو { title, style? } مع style 'DEFAULT' أو 'DESTRUCTIVE' أو 'CANCEL'.

var ActionSheet = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.ActionSheet;

async function photoMenu() {
  if (!ActionSheet) return;
  var opts = [
    { title: 'Share', style: 'DEFAULT' },
    { title: 'Edit', style: 'DEFAULT' },
    { title: 'Delete', style: 'DESTRUCTIVE' }
  ];
  var r = await ActionSheet.showActions({ title: 'Photo', message: 'Choose an action', options: opts });
  if (r.canceled || r.index < 0 || r.index >= opts.length) return;   // dismissed
  if (opts[r.index].title === 'Delete') deletePhoto();
}

في تطبيق مبني بالذكاء الاصطناعي يُعيد الغلاف بصيغة Capacitor القيمة { index: -1 } عند الإغلاق؛ أما الصيغة المختصرة actionSheet() فتُعيد null:

import { ActionSheet, actionSheet } from '@/lib/appmintNative';

const { index } = await ActionSheet.showActions({
  title: 'Photo',
  options: [{ title: 'Share' }, { title: 'Delete', destructive: true }],
});
if (index === 1) deletePhoto();

const i = await actionSheet('Sort by', [{ title: 'Name' }, { title: 'Date' }]);   // number | null

تأكيد الحذف الذي يعمل أيضًا في متصفح عادي:

async function confirmDelete(name) {
  const AS = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.ActionSheet;
  if (!AS) return window.confirm('Delete ' + name + '?');
  const r = await AS.showActions({
    title: 'Delete ' + name + '?',
    options: [{ title: 'Delete', style: 'DESTRUCTIVE' }, { title: 'Keep it', style: 'CANCEL' }]
  });
  return !r.canceled && r.index === 0;
}

ملاحظات: تحقّق دائمًا من حدّ الفهرس - ولا تعامل أي فهرس كاختيار حقيقي. شكل الخيار في الغلاف هو { title, destructive? }؛ ويحوّل destructive إلى نمط 'DESTRUCTIVE' في الملحق.

Capacitor.App Capacitor#

Capacitor.Plugins.App — getInfo(), getState(), exitApp(), minimizeApp(), addListener(...)

مثال

هوية التطبيق ودورة حياته: الاسم ومعرّف الحزمة والإصدار وهل التطبيق في المقدمة، وأحداث الإيقاف المؤقت والاستئناف (@capacitor/app 8.1.0).

يعيد: getInfo() ← { name, id, build, version }؛ getState() ← { isActive }؛ getLaunchUrl() ← { url } أو لا شيء؛ getAppLanguage() ← { value }؛ exitApp() / minimizeApp() ← void. addListener() ← Promise لمقبض مع remove()، كما في واجهة Capacitor نفسها - استخدم await معه أو سلسِل .then()؛ والقيمة المعادة لها أيضًا remove() بنفسها. يتطلب: لا شيء لتفعيله - كل تطبيق يستضيف الإضافة. يوجد window.Capacitor في الصفحات التي يقدمها التطبيق بنفسه (HTML وZIP والتطبيقات المبنية بالذكاء الاصطناعي)، وفي تطبيق وضع الموقع، على موقعك أنت (نطاقه ونطاقاته الفرعية) - وليس أبدًا على المواقع الأخرى التي يفتحها التطبيق. اكتشف الدعم.

var App = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.App;
if (App) {
  App.getInfo().then(function (i) {
    document.getElementById('about').textContent = i.name + ' ' + i.version + ' (' + i.build + ')';
  });
}

الإيقاف المؤقت والاستئناف - احفظ مسوّدة عندما يغادر المستخدم، وحدّث عندما يعود:

if (App) {
  App.addListener('appStateChange', function (s) {
    if (!s.isActive) saveDraft();      // went to the background
    else refreshInbox();               // back in front
  });
  App.addListener('pause', function () { console.log('paused'); });
  App.addListener('resume', function () { console.log('resumed'); });
}

في تطبيق مبني بالذكاء الاصطناعي:

import { App } from '@/lib/appmintNative';

const info = await App.getInfo();   // { name, id, build, version }
App.exitApp();                      // closes the app on Android

ملاحظات: لا يُطلَق الحدث backButton أبدًا: فالغلاف يحتفظ بزر الرجوع (يعمل الملحق مع disableBackButtonHandler). تعامل مع الرجوع عبر AppMint.setBackHandler(fn) أو الغلاف onBackGesture. وللروابط العميقة استخدم WebToApk.getLaunchUrl() / الحدث appmint:deep-link (أو launchUrl() / onDeepLink() في التطبيقات المبنية بالذكاء الاصطناعي). minimizeApp() خاص بـ Android فقط. لا تجعل زر الخروج الطريق الوحيد للمتابعة.

Capacitor.AppLauncher Capacitor#

Capacitor.Plugins.AppLauncher — canOpenUrl({ url }), openUrl({ url })

مثال

يفتح تطبيقًا آخر عبر رابط (whatsapp://send?text=… أو tel: أو geo: أو رابط https) ويسأل هل يستطيع أحدها التعامل معه (@capacitor/app-launcher 8.0.1).

القيم المُعادة: canOpenUrl() ← Promise { value: boolean }. openUrl() ← Promise { completed: boolean } (false عندما لا يفتح أي تطبيق الرابط). المتطلبات: لا شيء لتفعيله، فكل تطبيق يتضمن الملحق. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

var AL = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppLauncher;

async function chatOnWhatsApp(text) {
  if (!AL) return;
  var url = 'whatsapp://send?text=' + encodeURIComponent(text);
  var can = await AL.canOpenUrl({ url: url });
  if (!can.value) { alert('WhatsApp is not installed'); return; }
  var r = await AL.openUrl({ url: url });
  if (!r.completed) alert('Could not open WhatsApp');
}

في تطبيق مبني بالذكاء الاصطناعي:

import { appLauncher } from '@/lib/appmintNative';

if (await appLauncher.canOpen('tel:+15551234567')) await appLauncher.open('tel:+15551234567');

افتح رابط خريطة دون سؤال أولًا:

async function openMap(query) {
  const AL = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppLauncher;
  if (!AL) { window.open('https://maps.google.com/?q=' + encodeURIComponent(query)); return; }
  const r = await AL.openUrl({ url: 'geo:0,0?q=' + encodeURIComponent(query) });
  if (!r.completed) alert('No maps app could open this');
}

ملاحظات: في Android 11+ لا يستطيع التطبيق "رؤية" إلا التطبيقات الأخرى التي يصرّح بها. تصرّح التطبيقات المُنشأة بروابط https وgeo وgoogle.navigation وtel وmailto وsms وwhatsapp وintent وupi، لذا يجيب canOpenUrl بـ false للمخططات الأخرى (مثل spotify:) حتى لو كان التطبيق مثبّتًا. ويظل openUrl يعمل معها - استدعه وتحقّق من completed. وفي Android يقبل canOpenUrl أيضًا اسم حزمة ('com.whatsapp')، بنفس حدّ الرؤية.

Capacitor.AppUpdate Capacitor#

Capacitor.Plugins.AppUpdate — getAppUpdateInfo(), performImmediateUpdate(), startFlexibleUpdate(), completeFlexibleUpdate(), openAppStore()

مثال

يتحقّق في Google Play من وجود إصدار أحدث للتطبيق ويبدأ التحديث داخل التطبيق من Play. نسخة مضمّنة @capawesome/capacitor-app-update (template/plugins/app-update).

القيم المُعادة: getAppUpdateInfo() ← { currentVersionName, currentVersionCode, availableVersionCode, updateAvailability, updatePriority, immediateUpdateAllowed, flexibleUpdateAllowed, clientVersionStalenessDays?, installStatus } (رموز الإصدار نصوص؛ updateAvailability 2 = يوجد تحديث). performImmediateUpdate() / startFlexibleUpdate() ← { code }: 0 نجح، 1 أُلغي، 2 فشل، 3 غير متاح، 4 غير مسموح، 5 استدعِ getAppUpdateInfo() أولًا. openAppStore() ← void. المتطلبات: لا شيء لتفعيله؛ ويجب تثبيت التطبيق من Google Play. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

var AU = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppUpdate;

async function checkForUpdate() {
  if (!AU) return;
  try {
    var info = await AU.getAppUpdateInfo();          // must come first
    if (info.updateAvailability !== 2) return;       // up to date
    if (info.immediateUpdateAllowed) {
      var r = await AU.performImmediateUpdate();     // Play's full-screen update
      if (r.code === 1) console.log('user said not now');
    } else {
      await AU.openAppStore();                       // the Play listing
    }
  } catch (e) {
    console.log('Play update check failed:', e.message);  // e.g. no Play services
  }
}

تحديث مرن - نزّل في الخلفية، ثم أعد التشغيل عندما يوافق المستخدم:

async function flexibleUpdate() {
  if (!AU) return;
  var info = await AU.getAppUpdateInfo();
  if (info.updateAvailability !== 2 || !info.flexibleUpdateAllowed) return;
  await AU.addListener('onFlexibleUpdateStateChange', function (s) {
    if (s.installStatus === 2) console.log(s.bytesDownloaded + ' / ' + s.totalBytesToDownload);
    if (s.installStatus === 11) showRestartButton();   // 11 = DOWNLOADED
  });
  await AU.startFlexibleUpdate();
}
function restartNow() { if (AU) AU.completeFlexibleUpdate(); }

في تطبيق مبني بالذكاء الاصطناعي:

import { appUpdate } from '@/lib/appmintNative';

const info = await appUpdate.check();          // null on the web / without Play
if (info?.inPlace) await appUpdate.start();    // Android: immediate update

ملاحظات: استخدم updateAvailability === 2 كاختبار وحيد لوجود "تحديث متاح". يقارن check() في الغلاف بين currentVersionName وavailableVersionCode وهما لا يتساويان أبدًا على Android، لذا فإن updateAvailable غير موثوق هناك - اختبر inPlace بدلًا من ذلك. يُحَلّ start() بالقيمة true حتى عندما يلغي المستخدم.

Capacitor.AppwrightDocScanner Capacitor#

Capacitor.Plugins.AppwrightDocScanner — scan({ pageLimit, pdf, quality }), isSupported()

مثال

يفتح ماسح المستندات Google ML Kit: يصوّر المستخدم صفحة أو أكثر (أو يستوردها)، ثم تُكتشف الحواف وتُعدَّل، وتعود الصفحات صورًا بصيغة JPEG. ملحق Appwright الخاص (AppwrightDocScannerPlugin.kt).

القيم المُعادة: scan() ← Promise يُحَلّ دائمًا: { ok: true, pages: string[] (JPEG data URLs), pdfDataUrl?, pdfPath?, pdfError? } عند النجاح، { ok: false, cancelled: true } عندما يتراجع المستخدم، { ok: false, error } عندما يتعذّر تشغيل الماسح. isSupported() ← { value: boolean } (خدمات Google Play متوفرة). المتطلبات: لا شيء لتفعيله ولا إذن كاميرا (يعمل الماسح داخل خدمات Play التي تنزّل نموذجها عند أول استخدام).

var Scanner = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightDocScanner;

async function scanReceipt() {
  if (!Scanner) { alert('Scanning is not available here'); return; }
  var s = await Scanner.isSupported();
  if (!s.value) { alert('This phone has no Google Play services'); return; }
  var r = await Scanner.scan({ pageLimit: 1, pdf: false, quality: 0.8 });
  if (r.cancelled) return;
  if (!r.ok) { alert('Scanner error: ' + r.error); return; }
  document.getElementById('preview').src = r.pages[0];
}

pageLimit 0 = بلا حد. quality من 0.1 إلى 1 لصيغة JPEG. مع pdf: true يكتب الماسح أيضًا ملف PDF: pdfDataUrl هو نسخة منه كرابط data:application/pdf;base64,… تستطيع الصفحة تنزيله أو رفعه أو عرضه، وpdfPath هو نسخته في مجلد ذاكرة التخزين المؤقت للتطبيق. وإن تعذّرت قراءة PDF تعود الصفحات مع ذلك ويبيّن pdfError السبب.

في تطبيق مبني بالذكاء الاصطناعي (null عند الإلغاء أو الخطأ أو على الويب):

import { docScanner, can } from '@/lib/appmintNative';

if (can('docScanner') !== 'none') {
  const res = await docScanner.scan({ pageLimit: 5, pdf: true });
  if (res) setPages(res.pages);          // res.pdfPath when pdf: true
}

امسح عدة صفحات واعرض ملف PDF:

async function scanContract() {
  const Scanner = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightDocScanner;
  if (!Scanner) return;
  const r = await Scanner.scan({ pageLimit: 0, pdf: true, quality: 0.9 });
  if (!r.ok) { if (!r.cancelled) alert('Scanner error: ' + r.error); return; }
  if (r.pdfDataUrl) {
    const a = document.createElement('a');
    a.href = r.pdfDataUrl; a.download = 'contract.pdf'; a.click();
  } else if (r.pdfError) console.warn('PDF not made:', r.pdfError);
  console.log(r.pages.length + ' pages scanned');
}

ملاحظات: الصفحات روابط بيانات كاملة وقد تكون كبيرة - صغّرها قبل حفظ عدد كبير منها في localStorage. استخدم pdfDataUrl لا pdfPath - فمسار ملف الجهاز ليس رابطًا تستطيع الصفحة تحميله. تُتخطّى الصفحة التي لا يمكن قراءتها؛ وتعود بقية الصفحات.

Capacitor.AppwrightGallery Capacitor#

Capacitor.Plugins.AppwrightGallery — save({ source, fileName, album, kind })

مثال

يحفظ صورة أو فيديو في معرض الهاتف (Pictures/<album> أو Movies/<album>). ملحق غلاف التطبيق الخاص (AppwrightGalleryPlugin.kt).

القيم المُعادة: Promise لـ { uri } للعنصر المحفوظ. يُرفض بـ code: bad-source أو download-failed أو permission-denied (Android 7-9، رُفض التخزين) أو failed. المتطلبات: لا شيء لتفعيله؛ ولا إذن في Android 10+ (يطلب Android 7-9 إذن التخزين في المرة الأولى).

const Gallery = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightGallery;

async function saveCard(canvas) {
  const dataUrl = canvas.toDataURL('image/png');
  if (!Gallery) { const a = document.createElement('a'); a.href = dataUrl; a.download = 'card.png'; a.click(); return; }
  try {
    await Gallery.save({ source: dataUrl, fileName: 'birthday-card.png', album: 'Cards' });
    toast('Saved to your gallery');
  } catch (e) {
    toast('Could not save: ' + e.message);
  }
}

احفظ فيديو من خادمك في ألبومه الخاص، مع رسالة لكل رمز فشل:

const Gallery = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightGallery;

async function saveClip(url) {
  if (!Gallery) { window.open(url, '_blank'); return; }
  try {
    await Gallery.save({ source: url, fileName: 'highlight.mp4', album: 'Highlights', kind: 'video' });
    toast('Video saved to Movies/Highlights');
  } catch (e) {
    if (e.code === 'download-failed') toast('Could not download the video. Check your connection.');
    else if (e.code === 'permission-denied') toast('Allow storage access to save videos.');
    else toast('Could not save: ' + e.message);
  }
}

احفظ صورة عدّلها المستخدم كـ Blob (مثلًا من canvas.toBlob) بتحويلها أولًا إلى رابط data::

const Gallery = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightGallery;

function blobToDataUrl(blob) {
  return new Promise((resolve, reject) => {
    const r = new FileReader();
    r.onload = () => resolve(r.result);
    r.onerror = () => reject(r.error);
    r.readAsDataURL(blob);
  });
}

async function saveEditedPhoto(canvas) {
  if (!Gallery) return;
  const blob = await new Promise((res) => canvas.toBlob(res, 'image/jpeg', 0.92));
  const { uri } = await Gallery.save({ source: await blobToDataUrl(blob), fileName: 'edit-' + Date.now() + '.jpg', album: 'My Edits' });
  console.log('saved as', uri);
}

ملاحظات: source هو رابط data: أو رابط http(s) أو عنوان content:///file://؛ ويُقرأ kind ('image'/'video') من نوع MIME عند حذفه. تستخدم التطبيقات المبنية بالذكاء الاصطناعي saveToGallery() من @/lib/appmintNative.

Capacitor.AppwrightHaptics Capacitor#

Capacitor.Plugins.AppwrightHaptics

مثال

ملحق AppMint الخاص لأنماط اهتزاز غنية: قائمة نبضات، لكل منها وقت بدء ومدة وقوة خاصة بها.

القيم المُعادة: يُحَلّ play({ events }) بـ { ok: true } أو { ok: false, error } ('unsupported' = لا يوجد جهاز اهتزاز، 'empty' = لا أحداث، أو رسالة النظام). ولا يُرفض أبدًا. يُحَلّ isSupported() بـ { value: true/false }. المتطلبات: لا شيء لتفعيله - فكل تطبيق يصرّح بإذن الاهتزاز، سواء فُعّل الاهتزاز في الخطوة 4 (الوصول) أم لا. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

كل حدث هو { at, duration, intensity, sharpness }: at = البداية بالميلي ثانية، duration = المدة بالميلي ثانية (أقل من 20 تصبح نقرة 30 ms)، intensity = من 0 إلى 1 (الافتراضي 0.8). يُقبل sharpness لكنه يُتجاهل في Android.

var AH = window.Capacitor && window.Capacitor.Plugins ? window.Capacitor.Plugins.AppwrightHaptics : null;

async function heartbeat() {
  if (!AH) return false;
  var r = await AH.play({
    events: [
      { at: 0,   intensity: 0.8 },
      { at: 140, intensity: 0.5 },
      { at: 700, intensity: 0.8 },
      { at: 840, intensity: 0.5 }
    ]
  });
  if (!r.ok) console.log('pattern not played:', r.error);
  return r.ok;
}

document.getElementById('like').addEventListener('click', heartbeat);

أخفِ الزر على الهواتف التي لا تحتوي على جهاز اهتزاز:

(async function () {
  var btn = document.getElementById('feel-it');
  var ok = false;
  if (AH) { try { ok = (await AH.isSupported()).value; } catch (e) {} }
  btn.hidden = !ok;
})();

شغّل نقرتين سريعتين عند نجاح إجراء، وأبلغ عندما يتعذّر التشغيل:

async function successBuzz() {
  const AH = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightHaptics;
  if (!AH) return;
  const r = await AH.play({ events: [{ at: 0, duration: 40, intensity: 1 }, { at: 120, duration: 40, intensity: 0.6 }] });
  if (!r.ok && r.error === 'unsupported') console.log('this phone has no vibrator');
}

ملاحظات: تعمل القوة فقط على هواتف تتحكم بالسعة؛ أما الهواتف الأخرى فتحتفظ بالتوقيت بقوة ثابتة واحدة. إن رفض النظام الاهتزاز يجيب play بـ { ok: false, error: '<the system message>' }. وفي التطبيقات المبنية بالذكاء الاصطناعي استخدم hapticPlay من @/lib/appmintNative، وله أيضًا أنماط مسمّاة.

Capacitor.AppwrightLiveProgress Capacitor#

Capacitor.Plugins.AppwrightLiveProgress — start / update / end

مثال

ملحق Android الخاص بـ AppMint (اسمه في JS هو AppwrightLiveProgress) لإشعار حالة ثابت وصامت يتحدّث مباشرةً (شريط تقدّم أو مؤشّر دوّار أو ساعة جارية) ويفتح مسارًا عند النقر عليه.

القيم المُعادة: start(opts) / update(opts) ← Promise<{ ok: true, id }>، أو { ok: false, error } دون نشر شيء - 'permission' عندما تكون الإشعارات غير مسموحة (Android 13+: لم تُمنح بعد، أو أُوقفت للتطبيق)، و'channel_disabled' عندما أوقف المستخدم فئة "Live status"؛ end({ id }) ← Promise<void>. المتطلبات: تطبيق Android وإذن الإشعارات (Android 13+). غير متاح على الويب.

في تطبيق مبني بالذكاء الاصطناعي استخدم الغلاف liveProgress من @/lib/appmintNative:

import { liveProgress } from '@/lib/appmintNative';
await liveProgress.start({ id: 'order', title: 'Order #42', text: 'Being prepared', indeterminate: true });
await liveProgress.update({ id: 'order', title: 'Order #42', text: 'On the way', progress: 0.6, route: '/orders/42' });
await liveProgress.end('order');

في صفحة مكتوبة يدويًا:

async function showDelivery(step, total) {
  const P = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightLiveProgress;
  if (!P) return;                                   // not in the Android app
  if (window.Notification && Notification.permission !== 'granted') {
    if (await Notification.requestPermission() !== 'granted') return;
  }
  const r = await P.update({
    id: 'delivery',
    title: 'Your delivery',
    text: 'Stop ' + step + ' of ' + total,
    progress: step / total,                         // 0–1; or indeterminate: true
    route: '/track'                                 // opened when the card is tapped
  });
  if (!r.ok) console.warn('live card not posted:', r.error);
}

async function deliveryDone() {
  const P = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightLiveProgress;
  if (P) await P.end({ id: 'delivery' });
}

مؤقّت تمرين: يجعل startedAt البطاقة تعدّ تصاعديًا من تلقاء نفسها، فتُنشر مرة واحدة وتُنهى عند توقف التمرين:

const Live = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightLiveProgress;
let workoutStart = 0;

async function startWorkout(name) {
  workoutStart = Date.now();
  if (!Live) return;
  const r = await Live.start({ id: 'workout', title: name, text: 'Workout in progress', startedAt: workoutStart, route: '/workout' });
  if (!r.ok && r.error === 'channel_disabled') {
    showHint('Turn on "Live status" in this app\'s notification settings to see the timer.');
  }
}

async function stopWorkout() {
  if (Live) await Live.end({ id: 'workout' });
  saveWorkout({ durationMs: Date.now() - workoutStart });
}

الخيارات: id (الافتراضي 'live')، title، text، progress (من 0 إلى 1، احذفه لعدم وجود شريط)، indeterminate، startedAt (epoch بالميلي ثانية، يعرض ساعة جارية)، route.

ملاحظات: يؤدي start وupdate الشيء نفسه (نشر البطاقة أو استبدالها بالمعرّف نفسه). لا يمكن إزالة البطاقة بالسحب - استدعِ end دائمًا. اطلب الإذن أولًا: في Android 13+ ومن دونه يجيب start بـ error: 'permission' ولا يُنشر شيء.

Capacitor.AppwrightSystemPalette Capacitor#

Capacitor.Plugins.AppwrightSystemPalette — get()

مثال

يُعيد ألوان Material You في الهاتف (اللوحة التي يبنيها Android 12+ من الخلفية)، ليتطابق التطبيق مع تطبيقات النظام. ملحق AppMint الخاص (AppwrightSystemPalettePlugin.kt).

القيم المُعادة: Promise { available: false } قبل Android 12، وإلا { available: true, accent1, accent2, accent3, neutral1, neutral2 } - كل منها كائن من الدرجة إلى لون hex، بدرجات '0' و'10' و'50' و'100' … '900' و'1000' (مثل accent1['600'] === '#3F5F90'). المتطلبات: لا شيء لتفعيله، فكل تطبيق يتضمن الملحق. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

var Palette = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightSystemPalette;

async function useWallpaperColours() {
  if (!Palette) return;
  var p = await Palette.get();
  if (!p.available) return;                 // keep the app's own colours
  var root = document.documentElement.style;
  root.setProperty('--accent', p.accent1['600']);
  root.setProperty('--surface', p.neutral1['50']);
}
useWallpaperColours();

في تطبيق مبني بالذكاء الاصطناعي:

import { systemPalette } from '@/lib/appmintNative';

const p = await systemPalette();
if (p.available && p.accent1) document.documentElement.style.setProperty('--accent', p.accent1['600']);

اختر درجات فاتحة أو داكنة لتتوافق مع سمة النظام، وأعد قراءة اللوحة عندما يعود المستخدم بعد تغيير الخلفية:

var Palette = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightSystemPalette;
var darkQuery = window.matchMedia('(prefers-color-scheme: dark)');

async function applyPalette() {
  if (!Palette) return;
  var p = await Palette.get();
  if (!p.available) return;
  var dark = darkQuery.matches;
  var s = document.documentElement.style;
  s.setProperty('--accent', p.accent1[dark ? '200' : '600']);
  s.setProperty('--on-accent', p.accent1[dark ? '800' : '0']);
  s.setProperty('--bg', p.neutral1[dark ? '900' : '10']);
  s.setProperty('--text', p.neutral1[dark ? '100' : '900']);
}

darkQuery.addEventListener('change', applyPalette);
document.addEventListener('visibilitychange', function () {
  if (document.visibilityState === 'visible') applyPalette();
});
applyPalette();

ملاحظات: تحقّق من تباين النص قبل تطبيق ألوان اللوحة - فلا ضمان أن تُقرأ درجة من الخلفية جيدًا على خلفيتك. لا تكشف المتصفحات ألوان الخلفية: وعلى الويب يجيب الغلاف بـ { available: false }. تتغير الألوان عندما يغيّر المستخدم الخلفية؛ فاستدعِ get() مرة أخرى عند الاستئناف.

Capacitor.AppwrightWidget Capacitor#

Capacitor.Plugins.AppwrightWidget — set(card), clear(), placedCount(), requestPin()

مثال

يملأ ودجت الشاشة الرئيسية للتطبيق: بطاقة واحدة بعنوان وقيمة كبيرة وحتى اثني عشر سطرًا وألوانك ومسار للنقر - هي نفس البطاقة التي يقبلها AppMint.widget.set() بكل مفاتيحها. تُحفظ البطاقة على الهاتف وتظهر حتى بعد إعادة التشغيل. ملحق AppMint الخاص (AppwrightWidgetPlugin.kt).

القيم المُعادة: set({ title?, value?, caption?, lines?, route?, bg?, ink?, accent?, textSize?, align?, wrap?, … }) ← Promise { ok: true, placed }، يُرفض بسبب بطاقة غير صالحة (placed = الودجتات الموجودة حاليًا على الشاشة الرئيسية). clear() ← void. placedCount() ← { value }. requestPin() ← { value: boolean } (true عندما فُتحت ورقة "إضافة ودجت" في المشغّل). المتطلبات: لا شيء لتفعيله - فكل تطبيق يصرّح بالودجت. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

var Widget = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightWidget;

async function updateWidget(tasks) {
  if (!Widget) return;
  await Widget.set({
    title: 'Today',
    value: tasks.length + ' tasks',
    lines: tasks.slice(0, 6).map(function (t) { return t.title; }),
    route: '/today',                 // opened when the widget is tapped
    bg: '#0B0F1A', ink: '#FFFFFF', accent: '#22D3EE'
  });
}

اعرض وضعه (فقط عندما لا يكون هناك واحد موضوع بعد):

async function offerWidget() {
  if (!Widget) return;
  var n = (await Widget.placedCount()).value;
  if (n > 0) return;
  var r = await Widget.requestPin();
  if (!r.value) alert('Long-press the home screen, tap Widgets and pick this app.');
}

في تطبيق مبني بالذكاء الاصطناعي:

import { widget } from '@/lib/appmintNative';

await widget.set({ title: 'Today', value: '3 tasks', lines: ['Gym', 'Call mum'], route: '/today' });
if ((await widget.placedCount()) === 0) await widget.requestPin();

ملاحظات: الألوان بصيغة #RRGGBB أو #AARRGGBB (ألفا أولًا) أو rgba()؛ وتستخدم الألوان الفارغة بطاقة داكنة بقيمة سماوية. انظر AppMint.widget للأسطر المنسّقة وcaption وtextSize وalign وwrap وfont (ملف .ttf/.otf الخاص بك، يُعطى كمسار في تطبيقك). تفتح النقرة التطبيق على route كرابط عميق (اقرأه بـ WebToApk.getLaunchUrl() أو بالحدث appmint:deep-link). قبل أي set() يقول الودجت "Nothing to show yet"؛ ويعيده clear() إلى تلك الحالة. في المتصفح يكون can('widget') 'none'.

Capacitor.Badge Capacitor#

Capacitor.Plugins.Badge — the launcher-icon count

مثال

ملحق Badge (من capawesome) الذي يضبط الرقم على أيقونة التطبيق في شاشة المشغّل.

القيم المُعادة: Promises: set({ count }) وclear() وincrease() وdecrease() ← void؛ get() ← { count }؛ isSupported() ← { isSupported }. ليس لملحق Android طرق للأذونات. المتطلبات: لا شيء على Android. غير متاح على الويب.

في تطبيق مبني بالذكاء الاصطناعي استخدم الغلاف badge من @/lib/appmintNative:

import { badge } from '@/lib/appmintNative';
await badge.set(3);
await badge.clear();

في صفحة مكتوبة يدويًا:

async function setUnread(count) {
  const B = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Badge;
  if (!B) return false;                       // not inside the app
  try {
    const s = await B.isSupported();
    if (!s.isSupported) return false;         // this launcher shows no badges
    if (count > 0) await B.set({ count: count });
    else await B.clear();
    return true;
  } catch (e) {
    return false;
  }
}

window.addEventListener('appmint:push', function () {
  const inbox = JSON.parse(window.WebToApk ? WebToApk.getPushInbox() : '[]');
  setUnread(inbox.filter(function (m) { return !m.read; }).length);
});

عُدّ تصاعديًا وتنازليًا عند وصول العناصر وقراءتها:

const B = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Badge;
async function onNewMessage() { if (B) await B.increase(); }
async function onMessageRead() { if (B) await B.decrease(); }
async function showCurrent() {
  if (!B) return;
  const { count } = await B.get();
  console.log('badge shows', count);
}

ملاحظات: في Android يعتمد الشكل على المشغّل (رقم أو نقطة أو لا شيء). يكتب navigator.setAppBadge(n) في التطبيق على الشارة نفسها (وهو موجود فقط في المشغّلات التي تستطيع عرض عدّاد).

Capacitor.BarcodeScanner Capacitor#

Capacitor.Plugins.BarcodeScanner — scan, startScan, readBarcodesFromImage, isSupported

مثال

مسح QR والباركود بواجهة @capacitor-mlkit/barcode-scanning البرمجية - ملحق غلاف التطبيق الخاص (BarcodeScannerPlugin.kt) فوق ماسح الرموز نفسه من Google كما في AppMint.scanCode(). كما تشترك استدعاءات @capacitor-community/barcode-scanner الأقدم (startScan() الذي يُحَلّ بـ { hasContent, content } وcheckPermission وprepare وhideBackground) في هذا الاسم وتعمل أيضًا.

القيم المُعادة: scan({ formats?, autoZoom? }) ← { barcodes: [{ rawValue, displayValue, format, valueType, bytes?, cornerPoints?, urlBookmark?, wifi?, email?, phone?, sms?, geoPoint?, contactInfo?, calendarEvent?, driverLicense? }] } (يُرفض بـ "scan canceled." عندما يتراجع المستخدم)؛ readBarcodesFromImage({ path, formats? }) ← { barcodes }؛ isSupported() ← { supported }؛ isGoogleBarcodeScannerModuleAvailable() ← { available }؛ installGoogleBarcodeScannerModule() (التقدّم كـ googleBarcodeScannerModuleInstallProgress)؛ checkPermissions() / requestPermissions() ← { camera }؛ openSettings(). format هو 'QR_CODE' أو 'EAN_13' أو 'CODE_128'… المتطلبات: لا شيء لتفعيله ولا إذن كاميرا - فماسح Google يرسم شاشته بنفسه (تنزّله خدمات Play عند أول استخدام). يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

const Scanner = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.BarcodeScanner;

scanBtn.onclick = async () => {
  if (!Scanner) return;
  try {
    const { barcodes } = await Scanner.scan({ formats: ['QR_CODE'] });
    const code = barcodes[0];
    if (code.valueType === 'URL') location.href = code.urlBookmark.url;
    else resultBox.textContent = code.displayValue;
  } catch (e) {
    if (e.message !== 'scan canceled.') alert(e.message);
  }
};

البحث عن سعر من باركود منتج، بأسلوب المستمع (startScan + barcodesScanned) الذي تستخدمه تطبيقات ML Kit أصلًا:

const Scanner = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.BarcodeScanner;

async function scanProduct() {
  const listener = await Scanner.addListener('barcodesScanned', async ({ barcodes }) => {
    await listener.remove();
    await Scanner.stopScan();
    const ean = barcodes[0].rawValue;
    const res = await fetch('https://api.example.com/products/' + encodeURIComponent(ean));
    priceBox.textContent = res.ok ? (await res.json()).price : 'Not found';
  });
  await Scanner.startScan({ formats: ['EAN_13', 'EAN_8', 'UPC_A', 'UPC_E'] });
}

اقرأ رمز QR من صورة يملكها المستخدم أصلًا:

const Scanner = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.BarcodeScanner;
const Camera = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Camera;

async function qrFromGallery() {
  const photo = await Camera.getPhoto({ source: 'PHOTOS', resultType: 'uri', quality: 90 });
  const { barcodes } = await Scanner.readBarcodesFromImage({ path: photo.path, formats: ['QR_CODE'] });
  return barcodes.length ? barcodes[0].rawValue : null;
}

ملاحظات: يعرض startScan() في الإضافة الأصلية معاينة كاميرا حية خلف صفحة شفافة ويستمر في المسح حتى stopScan()؛ أما هنا فيفتح شاشة الماسح، ويُطلق أول رمز barcodeScanned / barcodesScanned، وتنتهي الجلسة (ثم يُنجز الاستدعاء بـ { hasContent, content, format } لكود المجتمع؛ والماسح المُغلق يُنجز بـ { hasContent: false }). الفلاش والتكبير يخصّان تلك المعاينة، لذا isTorchAvailable() هي false وتُرفض دوال ضبط الفلاش/التكبير - لشاشة الماسح زر مصباح خاص بها. تجيب checkPermissions() بـ granted عندما لا يملك التطبيق إذن كاميرا إطلاقًا (فالماسح لا يحتاج إليه)؛ والتطبيق الذي يستخدم الكاميرا أيضًا يحصل على حالتها الحقيقية. تستخدم التطبيقات المبنية بالذكاء الاصطناعي scanCode() من @/lib/appmintNative.

Capacitor.Browser Capacitor#

Capacitor.Plugins.Browser — open({ url, toolbarColor }), close(), addListener('browserFinished')

مثال

يفتح صفحة ويب في تبويب متصفح داخل التطبيق (Chrome Custom Tabs)، فيقرأ المستخدم الشروط أو المستندات أو صفحة تسجيل الدخول ويعود بإيماءة واحدة (@capacitor/browser 8.0.3).

القيم المُعادة: open() / close() ← Promise void. الأحداث: browserFinished (أغلق المستخدم التبويب) وbrowserPageLoaded. المتطلبات: لا شيء - فكل تطبيق يتضمن الملحق (يجب تثبيت متصفح يدعم Custom Tabs، مثل Chrome). يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

var Browser = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Browser;

async function openTerms() {
  if (!Browser) return;
  var h = await Browser.addListener('browserFinished', function () {
    console.log('user closed the terms');
    h.remove();
  });
  await Browser.open({ url: 'https://example.com/terms', toolbarColor: '#0B0F1A' });
}

في تطبيق مبني بالذكاء الاصطناعي (تبويب جديد على الويب):

import { browser } from '@/lib/appmintNative';

const off = browser.onClosed(() => refreshAccount());
await browser.open('https://example.com/terms', { toolbarColor: '#0B0F1A' });
// call off() when you no longer care

افتح رابطًا، وأغلق التبويب عندما تصل عودة تسجيل الدخول إلى الصفحة:

var Browser = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Browser;

async function openHelp() {
  if (!Browser) { window.open('https://example.com/help', '_blank'); return; }
  try {
    await Browser.open({ url: 'https://example.com/help' });
  } catch (e) {
    alert('Could not open the page: ' + e.message);
  }
}

window.addEventListener('appmint:deep-link', function () {
  if (Browser) Browser.close().catch(function () {});
});

ملاحظات: يجب ألا يكون url فارغًا. وtoolbarColor لون hex؛ ويُتجاهل اللون غير الصالح. كما يعمل close() على Android في إصدار هذا الملحق (إذ ينهي نشاط المضيف للتبويب) - وهذا مفيد بعد أن يعود رابط تسجيل الدخول إلى التطبيق. والتبويب متصفح منفصل: لا يشارك ملفات تعريف الارتباط الخاصة بالتطبيق ولا localStorage.

Capacitor.Calendar Capacitor#

Capacitor.Plugins.Calendar — createEvent, findEvents, deleteEvent, listCalendars, openCalendar

مثال

يضيف ويبحث ويحذف الأحداث في تقويم الهاتف نفسه (Android CalendarContract)، ويفتح تطبيق التقويم على تاريخ معيّن. نسخة مضمّنة @capacitor/calendar (template/plugins/calendar).

القيم المُعادة: createEvent() ← { id }؛ findEvents() ← { events: [{ id, title, location?, notes?, startDate, endDate, isAllDay, calendarId, calendarName?, attendees? }] }؛ listCalendars() ← { calendars: [{ id, name, displayName?, isPrimary }] }؛ deleteEvent() وopenCalendar() ← void. التواريخ بالميلي ثانية منذ epoch. المتطلبات: فعّل التقويم في الخطوة 4 (الوصول) عند الإنشاء. وفي تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي يضيف الإنشاء إذن التقويم تلقائيًا عندما تحتوي ملفاتك على استدعاء Calendar.<method>( مكتوب بهذه الصيغة تمامًا (كما أدناه) أو calendar.createEvent( في الغلاف. يطلب الملحق من المستخدم الوصول إلى التقويم عند أول استخدام. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

var Calendar = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Calendar;

async function addDentist() {
  if (!Calendar) return;
  var start = new Date('2026-10-02T10:00:00').getTime();
  try {
    var r = await Calendar.createEvent({
      title: 'Dentist', startDate: start, endDate: start + 60 * 60 * 1000,
      location: 'Main St 5', notes: 'Bring the card', isAllDay: false,
      firstReminderMinutes: 30
    });
    console.log('event id', r.id);
  } catch (e) {
    // e.code: OS-PLUG-CLDR-0020 permission denied, -0001 bad arguments
    alert('Could not add the event: ' + e.message);
  }
}

اقرأ هذا الأسبوع، واحذف حدثًا، وافتح التقويم:

async function thisWeek() {
  if (!Calendar) return;
  var now = Date.now();
  var found = await Calendar.findEvents({ startDate: now, endDate: now + 7 * 864e5 });
  found.events.forEach(function (ev) { console.log(ev.title, new Date(ev.startDate)); });
  if (found.events.length) await Calendar.deleteEvent({ id: found.events[0].id });
  await Calendar.openCalendar({ date: now });
}

في تطبيق مبني بالذكاء الاصطناعي (على الويب ينزّل createEvent ملف .ics؛ وتُعيد القراءة []):

import { calendar } from '@/lib/appmintNative';

if ((await calendar.requestPermission()) === 'granted') {
  const id = await calendar.createEvent({ title: 'Dentist', start: new Date('2026-10-02T10:00'), end: new Date('2026-10-02T11:00') });
  const week = await calendar.listEvents({ from: Date.now(), to: Date.now() + 7 * 864e5 });
  if (id) await calendar.deleteEvent(id);
  await calendar.openCalendar(new Date());
}

ملاحظات: من دون إذن التقويم في التطبيق لا يظهر أي مربع حوار وتُرفض كل استدعاءات الأحداث (إذن مفقود)؛ والمستخدم الذي يرفض يحصل على OS-PLUG-CLDR-0020. يُنشئ recurrence: { frequency: 'daily' | 'weekly' | 'monthly' | 'yearly', interval?, count?, endDate? } حدثًا متكررًا. يفتح createEventInteractively({ title, startDate, endDate }) محرّر تطبيق التقويم نفسه بدلًا من ذلك: يُحَلّ بـ { id } للحدث المحفوظ، ويُرفض بـ OS-PLUG-CLDR-0006 عندما يغلق المستخدم المحرّر دون حفظ (ويحتاج إلى صلاحية قراءة وكتابة للتمييز بين الحالتين). ينهي deleteEvent({ id, fromDate }) حدثًا متكررًا قبل fromDate ويُبقي التكرارات السابقة. يُعيد الغلاف null / [] / false بدلًا من رمي خطأ.

Capacitor.Camera Capacitor#

Capacitor.Plugins.Camera

مثال

ملحق Capacitor Camera الرسمي، المضمّن في التطبيق: التقط صورة بالكاميرا أو اختر صورًا من المعرض.

القيم المُعادة: Promises. getPhoto(opts) ← { dataUrl | base64String | webPath, format, saved } (يعتمد الحقل على resultType)، ويُرفض عندما يلغي المستخدم؛ pickImages(opts) ← { photos: [{ webPath, format }] }؛ checkPermissions() / requestPermissions({ permissions }) ← { camera, photos }. المتطلبات: فعّل الكاميرا في الخطوة 4 (الوصول). وفي تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي يضيف الإنشاء إذن الكاميرا تلقائيًا عندما تحتوي ملفاتك على Camera.getPhoto( مكتوبًا بهذه الصيغة تمامًا (كما أدناه)، أو getUserMedia مع فيديو، أو <input capture>. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

التقط صورة واعرضها.

async function takePhoto() {
  var Camera = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Camera;
  if (!Camera) { showMessage('Open the app to use the camera.'); return; }
  try {
    var perm = await Camera.requestPermissions({ permissions: ['camera'] });
    if (perm.camera !== 'granted') { showMessage('Camera permission was refused.'); return; }

    var photo = await Camera.getPhoto({
      source: 'CAMERA',          // 'CAMERA' | 'PHOTOS' | 'PROMPT'
      resultType: 'dataUrl',     // 'dataUrl' | 'base64' | 'uri'
      quality: 80,
      direction: 'REAR',         // 'REAR' | 'FRONT'
      correctOrientation: true,
      saveToGallery: false
    });
    document.getElementById('photo').src = photo.dataUrl;
  } catch (err) {
    // The user closed the camera (or no camera app). Nothing to do.
  }
}

اختر حتى 5 صور من المعرض.

async function choosePhotos() {
  var Camera = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Camera;
  if (!Camera) return;
  try {
    var result = await Camera.pickImages({ quality: 80, limit: 5 });
    result.photos.forEach(function (p) {
      var img = document.createElement('img');
      img.src = p.webPath;       // a URL the page can load
      document.getElementById('gallery').appendChild(img);
    });
  } catch (err) { /* cancelled */ }
}

في تطبيق مبني بالذكاء الاصطناعي استخدم الغلاف، وهو يعمل على الويب أيضًا (ويُعيد null بدلًا من رمي خطأ عند الإلغاء):

import { camera } from '@/lib/appmintNative';
const photo = await camera.take({ source: 'camera' });

ملاحظات: لا يحتاج pickImages إلى إذن كاميرا. وإن لم يكن إذن الكاميرا في التطبيق (كان الخيار معطّلًا ولم يجد الإنشاء أي استدعاء للكاميرا) يُرفض إذن الكاميرا دون أي مطالبة. كما أن <input type="file" accept="image/*" capture> يفتح الكاميرا أيضًا. الكاميرا حسّاسة: صرّح بها في نموذج Data safety في Play.

Capacitor.CapacitorBarcodeScanner Capacitor#

Capacitor.Plugins.CapacitorBarcodeScanner — scanBarcode({ hint })

مثال

مسح QR / الباركود باستدعاء واحد بواجهة @capacitor/barcode-scanner الرسمية - ملحق غلاف التطبيق الخاص (CapacitorBarcodeScannerPlugin.kt) فوق ماسح الرموز نفسه من Google كما في AppMint.scanCode(). الكود المكتوب لحزمة npm (CapacitorBarcodeScanner.scanBarcode({ hint: CapacitorBarcodeScannerTypeHint.ALL })) يعمل دون تغيير.

القيم المُعادة: scanBarcode({ hint, scanInstructions?, cameraDirection?, scanOrientation? }) ← { ScanResult, format }. hint / format هي أرقام الحزمة: 0 QR_CODE، 1 AZTEC، 2 CODABAR، 3 CODE_39، 4 CODE_93، 5 CODE_128، 6 DATA_MATRIX، 8 ITF، 9 EAN_13، 10 EAN_8، 11 PDF_417، 14 UPC_A، 15 UPC_E، 17 ALL. يُرفض بالرمز OS-PLUG-BARC-0006 عندما يغلق المستخدم الماسح. المتطلبات: لا شيء لتفعيله ولا إذن كاميرا - فماسح Google يرسم شاشته بنفسه (تنزّله خدمات Play عند أول استخدام). يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

const Scanner = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.CapacitorBarcodeScanner;

ticketBtn.onclick = async () => {
  if (!Scanner) return;
  try {
    const { ScanResult } = await Scanner.scanBarcode({ hint: 0 });   // QR codes only
    const ok = await fetch('/api/tickets/check?code=' + encodeURIComponent(ScanResult)).then((r) => r.ok);
    statusBox.textContent = ok ? 'Valid ticket' : 'Unknown ticket';
  } catch (e) {
    if (e.code !== 'OS-PLUG-BARC-0006') alert(e.message);
  }
};

اقبل أي رمز، وأخبر المستخدم بنوعه:

const Scanner = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.CapacitorBarcodeScanner;
const NAMES = { 0: 'QR code', 5: 'Code 128', 9: 'EAN-13', 10: 'EAN-8', 14: 'UPC-A', 15: 'UPC-E' };

async function scanAnything() {
  const { ScanResult, format } = await Scanner.scanBarcode({ hint: 17 });
  return (NAMES[format] || 'Barcode') + ': ' + ScanResult;
}

تعامل مع رفض لا يعني أن المستخدم أغلق الماسح:

const Scanner = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.CapacitorBarcodeScanner;

async function scanProduct() {
  if (!Scanner) { alert('Scanning works only inside the app'); return null; }
  try {
    const { ScanResult } = await Scanner.scanBarcode({ hint: 9 });   // EAN-13
    return ScanResult;
  } catch (e) {
    if (e.code === 'OS-PLUG-BARC-0006') return null;                 // user closed the scanner
    alert('Scan failed: ' + e.message);
    return null;
  }
}

ملاحظات: تنسّق scanInstructions وscanButton وcameraDirection وscanOrientation شاشة الكاميرا الخاصة بالمصدر الأصلي ولا أثر لها على ماسح Google (الذي يستخدم الكاميرا الخلفية دائمًا). لا تُدعم MAXICODE وRSS_14 وRSS_EXPANDED وUPC_EAN_EXTENSION كقيمة لـ hint (يُرفض OS-PLUG-BARC-0008)؛ ويمسح 17 (ALL) كل الأنواع المدعومة. تستخدم التطبيقات المبنية بالذكاء الاصطناعي scanCode() من @/lib/appmintNative.

Capacitor.CapacitorCookies Capacitor#

Capacitor.Plugins.CapacitorCookies — getCookies / setCookie / deleteCookie / clearCookies / clearAllCookies

مثال

يقرأ ويكتب ملفات تعريف الارتباط الخاصة بالتطبيق، بما فيها ملفات مضيف آخر تتحدث إليه صفحتك (من Capacitor core CapacitorCookies).

يعيد: getCookies({ url? }) ← { [name]: value } - مع url، ملفات تعريف الارتباط التي سيرسلها ذلك العنوان (المضيف والمسار)؛ وبدونه، document.cookie الخاصة بالصفحة نفسها. يرفض url الذي ليس http(s). setCookie({ key, value, url?, path?, expires? })، deleteCookie({ key, url? })، clearCookies({ url? })، clearAllCookies() ← Promise ‏void؛ وsetCookie / deleteCookie تُرفضان دون سلسلة key (وvalue) ولا تكتبان شيئًا حينها. يتطلب: لا شيء لتفعيله - كل تطبيق يستضيف الإضافة. يوجد window.Capacitor في الصفحات التي يقدمها التطبيق بنفسه (HTML وZIP والتطبيقات المبنية بالذكاء الاصطناعي)، وفي تطبيق وضع الموقع، على موقعك أنت (نطاقه ونطاقاته الفرعية) - وليس أبدًا على المواقع الأخرى التي يفتحها التطبيق. اكتشف الدعم.

var Cookies = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.CapacitorCookies;

// Read the session cookie your API host set — not the page's own cookies.
async function apiSession() {
  if (!Cookies) return null;          // not inside the app
  var jar = await Cookies.getCookies({ url: 'https://api.example.com/' });
  return jar.session || null;
}

// Remember a choice for the API host, for 30 days, on one path only.
async function rememberRegion(region) {
  if (!Cookies) return;
  var expires = new Date(Date.now() + 30 * 864e5).toUTCString();
  await Cookies.setCookie({ url: 'https://api.example.com', path: '/v2', key: 'region', value: region, expires: expires });
}

تسجيل الخروج: احذف ملفات تعريف الارتباط لذلك المضيف، أو كل ملف يحتفظ به التطبيق.

async function signOut(everything) {
  if (!Cookies) return;
  if (everything) await Cookies.clearAllCookies();
  else await Cookies.clearCookies({ url: 'https://api.example.com' });
}

ملاحظات: لا يُعاد ملف تعريف الارتباط الذي ضُبط مع path إلا لـ url تحت ذلك المسار. تُفكّ ترميزات القيم (URL-decoded) عند القراءة. تُشارَك ملفات تعريف الارتباط بين كل صفحة يعرضها التطبيق، لذا لا تحتفظ فيها بأسرار قد تقرؤها صفحة غريبة - مكانها Preferences أو التخزين الآمن للتطبيق.

Capacitor.CapacitorSQLite Capacitor#

Capacitor.Plugins.CapacitorSQLite — createConnection, open, execute, query, run, executeSet, close

مثال

قاعدة بيانات SQLite حقيقية على الهاتف، بواجهة @capacitor-community/sqlite البرمجية. نسخة @capacitor-community/sqlite (template/plugins/sqlite) مضمّنة، تعمل على SQLite الخاصة بنظام Android. الكود المكتوب لحزمة npm (new SQLiteConnection(CapacitorSQLite)) يعمل دون تغيير؛ أما الصفحة المكتوبة يدويًا فتستدعي كائن الملحق مباشرةً كما يلي.

القيم المُعادة: createConnection({ database, version, encrypted: false, mode: 'no-encryption', readonly: false }) وopen({ database }) وclose({ database }) وcloseConnection({ database }) ← void؛ execute({ database, statements }) ← { changes: { changes } }؛ run({ database, statement, values }) ← { changes: { changes, lastId } }؛ query({ database, statement, values }) ← { values: [rows] }؛ executeSet({ database, set: [{ statement, values }] }) ← { changes }؛ isDBExists({ database }) / isDBOpen({ database }) ← { result }؛ beginTransaction وcommitTransaction وrollbackTransaction وisTransactionActive({ database })؛ deleteDatabase({ database })؛ exportToJson / importFromJson للنسخ الاحتياطية. المتطلبات: لا شيء لتفعيله ولا إذن. تعيش قاعدة البيانات في التخزين الخاص بالتطبيق وتبقى بعد تحديثاته. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

const DB = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.CapacitorSQLite;
const database = 'notes';

async function openNotes() {
  if (!DB) return false;
  await DB.createConnection({ database, version: 1, encrypted: false, mode: 'no-encryption', readonly: false });
  await DB.open({ database, readonly: false });
  await DB.execute({ database, statements:
    'CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY AUTOINCREMENT, text TEXT NOT NULL, at INTEGER);' });
  return true;
}

async function addNote(text) {
  const r = await DB.run({ database, statement: 'INSERT INTO notes (text, at) VALUES (?, ?);', values: [text, Date.now()] });
  return r.changes.lastId;
}

async function listNotes() {
  const r = await DB.query({ database, statement: 'SELECT id, text, at FROM notes ORDER BY at DESC;', values: [] });
  return r.values || [];
}

عدة عمليات كتابة يجب أن تنجح كلها أو لا تنجح أي منها - معاملة واحدة بـ executeSet:

const DB = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.CapacitorSQLite;

async function moveMoney(database, fromId, toId, amount) {
  await DB.executeSet({ database, transaction: true, set: [
    { statement: 'UPDATE accounts SET balance = balance - ? WHERE id = ?;', values: [amount, fromId] },
    { statement: 'UPDATE accounts SET balance = balance + ? WHERE id = ?;', values: [amount, toId] },
    { statement: 'INSERT INTO ledger (from_id, to_id, amount, at) VALUES (?, ?, ?, ?);', values: [fromId, toId, amount, Date.now()] },
  ] });
}

افتح قاعدة البيانات مرة واحدة فقط عند كل تشغيل للتطبيق، حتى عندما تطلبها شاشتان في الوقت نفسه:

const DB = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.CapacitorSQLite;
let ready = null;

function db(database) {
  if (!ready) ready = (async () => {
    try {
      await DB.createConnection({ database, version: 1, encrypted: false, mode: 'no-encryption', readonly: false });
    } catch (e) {
      if (!/already exists/i.test(e.message)) throw e;   // a page reload keeps the native connection
    }
    if (!(await DB.isDBOpen({ database, readonly: false })).result) await DB.open({ database, readonly: false });
    return database;
  })();
  return ready;
}

ملاحظات: يُبنى التطبيق بدون SQLCipher، لذا فقواعد البيانات غير مشفّرة: يُرفض encrypted: true وsetEncryptionSecret وchangeEncryptionSecret وisSecretStored وبقية استدعاءات التشفير بالرسالة "Encryption is not available in this app". يجيب isInConfigEncryption() بـ false. كل شيء آخر (الاستعلامات والقيم المرتبطة والمعاملات والترقيات ذات الإصدارات عبر addUpgradeStatement واستيراد/تصدير JSON) هو كود الملحق نفسه. تحمل الرفضات نص خطأ SQLite.

Capacitor.Clipboard Capacitor#

Capacitor.Plugins.Clipboard — write(options), read()

مثال

ينسخ نصًا (أو رابطًا) إلى حافظة النظام ويقرأه منها (@capacitor/clipboard 8.0.1).

يعيد: write({ string?, url?, image?, label? }) ← Promise ‏void. read() ← Promise ‏{ value, type } (type هو 'text/plain' للنص). يتطلب: لا شيء لتفعيله - كل تطبيق يستضيف الإضافة. يوجد window.Capacitor في الصفحات التي يقدمها التطبيق بنفسه (HTML وZIP والتطبيقات المبنية بالذكاء الاصطناعي)، وفي تطبيق وضع الموقع، على موقعك أنت (نطاقه ونطاقاته الفرعية) - وليس أبدًا على المواقع الأخرى التي يفتحها التطبيق. اكتشف الدعم.

async function copyCode(code) {
  var Clipboard = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Clipboard;
  if (!Clipboard) { alert('Copy is not available here'); return; }
  await Clipboard.write({ string: code, label: 'Invite code' });
}

async function pasteCode(input) {
  var Clipboard = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Clipboard;
  if (!Clipboard) return;
  try {
    var r = await Clipboard.read();
    input.value = r.value;
  } catch (e) {
    // An EMPTY clipboard is a rejection: "There is no data on the clipboard".
    if (/no data/i.test(e.message)) input.value = '';
    else alert('Could not read the clipboard: ' + e.message);
  }
}

في تطبيق مبني بالذكاء الاصطناعي يحوّل الغلاف بصيغة Capacitor الحافظة الفارغة إلى '':

import { Clipboard } from '@/lib/appmintNative';

await Clipboard.write({ string: 'ABC-123' });
const { value } = await Clipboard.read();   // '' when the clipboard is empty

انسخ رابطًا وأكّد ذلك باهتزاز خفيف:

async function copyLink(url) {
  var P = window.Capacitor && window.Capacitor.Plugins;
  if (!P || !P.Clipboard) { alert('Copy is not available here'); return; }
  await P.Clipboard.write({ url: url });
  if (P.Haptics) P.Haptics.impact({ style: 'LIGHT' });
  copiedToast.hidden = false;
}

ملاحظات: يحتاج write إلى أحد string أو url أو image (رابط بيانات)، وإلا يُرفض بالرسالة "No data provided". يعرض Android 12+ إشعاره الخاص "pasted from…" عند القراءة - وهذا من النظام وليس من التطبيق. يرسل write في الغلاف فقط string || url.

Capacitor.Contacts Capacitor#

Capacitor.Plugins.Contacts — getContacts, getContact, pickContact, createContact, deleteContact

مثال

دفتر عناوين الهاتف بواجهة @capacitor-community/contacts البرمجية. نسخة @capacitor-community/contacts (template/plugins/contacts) مضمّنة. الكود المكتوب لحزمة npm يعمل دون تغيير.

القيم المُعادة: checkPermissions() / requestPermissions() ← { contacts, contactsWrite } (كل منها 'granted' | 'denied' | 'prompt'؛ contacts للقراءة، وcontactsWrite للإضافة والحذف)؛ getContacts({ projection }) ← { contacts: [ContactPayload] }؛ getContact({ contactId, projection }) وpickContact({ projection }) ← { contact }؛ createContact({ contact }) ← { contactId }؛ deleteContact({ contactId }) ← void. يحدّد projection الحقول: { name, phones, emails, organization, birthday, note, urls, postalAddresses, image } (كل منها true). المتطلبات: فعّل جهات الاتصال في الخطوة 4 (الوصول) للقراءة، وتعديل جهات الاتصال أيضًا للإضافة أو الحذف. تفعّلها تطبيقات HTML وZIP وAI-Studio تلقائيًا عندما يستدعي الكود Contacts.getContacts( / getContact( / pickContact( (للقراءة) أو Contacts.createContact( / deleteContact( (للاثنين). يطلب الملحق الإذن من المستخدم عند أول استخدام؛ وإن لم يكن الخيار مفعّلًا يُرفض الاستدعاء بالرسالة "Permission is required to access contacts." - وفي تطبيق لم تُفعَّل فيه جهات الاتصال ولا الرسائل ولا سجل المكالمات لا يكون الملحق موجودًا في التطبيق أصلًا (يرفض Capacitor بالرسالة "not implemented"). يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

const Contacts = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Contacts;

async function friendsWithPhones() {
  if (!Contacts) return [];
  const { contacts } = await Contacts.requestPermissions();
  if (contacts !== 'granted') return [];
  const res = await Contacts.getContacts({ projection: { name: true, phones: true } });
  return res.contacts
    .filter((c) => c.phones && c.phones.length)
    .map((c) => ({ id: c.contactId, name: (c.name && c.name.display) || '', phone: c.phones[0].number }));
}

دع المستخدم يختار شخصًا واحدًا من أداة الاختيار في النظام:

const Contacts = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Contacts;

inviteBtn.onclick = async () => {
  if (!Contacts) return;
  try {
    const { contact } = await Contacts.pickContact({ projection: { name: true, phones: true, emails: true } });
    inviteField.value = (contact.emails && contact.emails[0] && contact.emails[0].address) || '';
  } catch (e) {
    // The user closed the picker, or refused access.
  }
};

احفظ بطاقة عمل من التطبيق في دفتر العناوين:

const Contacts = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Contacts;

async function saveCard(card) {
  const perm = await Contacts.requestPermissions();
  if (perm.contactsWrite !== 'granted') return null;
  const { contactId } = await Contacts.createContact({ contact: {
    name: { given: card.first, family: card.last },
    organization: { company: card.company, jobTitle: card.title },
    phones: [{ type: 'work', label: 'work', number: card.phone }],
    emails: [{ type: 'work', label: 'work', address: card.email }],
  } });
  return contactId;
}

ملاحظات: يُرفض pickContact() بالرمز CANCELLED عندما ينسحب المستخدم من أداة الاختيار. لا يملأ Android الحقول contactId وname وphones وemails وorganization وbirthday وnote وurls وpostalAddresses وimage إلا عندما يطلبها الإسقاط (projection) - اطلب فقط ما تعرضه. قراءة جهات الاتصال إذن حساس على Google Play: اشرح السبب في نموذج أمان البيانات في صفحة التطبيق. تستخدم التطبيقات المبنية بالذكاء الاصطناعي listContacts() وpickContact() وaddContact() من @/lib/appmintNative.

Capacitor.Device Capacitor#

Capacitor.Plugins.Device

مثال

ملحق Capacitor Device الرسمي، المضمّن في التطبيق: طراز الهاتف وإصدار النظام والبطارية ومعرّف ثابت واللغة.

القيم المُعادة: Promises. getInfo() ← { model, platform, operatingSystem, osVersion, androidSDKVersion, manufacturer, isVirtual, webViewVersion, … }؛ getId() ← { identifier }؛ getBatteryInfo() ← { batteryLevel (0–1), isCharging }؛ getLanguageCode() ← { value }. المتطلبات: لا شيء لتفعيله. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

اعرض الهاتف والبطارية.

async function showDevice() {
  var cap = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Device;
  if (!cap) { document.getElementById('out').textContent = navigator.userAgent; return; }
  try {
    var info = await cap.getInfo();
    var battery = await cap.getBatteryInfo();
    document.getElementById('out').textContent =
      info.manufacturer + ' ' + info.model + ' - ' + info.operatingSystem + ' ' + info.osVersion +
      ' - battery ' + Math.round(battery.batteryLevel * 100) + '%' + (battery.isCharging ? ' (charging)' : '');
  } catch (err) {
    document.getElementById('out').textContent = 'Could not read device info';
  }
}

معرّف ثابت واللغة.

async function helloServer() {
  var cap = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Device;
  if (!cap) return;
  var id = (await cap.getId()).identifier;
  var lang = (await cap.getLanguageCode()).value;     // e.g. "en"
  await fetch('https://api.example.com/hello?lang=' + encodeURIComponent(lang), {
    method: 'POST', body: JSON.stringify({ device: id })
  });
}

في تطبيق مبني بالذكاء الاصطناعي استخدم الغلاف، وهو يعمل على الويب أيضًا:

import { Device } from '@/lib/appmintNative';
const info = await Device.getInfo();

ملاحظات: لمعرّف إنشاء Android وتصحيح الأمان والشاشة والذاكرة والشبكة يعطي WebToApk.getDeviceInfo() في الغلاف تفاصيل أكثر. معرّفات الملحق ليست معرّفات عتاد؛ وقد تتغير بعد إعادة ضبط المصنع.

Capacitor.Dialog Capacitor#

Capacitor.Plugins.Dialog — alert, confirm, prompt

مثال

يعرض مربع حوار حقيقيًا من نظام Android: رسالة أو سؤال نعم/لا أو سؤال نصي من سطر واحد (@capacitor/dialog 8.0.1).

القيم المُعادة: alert({ title?, message, buttonTitle? }) ← Promise void. confirm({ title?, message, okButtonTitle?, cancelButtonTitle? }) ← { value: boolean }. prompt({ title?, message, okButtonTitle?, cancelButtonTitle?, inputPlaceholder?, inputText? }) ← { value: string, cancelled: boolean }. المتطلبات: لا شيء لتفعيله، فكل تطبيق يتضمن الملحق. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

var Dialog = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Dialog;

async function deleteNote(id) {
  if (!Dialog) return;
  var r = await Dialog.confirm({
    title: 'Delete note?',
    message: 'This cannot be undone.',
    okButtonTitle: 'Delete',
    cancelButtonTitle: 'Keep'
  });
  if (r.value) removeNote(id);
}

التنبيه والإدخال:

async function renameFlow() {
  if (!Dialog) return;
  await Dialog.alert({ title: 'Saved', message: 'Your changes are stored.', buttonTitle: 'OK' });

  var p = await Dialog.prompt({ title: 'Rename', message: 'New name', inputText: 'Groceries', inputPlaceholder: 'List name' });
  if (!p.cancelled && p.value.trim()) renameList(p.value.trim());
}

في تطبيق مبني بالذكاء الاصطناعي لا يرفض الغلاف أبدًا: يكون الإغلاق { value: false } / { cancelled: true }، وعلى الويب يستخدم مربعات حوار المتصفح نفسها:

import { Dialog } from '@/lib/appmintNative';

const { value } = await Dialog.confirm({ title: 'Delete note?', message: 'This cannot be undone.' });
if (value) removeNote(id);

ملاحظات: message مطلوب - ومن دونه يرفض الملحق بالرسالة "Please provide a message for the dialog". مربعات الحوار مانعة (modal)؛ فاستخدمها فقط للقرارات التي يجب أن توقف المتابعة.

Capacitor.Filesystem Capacitor#

Capacitor.Plugins.Filesystem

مثال

ملحق Filesystem القياسي من Capacitor: اقرأ واكتب ملفات في تخزين التطبيق الخاص به (وليس مجلدًا اختاره المستخدم - لذلك استخدم WebToApkFS).

القيم المُعادة: تُعيد كل طريقة Promise وتُرفض عند الفشل (مثل قراءة ملف غير موجود). readFile ← { data }، writeFile ← { uri }، readdir ← { files: [{ name, type, size, mtime, uri }] }، mkdir / deleteFile ← لا شيء. المتطلبات: لا شيء لتفعيله؛ ولا يحتاج 'DATA' و'CACHE' إلى إذن.

ليس في الصفحة استيراد Directory / Encoding، لذا مرّر النصوص العادية 'DATA' (ملفات التطبيق الخاصة، تبقى حتى إلغاء التثبيت) أو 'CACHE' (قد يمسحها النظام)، و'utf8' للنص:

async function saveAndReadBack() {
  var Fs = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Filesystem;
  if (!Fs) { console.log('Filesystem plugin not available (not inside the app)'); return; }

  try {
    await Fs.mkdir({ path: 'notes', directory: 'DATA', recursive: true });
    await Fs.writeFile({ path: 'notes/today.txt', data: 'Hello', directory: 'DATA', encoding: 'utf8', recursive: true });

    var r = await Fs.readFile({ path: 'notes/today.txt', directory: 'DATA', encoding: 'utf8' });
    console.log('text:', r.data);

    var dir = await Fs.readdir({ path: 'notes', directory: 'DATA' });
    dir.files.forEach(function (f) { console.log(f.type, f.name, f.size); });   // type: 'file' | 'directory'

    await Fs.deleteFile({ path: 'notes/today.txt', directory: 'DATA' });
  } catch (e) {
    console.log('File error:', e.message);   // e.g. the file does not exist
  }
}

احذف encoding لكتابة أو قراءة بيانات ثنائية بصيغة base64:

var Fs = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Filesystem;
var base64Data = btoa('raw bytes here');   // any base64 string, e.g. from a canvas or FileReader
if (Fs) {
  Fs.writeFile({ path: 'blobs/data.bin', data: base64Data, directory: 'CACHE', recursive: true })
    .then(function (w) { console.log('written to', w.uri); })
    .catch(function (e) { console.log('write failed:', e.message); });
}

في التطبيقات المبنية بالذكاء الاصطناعي، الغلاف @/lib/appmintNative الذي يستخدم هذا الملحق هو fileTransfer (يجهّز التنزيلات والرفوعات في 'CACHE'):

import { fileTransfer } from '@/lib/appmintNative';

const r = await fileTransfer.download('https://example.com/report.pdf', 'report.pdf', {
  onProgress: (p) => console.log(Math.round(p.fraction * 100) + '%'),
});
if (r.path) console.log('saved in the app at', r.path);   // on the web the browser saves it instead

ملاحظات: window.Capacitor موجود فقط داخل التطبيق - اكتشف الدعم دائمًا. الملفات في 'DATA' خاصة بالتطبيق وتُحذف عند إلغاء التثبيت؛ ولوضع ملفات يراها المستخدم، استخدم WebToApkFS (مجلد يمنحه المستخدم) أو تنزيلًا.

Capacitor.FileTransfer Capacitor#

Capacitor.Plugins.FileTransfer

مثال

ملحق Capacitor File Transfer الرسمي: ينزّل عنوان URL إلى ملف على الهاتف، أو يرفع ملفًا من الهاتف، بشكل أصلي مع أحداث تقدّم.

القيم المُعادة: downloadFile({url, path, headers?, progress?}) ← Promise<{path}>؛ uploadFile({url, path, fileKey?, mimeType?, method?, headers?, progress?}) ← Promise<{bytesSent, responseCode, response, headers}>. يصل التقدّم كأحداث مستمع 'progress' {type, url, bytes, contentLength, lengthComputable}. يُرفض الفشل بـ {message, code}. المتطلبات: لا شيء لتفعيله. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى. تحقّق دائمًا من وجوده قبل الاستخدام.

نزّل إلى ذاكرة التخزين المؤقت للتطبيق مع عرض التقدّم:

async function download(url, name, onPct) {
  const P = window.Capacitor && window.Capacitor.Plugins;
  if (!P || !P.FileTransfer || !P.Filesystem) return null;          // not in the app
  const target = await P.Filesystem.getUri({ path: name, directory: 'CACHE' });
  const listener = await P.FileTransfer.addListener('progress', function (p) {
    if (p.url === url && p.lengthComputable) onPct(Math.round(p.bytes / p.contentLength * 100));
  });
  try {
    const r = await P.FileTransfer.downloadFile({ url: url, path: target.uri, progress: true });
    return r.path;
  } catch (e) {
    alert('Download failed: ' + e.message);
    return null;
  } finally {
    listener.remove();
  }
}

ارفع ملفًا موجودًا أصلًا على الهاتف:

const P = window.Capacitor && window.Capacitor.Plugins;
if (P && P.FileTransfer) {
  P.FileTransfer.uploadFile({
    url: 'https://example.com/api/upload', path: localFileUri,
    fileKey: 'file', mimeType: 'image/jpeg', headers: { Authorization: 'Bearer ' + token }
  }).then(function (r) { console.log('server said', r.responseCode, r.response); })
    .catch(function (e) { alert('Upload failed: ' + e.message); });
}

أبلغ عن فشل نقل بحسب رمزه:

async function safeDownload(url, target) {
  const P = window.Capacitor && window.Capacitor.Plugins;
  if (!P || !P.FileTransfer) { alert('Downloads need the app'); return null; }
  try {
    const r = await P.FileTransfer.downloadFile({ url: url, path: target });
    return r.path;
  } catch (e) {
    console.log('transfer failed', e.code, e.message);
    alert('Could not download the file.');
    return null;
  }
}

ملاحظات: يجب أن يكون المسار مسار ملف أو عنوان file://، لا Blob - اكتب الـ Blob في ذاكرة التخزين المؤقت بـ Filesystem.writeFile أولًا. في Android 10 وما قبله يطلب المسار في التخزين العام إذن التخزين. وفي التطبيقات المبنية بالذكاء الاصطناعي فضّل fileTransfer من @/lib/appmintNative، فهو يفعل كل ذلك ويعمل أيضًا على الويب.

Capacitor.FileViewer Capacitor#

Capacitor.Plugins.FileViewer

مثال

ملحق Capacitor File Viewer الرسمي: يفتح مستندًا أو ملف وسائط في تطبيق العرض بالهاتف، من عنوان ويب أو مسار ملف محلي أو ملف مضمّن في التطبيق.

القيم المُعادة: تُعيد كل طريقة Promise<void> يُرفض بـ {message, code} عندما لا يستطيع أي شيء فتح الملف. المتطلبات: لا شيء لتفعيله. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى. تحقّق دائمًا من وجوده قبل الاستخدام.

async function openPdf(url) {
  const FV = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.FileViewer;
  if (!FV) { window.open(url, '_blank'); return; }          // a normal browser: open a tab
  try {
    await FV.openDocumentFromUrl({ url: url });
  } catch (e) {
    alert('No app on this phone can open this file.');
  }
}

ملف على الهاتف، مثل ملف كُتب باستخدام Capacitor.Plugins.Filesystem:

async function openSaved(name) {
  const P = window.Capacitor && window.Capacitor.Plugins;
  if (!P || !P.FileViewer || !P.Filesystem) return;
  const f = await P.Filesystem.getUri({ path: name, directory: 'CACHE' });
  try {
    await P.FileViewer.openDocumentFromLocalPath({ path: f.uri.replace(/^file:\/\//, '') });
  } catch (e) {
    alert('Cannot open ' + name + ': ' + e.message);
  }
}

openDocumentFromResources({path}) يفتح ملفًا مضمّنًا في مجلد assets الخاص بملف APK. وتوجد الطرق الثلاث نفسها أيضًا بأسماء previewMediaContentFromUrl وpreviewMediaContentFromLocalPath وpreviewMediaContentFromResources للفيديو والصوت.

نزّل ملفًا وافتحه عند وصوله:

async function downloadAndOpen(url, name) {
  const P = window.Capacitor && window.Capacitor.Plugins;
  if (!P || !P.FileTransfer || !P.Filesystem || !P.FileViewer) return;
  const f = await P.Filesystem.getUri({ path: name, directory: 'CACHE' });
  const r = await P.FileTransfer.downloadFile({ url: url, path: f.uri });
  await P.FileViewer.openDocumentFromLocalPath({ path: r.path.replace(/^file:\/\//, '') });
}

ملاحظات: في التطبيقات المبنية بالذكاء الاصطناعي فضّل fileViewer.open(target) من @/lib/appmintNative، فهو يختار الطريقة المناسبة ويعمل أيضًا على الويب. المسار المحلي هو مسار عادي (بلا file://).

Capacitor.Geolocation Capacitor#

Capacitor.Plugins.Geolocation

مثال

ملحق Capacitor Geolocation الرسمي، المضمّن في التطبيق: موقع الهاتف لمرة واحدة، أو تحديثات مستمرة.

القيم المُعادة: Promises. getCurrentPosition(opts) ← { timestamp, coords: { latitude, longitude, accuracy, altitude, speed, heading, … } }؛ watchPosition(opts, callback) ← معرّف المراقبة (نص)؛ clearWatch({ id })؛ checkPermissions() / requestPermissions() ← { location, coarseLocation } ('granted' | 'denied' | 'prompt' | …). المتطلبات: GPS و/أو الموقع في الخطوة 4 (الوصول) - وفي تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي يفعّلها الإنشاء أيضًا تلقائيًا عندما تستدعي ملفاتك getCurrentPosition أو watchPosition. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

الموقع لمرة واحدة، مع السؤال أولًا.

async function whereAmI() {
  var geo = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Geolocation;
  if (!geo) { showMessage('Open the app to use location.'); return; }
  try {
    var perm = await geo.checkPermissions();
    if (perm.location !== 'granted' && perm.coarseLocation !== 'granted') {
      perm = await geo.requestPermissions();
      if (perm.location !== 'granted' && perm.coarseLocation !== 'granted') {
        showMessage('Location is off for this app.');
        return;
      }
    }
    var pos = await geo.getCurrentPosition({ enableHighAccuracy: true, timeout: 15000 });
    showMessage(pos.coords.latitude.toFixed(5) + ', ' + pos.coords.longitude.toFixed(5));
  } catch (err) {
    showMessage('No location: ' + err.message);   // location off in settings, timeout, …
  }
}

تحديثات مستمرة. تتلقى دالة الاستدعاء (position, error).

var watchId = null;

async function startTracking() {
  var geo = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Geolocation;
  if (!geo) return;
  watchId = await geo.watchPosition({ enableHighAccuracy: true }, function (pos, err) {
    if (err || !pos) return;
    addPointToMap(pos.coords.latitude, pos.coords.longitude);
  });
}

async function stopTracking() {
  var geo = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Geolocation;
  if (geo && watchId) { await geo.clearWatch({ id: watchId }); watchId = null; }
}

في تطبيق مبني بالذكاء الاصطناعي استخدم الغلاف، وهو يعمل على الويب أيضًا:

import { Geolocation } from '@/lib/appmintNative';
const pos = await Geolocation.getCurrentPosition({ enableHighAccuracy: true });

ملاحظات: يعمل navigator.geolocation العادي في التطبيق أيضًا. وإن كانت مفاتيح الموقع معطّلة وقت الإنشاء يُرفض الإذن دون أي مطالبة. وفي Android 12+ قد يسمح المستخدم بالموقع التقريبي فقط (coarseLocation ممنوح). الموقع بيانات شخصية: صرّح به في نموذج Data safety في Play.

Capacitor.Haptics Capacitor#

Capacitor.Plugins.Haptics

مثال

ملحق Capacitor Haptics الرسمي (@capacitor/haptics)، المضمّن في التطبيق: نقرات خفيفة/متوسطة/قوية، وملاحظات النجاح/التحذير/الخطأ، ونبضات الاختيار، والاهتزاز العادي.

يعيد: كل دالة تعيد Promise يُنجز بلا شيء. يتطلب: لا شيء لتفعيله - كل تطبيق يعلن إذن الاهتزاز، سواء حُدّد الاهتزاز في الخطوة 4 (الوصول) أم لا. يوجد window.Capacitor في الصفحات التي يقدمها التطبيق بنفسه (HTML وZIP والتطبيقات المبنية بالذكاء الاصطناعي)، وفي تطبيق وضع الموقع، على موقعك أنت (نطاقه ونطاقاته الفرعية) - وليس أبدًا على المواقع الأخرى التي يفتحها التطبيق. اكتشف الدعم.

function haptics() {
  return window.Capacitor && window.Capacitor.Plugins ? window.Capacitor.Plugins.Haptics : null;
}

async function tap(style) {            // 'LIGHT' | 'MEDIUM' | 'HEAVY'
  var h = haptics();
  if (!h) return;
  try { await h.impact({ style: style }); } catch (e) { console.log('haptics failed', e); }
}

async function result(type) {          // 'SUCCESS' | 'WARNING' | 'ERROR'
  var h = haptics();
  if (h) { try { await h.notification({ type: type }); } catch (e) {} }
}

document.getElementById('add').addEventListener('click', function () { tap('LIGHT'); });
document.getElementById('pay').addEventListener('click', function () { result('SUCCESS'); });

نبضات اختيار لمنتقٍ أو شريط تمرير، واهتزاز عادي:

var h = window.Capacitor && window.Capacitor.Plugins ? window.Capacitor.Plugins.Haptics : null;
var wheel = document.getElementById('wheel');

wheel.addEventListener('pointerdown', function () { if (h) h.selectionStart(); });
wheel.addEventListener('change', function () { if (h) h.selectionChanged(); });
wheel.addEventListener('pointerup', function () { if (h) h.selectionEnd(); });

document.getElementById('buzz').addEventListener('click', function () {
  if (h) h.vibrate({ duration: 300 });   // milliseconds
});

اهتزّ عند حدوث خطأ واقرنه برسالة ظاهرة:

var h = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Haptics;

function rejectInput(field) {
  field.classList.add('invalid');
  if (h) h.notification({ type: 'ERROR' }).catch(function () {});
}

ملاحظات: النصوص بأحرف كبيرة ('HEAVY' لا 'heavy'). وفي التطبيقات المبنية بالذكاء الاصطناعي يعطي import { Haptics } from '@/lib/appmintNative' الأسماء نفسها. ولإيقاعات مخصّصة بقوة لكل نبضة، استخدم Capacitor.Plugins.AppwrightHaptics.

Capacitor.InAppReview Capacitor#

Capacitor.Plugins.InAppReview — requestReview()

مثال

يطلب من Google Play عرض ورقة "قيّم هذا التطبيق" الخاصة به داخل التطبيق. نسخة مضمّنة @capacitor-community/in-app-review (template/plugins/in-app-review).

القيم المُعادة: Promise void - يُحَلّ عندما ينتهي تدفّق Play، ولا تخبر Play أبدًا هل عُرضت الورقة أو ماذا فعل المستخدم. ويُرفض عندما يتعذّر على Play بدء التدفّق. المتطلبات: لا شيء لتفعيله؛ ويجب تثبيت التطبيق من Google Play لتظهر الورقة. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

var Review = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.InAppReview;

async function afterThirdOrder() {
  if (!Review) return;
  try {
    await Review.requestReview();
  } catch (e) {
    console.log('review flow not available:', e.message);
  }
  // continue the app flow either way — you cannot know if the user rated
}

في تطبيق مبني بالذكاء الاصطناعي:

import { requestReview } from '@/lib/appmintNative';

// after a success moment, not on launch
const asked = await requestReview();   // false where nothing could be asked (web)

اسأل مرة واحدة بعد لحظة نجاح، وتذكّر أنك سألت:

var Review = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.InAppReview;

async function maybeAskForReview() {
  if (!Review || localStorage.getItem('review_asked')) return;
  localStorage.setItem('review_asked', '1');
  try { await Review.requestReview(); } catch (e) { console.log('review not shown:', e.message); }
}

ملاحظات: تحدّ Play من عدد مرات ظهور الورقة ولا تعرضها مع كل استدعاء - فلا تربط مكافأة بها ولا تطلبها من زر "قيّمنا" متوقعًا أن تُفتح (افتح صفحة المتجر لذلك). اطلبها بعد لحظة نجاح، لا عند بدء التشغيل. وغالبًا لا يعرض التثبيت الجانبي أو تثبيت التصحيح شيئًا.

Capacitor.KeepAwake Capacitor#

Capacitor.Plugins.KeepAwake — keepAwake(), allowSleep(), isKeptAwake(), isSupported()

مثال

يمنع الشاشة من الخفوت والقفل بينما يجب أن يبقى شيء ظاهرًا - وصفة أو مسار أو رمز QR أو مؤقّت. نسخة مضمّنة @capacitor-community/keep-awake (template/plugins/keep-awake).

القيم المُعادة: keepAwake() / allowSleep() ← Promise void. isKeptAwake() ← { isKeptAwake: boolean }. isSupported() ← { isSupported: true }. المتطلبات: لا شيء - فكل تطبيق يتضمن الملحق؛ ولا إذن. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

var KA = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.KeepAwake;

async function startCooking() {
  if (KA) await KA.keepAwake();
  showSteps();
}

async function stopCooking() {
  if (KA) await KA.allowSleep();      // always release when the view closes
}

if (KA) KA.isKeptAwake().then(function (r) { console.log('awake?', r.isKeptAwake); });

في تطبيق مبني بالذكاء الاصطناعي (واجهة Screen Wake Lock API على الويب):

import { keepAwake } from '@/lib/appmintNative';

useEffect(() => {
  void keepAwake(true);
  return () => { void keepAwake(false); };
}, []);

أبقِ الشاشة مضاءة فقط أثناء عمل المؤقّت:

var KA = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.KeepAwake;

async function runTimer(seconds) {
  if (KA) await KA.keepAwake();
  await new Promise(function (done) { setTimeout(done, seconds * 1000); });
  if (KA) await KA.allowSleep();
  alert('Time is up');
}

ملاحظات: العلم يخص نافذة التطبيق: لا يعمل إلا والتطبيق في المقدمة، ويبقى مفعّلًا حتى allowSleep() - فحرّره عند كل خروج من الشاشة. يُعيد keepAwake() القيمة false عندما يتعذّر إبقاء الشاشة مضاءة.

Capacitor.Keyboard Capacitor#

Capacitor.Plugins.Keyboard — show(), hide(), addListener('keyboardDidShow' | ...)

مثال

يفتح لوحة المفاتيح البرمجية أو يغلقها ويخبر الصفحة متى تظهر وما ارتفاعها (@capacitor/keyboard 8.0.5).

القيم المُعادة: show() / hide() ← Promise void. addListener(event, fn) ← Promise لمقبض فيه remove()، كما في واجهة Capacitor نفسها - استخدم await عليه أو سلسل .then()؛ كما أن القيمة المُعادة نفسها تحتوي remove()؛ وتمرّر أحداث الإظهار { keyboardHeight } بوحدات CSS بكسل. الأحداث: keyboardWillShow وkeyboardDidShow وkeyboardWillHide وkeyboardDidHide. المتطلبات: لا شيء لتفعيله، فكل تطبيق يتضمن الملحق. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

var Keyboard = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Keyboard;

if (Keyboard) {
  Keyboard.addListener('keyboardDidShow', function (info) {
    console.log('keyboard height', info.keyboardHeight);
    document.body.classList.add('typing');       // e.g. hide the bottom tab bar
  });
  Keyboard.addListener('keyboardDidHide', function () {
    document.body.classList.remove('typing');
  });
}

function onSend() {
  if (Keyboard) Keyboard.hide().catch(function () {});   // rejects if no field is focused
}

في تطبيق مبني بالذكاء الاصطناعي استخدم onKeyboard() للتخطيط - فهو يُبلغ فقط عن جزء لوحة المفاتيح الذي لا تتجنبه الصفحة المعدّلة الحجم أصلًا - واستخدم hideKeyboard() لإغلاقها:

import { onKeyboard, hideKeyboard, Keyboard } from '@/lib/appmintNative';

const off = onKeyboard((px) => { composer.style.paddingBottom = `${px}px`; });  // call off() on unmount
hideKeyboard();

const h = Keyboard.addListener('keyboardWillShow', ({ keyboardHeight }) => console.log(keyboardHeight));
h.remove();

أظهر لوحة المفاتيح لحقل بعد نقرة زر:

var Keyboard = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Keyboard;

searchBtn.addEventListener('click', function () {
  searchInput.focus();
  if (Keyboard) Keyboard.show().catch(function () {});
});

ملاحظات: يعدّل الغلاف حجم الصفحة للوحة المفاتيح أصلًا، لذا لا تضف keyboardHeight كاملًا كهامش - فالشريط السفلي فوق المفاتيح أصلًا. setAccessoryBarVisible وsetStyle وsetResizeMode وsetScroll غير مُنفَّذة على Android - لا تستدعها. يُرفض hide() بالرسالة "Can't close keyboard, not currently focused" عندما لا يكون هناك أي تركيز.

Capacitor.LocalNotifications Capacitor#

Capacitor.Plugins.LocalNotifications — schedule, cancel, getPending, requestPermissions, events

مثال

إشعارات ينشرها التطبيق بنفسه - الآن أو في وقت معيّن - بواجهة @capacitor/local-notifications الرسمية (com.capacitorjs:local-notifications). الكود المكتوب لحزمة npm يعمل دون تغيير.

القيم المُعادة: requestPermissions() / checkPermissions() ← { display: 'granted' | 'denied' | 'prompt' }؛ schedule({ notifications: [{ id, title, body, schedule?: { at | on | every }, extra?, actionTypeId?, channelId? }] }) ← { notifications: [{ id }] }؛ getPending() ← { notifications }؛ cancel({ notifications: [{ id }] })؛ registerActionTypes({ types })؛ createChannel / listChannels / deleteChannel؛ checkExactNotificationSetting() ← { exact_alarm }. الأحداث: localNotificationReceived وlocalNotificationActionPerformed (actionId هو 'tap' عند النقر على الإشعار نفسه). المتطلبات: فعّل الإشعارات في الخطوة 4 (الوصول) - فمن دونه لا يكون الملحق في التطبيق وكل استدعاء يُرفض بالرسالة "not implemented". وللإشعارات المؤقّتة فعّل أيضًا ⏰ التذكيرات (التوقيت الدقيق والبقاء بعد إعادة التشغيل). تفعّل تطبيقات HTML وZIP وAI-Studio الاثنين تلقائيًا عندما يستورد الكود @capacitor/local-notifications ويستدعي LocalNotifications.schedule(. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

const LN = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.LocalNotifications;

async function remindIn(minutes, text) {
  if (!LN) return false;
  const { display } = await LN.requestPermissions();
  if (display !== 'granted') return false;
  await LN.schedule({ notifications: [{
    id: Date.now() % 2147483647,
    title: 'Reminder',
    body: text,
    schedule: { at: new Date(Date.now() + minutes * 60000), allowWhileIdle: true },
    extra: { screen: 'tasks' },
  }] });
  return true;
}

افتح الشاشة الصحيحة عندما ينقر المستخدم على إشعار - وكذلك عندما بدأت النقرة تشغيل التطبيق، لأن الحدث ينتظر المستمع:

const LN = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.LocalNotifications;

if (LN) {
  LN.addListener('localNotificationActionPerformed', ({ actionId, notification }) => {
    if (actionId === 'tap' && notification.extra && notification.extra.screen) {
      location.hash = '#/' + notification.extra.screen;
    }
  });
}

تذكير يومي بعادة مع زرَّي تم / غفوة:

const LN = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.LocalNotifications;

async function dailyWaterReminder() {
  await LN.registerActionTypes({ types: [{ id: 'water', actions: [
    { id: 'done', title: 'Done' },
    { id: 'snooze', title: 'In 30 min' },
  ] }] });
  await LN.schedule({ notifications: [{
    id: 42, title: 'Drink water', body: 'Time for a glass of water.',
    actionTypeId: 'water', schedule: { on: { hour: 10, minute: 0 }, repeats: true },
  }] });
  LN.addListener('localNotificationActionPerformed', ({ actionId }) => {
    if (actionId === 'snooze') {
      LN.schedule({ notifications: [{ id: 43, title: 'Drink water', body: 'Snoozed reminder.',
        schedule: { at: new Date(Date.now() + 30 * 60000) } }] });
    }
  });
}

ملاحظات: في Android 12+ يحتاج الإشعار المؤقّت إلى إذن المنبّه الدقيق: قد يفتح أول schedule() صفحة "المنبّهات والتذكيرات" في النظام؛ وإن تركه المستخدم معطّلًا فسيظل الإشعار يصل، متأخرًا قليلًا (تحمل النتيجة warning). تستخدم إشعارات الملحق قناة "Default" الخاصة بها، منفصلة عن بقية إشعارات التطبيق. لا يُدعم sound من ملفات الويب (يشغّل Android الصوت الافتراضي للقناة). تصل النقرات إلى الصفحة عبر شاشة القفل عندما يكون للتطبيق رمز PIN. تستخدم التطبيقات المبنية بالذكاء الاصطناعي notify() / LocalNotifications.schedule من @/lib/appmintNative، وهو ينشر عبر مكدّس إشعارات التطبيق نفسه.

Capacitor.Network Capacitor#

Capacitor.Plugins.Network — getStatus(), addListener('networkStatusChange')

مثال

يخبر الصفحة هل الهاتف متصل بالإنترنت وبأي نوع اتصال، ومتى يتغيّر ذلك (@capacitor/network 8.0.1).

القيم المُعادة: getStatus() ← Promise { connected: boolean, connectionType: 'wifi' | 'cellular' | 'none' | 'unknown' }. يمرّر الحدث networkStatusChange الشكل نفسه. المتطلبات: لا شيء لتفعيله، فكل تطبيق يتضمن الملحق. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

var Network = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Network;

function showBanner(s) {
  document.getElementById('offline').hidden = s.connected;
}

if (Network) {
  Network.getStatus().then(showBanner);
  Network.addListener('networkStatusChange', function (s) {
    showBanner(s);
    if (s.connected) retryQueuedWrites();
    if (s.connectionType === 'cellular') console.log('on mobile data');
  });
}

في تطبيق مبني بالذكاء الاصطناعي:

import { Network, networkStatus, onNetworkChange } from '@/lib/appmintNative';

const { connected, connectionType } = await Network.getStatus();
const now = networkStatus();                        // sync: { connected, type }
const off = onNetworkChange((s) => setOnline(s.connected));   // call off() on unmount

أوقف رفعًا كبيرًا مؤقتًا عند استخدام بيانات الجوال:

var Network = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Network;

async function canUploadVideo() {
  if (!Network) return navigator.onLine;
  var s = await Network.getStatus();
  if (!s.connected) return false;
  return s.connectionType === 'wifi' || confirm('Upload over mobile data?');
}

ملاحظات: يعني connected أن الهاتف متصل بشبكة، لا أن خادمك يجيب - فتعامل مع الطلبات الفاشلة رغم ذلك. يقرأ Network.getStatus() وnetworkStatus() في الغلاف من navigator.onLine / navigator.connection لا من الملحق، لذا قد يكون نوعهما 'unknown' أو فئة سرعة مثل '4g'؛ أما onNetworkChange() فيستخدم حدث الملحق في التطبيق.

Capacitor.Preferences Capacitor#

Capacitor.Plugins.Preferences — get / set / remove / keys / clear

مثال

مخزن مفتاح-قيمة أصلي صغير (Android SharedPreferences) للنصوص (@capacitor/preferences 8.0.1).

القيم المُعادة: set({ key, value }) وremove({ key }) وclear() ← Promise void؛ get({ key }) ← { value } (null عندما يكون المفتاح غير موجود)؛ keys() ← { keys: string[] }. المتطلبات: لا شيء لتفعيله، فكل تطبيق يتضمن الملحق. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

var Prefs = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Preferences;

async function saveTheme(name) {
  if (!Prefs) return false;          // not inside the app
  await Prefs.set({ key: 'theme', value: name });
  return true;
}

async function loadTheme() {
  if (!Prefs) return null;
  var r = await Prefs.get({ key: 'theme' });
  return r.value;            // null if never saved
}

يجب تحويل الكائنات إلى نص أولًا:

async function saveUser() {
  if (!Prefs) return;
  await Prefs.set({ key: 'user', value: JSON.stringify({ id: 7, name: 'Asha' }) });
  var u = JSON.parse((await Prefs.get({ key: 'user' })).value || 'null');
  var all = (await Prefs.keys()).keys;
  await Prefs.remove({ key: 'user' });
}

اعرض المفاتيح المحفوظة وامسحها من شاشة الإعدادات:

async function resetSettings() {
  if (!Prefs) return;
  var k = (await Prefs.keys()).keys;
  console.log('clearing', k.length, 'saved values');
  await Prefs.clear();
}

ملاحظات: ليس لـ @/lib/appmintNative غلاف Preferences، وذلك عن قصد: فـ localStorage حقيقي ودائم في التطبيق أصلًا، ومخزن ثانٍ سيشتّت بيانات تطبيق واحد في مكانين. يجب أن تواصل التطبيقات المبنية بالذكاء الاصطناعي استخدام localStorage (أو بيئة تشغيل البيانات). استخدم Preferences في صفحة مكتوبة يدويًا فقط إن أردت وضع كل إعداداتك هناك، واختر مخزنًا واحدًا لكل قيمة. يغيّر configure({ group }) اسم المخزن (الافتراضي CapacitorStorage).

Capacitor.PrivacyScreen Capacitor#

Capacitor.Plugins.PrivacyScreen — enable(), disable(), isEnabled()

مثال

ملحق @capacitor/privacy-screen الرسمي: يمنع لقطات الشاشة وتسجيل الشاشة ويخفي محتوى التطبيق في عرض التطبيقات الأخيرة. مضمّن بحيث تعمل مشاريع Capacitor المستوردة التي تستدعيه.

القيم المُعادة: enable({android}) ← {success}؛ disable() ← {success}؛ isEnabled() ← {enabled}. المتطلبات: لا شيء لتفعيله.

const Privacy = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.PrivacyScreen;

async function openVault() {
  if (Privacy) await Privacy.enable({ android: { privacyModeOnActivityHidden: 'dim' } });
  showVault();
}
async function closeVault() {
  if (Privacy) await Privacy.disable();
}

تحقّق من الحالة الحالية قبل التبديل:

async function toggleProtection() {
  if (!Privacy) return;
  var s = await Privacy.isEnabled();
  if (s.enabled) await Privacy.disable();
  else await Privacy.enable();
}

احمِ الشاشة فقط أثناء فتح عرض حسّاس:

async function showCardNumber() {
  if (Privacy) await Privacy.enable({ android: { privacyModeOnActivityHidden: 'splash' } });
  cardBox.hidden = false;
}
async function hideCardNumber() {
  cardBox.hidden = true;
  if (Privacy) await Privacy.disable();
}

ملاحظات: privacyModeOnActivityHidden: 'none' أو 'dim' أو 'splash'. يجب أن يستخدم الكود المكتوب للتطبيق نفسه WebToApk.setSecureScreen(true) / privacyScreen.enable() من @/lib/appmintNative، الذي يتبع الصفحة (يُحرَّر عندما يغادرها التطبيق)؛ وعند الجمع بين الاثنين يسري آخر استدعاء.

Capacitor.PushNotifications Capacitor#

Capacitor.Plugins.PushNotifications — register, requestPermissions, registration, pushNotificationReceived

مثال

إشعارات Push من خادمك الخاص عبر مشروع Firebase الخاص بك، بواجهة @capacitor/push-notifications الرسمية (com.capacitorjs:push-notifications، المسجَّلة بواسطة PushNotificationsPlugin.kt في الغلاف). الكود المكتوب لحزمة npm يعمل دون تغيير: register()، ثم أرسل إلى الرمز المميّز من الحدث registration باستخدام Firebase Admin SDK أو واجهة FCM HTTP v1 البرمجية.

القيم المُعادة: requestPermissions() / checkPermissions() ← { receive: 'granted' | 'denied' | 'prompt' }؛ register() / unregister() ← void (يصل الرمز المميّز في الحدث registration { value }، ويصل الفشل في registrationError { error })؛ getDeliveredNotifications() ← { notifications }؛ removeDeliveredNotifications({ notifications })، removeAllDeliveredNotifications()؛ createChannel({ id, name, importance? })، listChannels()، deleteChannel({ id }). الأحداث: pushNotificationReceived (رسالة أثناء فتح التطبيق: { id, title?, body?, data }) وpushNotificationActionPerformed ({ actionId: 'tap', notification: { id, data } }). المتطلبات: أنشئ التطبيق مع تفعيل AppMint Push وارفع ملف google-services.json (خطوة الإشعارات) - فهذا هو مشروع Firebase الذي ينتمي إليه الرمز المميّز. من دونه يُرفض register() بالرمز PUSH_NOT_CONFIGURED ("Push is not set up in this app"). تفعّل تطبيقات HTML وZIP وAI-Studio خيار الإشعارات تلقائيًا عندما يستدعي الكود PushNotifications.register( / requestPermissions( / addListener(. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

const Push = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.PushNotifications;

async function enablePush(userId) {
  if (!Push) return;
  await Push.addListener('registration', ({ value }) => {
    // Your server stores the token and sends to it later.
    fetch('https://api.example.com/push-tokens', {
      method: 'POST', headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ userId, token: value, platform: 'android' }),
    });
  });
  await Push.addListener('registrationError', ({ error }) => console.warn('push:', error));
  const { receive } = await Push.requestPermissions();
  if (receive !== 'granted') return;
  try { await Push.register(); }
  catch (e) { if (e.code === 'PUSH_NOT_CONFIGURED') alert('Push is not set up in this build.'); }
}

تفاعل مع رسالة أثناء فتح التطبيق، ومع نقرة على رسالة وصلت أثناء إغلاقه (ينتظر حدث النقر مستمعك، فلا يضيع عند التشغيل البارد):

const Push = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.PushNotifications;

if (Push) {
  Push.addListener('pushNotificationReceived', (msg) => {
    alert((msg.title || 'New message') + (msg.body ? ': ' + msg.body : ''));
  });
  Push.addListener('pushNotificationActionPerformed', ({ notification }) => {
    const orderId = notification.data && notification.data.orderId;
    if (orderId) location.hash = '#/orders/' + orderId;
  });
}

قناة للتنبيهات المهمة (Android 8+)، تستخدمها الرسائل التي تسمّيها في android.notification.channel_id:

const Push = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.PushNotifications;

async function setupChannels() {
  if (!Push) return;
  await Push.createChannel({ id: 'orders', name: 'Order updates', description: 'Your order status', importance: 4, vibration: true });
}

ملاحظات: الرسالة المرسلة مع كتلة notification يعرضها Android نفسه بينما التطبيق في الخلفية؛ والنقر عليها يفتح التطبيق (عبر شاشة القفل عندما يكون للتطبيق PIN) ويطلق pushNotificationActionPerformed. أثناء فتح التطبيق تصل الرسائل كـ pushNotificationReceived ولا تُرسم في الدرج. رسائل البيانات فقط تصل إلى الصفحة فقط أثناء فتحها. تستمر رسائل AppMint Push بالعمل جنبًا إلى جنب - فهي لا تصل أبدًا إلى هذه الإضافة. في تطبيق بُني بـ OneSignal بدلًا من AppMint Push، تُسلَّم الرسائل عبر OneSignal ولا تصل أبدًا إلى هذه الإضافة. تتلقى التطبيقات المبنية بالذكاء الاصطناعي إشعارات المطوّر عبر pushInbox() من @/lib/appmintNative.

Capacitor.ScreenBrightness Capacitor#

Capacitor.Plugins.ScreenBrightness — getBrightness(), setBrightness({ brightness })

مثال

يضبط سطوع شاشة نافذة التطبيق - سطوع كامل لبطاقة صعود الطائرة أو رمز QR، ثم يعود. نسخة مضمّنة @capacitor-community/screen-brightness (template/plugins/screen-brightness).

القيم المُعادة: getBrightness() ← Promise { brightness } (من 0 إلى 1، أو -1 عندما لا يكون التطبيق قد تجاوزه وتتبع الشاشة النظام). setBrightness({ brightness }) ← Promise void. المتطلبات: لا شيء - فكل تطبيق يتضمن الملحق؛ ولا إذن (فهو يغيّر نافذة هذا التطبيق فقط، لا إعداد النظام). يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

var SB = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.ScreenBrightness;

async function showTicket() {
  if (SB) await SB.setBrightness({ brightness: 1 });     // full brightness
  document.getElementById('qr').hidden = false;
}

async function hideTicket() {
  if (SB) await SB.setBrightness({ brightness: -1 });    // -1 = follow the system again
  document.getElementById('qr').hidden = true;
}

في تطبيق مبني بالذكاء الاصطناعي:

import { brightness } from '@/lib/appmintNative';

await brightness.set(1);        // clamped to 0–1; false where not available (web)
// ... on leaving the screen
await brightness.restore();
const level = await brightness.get();   // -1 = following the system / not available

اقرأ المستوى الحالي أولًا واستعده كما هو تمامًا:

var SB = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.ScreenBrightness;
var before = -1;

async function boost() {
  if (!SB) return;
  before = (await SB.getBrightness()).brightness;
  await SB.setBrightness({ brightness: 1 });
}
async function undo() {
  if (SB) await SB.setBrightness({ brightness: before });
}

ملاحظات: مرّر دائمًا رقمًا - فـ setBrightness({}) لا قيمة فيه لتطبيقها. استعد بـ -1 عند إغلاق العرض؛ كما ينتهي التجاوز عند إغلاق التطبيق. لا تستطيع صفحة ويب التحكم بالإضاءة الخلفية، لذا فإن can('brightness') هو 'none' في المتصفح.

Capacitor.ScreenOrientation Capacitor#

Capacitor.Plugins.ScreenOrientation — orientation(), lock({ orientation }), unlock()

مثال

يقرأ اتجاه الشاشة، ويثبّته لعرض واحد (لعبة أو مخطط أو لوحة توقيع) ثم يحرّره (@capacitor/screen-orientation 8.0.1).

القيم المُعادة: orientation() ← Promise { type } (مثل 'portrait-primary'). lock() / unlock() ← Promise void. يمرّر الحدث screenOrientationChange القيمة { type }. المتطلبات: لا شيء لتفعيله، فكل تطبيق يتضمن الملحق. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

orientation القيم: 'portrait', 'landscape', 'portrait-primary', 'portrait-secondary', 'landscape-primary', 'landscape-secondary', 'natural', 'any'.

var SO = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.ScreenOrientation;

async function openChart() {
  if (SO) await SO.lock({ orientation: 'landscape' });
  showChart();
}

async function closeChart() {
  if (SO) await SO.unlock();          // always release when leaving the view
  hideChart();
}

if (SO) {
  SO.orientation().then(function (o) { console.log('now', o.type); });
  SO.addListener('screenOrientationChange', function (o) { console.log('rotated to', o.type); });
}

في تطبيق مبني بالذكاء الاصطناعي:

import { lockOrientation, ScreenOrientation } from '@/lib/appmintNative';

const ok = await lockOrientation('landscape');   // false when refused (always on desktop)
await lockOrientation('any');                    // release

await ScreenOrientation.lock({ orientation: 'portrait' });
await ScreenOrientation.unlock();

تعامل مع قفل مرفوض وأخبر المستخدم:

var SO = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.ScreenOrientation;
var Toast = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Toast;

async function tryLandscape() {
  if (!SO) { document.getElementById('rotate-hint').hidden = false; return; }   // ask the user to rotate by hand
  try {
    await SO.lock({ orientation: 'landscape' });
  } catch (e) {
    if (Toast) Toast.show({ text: 'Could not rotate the screen', duration: 'short' });
  }
}

ملاحظات: لا تعرف الأغلفة إلا portrait / landscape / any: فـ ScreenOrientation.lock({ orientation: 'landscape-secondary' }) يثبّت على landscape العادي. امنح المستخدم طريقة للخروج من عرض مثبَّت.

Capacitor.ScreenReader Capacitor#

Capacitor.Plugins.ScreenReader — isEnabled(), speak({ value, language }), addListener('stateChange')

مثال

يخبر الصفحة هل TalkBack مفعّل، وينطق إعلانًا (@capacitor/screen-reader 8.0.1).

القيم المُعادة: isEnabled() ← Promise { value: boolean }. speak() ← Promise void. يمرّر الحدث stateChange القيمة { value: boolean } عند تشغيل TalkBack أو إيقافه. المتطلبات: لا شيء لتفعيله، فكل تطبيق يتضمن الملحق. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

var SR = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.ScreenReader;

function setCalmMode(on) {
  document.documentElement.classList.toggle('no-motion', on);   // shorten animations
}

if (SR) {
  SR.isEnabled().then(function (r) { setCalmMode(r.value); });
  SR.addListener('stateChange', function (s) { setCalmMode(s.value); });
}

async function announceSaved() {
  if (SR) await SR.speak({ value: 'Order placed', language: 'en' });
}

في تطبيق مبني بالذكاء الاصطناعي (يستخدم speak تحويل النص إلى كلام على الويب؛ وisEnabled() هو false هناك):

import { screenReader } from '@/lib/appmintNative';

const on = await screenReader.isEnabled();
const off = screenReader.onChange((enabled) => setCalmMode(enabled));
await screenReader.speak('Order placed', 'en');

تحقّق من وجود الملحق وأبقِ الصفحة قابلة للاستخدام بدونه:

var SR = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.ScreenReader;

async function say(message) {
  if (SR) { await SR.speak({ value: message }); return; }
  document.getElementById('live-region').textContent = message;   // an aria-live="polite" element
}

ملاحظات: الافتراضي لـ language هو 'en'. فضّل الترميز العادي القابل للوصول (التسميات ومناطق aria-live) لمعظم الإعلانات؛ واستخدم speak لتغييرات الحالة القصيرة المهمة. لا توجد واجهة ويب لاكتشاف قارئ الشاشة، لذا فإن can('screenReader') هو 'none' في المتصفح.

Capacitor.Share Capacitor#

Capacitor.Plugins.Share — share(options), canShare()

مثال

يفتح ورقة المشاركة في Android بعنوان ونص و/أو رابط (@capacitor/share 8.0.1).

القيم المُعادة: share() ← Promise { activityType } (اسم مكوّن التطبيق المختار، وقد يكون '')؛ ويُرفض عندما يغلق المستخدم الورقة. canShare() ← Promise { value: boolean }. المتطلبات: لا شيء لتفعيله، فكل تطبيق يتضمن الملحق. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

async function shareLink() {
  var Share = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Share;
  if (!Share) { alert('Sharing is not available here'); return; }
  // Only pass the fields you have. A present but empty `url` is rejected ("Unsupported url").
  var opts = { title: 'My list', text: 'Milk, eggs, bread' };
  var link = 'https://example.com/list/42';
  if (link) opts.url = link;
  try {
    var r = await Share.share(opts);
    console.log('shared to', r.activityType);
  } catch (e) {
    console.log('share cancelled or failed:', e.message);   // "Share canceled" on dismiss
  }
}

في تطبيق مبني بالذكاء الاصطناعي للغلاف الشكل نفسه ويُرفض أيضًا عند الإلغاء:

import { Share, can } from '@/lib/appmintNative';

if (can('share') !== 'none') {
  try {
    await Share.share({ title: 'My list', text: 'Milk, eggs, bread', url: 'https://example.com/list/42' });
  } catch {
    // the user closed the sheet — not an error to show
  }
}

تحقّق من canShare() أولًا وأخفِ الزر عندما لا تكون المشاركة متاحة:

var Share = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Share;
var btn = document.getElementById('share');

btn.hidden = true;
if (Share) {
  Share.canShare().then(function (r) { btn.hidden = !r.value; });
}

ملاحظات: يجب أن يكون url بصيغة http(s):// أو file://. يحدّد dialogTitle عنوان نافذة الاختيار. تتطلب مشاركة الملفات مسارات file:// يملكها التطبيق؛ ومن صفحة ويب شارك الملفات باستخدام navigator.share({ files }) بدلًا من ذلك. يمكن فتح مشاركة واحدة فقط في كل مرة ("Can't share while sharing is in progress").

Capacitor.SpeechRecognition Capacitor#

Capacitor.Plugins.SpeechRecognition

مثال

يحوّل كلام المستخدم إلى نص بواسطة التعرّف على الكلام في Android (ملحق @capacitor-community/speech-recognition المجتمعي، المضمّن في التطبيق). لا يملك Android WebView تعرّفًا على الكلام عبر Web Speech، لذا فهذه هي طريقة الإملاء.

القيم المُعادة: Promises. available() ← { available }. checkPermissions() / requestPermissions() ← { speechRecognition: 'granted' | 'denied' | 'prompt' }. start({ partialResults: false }) ← { status: 'success', matches: [...] } بعد أن يتوقف المستخدم عن الكلام؛ ويُرفض عند أخطاء مثل عدم وجود كلام أو عدم وجود تطابق. المتطلبات: فعّل الميكروفون في الخطوة 4 (الوصول) عند الإنشاء (وفي تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي يضيف الإنشاء إذن الميكروفون تلقائيًا عندما تذكر ملفاتك SpeechRecognition)، ويجب أن يسمح المستخدم بالميكروفون. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

إملاء لمرة واحدة (بالطريقة نفسها التي يستخدمها @/lib/appmintNative speech):

var SR = window.Capacitor && window.Capacitor.Plugins ? window.Capacitor.Plugins.SpeechRecognition : null;

async function dictate() {
  var out = document.getElementById('text');
  if (!SR || !(await SR.available()).available) { out.textContent = 'Voice input is not available on this phone.'; return; }

  var p = (await SR.checkPermissions()).speechRecognition;
  if (p !== 'granted') p = (await SR.requestPermissions()).speechRecognition;
  if (p !== 'granted') { out.textContent = 'Microphone permission was not given.'; return; }

  try {
    var r = await SR.start({ language: 'en-US', maxResults: 3, partialResults: false, popup: false });
    out.textContent = r.matches && r.matches.length ? r.matches[0] : '';
  } catch (e) {
    out.textContent = 'Did not catch that. Try again.';
  }
}

document.getElementById('mic').addEventListener('click', dictate);

نص مباشر أثناء كلام المستخدم (partialResults: true)، وحالة الاستماع:

// Add the listeners ONCE, when the page starts.
if (SR) {
  SR.addListener('partialResults', function (d) {
    // Words as they come, and then the final text, all arrive here.
    if (d.matches && d.matches.length) document.getElementById('text').textContent = d.matches[0];
  });
  SR.addListener('listeningState', function (d) {
    // 'started' = the user began speaking, 'stopped' = the user stopped speaking
    document.body.classList.toggle('listening', d.status === 'started');
  });
}

document.getElementById('mic-live').addEventListener('click', async function () {
  if (!SR) return;
  try {
    await SR.start({ language: 'en-US', partialResults: true, popup: false });   // resolves at once, no matches
  } catch (e) {
    document.getElementById('text').textContent = 'Could not start: ' + e.message;
  }
});

document.getElementById('stop').addEventListener('click', function () { if (SR) SR.stop(); });

اعرض زر الميكروفون فقط عندما يمكن أن يعمل الإملاء، وتوقف عن الاستماع عندما تُخفى الصفحة:

var SR = window.Capacitor && window.Capacitor.Plugins ? window.Capacitor.Plugins.SpeechRecognition : null;
var mic = document.getElementById('mic');

mic.hidden = true;
if (SR) {
  SR.available().then(function (r) { mic.hidden = !r.available; });
}

document.addEventListener('visibilitychange', function () {
  if (document.hidden && SR) SR.stop();
});

ملاحظات:

  • في الوضع المباشر (partialResults: true)، يصل النص النهائي بعد الحالة 'stopped'، فلا تُزل المستمع partialResults عند 'stopped'. ولا يُبلَّغ في هذا الوضع عن خطأ يحدث بعد البدء (لا كلام، لا تطابق).
  • language هو وسم مثل 'en-US' أو 'ta-IN' (الافتراضي: لغة الهاتف). يعرض popup: true مربع الاستماع الخاص بـ Google بدلًا من ذلك. كما يوجد getSupportedLanguages() ← { languages } وisListening() ← { listening }.
  • start() يُرفض بـ 'Missing permission' إن لم تطلب الإذن أولًا. يتوقف الاستماع من تلقاء نفسه بعد توقف في الكلام.

Capacitor.SplashScreen Capacitor#

Capacitor.Plugins.SplashScreen — show(), hide()

مثال

واجهة @capacitor/splash-screen البرمجية فوق شاشة البداية الخاصة بالتطبيق، بحيث تعمل في تطبيق Android مشاريع Capacitor / AI Studio المستوردة التي تستدعي SplashScreen.hide() بمجرد أن تصبح شاشتها الأولى جاهزة.

القيم المُعادة: hide() ← Promise (تختفي شاشة البداية بعد انقضاء الحد الأدنى لوقتها الذي حدّده المنشئ)؛ show({showDuration, autoHide}) ← Promise (تعود شاشة البداية فوق الصفحة، ومع autoHide - وهو الافتراضي - تختفي بعد showDuration ميلي ثانية، الافتراضي 3000). المتطلبات: لا شيء لتفعيله.

const Splash = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.SplashScreen;

async function boot() {
  await loadFirstScreen();
  if (Splash) await Splash.hide();     // otherwise the shell hides it at first paint
}
boot();

غطِّ تبديل حساب بطيئًا بشاشة البداية، وأبقِها حتى تظهر البيانات الجديدة على الشاشة:

const Splash = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.SplashScreen;

async function switchAccount(accountId) {
  if (Splash) await Splash.show({ autoHide: false });   // stays until hide()
  try {
    await loadAccount(accountId);
    renderHome();
  } finally {
    if (Splash) await Splash.hide();
  }
}

اعرض شاشة البداية المميّزة للحظة بعد تسجيل الخروج؛ وتختفي من تلقاء نفسها بعد showDuration:

const Splash = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.SplashScreen;

async function signOut() {
  await clearSession();
  showSignInScreen();
  if (Splash) {
    try {
      await Splash.show({ showDuration: 1200 });
    } catch (e) {
      console.warn('splash not shown:', e.code);
    }
  }
}

ملاحظات: من دون استدعاء hide() تختفي شاشة البداية مع أول رسم كما في كل تطبيق. تستخدم التطبيقات المبنية بالذكاء الاصطناعي splash.hold() / splash.ready() من @/lib/appmintNative.

Capacitor.StatusBar Capacitor#

Capacitor.Plugins.StatusBar — setStyle, setBackgroundColor, show, hide, getInfo

مثال

يغيّر نمط أيقونات شريط الحالة ولونه وظهوره (@capacitor/status-bar 8.0.2).

القيم المُعادة: كل طريقة ← Promise void، عدا getInfo() ← { visible, style, color, overlays, height }. المتطلبات: لا شيء لتفعيله، فكل تطبيق يتضمن الملحق. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

style يسمّي الخلفية التي يناسبها: 'DARK' = أيقونات فاتحة (لشريط داكن)، 'LIGHT' = أيقونات داكنة، 'DEFAULT' = اتباع النظام.

var StatusBar = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.StatusBar;
if (StatusBar) {
  StatusBar.setStyle({ style: 'DARK' });          // light icons
  StatusBar.hide();                               // e.g. while a video plays
  // later
  StatusBar.show();
}

لوّن الشريط بجسر الغلاف، لا بالملحق. في Android 15+ لا يفعل setBackgroundColor الخاص بالملحق شيئًا (فنمط edge-to-edge يتجاهله). يرسم الغلاف الشريط بنفسه ويأخذ نمط الأيقونات في الاستدعاء نفسه:

if (window.WebToApk && window.WebToApk.setStatusBarColor) {
  window.WebToApk.setStatusBarColor('#0B0F1A', 'light');   // dark bar, light icons
} else if (StatusBar) {
  StatusBar.setBackgroundColor({ color: '#0B0F1A' });      // hex only, e.g. #RRGGBB
}

في تطبيق مبني بالذكاء الاصطناعي استخدم setStatusBar(color, contentStyle)؛ فهو يفضّل جسر الغلاف ويضبط اللون والأيقونات في استدعاء واحد:

import { setStatusBar, StatusBar } from '@/lib/appmintNative';

setStatusBar('#0B0F1A', 'light');                       // dark bar, light icons
await StatusBar.setBackgroundColor({ color: '#FFFFFF' }); // colour only; icons picked from luminance

ملاحظات: يرفض setBackgroundColor لونًا ليس نص hex. في الإنشاءات بملء الشاشة / وضع الكشك يخفي الغلاف الأشرطة. وفي تطبيق Android لا يغيّر StatusBar.setStyle() في الغلاف وحده شيئًا (فالجسر يحتاج إلى لون ليرسمه) - مرّر النمط إلى setStatusBar() مع لون.

Capacitor.TextToSpeech Capacitor#

Capacitor.Plugins.TextToSpeech — speak(), stop(), getSupportedLanguages(), getSupportedVoices()

مثال

إضافة المجتمع @capacitor-community/text-to-speech فوق محرك تحويل النص إلى كلام في الهاتف. مدمجة ليعمل بها مشاريع Capacitor المستوردة التي تستدعيها.

القيم المُعادة: speak({text, lang, rate, pitch, volume, voice}) ← Promise يُحَلّ عند الانتهاء من نطق الجملة؛ stop()؛ getSupportedLanguages() ← {languages}؛ getSupportedVoices() ← {voices}؛ isLanguageSupported({lang}) ← {supported}. المتطلبات: لا شيء لتفعيله.

const Tts = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.TextToSpeech;

async function readAloud(text) {
  if (!Tts) { speechSynthesis.speak(new SpeechSynthesisUtterance(text)); return; }
  await Tts.speak({ text: text, lang: 'en-US', rate: 1.0, pitch: 1.0, volume: 1.0 });
}
document.getElementById('stop').onclick = () => Tts && Tts.stop();

تحقّق من اللغة قبل النطق واعرض رسالة عندما لا تكون مثبّتة:

const Tts = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.TextToSpeech;

async function speakIn(lang, text) {
  if (!Tts) return false;
  const { supported } = await Tts.isLanguageSupported({ lang: lang });
  if (!supported) { showMessage('This phone has no ' + lang + ' voice installed.'); return false; }
  await Tts.speak({ text: text, lang: lang });
  return true;
}

اقرأ فقرة بصوت عالٍ مع زر إيقاف، وتعامل مع خطأ النطق:

const Tts = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.TextToSpeech;

async function readParagraph(el, btn) {
  if (!Tts) return;
  btn.textContent = 'Stop';
  btn.onclick = () => Tts.stop();
  try {
    await Tts.speak({ text: el.textContent, lang: 'en-US', rate: 0.9 });   // resolves when finished
  } catch (e) {
    console.warn('could not speak:', e.message);
  } finally {
    btn.textContent = 'Listen';
    btn.onclick = () => readParagraph(el, btn);
  }
}

ملاحظات: يعمل window.speechSynthesis في التطبيق أيضًا (المحرّك الخاص بالغلاف) وهو ما تستخدمه التطبيقات المبنية بالذكاء الاصطناعي عبر speak()؛ وهذا الملحق هو المسار بصيغة Capacitor.

Capacitor.TextZoom Capacitor#

Capacitor.Plugins.TextZoom — get(), getPreferred(), set({ value })

مثال

يقرأ ويغيّر حجم نص صفحة التطبيق، لتحكّم "حجم النص" داخل التطبيق (@capacitor/text-zoom 8.0.1).

القيم المُعادة: get() ← Promise { value } (التكبير الحالي، 1 = 100%). getPreferred() ← { value } (الحجم الذي اختاره المستخدم في إعدادات Android). set({ value }) ← Promise void. المتطلبات: لا شيء لتفعيله، فكل تطبيق يتضمن الملحق. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

var TZ = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.TextZoom;

async function biggerText() {
  if (!TZ) return;
  var now = (await TZ.get()).value;
  await TZ.set({ value: Math.min(2, now + 0.1) });
}

async function resetText() {
  if (!TZ) return;
  var pref = (await TZ.getPreferred()).value;   // e.g. 1.15 when the phone uses large text
  await TZ.set({ value: pref });
}

في تطبيق مبني بالذكاء الاصطناعي (على الويب يغيّر حجم خط الجذر بدلًا من ذلك):

import { textZoom } from '@/lib/appmintNative';

const size = await textZoom.get();
await textZoom.set(size + 0.1);                     // clamped to 0.5–3
await textZoom.set(await textZoom.getPreferred());  // back to the system size

شريط تمرير لحجم النص يتذكّر الاختيار:

var TZ = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.TextZoom;
var slider = document.getElementById('text-size');   // <input type="range" min="0.8" max="1.6" step="0.1">

if (TZ) {
  var saved = parseFloat(localStorage.getItem('textZoom'));
  if (saved) TZ.set({ value: saved });
  slider.addEventListener('change', function () {
    TZ.set({ value: parseFloat(slider.value) });
    localStorage.setItem('textZoom', slider.value);
  });
} else {
  slider.hidden = true;
}

ملاحظات: يتبع التطبيق حجم نص النظام أصلًا؛ وهذا لتحكّم إضافي داخل التطبيق. اختبر تخطيطك بالأحجام الكبيرة - فالارتفاعات الثابتة لعناصر النص ستقتطعه.

Capacitor.Toast Capacitor#

Capacitor.Plugins.Toast — show({ text, duration, position })

مثال

يعرض رسالة Toast قصيرة من نظام Android مثل "Saved" أو "Copied" (@capacitor/toast 8.0.1).

القيم المُعادة: Promise void. المتطلبات: لا شيء لتفعيله، فكل تطبيق يتضمن الملحق. يوجد window.Capacitor في الصفحات التي يقدّمها التطبيق بنفسه (تطبيقات HTML وZIP والمبنية بالذكاء الاصطناعي)، وفي تطبيق بوضع موقع ويب على موقعك أنت (نطاقه ونطاقاته الفرعية)، ولا يوجد أبدًا في المواقع الأخرى التي يفتحها التطبيق. تحقّق من وجوده قبل الاستخدام.

var Toast = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Toast;

function saved() {
  if (Toast) Toast.show({ text: 'Saved', duration: 'short', position: 'bottom' });
}

duration هو 'short' (نحو ثانيتين) أو 'long' (نحو 3.5 ثانية). وposition هو 'top' أو 'center' أو 'bottom'.

في تطبيق مبني بالذكاء الاصطناعي استخدم toast() أو Toast بصيغة Capacitor؛ وعلى الويب يرسمان شريط إشعار (snackbar) بألوان التطبيق:

import { Toast, toast } from '@/lib/appmintNative';

await Toast.show({ text: 'Added to list', duration: 'long' });
toast('Copied');   // short form, same result

تحقّق من وجود الملحق واعرض رسالة داخل الصفحة عندما يكون غير موجود:

function notify(message) {
  var Toast = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Toast;
  if (Toast) { Toast.show({ text: message, duration: 'short' }); return; }
  var el = document.getElementById('snackbar');   // a plain in-page message
  el.textContent = message;
  el.hidden = false;
  setTimeout(function () { el.hidden = true; }, 2000);
}

ملاحظات: text مطلوب ("Must provide text"). يرسم النظام الرسالة العابرة (toast) بأسلوب مصنّع الهاتف، وتتجاهل إصدارات Android الأحدث position للرسائل النصية. من السهل تفويت الرسالة العابرة - لا تستخدمها أبدًا لخطأ يجب على المستخدم التصرف بشأنه؛ استخدم Dialog.alert لذلك.

Capacitor.Torch Capacitor#

Capacitor.Plugins.Torch — enable(), disable(), toggle(), isEnabled(), isAvailable()

مثال

مصباح الهاتف، بواجهة @capawesome/capacitor-torch البرمجية. ملحق غلاف التطبيق الخاص (TorchPlugin.kt).

القيم المُعادة: enable() / disable() / toggle() ← Promise (يُرفض بـ CAMERA_IN_USE أثناء فتح بث كاميرا التطبيق نفسه، وبـ UNAVAILABLE إن لم يكن هناك فلاش)؛ isEnabled() ← {enabled}؛ isAvailable() ← {available}. المتطلبات: لا شيء لتفعيله ولا إذن.

const Torch = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Torch;

async function setupLightButton() {
  if (!Torch || !(await Torch.isAvailable()).available) { lightBtn.hidden = true; return; }
  lightBtn.onclick = async () => {
    try {
      await Torch.toggle();
      lightBtn.classList.toggle('on', (await Torch.isEnabled()).enabled);
    } catch (e) {
      alert(e.message);
    }
  };
}

أطلق إشارة SOS ضوئية من زر، وأطفئ الضوء إذا أخذت الكاميرا الفلاش في المنتصف:

const Torch = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Torch;
const wait = (ms) => new Promise((r) => setTimeout(r, ms));

sosBtn.onclick = async () => {
  if (!Torch || !(await Torch.isAvailable()).available) return;
  const flashes = [200, 200, 200, 600, 600, 600, 200, 200, 200];   // S O S, in ms
  try {
    for (const ms of flashes) {
      await Torch.enable();  await wait(ms);
      await Torch.disable(); await wait(200);
    }
  } catch (e) {
    await Torch.disable().catch(() => {});
    alert(e.code === 'CAMERA_IN_USE' ? 'Close the camera first.' : e.message);
  }
};

أبقِ زر الضوء متوافقًا مع النظام، لأن المستخدم قد يشغّل المصباح من لوحة الإعدادات السريعة بينما التطبيق في الخلفية:

const Torch = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Torch;

async function syncLightButton() {
  if (!Torch) return;
  const { enabled } = await Torch.isEnabled();
  lightBtn.classList.toggle('on', enabled);
  lightBtn.setAttribute('aria-pressed', String(enabled));
}

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'visible') syncLightButton();
});
syncLightButton();

ملاحظات: يتبع الضوء حالة النظام (إطفاؤه من لوحة الإعدادات السريعة يُقرأ مطفأً) ويُطفأ عند إغلاق التطبيق. وأثناء فتح بث getUserMedia أضئه عبر المسار: track.applyConstraints({ advanced: [{ torch: true }] }). تستخدم التطبيقات المبنية بالذكاء الاصطناعي torch من @/lib/appmintNative.

يتم إنشاؤه من بيئة تشغيل التطبيق وملفات الأمثلة الخاصة به عند كل بناء للمستندات. اقرأه بصيغة Markdown · جميع العائلات.

تمت المراجعة مقابل الجسر المُرفق بتاريخ 2026-10-22.