كل رقم في هذه الصفحة مقروء من الكود أو من الخادم الحيّ، لا مقدَّر. والمؤشّرات في الأعلى تفحص الخدمة الآن وأنت تقرأ. انتقل بين التبويبات الأربعة — وتبويب «وين ألقى كل شيء» فيه مكان كل شيء قد يُسأل عنه ورابطه المباشر.
أربع خطوات لا خامسة لها. ولا خطوة منها تكتب حرفاً في وصف المنتج ولا في قالب المتجر.
store_id، وصور في تخزين منفصل.تُفحص عند فتح الصفحة وتُحدَّث كل ٦٠ ثانية. لو كانت كلها خضراء فالخدمة تعمل الآن.
لا تحتاج تسجيل الدخول أثناء الاجتماع. كل ما ستعرضه روابط عامة وصفحات داخل سلة. والدخول يلزم فقط لو طلبوا استعلاماً حيّاً من قاعدة البيانات — وطريقته في تبويب «وين ألقى كل شيء».
ابدأ الاجتماع بهذا. الألم أولاً، ثم الحل — لا بقائمة مزايا.
وصف المنتج في سلة نص خام. والتاجر الذي يريد عرض ضماناته أو الإجابة عن أسئلة متكررة أو وضع شهادات عملاء لا يملك إلا فقرة طويلة يقرؤها قليل. وسلة أزالت محرر HTML من الوصف، فحتى لصق كود لم يعد ممكناً.
فالزائر يصل إلى صفحة دفع التاجر ثمن وصوله، ثم يخرج لأن الصفحة لم تجب عن سؤاله.
| المجموعة | البلوكات |
|---|---|
| إقناع | الضمانات · صندوق الثقة · خطوات الاستلام · السعر والخصم · تأطير السعر · جدول المواصفات · شهادات العملاء · الأسئلة الشائعة |
| إجراء | زر الإضافة للسلة · زر برابط · زر واتساب · عدّاد تنازلي |
| وسائط | صورة · فيديو · مقارنة قبل/بعد |
| محتوى | نص · فاصل · شارة |
البلوكات تُخزَّن عندنا وتُرسم وقت التشغيل. إيقاف التطبيق يعيد كل شيء كما كان بلا أثر.
قراءة فقط، ولا نصل لعملاء التاجر ولا طلباته ولا مدفوعاته.
لا كوكيز ولا تحليلات ولا تخزين لأي بيانات عن زوار المتجر.
كلها مقروءة من الكود أو من قاعدة البيانات الحيّة — ولا واحد منها تقدير.
رسالتهم حرفياً: «الصلاحيات، الأحداث، نمط المصادقة، رابط استقبال التنبيهات». هذا التبويب هو جوهر الاجتماع.
app.*. ولا حدث متجر واحد.اثنتان فقط وكلتاهما قراءة. وننادي نقطتين لا غير في واجهة سلة كلها.
| الصلاحية | النوع | لماذا نحتاجها |
|---|---|---|
| settings | read | لمعرفة معرّف المتجر فقط |
| products | read | لعرض قائمة منتجات التاجر في المنتقي |
| النقطة | متى | لماذا |
|---|---|---|
| GET /store/info | مرة واحدة عند التثبيت | لربط معرّف التاجر بمعرّف المتجر |
| GET /products | عند فتح منتقي المنتجات | لعرض قائمة منتجات التاجر |
طلبنا أقل ما يلزم. لا نستطيع تعديل أي شيء في متجر التاجر حتى لو أردنا، ولا نرى طلباته ولا عملاءه.
أحد عشر حدثاً، كلها app.* تصل تلقائياً. ولم نشترك في أي حدث
متجر — لا order.* ولا product.* ولا غيرها.
| الحدث | المجموعة | ما نفعله عنده |
|---|---|---|
| app.store.authorize | تثبيت | يصل التوكن — نخزّنه ونربط معرّف التاجر بمعرّف المتجر |
| app.installed | تثبيت | نسجّل التثبيت ونستقبل الإعدادات الأولى |
| app.uninstalled | تثبيت | نمحو التوكنات فوراً ونعلّم المتجر مُلغى — ولا نحذف بياناته |
| app.settings.updated | تثبيت | نحدّث إعدادات التطبيق عند التاجر |
| app.trial.started | اشتراك | نفتح الخدمة |
| app.trial.expired | اشتراك | نغلق الخدمة |
| app.trial.canceled | اشتراك | نغلق الخدمة |
| app.subscription.started | اشتراك | نفتح الخدمة |
| app.subscription.renewed | اشتراك | نمدّد الصلاحية |
| app.subscription.expired | اشتراك | نغلق الخدمة — والبيانات تبقى |
| app.subscription.canceled | اشتراك | نغلق الخدمة — والبيانات تبقى |
لأن سلة تحصّل وتخبرنا، ونحن نوقف الخدمة عند انتهاء التجربة أو الاشتراك. بدونها يستمر التاجر بلا اشتراك.
بلا صفحة callback عندنا. التوكن يصل موقَّعاً في حدث
app.store.authorize، فلا يمرّ في متصفح أحد.
| البند | التفصيل |
|---|---|
| التخزين | صفّ مستقل لكل متجر في قاعدة البيانات |
| التجديد | تلقائي قبل انتهائه بدقيقتين |
| التزامن | قفل ذرّي يمنع تجديدين معاً — لأن refresh_token في سلة أحادي الاستخدام، والتجديد المزدوج يُبطل الجلسة |
| الإلغاء | يُمحى فوراً عند app.uninstalled |
توكن سلة المدمج عمره دقائق، وتجديده يعيد رسم الإطار فيفقد التاجر مكانه في العمل. فنستعمله مرة واحدة لإثبات الهوية، ثم نصدر جلستنا.
نقطة واحدة تستقبل الأحداث الأحد عشر. وكل طلب يُرفض ما لم يحمل توقيعاً صحيحاً.
HMAC-SHA256 بالسر المشترك، ومقارنة ثابتة الزمن401 ورفض
ولو فتحته في المتصفح مباشرة يرجع 405 method not allowed لأن الويبهوك
POST فقط. وكلاهما رفض، لكن الـ401 هو إثبات التوقيع.
| الحالة | ما نفعله |
|---|---|
| منع التكرار | مفتاح من بصمة الجسم الخام — لا من created_at الذي قد يغيب |
| آلة الحالات | processing → ok / failed — والفاشل يحتفظ بحمولته لإعادة التشغيل |
| خطأ عابر | 500 لتعيد سلة الإرسال |
| حمولة فاسدة | 200 فلا تُعاد بلا فائدة |
| الكنس | مهمة يومية ٣:٠٠ UTC — الناجح يُمسح بعد ٧ أيام والفاشل بعد ٩٠ |
المسجَّل في بورتال سلة ليس المحرك — بل محمِّل صغير لا يفعل إلا ثلاثة أشياء. وهذا سؤال سيأتي، فاعرضه أنت.
| الحقل | القيمة |
|---|---|
| المكان | بوابة الشركاء ← التطبيق 108868304 ← App Snippets |
| place | before |
| tag | body |
| الحقن | تلقائي بعد تثبيت التطبيق — تستضيفه سلة نفسها على salla-dev-portal.s3 |
| الحجم | ١١٩٤ حرفاً من أصل ٥٠٬٠٠٠ مسموحة |
(function () {
var URL = 'https://product-blocks-panel.pages.dev/engine/pb-engine.js';
// حارس التكرار: التنقّل الداخلي في المتجر قد يعيد تنفيذ السنِبِت،
// وتحميل المحرك مرتين يعني مراقبَين ورسماً مزدوجاً.
if (document.querySelector('script[data-pb-loader]')) return;
var s = document.createElement('script');
s.src = URL;
s.async = true;
s.setAttribute('data-pb-loader', '1');
document.head.appendChild(s);
})();
store.id و product.idGET /v1/blocks ويحفظ الرد في ذاكرة الجلسةdocument.body لإعادة الرسم بعد التنقّل الداخليثلاثة أسباب — وبموافقة سلة الصريحة المكتوبة. التفصيل في تبويب «الأسئلة والعرض» ← البند الحسّاس الثاني.
هذا أدقّ سؤال تقني سيأتي، فاعرضه أنت. البلوكات مخزَّنة عندنا لا عند سلة — فكيف تصل إلى الصفحة، وكيف يُحدَّد مكانها، وماذا يحدث لو تغيّر القالب.
</body> بعد التثبيت. نحن لا نحقن شيئاً بأنفسنا.script async واحد. وينتظر تهيئة Salla SDK حتى ١٠ ثوانٍ.GET /v1/blocks.div مستقل يضعه جاراً لعنصر الوصف — لا داخله.| ما يحتاجه | من أين يأخذه | لماذا بهذا الترتيب |
|---|---|---|
| هل هي صفحة منتج | /p{رقم} في الرابط أو page.slug === 'product.single' | شرطان، يكفي أحدهما — فلا نعتمد على مصدر واحد |
| معرّف المنتج | الرابط أولاً: /p(\d+) ثم salla.config page.id | الرابط مقدَّم عمداً: بعد التنقّل الداخلي يتأخر salla.config عن الواقع لحظات، أما الرابط فيتغيّر فوراً |
| معرّف المتجر | salla.config.get('store.id') | من سلة نفسها — لا من مُدخل ولا من الرابط |
يجرّب أربعة محدّدات بالترتيب ويقف عند أول ما يجده. والترتيب مقصود: الأول رسمي من سلة، والبواقي أسماء شائعة في الثيمات.
| الترتيب | المحدّد | مصدره |
|---|---|---|
| ١ | [data-testid="store-product-description"] | سمة رسمية من سلة — الأثبت |
| ٢ | .product__description | تسمية BEM شائعة في الثيمات |
| ٣ | .product-description | تسمية بديلة شائعة |
| ٤ | .article--main | احتياطي أخير |
لا يرسم شيئاً وينصرف بهدوء. لا خطأ في الكونسول ولا عنصر ناقص ولا تخطيط مكسور — الصفحة تبقى كما لو أن التطبيق غير مثبَّت. بلوك غائب أهون من صفحة مكسورة.
التاجر يختار الموضع من إعدادات التطبيق، وكل خيار عملية DOM واحدة:
| الخيار | العملية | الأثر على عنصر الوصف |
|---|---|---|
| فوق الوصف | insertBefore(root, host) | لا يُمسّ |
| تحت الوصف الافتراضي | insertBefore(root, host.nextSibling) | لا يُمسّ |
| بدل الوصف | host.style.display = 'none' + insertBefore بعده | يُخفى بـCSS — ولا يُحذف |
١ · لا نكتب داخل أي عنصر من عناصر الثيم. نبني div
جديداً ونضعه جاراً لعنصر الوصف. ولا نستعمل innerHTML
على شيء يملكه القالب.
٢ · حتى «بدل الوصف» لا يحذف الوصف — يضع عليه
display:none وسمة data-pb-hidden. فالتراجع فوري وكامل.
٣ · كل ما نضيفه موسوم بـdata-pb — فالتنظيف يعرف
ما يزيله بدقة، ولا يلمس شيئاً آخر في الصفحة.
هذه مشكلة حقيقية واجهناها وحللناها. سلة تبدّل محتوى الصفحة بلا
إعادة تحميل، فيُمسح ما رسمناه، وonReady لا يُطلق ثانيةً.
ولا يوجد حدث تنقّل موثَّق في Twilight — راجعنا الدوكس.
فالحل ألّا نعتمد على حدث أصلاً:
| الآلية | التفصيل |
|---|---|
MutationObserver | على document.body بـchildList + subtree، مؤجَّل ٢٢٠ مللي فلا يعمل مع كل تغيّر صغير |
popstate | زر رجوع المتصفح لا يُطلق تغييراً في DOM دائماً |
pageshow | الرجوع من تبويب آخر بعد أن أفرغ المتصفح الصفحة من الذاكرة |
| وفحص رخيص قبل كل رسم | هل هي صفحة منتج؟ وهل جذرنا موجود؟ وهل data-pb-product يطابق المنتج الحالي؟ — وإلا لا نفعل شيئاً |
في إرشادات سلة: «لا تستخدم body كمرجع مباشر».
ونحن نراقب document.body. سألنا دعمكم عن هذا صراحةً،
وجاء الجواب (٥ أغسطس ٢٠٢٦):
«لا توجد طريقة محددة يجب اتباعها في هذه الحالة، ويمكنكم استخدام أي نهج ترونه مناسبًا، بشرط ألا يخالف إرشادات سلة الخاصة بتطوير التطبيقات.»
وما تقوله بعده:
القاعدة في إرشاداتكم واردة في سياق innerHTML تحديداً
— «استعمل innerHTML بمسؤولية، ولا تستخدم body كمرجع
مباشر». أي المقصود الكتابة على الجسم، لا مراقبته.
ونحن نراقب ولا نكتب. لا نلمس body.innerHTML ولا
نضيف إليه شيئاً — بلوكاتنا تُحقن في حاوية الوصف وحدها.
ولو كانت قراءتنا خاطئة عندكم، قولوها ونغيّرها. لكن لا يوجد حدث تنقّل موثَّق في Twilight، فنحتاج بديلاً تقترحونه.
| الحالة | السلوك |
|---|---|
| لم نجد نقطة التثبيت | لا نرسم شيئاً والصفحة كما هي |
| الخادم لا يستجيب | ننظّف ونخرج — ولا نترك بلوكات منتج سابق معروضة |
| الزائر انتقل أثناء انتظار الشبكة | نفحص المنتج ثانيةً بعد وصول الرد ونلغي الرسم إن تغيّر |
| عمليتا رسم متزامنتان | قفل mounting يمنع التداخل |
| المحرك وصل قبل SDK سلة | ينتظر حتى ١٠ ثوانٍ بمؤقّت يوقف نفسه — لا setInterval |
| التطبيق معطَّل أو بلا بلوكات | ننظّف ونخرج فيختفي كل أثر فوراً |
| بلوك واحد فشل رسمه | البواقي تُرسم، ونسجّل تحذيراً واحداً فقط |
| ما نضيفه | متى | ملاحظة |
|---|---|---|
script المحمِّل | دائماً | ١١٩٤ حرفاً — تحقنه سلة |
script المحرك | دائماً | ~٤٥ ك.ب مضغوطاً من CDN |
style#pb-style | عند أول رسم | مرة واحدة، مضمَّن، لا طلب شبكة |
link#pb-sicons | شرطياً فقط | خط أيقونات سلة ٣١٢ ك.ب — لا يُحمَّل إلا إن استعمل التاجر أيقونة فعلاً |
div.pb-root | عند الرسم | عنصر واحد يحمل كل البلوكات، بـfont-family: var(--font-main) |
خط الأيقونات كان يُحقن دائماً — ٣١٢ ك.ب على كل صفحة منتج ولو لم تحمل البلوكات أيقونة واحدة. جعلناه شرطياً لأن دوكسكم تقيس أداء صفحة الزائر عند المراجعة. هذا مثال على أننا قرأنا متطلباتكم لا أننا بنينا ثم سألنا.
كل شيء على Cloudflare — لا مزوّد آخر ولا خادم ثالث. والعزل في بنية التخزين نفسها لا في شرط برمجي يمكن نسيانه.
لوحة التاجر (مدمجة في سلة)
│ توكن سلة ← مرة واحدة ← جلسة موقّعة منّا
▼
┌────────────────────────────────────────────┐
│ Cloudflare Worker + D1 + R2 │
└────────────────────────────────────────────┘
▲ POST /v1/webhook ← أحداث سلة (تحقق HMAC)
│ GET /v1/blocks → { settings, blocks }
▼
سنِبِت المتجر (محمِّل) ──→ المحرك من Cloudflare Pages
▼
صفحة المنتج عند الزائر
الخادم — الويبهوك والواجهة الإدارية والمهمة اليومية
إعدادات البلوكات وتوكنات المتاجر وسجل الأحداث
صور التجّار — تُخدَّم عبر الـWorker لا برابط مباشر
لوحة التحكم + ملف المحرك + الصفحات القانونية
WHERE store_id = ?{store_id}/مراجعة أمنية داخلية فحصت هذا البند خصيصاً ولم تجد أي مسار لبيانات متجر آخر.
لو سُئلت عن أي شيء — أين مخزَّن، من أين يُضبط، كيف أُريه — فالجواب هنا بمكانه ورابطه المباشر. الروابط تفتح في تبويب جديد، وأزرار النسخ تنسخ القيمة كما هي.
كل ما يخص إعدادات التطبيق عند سلة. افتح صفحة التطبيق أولاً، ثم تنقّل بين تبويباتها — الجدول يذكر اسم التبويب لكل بند.
| لو سألوك عن | التبويب في صفحة التطبيق | القيمة عندنا |
|---|---|---|
| الصلاحيات | الصلاحيات / Scopes | settings:read · products:read — لا غير |
| الأحداث والويبهوك | الويبهوكس / Webhooks | نقطة واحدة · استراتيجية Signature · ١١ حدثاً |
| نمط المصادقة | الإعدادات / OAuth | Easy Mode + redirect_urls |
| Client ID | الإعدادات | 4c335b9e-205d-4bd1-b11b-2d1769f35957 |
| السنِبِت | App Snippets | محمِّل ١١٩٤ حرفاً · place: before · tag: body |
| الصفحة المدمجة | Embedded Pages | المسار blocks-builder |
| التسعير والباقات | الباقات / Plans | ٤٩ ر.س شهرياً · تجربة ٧ أيام |
| ملف النشر | النشر / Publish | ٤ من ٥ أقسام مكتملة — الناقص: الفيديو |
| الاسم والوصف واللقطات | صفحة التطبيق / Page Builder | «وصّاف يوصف منتجك عنك» |
أي شاشة تعرض client_secret أو webhook_secret.
لا أحد سيطلبهما — وسلة تملكهما أصلاً. وإن احتجت فتح تبويب
الإعدادات، مرّر بسرعة أو شارك نافذة بعينها لا الشاشة كاملة.
البنية كلها في حساب واحد. لن تحتاجها في الاجتماع — لكن لو طُلب إثبات أن البيانات معزولة أو أن الخادم يعمل، فهذه أسرع طريق.
| لو سألوك عن | القسم في اللوحة | الاسم عندنا |
|---|---|---|
| الخادم | Workers & Pages | product-blocks-api |
| قاعدة البيانات | Storage → D1 | product-blocks-db |
| الصور | R2 Object Storage | product-blocks-media |
| اللوحة والمحرك | Workers & Pages | product-blocks-panel |
| الأسرار | الخادم → Settings → Variables | SALLA_WEBHOOK_SECRET · SALLA_CLIENT_SECRET · ADMIN_KEY |
| المهمة اليومية | الخادم → Settings → Triggers | 0 3 * * * — حذف وسائط المغادرين وكنس السجل |
| السجلات الحيّة | الخادم → Logs | أو بالأمر wrangler tail أدناه |
| البيانة | المكان | الجدول / البادئة |
|---|---|---|
| بلوكات التاجر | D1 | block_configs |
| توكنات المتاجر | D1 | stores |
| سجل أحداث الويبهوك | D1 | webhook_events |
| صور التجّار | R2 | product-blocks-media/{store_id}/ |
| الجلسات | — | لا تُخزَّن — موقّعة ويُتحقّق منها بالتوقيع |
| بيانات زوار المتاجر | — | لا شيء إطلاقاً |
استعمل الاستعلامات أعلاه فقط. لا تشغّل SELECT * على
stores — فيه التوكنات.
هنا يحدث العرض الحيّ. افتح الاثنين قبل الاجتماع وأبقهما في تبويبين.
بلا تسجيل دخول. جرّبها من جوالك أو من نافذة خفية قبل الاجتماع لتطمئن.
| لو سألوك | أرِهم |
|---|---|
| «كيف يبني التاجر؟» | اللوحة ← إضافة بلوك ← عدّل نصاً وأشِر للمعاينة |
| «وين تظهر للزائر؟» | صفحة المنتج — تحت الوصف مباشرة |
| «هل ترث ألوان القالب؟» | قارن لون الأزرار في البلوكات بلون أزرار المتجر |
| «هل تعمل على الجوال؟» | افتح نفس الرابط من جوالك — أقوى من أي شرح |
| «مسودة أم منشور؟» | عدّل في اللوحة بلا نشر، وأعد تحميل صفحة المنتج — لا يتغير شيء |
لو طلبوا مراجعة الكود أو ملفاً بعينه. كل شيء في
G:\product-blocks.
| لو سألوك عن | الملف |
|---|---|
| المحرك — مصدره | dashboard/src/engine/index.js |
| المحرك — المبني | dashboard/public/engine/pb-engine.js |
| السنِبِت المسجَّل في سلة | dashboard/snippet-loader.js |
| البلوكات الـ١٨ | dashboard/src/blocks/*.js |
| معالج الويبهوك | src/webhook.js |
| التحقق من التوقيع | src/lib/crypto.js |
| التوكنات وقفل التجديد | src/lib/stores.js |
| نداءات واجهة سلة | src/lib/salla.js |
| جلسة اللوحة | src/lib/session.js |
| الصور والحصص | src/lib/media.js |
| الموجّه والمهمة اليومية | src/index.js |
| الإعدادات والبيئة | wrangler.toml |
| الصفحات القانونية | dashboard/public/legal/ |
| لو احتجت | الملف |
|---|---|
| هذا الملف نصّاً | docs/MEETING-BRIEF.md |
| حالة المشروع والقرارات | docs/PROJECT-CONTEXT.md |
| فخاخ سلة المثبتة بالتجربة | docs/SALLA-FACTS.md |
| خريطة دوكس سلة | docs/SALLA-DOCS-MAP.md |
| ما بقي قبل النشر | docs/BEFORE-PUBLISH.md |
| الهوية ونصوص المتجر | docs/BRAND-BRIEF.md |
كلها تعمل بلا تسجيل دخول. وأي واحد منها يمكن أن ترسله لسلة مباشرة.
عامة : /v1/health · /v1/blocks · /v1/webhook · /v1/theme · /v1/media/*
اللوحة: /v1/admin/{session, me, products, blocks, publish, settings, media}
اقرأ الجواب كما هو — كلها صحيحة ومبنية على الكود. افتح ما تحتاجه وقت السؤال.
لا، ولا حرفاً. البلوكات تُخزَّن عندنا كـ JSON، وسنِبِت التطبيق يرسمها في صفحة المنتج وقت التشغيل. إيقاف التطبيق يعيد كل شيء كما كان.
تحت وصف المنتج افتراضياً، والتاجر يختار: فوق الوصف أو تحته أو بدلاً منه.
ونحدّد المكان بأربعة محدّدات نجرّبها بالترتيب — أولها سمة سلة الرسمية data-testid="store-product-description". ولو لم نجد أياً منها لا نرسم شيئاً.
سلة تحقن سنِبِت التطبيق تلقائياً، والسنِبِت يجلب محرّكنا، والمحرك يقرأ معرّف المتجر والمنتج ثم يجلب البلوكات ويبني عنصراً جديداً مستقلاً يضعه جاراً لعنصر الوصف.
لا نكتب داخل أي عنصر يملكه القالب، ولا نستعمل innerHTML عليه. وكل ما نضيفه موسوم بـdata-pb فيُزال بدقة.
هذه مشكلة واجهناها فعلاً: سلة تبدّل محتوى الصفحة بلا إعادة تحميل فيُمسح ما رسمناه، وonReady لا يُطلق ثانيةً. ولا حدث تنقّل موثَّق في Twilight.
فلا نعتمد على حدث أصلاً: نراقب الصفحة بـMutationObserver مؤجَّل، ونعيد الرسم حين يتغيّر المنتج فعلاً. ونراقب ولا نكتب على الجسم.
لا يتطلب قالباً بعينه ولا تعديله. والبلوكات ترث --color-primary و --font-main من القالب تلقائياً. ولو لم نجد نقطة التثبيت، لا نرسم شيئاً — لا نكسر الصفحة.
لا نستدعي واجهة السلة إطلاقاً. الزر عندنا يفوّض النقرة لزر سلة الأصلي في الصفحة، فيرث الكمية والخيارات والتحقق والتنبيهات كما هي. واجهة أجمل للزر نفسه.
من صفحة المتجر نفسها. ننسخ عنصر السعر كما رسمته سلة، فيأتي بعملة الزائر وبنظام أرقام المتجر. ولو لم نجد سعراً لا نرسم البلوك إطلاقاً — لا نعرض رقماً افتراضياً.
Cloudflare — D1 للإعدادات و R2 للصور. ولا مزوّد آخر. والتفصيل الكامل في تبويب «وين ألقى كل شيء».
لا. لا كوكيز ولا تحليلات ولا تخزين. والاستثناء الوحيد: إن استعمل التاجر بلوك فيديو يوتيوب، فمشغّل يوتيوب يتبع سياسة جوجل — وهو مذكور صراحةً في سياسة خصوصيتنا.
نمحو التوكنات فوراً، ونُبقي الإعدادات والصور ٣٠ يوماً ثم تُحذف نهائياً بمهمة مجدولة يومية.
والمهلة مقصودة: من يلغي بالخطأ ويعيد التثبيت خلال شهر يجد كل شيء كما تركه.
ملف واحد ~٤٥ ك.ب يُخدَّم مضغوطاً من شبكة توزيع عالمية. وخط الأيقونات لا يُحمَّل إلا إن استعمل التاجر بلوكاً فيه أيقونة.
البلوكات لا تظهر مؤقتاً، والمتجر وصفحات المنتجات وسلة الشراء تعمل طبيعياً — لأننا طبقة إضافية لا جزء من المتجر.
العزل في بنية التخزين نفسها لا في شرط برمجي: كل استعلام يحمل store_id، وكل ملف تحت بادئة معرّف المتجر. وهوية المتجر تأتي من توكن سلة لا من مُدخل مستخدم.
نعم — مسودة ونشر منفصلان. المسودة تُحفظ تلقائياً كل ثانيتين ولا يراها أحد، والنشر هو ما يجعلها تظهر.
٥ جيجابايت و١٠٠٠ صورة لكل متجر، والصور تُضغط في متصفح التاجر قبل الرفع. ولا نستضيف فيديو — بلوك الفيديو يقبل رابطاً خارجياً فقط.
٤٩ ريالاً شهرياً تُحصَّل عبر سلة من محفظته أو وسيلة دفعه المرتبطة. ولا نستلم مدفوعات ولا نرى بيانات بطاقات إطلاقاً.
لا تنتظر أن تُكتشف — اذكرها أنت أولاً. المخالفة المكشوفة يُدافَع عنها، والمخفيّة تُقرأ مسألة نزاهة. وفي كل واحدة جواب دعمكم أو تبريركم الخاص.
قواعد السنِبِتس تمنع iframe، ونحن نستعمله في بلوك الفيديو.
نستعمل تضمين يوتيوب في بلوك الفيديو، ونعرضه عليكم صراحةً ونطلب استثناءً.
لماذا: لا بديل يعطي التجربة نفسها. البديل الوحيد صورة تفتح يوتيوب في تبويب جديد — تُخرج الزائر من صفحة المنتج وتقتل معدل التحويل.
وما فعلناه لتقليل الأثر:
youtube-nocookie.com وهو الأقل تتبّعاًloading="lazy" — لا يُحمَّل شيء قبل أن يضغط الزائر التشغيلولو رأيتم أنه غير مقبول، البديل جاهز عندنا ونحذفه.
كان في الكود تمويه بسيط لاسم العنصر وحذفناه، والكود المرفوع اليوم نظيف
وغير مموّه — يكتب createElement('iframe') صراحةً.
السنِبِت في البورتال محمِّل ١١٩٤ حرفاً يسحب المحرك من
product-blocks-panel.pages.dev.
سألنا الدعم عن هذا قبل أن نبنيه، وجاء الجواب:
«لا مانع من استضافته على نطاقكم. وسنراجعه إما بالوصول إلى البرنامج النصي أو بطلب مشاركته من المطور.»
ولماذا فعلناه:
والملف متاح لكم في أي وقت — والكود المصدري كامل ومعلَّق بالعربية.
document.body
إرشاداتكم تقول «لا تستخدم body كمرجع مباشر»، ونحن
نراقبه بـMutationObserver — لأنه الآلية الوحيدة التي
تُبقي البلوكات بعد التنقّل الداخلي، إذ لا حدث تنقّل موثَّق في Twilight.
سألنا الدعم صراحةً ولم يمنعه، والقاعدة واردة في سياق innerHTML.
ونحن نراقب ولا نكتب على الجسم إطلاقاً.
والشرح الكامل — بجواب الدعم حرفياً وبالبديل الذي نطلبه منهم — في تبويب «الربط التقني» ← كيف تظهر البلوكات ← البند ٤.
الألم أولاً، ثم الحل. ولو قاطعوك بسؤال، جاوب من الأسئلة أعلاه وعُد لموضعك.
«هذا كل ما يراه زائر التاجر اليوم.»
«اللوحة مدمجة — التاجر لا يغادر سلة.»
«ما تراه هنا هو ما سيراه الزائر.»
«الحفظ تلقائي والمسودة لا يراها أحد. والنشر هو ما يظهرها.»
/v1/health ⇐ ok: true«الخادم حيّ، والصلاحيتان قراءة فقط، ولا نلمس وصف المنتج.»
لا تدّعِ ما لم يحدث. المراجع يكتشف، والصدق يبني ثقة تنفعك في كل جولة قادمة.
| لو سألوا | قل |
|---|---|
| «على كم متجر جُرّب؟» | «متجر تجريبي واحد بثيم رائد، ومتجري الشخصي. لم يستعمله تاجر آخر بعد» |
| «كم عدد المستخدمين؟» | «صفر — هذا أول إصدار» |
| «هل جُرّب على كل الثيمات؟» | «لا. جرّبناه على رائد، والمحدّدات مكتوبة لتعمل على غيره، لكننا لم نختبر كل الثيمات. ولو لم نجد نقطة التثبيت لا نرسم شيئاً — لا نكسر الصفحة» |
«يزيد المبيعات ٣٠٪» أو أي نسبة. لا نملك بياناً يدعمها.
أحداث الاشتراك مبنية ومعالَجة، لكنها لم تصل فعلياً بعد لأن
التطبيق ما زال development بلا باقة منشورة. اختبرنا منطق البوابة
يدوياً، والتحقق الحقيقي عند أول تاجر.
وصّاف يضيف بلوكات إقناع تحت وصف المنتج بلا كود وبلا لمس القالب.
صلاحيتان قراءة فقط · Easy Mode · ويبهوك موقَّع بـ HMAC · وكل شيء على Cloudflare.
وبندان نعرضهما صراحةً: تضمين يوتيوب، والمحرك على نطاقنا بموافقة الدعم المكتوبة.