@quran-lab/tajweedformer 0.0.0-stage → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +148 -0
  3. package/README.md +121 -2
  4. package/dist/tajweedformer-opus.mjs +2 -0
  5. package/dist/tajweedformer-opus.wasm +0 -0
  6. package/dist/tajweedformer-scorer.mjs +2 -0
  7. package/dist/tajweedformer-scorer.wasm +0 -0
  8. package/dist/tajweedformer.mjs +2 -0
  9. package/dist/tajweedformer.wasm +0 -0
  10. package/docs/api.md +167 -0
  11. package/examples/browser.html +69 -0
  12. package/examples/node.mjs +37 -0
  13. package/package.json +81 -6
  14. package/web/consent/copy.js +43 -0
  15. package/web/consent/decks.js +382 -0
  16. package/web/consent/element.js +164 -0
  17. package/web/consent/index.d.ts +65 -0
  18. package/web/consent/index.js +154 -0
  19. package/web/consent/state.js +100 -0
  20. package/web/index.d.ts +4 -0
  21. package/web/index.js +10 -0
  22. package/web/platform/client.js +366 -0
  23. package/web/platform/contributor-key.js +112 -0
  24. package/web/platform/index.d.ts +114 -0
  25. package/web/platform/index.js +7 -0
  26. package/web/platform/storage.js +50 -0
  27. package/web/platform/telemetry.js +199 -0
  28. package/web/scorer.d.ts +4 -0
  29. package/web/scorer.js +10 -0
  30. package/web/sharing/encoder.js +155 -0
  31. package/web/sharing/index.d.ts +215 -0
  32. package/web/sharing/index.js +257 -0
  33. package/web/sharing/oggopus.js +294 -0
  34. package/web/sharing/outbox.js +606 -0
  35. package/web/sharing/resampler.js +122 -0
  36. package/web/sharing/uploader.js +171 -0
  37. package/web/tajweed/decode.js +213 -0
  38. package/web/tajweed/delivery.js +317 -0
  39. package/web/tajweed/engine.js +600 -0
  40. package/web/tajweed/heads.js +73 -0
  41. package/web/tajweed/index.d.ts +737 -0
  42. package/web/tajweed/index.js +12 -0
  43. package/web/tajweed/manifest.js +452 -0
  44. package/web/tajweed/node.d.ts +3 -0
  45. package/web/tajweed/node.js +59 -0
  46. package/web/tajweed/settings.js +483 -0
  47. package/web/tajweed/tajweedformer.js +369 -0
@@ -0,0 +1,382 @@
1
+ // GENERATED by scripts/gen_decks.mjs from the sharing copy decks (quranlab.sharing-copy/1). Do not edit.
2
+ export const DECKS = {
3
+ "ar": {
4
+ "format": "quranlab.sharing-copy/1",
5
+ "version": "2026-11-07.1",
6
+ "lang": "ar",
7
+ "dir": "rtl",
8
+ "consent_version": "share-2026-11-07",
9
+ "delete_days": 30,
10
+ "strings": {
11
+ "sheet.title": "اجعل تلاوتك خيراً مستمراً",
12
+ "sheet.body": "عندما تشارك تلاواتك، فإنها تساعد في تدريب نموذج التجويد المجاني في هذا التطبيق على الاستماع بشكل أفضل. وكل متعلم يعينه بعدك هو عمل صالح قد يستمر طويلاً بعد فراغك من التلاوة إن شاء الله: صدقة جارية.",
13
+ "sheet.hadith": "قال النبي صلى الله عليه وسلم: «إِذَا مَاتَ الإِنْسَانُ انْقَطَعَ عَنْهُ عَمَلُهُ إِلاَّ مِنْ ثَلاَثَةٍ إِلاَّ مِنْ صَدَقَةٍ جَارِيَةٍ أَوْ عِلْمٍ يُنْتَفَعُ بِهِ أَوْ وَلَدٍ صَالِحٍ يَدْعُو لَهُ»",
14
+ "sheet.hadith_ref": "صحيح مسلم 1631",
15
+ "sheet.mistakes": "أخطاؤك هي الجزء الأثمن، فهي تُعلّم النموذج كيف يصوّب للمتعلم التالي برفق.",
16
+ "sheet.what": "التسجيل الصوتي لجلسات تلاوتك في هذا التطبيق أثناء استماعه، والآيات التي تلوتها، وتفاصيل تقنية أساسية مثل إصدار النموذج ونوع الجهاز. دون اسم، أو جهات اتصال، أو موقع جغرافي.",
17
+ "sheet.who": "Quran Lab، وهو وقف. النماذج التي يدرّبها مجانية للجميع دائماً، ولا تُباع أبداً.",
18
+ "sheet.control": "يمكنك التوقف في أي وقت وحذف ما شاركته من الإعدادات.",
19
+ "sheet.share": "مشاركة تلاواتي",
20
+ "sheet.not_now": "ليس الآن",
21
+ "sheet.guardian.title": "سؤال للوالدين أو ولي الأمر",
22
+ "sheet.guardian.body": "هذا التطبيق مخصص للأطفال. يُرجى قراءة هذا مع طفلك: تفعيل المشاركة يرسل التسجيل الصوتي لتلاواته إلى Quran Lab لمساعدة متعلمين آخرين. لا يمكن تفعيل هذا الخيار إلا من قِبل أحد الوالدين أو ولي الأمر.",
23
+ "sheet.guardian.confirm": "أنا والد هذا الطفل أو ولي أمره، وأوافق على مشاركة تلاواته.",
24
+ "sheet.guardian.share": "المشاركة كولي أمر",
25
+ "thanks": "جزاك الله خيراً. تلاوتك تساعد الآن متعلمين آخرين. تقبل الله منك.",
26
+ "settings.toggle": "مشاركة تلاواتي",
27
+ "settings.what": "ما الذي تتم مشاركته",
28
+ "settings.delete": "حذف ما شاركته",
29
+ "what.detail": "عند تفعيل المشاركة، يُرسل التسجيل الصوتي لجلسات تلاوتك في هذا التطبيق إلى Quran Lab، مع الآية التي تلوتها، وتفاصيل تقنية عامة (إصدار النموذج، نوع الجهاز، إصدار النظام، واليوم)، ورمز عشوائي خاص بهذا التثبيت. ولا نرسل أبداً اسمك أو حسابك أو جهات اتصالك أو موقعك أو معرّفات جهازك. تُحفظ البيانات في الاتحاد الأوروبي وتُستخدم فقط لتحسين نماذج تلاوة القرآن الكريم المجانية. إيقاف المشاركة يوقف الرفع فوراً، ويمكنك طلب حذف كل ما شاركته.",
30
+ "delete.confirm": "ستُحذف تلاواتك المشاركة من خوادم Quran Lab خلال {days} يوماً.",
31
+ "delete.action": "حذف",
32
+ "delete.cancel": "إلغاء",
33
+ "delete.done": "تم استلام طلبك. سيتم حذف ما شاركته خلال {days} يوماً.",
34
+ "reask": "قد تساعد تلاوتك اليوم متعلماً آخر غداً. هل ترغب في مشاركتها؟",
35
+ "impact.minutes": {
36
+ "zero": "شاركت {minutes} دقيقة من التلاوة. جزاك الله خيراً.",
37
+ "one": "شاركت {minutes} دقيقة من التلاوة. جزاك الله خيراً.",
38
+ "two": "شاركت {minutes} دقيقة من التلاوة. جزاك الله خيراً.",
39
+ "few": "شاركت {minutes} دقائق من التلاوة. جزاك الله خيراً.",
40
+ "many": "شاركت {minutes} دقيقة من التلاوة. جزاك الله خيراً.",
41
+ "other": "شاركت {minutes} دقيقة من التلاوة. جزاك الله خيراً."
42
+ }
43
+ },
44
+ "hadith_source": {
45
+ "collection": "Sahih Muslim",
46
+ "number": 1631,
47
+ "url": "https://sunnah.com/muslim:1631",
48
+ "translation_source": "https://sunnah.com/muslim:1631",
49
+ "translated": false,
50
+ "verified_at": "2026-11-07",
51
+ "note": "matn as printed on sunnah.com; word for word the same as HadeethEnc ar (diacritics aside)"
52
+ }
53
+ },
54
+ "bn": {
55
+ "format": "quranlab.sharing-copy/1",
56
+ "version": "2026-11-07.1",
57
+ "lang": "bn",
58
+ "dir": "ltr",
59
+ "consent_version": "share-2026-11-07",
60
+ "delete_days": 30,
61
+ "strings": {
62
+ "sheet.title": "আপনার তিলাওয়াতের কল্যাণ অব্যাহত থাকুক",
63
+ "sheet.body": "আপনি যখন আপনার তিলাওয়াত শেয়ার করেন, তা এই অ্যাপের ফ্রি তাজবিদ মডেলকে আরও ভালোভাবে শুনতে শেখায়। আপনার পরে এর মাধ্যমে উপকৃত হওয়া প্রতিটি শিক্ষার্থী আপনার জন্য এমন এক নেক আমল হতে পারে, যা তিলাওয়াত শেষ করার পরও অব্যাহত থাকতে পারে, ইনশাআল্লাহ: সাদাকায়ে জারিয়া।",
64
+ "sheet.hadith": "রাসূলুল্লাহ সাল্লাল্লাহু আলাইহি ওয়াসাল্লাম বলেছেন: “যখন মানুষ মৃত্যুবরণ করে তখন তার সমস্ত আমল বন্ধ হয়ে যায় তিন প্রকার আমল ছাড়া। সাদাকা জারিয়াহ্(চালু থাকে এমন) অথবা এমন ইলম যার দ্বারা উপকার হয় অথবা পুণ্যবান সন্তান যে তার জন্যে দু’আ করতে থাকে”",
65
+ "sheet.hadith_ref": "সহিহ মুসলিম 1631",
66
+ "sheet.mistakes": "আপনার ভুলগুলোই এখানে সবচেয়ে মূল্যবান অংশ। এগুলো পরবর্তী শিক্ষার্থীকে নম্রভাবে ভুল শুধরে দিতে মডেলটিকে শেখায়।",
67
+ "sheet.what": "অ্যাপটি শোনার সময় এতে আপনার তিলাওয়াতের অডিও, যে আয়াতগুলো আপনি তিলাওয়াত করেছেন, এবং মডেল ভার্সন ও ডিভাইসের ধরনের মতো সাধারণ কিছু কারিগরি তথ্য। কোনো নাম, যোগাযোগের তথ্য বা লোকেশন নয়।",
68
+ "sheet.who": "Quran Lab, একটি ওয়াকফ। এর তৈরি মডেলগুলো সবার জন্য চিরকাল সম্পূর্ণ বিনামূল্যে এবং এগুলো কখনোই বিক্রি করা হয় না।",
69
+ "sheet.control": "আপনি যেকোনো সময় এটি বন্ধ করতে পারেন এবং যা শেয়ার করেছেন তা সেটিংস থেকে মুছে ফেলতে পারেন।",
70
+ "sheet.share": "আমার তিলাওয়াত শেয়ার করুন",
71
+ "sheet.not_now": "এখন নয়",
72
+ "sheet.guardian.title": "পিতামাতা বা অভিভাবকের জন্য একটি প্রশ্ন",
73
+ "sheet.guardian.body": "এই অ্যাপটি শিশুদের জন্য তৈরি। অনুগ্রহ করে আপনার সন্তানের সাথে এটি পড়ুন: শেয়ার চালু করলে তার তিলাওয়াতের অডিও Quran Lab-এ পাঠানো হবে, যা অন্যান্য শিক্ষার্থীদের সাহায্য করবে। কেবল পিতামাতা বা অভিভাবকই এটি চালু করতে পারবেন।",
74
+ "sheet.guardian.confirm": "আমি এই শিশুর পিতামাতা বা অভিভাবক, এবং তার তিলাওয়াত শেয়ার করতে সম্মত।",
75
+ "sheet.guardian.share": "অভিভাবক হিসেবে শেয়ার করুন",
76
+ "thanks": "জাযাকাল্লাহু খাইরান। আপনার তিলাওয়াত এখন অন্যান্য শিক্ষার্থীদের সাহায্য করছে। আল্লাহ আপনার পক্ষ থেকে এটি কবুল করুন।",
77
+ "settings.toggle": "আমার তিলাওয়াত শেয়ার করুন",
78
+ "settings.what": "যা শেয়ার করা হয়",
79
+ "settings.delete": "শেয়ার করা তিলাওয়াত মুছুন",
80
+ "what.detail": "শেয়ারিং চালু থাকলে, এই অ্যাপে আপনার তিলাওয়াতের অডিও Quran Lab-এ পাঠানো হয়। সাথে থাকে আপনার পড়া আয়াত, সাধারণ কিছু কারিগরি তথ্য (মডেল সংস্করণ, ডিভাইসের ধরন, সিস্টেম সংস্করণ, দিন) এবং এই ইনস্টলেশনের জন্য একটি র‍্যান্ডম কোড। আপনার নাম, অ্যাকাউন্ট, পরিচিতি তালিকা, লোকেশন কিংবা ডিভাইসের পরিচয় শনাক্তকারী কোনো তথ্য কখনোই নেওয়া হয় না। এই তথ্যগুলো ইউরোপীয় ইউনিয়নে (EU) সংরক্ষণ করা হয় এবং শুধুমাত্র বিনামূল্যের কুরআন তিলাওয়াত মডেলগুলোর মান উন্নত করার কাজে ব্যবহার করা হয়। শেয়ারিং বন্ধ করার সাথে সাথেই অডিও পাঠানো বন্ধ হয়ে যায়, এবং আপনি চাইলে আপনার শেয়ার করা সমস্ত তথ্য মুছে ফেলার অনুরোধ করতে পারেন।",
81
+ "delete.confirm": "আপনার শেয়ার করা তিলাওয়াত {days} দিনের মধ্যে Quran Lab-এর সার্ভার থেকে মুছে ফেলা হবে।",
82
+ "delete.action": "মুছে ফেলুন",
83
+ "delete.cancel": "বাতিল করুন",
84
+ "delete.done": "আপনার অনুরোধ গ্রহণ করা হয়েছে। আপনি যা শেয়ার করেছেন তা {days} দিনের মধ্যে মুছে ফেলা হবে।",
85
+ "reask": "আজকের আপনার তিলাওয়াত হয়তো আগামী দিনে অন্য কোনো শিক্ষার্থীকে সাহায্য করতে পারে। আপনি কি এটি শেয়ার করতে চান?",
86
+ "impact.minutes": {
87
+ "one": "আপনি {minutes} মিনিট তিলাওয়াত শেয়ার করেছেন। আল্লাহ আপনাকে উত্তম প্রতিদান দিন।",
88
+ "other": "আপনি {minutes} মিনিট তিলাওয়াত শেয়ার করেছেন। আল্লাহ আপনাকে উত্তম প্রতিদান দিন।"
89
+ }
90
+ },
91
+ "hadith_source": {
92
+ "collection": "Sahih Muslim",
93
+ "number": 1631,
94
+ "url": "https://sunnah.com/muslim:1631",
95
+ "translation_source": "https://hadeethenc.com/bn/browse/hadith/65566",
96
+ "translated": false,
97
+ "verified_at": "2026-11-07",
98
+ "note": "established translation published by HadeethEnc"
99
+ }
100
+ },
101
+ "en": {
102
+ "format": "quranlab.sharing-copy/1",
103
+ "version": "2026-11-07.1",
104
+ "lang": "en",
105
+ "dir": "ltr",
106
+ "consent_version": "share-2026-11-07",
107
+ "delete_days": 30,
108
+ "strings": {
109
+ "sheet.title": "Let your recitation keep doing good",
110
+ "sheet.body": "When you share your recitations, they help teach this app's free tajweed model to listen better. Every learner it helps after you is a good deed that may continue long after you finish reciting, in sha Allah: a sadaqah jariyah.",
111
+ "sheet.hadith": "The Prophet, peace be upon him, said: \"When a person dies, his deeds come to an end except for three things: ongoing charity, beneficial knowledge, or a pious child who supplicates for him.\"",
112
+ "sheet.hadith_ref": "Sahih Muslim 1631",
113
+ "sheet.mistakes": "Your mistakes are the most valuable part. They teach the model to correct the next learner gently.",
114
+ "sheet.what": "The audio of your recitation sessions in this app, while it listens, the ayat you recited, and basic technical details such as the model version and device type. No name, no contacts, no location.",
115
+ "sheet.who": "Quran Lab, a waqf. The models it trains are free for everyone, forever, and are never sold.",
116
+ "sheet.control": "You can stop at any time and delete what you shared from the settings.",
117
+ "sheet.share": "Share my recitations",
118
+ "sheet.not_now": "Not now",
119
+ "sheet.guardian.title": "A question for a parent or guardian",
120
+ "sheet.guardian.body": "This app is made for children. Please read this with your child: sharing sends the audio of their recitations to Quran Lab to help other learners. Only a parent or guardian can turn it on.",
121
+ "sheet.guardian.confirm": "I am this child's parent or guardian, and I agree to share their recitations.",
122
+ "sheet.guardian.share": "Share as parent or guardian",
123
+ "thanks": "Jazak Allahu khayran. Your recitation is now helping other learners. May Allah accept it from you.",
124
+ "settings.toggle": "Share my recitations",
125
+ "settings.what": "What is shared",
126
+ "settings.delete": "Delete what I shared",
127
+ "what.detail": "When sharing is on, the audio of your recitation sessions in this app is sent to Quran Lab, with the ayah you recited, coarse technical details (model version, device type, system version, the day) and a random code for this install. Never your name, account, contacts, location or device identifiers. It is stored in the EU and used only to improve free Quran recitation models. Turning sharing off stops uploads at once, and you can ask for everything you shared to be deleted.",
128
+ "delete.confirm": "Your shared recitations will be deleted from Quran Lab's servers within {days} days.",
129
+ "delete.action": "Delete",
130
+ "delete.cancel": "Cancel",
131
+ "delete.done": "Your request is received. What you shared will be deleted within {days} days.",
132
+ "reask": "Your recitation today could help another learner tomorrow. Would you like to share it?",
133
+ "impact.minutes": {
134
+ "one": "You have shared {minutes} minute of recitation. May Allah reward you.",
135
+ "other": "You have shared {minutes} minutes of recitation. May Allah reward you."
136
+ }
137
+ },
138
+ "hadith_source": {
139
+ "collection": "Sahih Muslim",
140
+ "number": 1631,
141
+ "url": "https://sunnah.com/muslim:1631",
142
+ "translation_source": "https://hadeethenc.com/en/browse/hadith/65566",
143
+ "translated": false,
144
+ "verified_at": "2026-11-07",
145
+ "note": "English of HadeethEnc (Encyclopedia of Translated Prophetic Hadiths), checked against sunnah.com (Abdul Hamid Siddiqui translation, same meaning)"
146
+ }
147
+ },
148
+ "fr": {
149
+ "format": "quranlab.sharing-copy/1",
150
+ "version": "2026-11-07.1",
151
+ "lang": "fr",
152
+ "dir": "ltr",
153
+ "consent_version": "share-2026-11-07",
154
+ "delete_days": 30,
155
+ "strings": {
156
+ "sheet.title": "Que votre récitation continue de faire le bien",
157
+ "sheet.body": "Lorsque vous partagez vos récitations, elles permettent d'entraîner le modèle de tajwid gratuit de l'application à mieux écouter. Chaque apprenant qu'il aide après vous est une bonne action qui peut perdurer bien après votre récitation, inchâAllah : une sadaqa jâriya (aumône continue).",
158
+ "sheet.hadith": "Le Prophète, que la paix soit sur lui, a dit : « Quand une personne meurt, ses actions prennent fin sauf trois choses : une aumône continue (sadaqa jâriya), une science utile, ou un enfant pieux qui fait des invocations (du'â) pour elle. »",
159
+ "sheet.hadith_ref": "Sahih Muslim 1631",
160
+ "sheet.mistakes": "Vos erreurs sont ce qu'il y a de plus précieux. Elles apprennent au modèle à corriger le prochain apprenant avec douceur.",
161
+ "sheet.what": "L'enregistrement audio de vos séances de récitation dans cette application lorsqu'elle est à l'écoute, les versets que vous avez récités, et des détails techniques simples comme la version du modèle et le type d'appareil. Aucun nom, aucun contact, aucune localisation.",
162
+ "sheet.who": "Quran Lab, un waqf. Les modèles entraînés sont gratuits pour tous, pour toujours, et ne sont jamais vendus.",
163
+ "sheet.control": "Vous pouvez arrêter à tout moment et supprimer ce que vous avez partagé depuis les paramètres.",
164
+ "sheet.share": "Partager mes récitations",
165
+ "sheet.not_now": "Pas maintenant",
166
+ "sheet.guardian.title": "Une question pour un parent ou tuteur",
167
+ "sheet.guardian.body": "Cette application est conçue pour les enfants. Merci de lire ceci avec votre enfant : le partage transmet l'audio de ses récitations à Quran Lab afin d'aider d'autres apprenants. Seul un parent ou un tuteur peut l'activer.",
168
+ "sheet.guardian.confirm": "Je suis le parent ou le tuteur de cet enfant, et j'accepte de partager ses récitations.",
169
+ "sheet.guardian.share": "Partager (parent ou tuteur)",
170
+ "thanks": "Jazâk Allâhu khayran. Votre récitation aide désormais d'autres apprenants. Qu'Allah l'accepte de votre part.",
171
+ "settings.toggle": "Partager mes récitations",
172
+ "settings.what": "Ce qui est partagé",
173
+ "settings.delete": "Supprimer mes partages",
174
+ "what.detail": "Lorsque le partage est activé, l'audio de vos séances de récitation dans cette application est envoyé à Quran Lab, avec le verset que vous avez récité, des détails techniques généraux (version du modèle, type d'appareil, version du système, date) et un code aléatoire propre à cette installation. Jamais votre nom, votre compte, vos contacts, votre localisation ou les identifiants de votre appareil. Ces données sont stockées dans l'UE et utilisées uniquement pour améliorer des modèles gratuits de récitation du Coran. Désactiver le partage interrompt immédiatement les envois, et vous pouvez demander la suppression de tout ce que vous avez partagé.",
175
+ "delete.confirm": "Vos récitations partagées seront supprimées des serveurs de Quran Lab d'ici {days} jours.",
176
+ "delete.action": "Supprimer",
177
+ "delete.cancel": "Annuler",
178
+ "delete.done": "Votre demande a bien été reçue. Ce que vous avez partagé sera supprimé d'ici {days} jours.",
179
+ "reask": "Votre récitation d'aujourd'hui pourrait aider un autre apprenant demain. Souhaitez-vous la partager ?",
180
+ "impact.minutes": {
181
+ "one": "Vous avez partagé {minutes} minute de récitation. Qu'Allah vous récompense.",
182
+ "many": "Vous avez partagé {minutes} de minutes de récitation. Qu'Allah vous récompense.",
183
+ "other": "Vous avez partagé {minutes} minutes de récitation. Qu'Allah vous récompense."
184
+ }
185
+ },
186
+ "hadith_source": {
187
+ "collection": "Sahih Muslim",
188
+ "number": 1631,
189
+ "url": "https://sunnah.com/muslim:1631",
190
+ "translation_source": null,
191
+ "translated": true,
192
+ "verified_at": "2026-11-07",
193
+ "note": "no established translation of this hadith found online in this language (HadeethEnc has none for 65566); translated by the router from the verified English and back-translated: When a person dies, their actions come to an end except three things: a continuous charity (sadaqa jâriya), useful knowledge, or a pious child who makes invocations (du'â) for them."
194
+ }
195
+ },
196
+ "id": {
197
+ "format": "quranlab.sharing-copy/1",
198
+ "version": "2026-11-07.1",
199
+ "lang": "id",
200
+ "dir": "ltr",
201
+ "consent_version": "share-2026-11-07",
202
+ "delete_days": 30,
203
+ "strings": {
204
+ "sheet.title": "Biarkan bacaan Anda terus mengalirkan kebaikan",
205
+ "sheet.body": "Saat Anda membagikan bacaan Anda, rekaman tersebut membantu melatih model tajwid gratis di aplikasi ini agar dapat menyimak dengan lebih baik. Setiap pembelajar yang terbantu setelahnya bisa menjadi kebaikan yang terus mengalir walau Anda telah selesai membaca, insya Allah: sebuah sedekah jariyah.",
206
+ "sheet.hadith": "Rasulullah shallallahu 'alaihi wa sallam bersabda: \"Apabila manusia meninggal, maka terputuslah amalnya kecuali tiga perkara: sedekah yang mengalir, ilmu yang dimanfaatkan, dan anak saleh yang mendoakannya.\"",
207
+ "sheet.hadith_ref": "Shahih Muslim 1631",
208
+ "sheet.mistakes": "Kekeliruan bacaan Anda justru bagian yang paling berharga. Dari situ, model ini belajar cara mengoreksi pembelajar berikutnya dengan lembut.",
209
+ "sheet.what": "Audio dari sesi bacaan Anda di aplikasi ini saat aplikasi menyimak, ayat yang Anda baca, serta informasi teknis dasar seperti versi model dan jenis perangkat. Tanpa nama, tanpa kontak, tanpa lokasi.",
210
+ "sheet.who": "Quran Lab, sebuah inisiatif wakaf. Model yang dilatih di sini gratis untuk siapa saja, selamanya, dan tidak pernah diperjualbelikan.",
211
+ "sheet.control": "Anda bisa berhenti kapan saja dan menghapus apa yang telah Anda bagikan melalui menu pengaturan.",
212
+ "sheet.share": "Bagikan bacaan saya",
213
+ "sheet.not_now": "Nanti saja",
214
+ "sheet.guardian.title": "Pertanyaan untuk orang tua atau wali",
215
+ "sheet.guardian.body": "Aplikasi ini ditujukan untuk anak-anak. Mohon baca ini bersama anak Anda: mengaktifkan fitur berbagi akan mengirimkan audio bacaan mereka ke Quran Lab untuk membantu pembelajar lain. Hanya orang tua atau wali yang dapat mengaktifkannya.",
216
+ "sheet.guardian.confirm": "Saya adalah orang tua atau wali anak ini, dan saya menyetujui untuk membagikan rekaman bacaannya.",
217
+ "sheet.guardian.share": "Bagikan sebagai orang tua/wali",
218
+ "thanks": "Jazakallahu khairan. Bacaan Anda kini turut membantu pembelajar lain. Semoga Allah menerimanya dari Anda.",
219
+ "settings.toggle": "Bagikan bacaan saya",
220
+ "settings.what": "Data yang dibagikan",
221
+ "settings.delete": "Hapus rekaman yang dibagikan",
222
+ "what.detail": "Saat fitur berbagi aktif, audio dari sesi bacaan Anda di aplikasi ini akan dikirimkan ke Quran Lab, beserta ayat yang Anda baca, informasi teknis umum (versi model, jenis perangkat, versi sistem, hari), dan kode acak untuk instalasi ini. Sama sekali bukan nama, akun, kontak, lokasi, atau tanda pengenal perangkat Anda. Data ini disimpan di Uni Eropa dan hanya digunakan untuk menyempurnakan model bacaan Al-Qur'an yang gratis. Mematikan fitur berbagi akan langsung menghentikan pengiriman data, dan Anda dapat meminta agar semua yang pernah dibagikan dihapus.",
223
+ "delete.confirm": "Rekaman bacaan yang Anda bagikan akan dihapus dari server Quran Lab dalam waktu {days} hari.",
224
+ "delete.action": "Hapus rekaman",
225
+ "delete.cancel": "Batal hapus",
226
+ "delete.done": "Permintaan Anda telah diterima. Rekaman yang Anda bagikan akan dihapus dalam waktu {days} hari.",
227
+ "reask": "Bacaan Anda hari ini bisa membantu pembelajar lain esok hari. Bersediakah Anda membagikannya?",
228
+ "impact.minutes": {
229
+ "other": "Anda telah membagikan {minutes} menit bacaan. Semoga Allah membalas kebaikan Anda."
230
+ }
231
+ },
232
+ "hadith_source": {
233
+ "collection": "Sahih Muslim",
234
+ "number": 1631,
235
+ "url": "https://sunnah.com/muslim:1631",
236
+ "translation_source": "https://hadeethenc.com/id/browse/hadith/65566",
237
+ "translated": false,
238
+ "verified_at": "2026-11-07",
239
+ "note": "established translation published by HadeethEnc; one typo in the source corrected (mendokannya -> mendoakannya)"
240
+ }
241
+ },
242
+ "ms": {
243
+ "format": "quranlab.sharing-copy/1",
244
+ "version": "2026-11-07.1",
245
+ "lang": "ms",
246
+ "dir": "ltr",
247
+ "consent_version": "share-2026-11-07",
248
+ "delete_days": 30,
249
+ "strings": {
250
+ "sheet.title": "Biarlah bacaan anda terus mengalirkan kebaikan",
251
+ "sheet.body": "Apabila anda berkongsi bacaan, ia membantu melatih model tajwid percuma aplikasi ini agar dapat mendengar dengan lebih baik. Setiap pembelajar yang dibantunya selepas ini moga menjadi kebaikan yang terus mengalir buat anda walaupun selepas selesai membaca, insya-Allah: sebagai sedekah jariah.",
252
+ "sheet.hadith": "Rasulullah SAW bersabda: \"Apabila seseorang meninggal dunia, terputuslah amalannya melainkan tiga perkara: sedekah jariah, ilmu yang bermanfaat, atau anak soleh yang mendoakannya.\"",
253
+ "sheet.hadith_ref": "Sahih Muslim 1631",
254
+ "sheet.mistakes": "Kesilapan anda adalah bahagian yang paling bernilai. Ia mengajar model ini menegur pelajar seterusnya dengan lembut.",
255
+ "sheet.what": "Audio daripada sesi bacaan anda dalam aplikasi ini semasa ia mendengar, ayat yang dibaca, serta maklumat teknikal asas seperti versi model dan jenis peranti. Tiada nama, senarai kenalan mahupun lokasi.",
256
+ "sheet.who": "Quran Lab, sebuah inisiatif wakaf. Model yang dilatih adalah percuma untuk semua, selama-lamanya, dan tidak akan sesekali dijual.",
257
+ "sheet.control": "Anda boleh berhenti pada bila-bila masa dan memadam apa yang telah dikongsi melalui tetapan.",
258
+ "sheet.share": "Kongsi bacaan saya",
259
+ "sheet.not_now": "Bukan sekarang",
260
+ "sheet.guardian.title": "Pertanyaan untuk ibu bapa atau penjaga",
261
+ "sheet.guardian.body": "Aplikasi ini direka untuk kanak-kanak. Sila baca ini bersama anak anda: perkongsian ini akan menghantar audio bacaan mereka kepada Quran Lab untuk membantu pembelajar lain. Hanya ibu bapa atau penjaga yang boleh mengaktifkannya.",
262
+ "sheet.guardian.confirm": "Saya ibu bapa atau penjaga kepada anak ini, dan saya bersetuju untuk berkongsi bacaan mereka.",
263
+ "sheet.guardian.share": "Kongsi sebagai penjaga",
264
+ "thanks": "Jazakallahu khairan. Bacaan anda kini membantu pembelajar lain. Semoga Allah menerimanya daripada anda.",
265
+ "settings.toggle": "Kongsi bacaan saya",
266
+ "settings.what": "Maklumat yang dikongsi",
267
+ "settings.delete": "Padam apa yang dikongsi",
268
+ "what.detail": "Apabila perkongsian diaktifkan, audio sesi bacaan anda dalam aplikasi ini akan dihantar kepada Quran Lab bersama ayat yang dibaca, maklumat teknikal asas (versi model, jenis peranti, versi sistem, hari) serta kod rawak bagi pemasangan ini. Tiada nama, akaun, senarai kenalan, lokasi atau maklumat pengecam peranti anda yang diambil. Data disimpan di EU dan hanya digunakan untuk menambah baik model bacaan al-Quran yang percuma. Mematikan perkongsian akan menghentikan muat naik serta-merta, dan anda boleh memohon agar semua data yang telah dikongsi dipadamkan.",
269
+ "delete.confirm": "Bacaan yang telah anda kongsi akan dipadamkan daripada pelayan Quran Lab dalam masa {days} hari.",
270
+ "delete.action": "Padam",
271
+ "delete.cancel": "Batal",
272
+ "delete.done": "Permintaan anda telah diterima. Apa yang telah dikongsi akan dipadamkan dalam masa {days} hari.",
273
+ "reask": "Bacaan anda hari ini mungkin dapat membantu pembelajar lain esok. Sudikah anda berkongsinya?",
274
+ "impact.minutes": {
275
+ "other": "Anda telah berkongsi {minutes} minit bacaan. Semoga Allah membalas kebaikan anda."
276
+ }
277
+ },
278
+ "hadith_source": {
279
+ "collection": "Sahih Muslim",
280
+ "number": 1631,
281
+ "url": "https://sunnah.com/muslim:1631",
282
+ "translation_source": null,
283
+ "translated": true,
284
+ "verified_at": "2026-11-07",
285
+ "note": "no established translation of this hadith found online in this language (HadeethEnc has none for 65566); translated by the router from the verified English and back-translated: When a person dies, their deeds are cut off except for three things: continuous charity, beneficial knowledge, or a righteous child who prays for them."
286
+ }
287
+ },
288
+ "tr": {
289
+ "format": "quranlab.sharing-copy/1",
290
+ "version": "2026-11-07.1",
291
+ "lang": "tr",
292
+ "dir": "ltr",
293
+ "consent_version": "share-2026-11-07",
294
+ "delete_days": 30,
295
+ "strings": {
296
+ "sheet.title": "Tilavetiniz hayra vesile olmaya devam etsin",
297
+ "sheet.body": "Okumalarınızı paylaştığınızda, bu uygulamanın ücretsiz tecvid modelinin daha iyi dinlemeyi öğrenmesine yardımcı olursunuz. Sizden sonra istifade eden her bir öğrenci, okumanız bittikten çok sonra da devam edebilecek bir hayır olabilir, inşallah: bir sadaka-i câriye.",
298
+ "sheet.hadith": "Peygamberimiz (aleyhisselam) şöyle buyurmuştur: «Bir insan öldüğü zaman, üç ameli dışında bütün amellerinin sevabı kesilir: Sadaka-i câriye, kendisinden faydalanılan ilim ve kendisine dua eden salih evlat.»",
299
+ "sheet.hadith_ref": "Sahih Müslim 1631",
300
+ "sheet.mistakes": "Hatalarınız işin en kıymetli kısmıdır. Modele, bir sonraki talebeyi incitmeden, nazikçe düzeltmeyi öğretirler.",
301
+ "sheet.what": "Uygulama dinlerken yapılan tilavet kayıtlarınız, okuduğunuz ayetler ile model sürümü ve cihaz türü gibi temel teknik detaylar. İsim, rehber veya konum asla paylaşılmaz.",
302
+ "sheet.who": "Quran Lab bir vakıftır. Geliştirdiği modeller herkes için daima ücretsizdir ve asla satılmaz.",
303
+ "sheet.control": "Dilediğiniz an paylaşımı durdurabilir ve paylaştıklarınızı ayarlar bölümünden silebilirsiniz.",
304
+ "sheet.share": "Tilavetlerimi paylaş",
305
+ "sheet.not_now": "Şimdi değil",
306
+ "sheet.guardian.title": "Anne, baba veya vasiler için bir soru",
307
+ "sheet.guardian.body": "Bu uygulama çocuklar için hazırlanmıştır. Lütfen burayı çocuğunuzla birlikte okuyun: Paylaşım açıldığında, diğer öğrencilere yardımcı olmak amacıyla okuma ses kayıtları Quran Lab'e iletilir. Bunu yalnızca bir ebeveyn veya vasi açabilir.",
308
+ "sheet.guardian.confirm": "Bu çocuğun anne babası veya vasisiyim, okuma kayıtlarının paylaşılmasını onaylıyorum.",
309
+ "sheet.guardian.share": "Veli olarak paylaş",
310
+ "thanks": "Allah razı olsun. Tilavetiniz artık diğer öğrencilere de yardımcı oluyor. Allah kabul etsin.",
311
+ "settings.toggle": "Tilavetlerimi paylaş",
312
+ "settings.what": "Neler paylaşılır",
313
+ "settings.delete": "Paylaştıklarımı sil",
314
+ "what.detail": "Paylaşım açıkken, bu uygulamadaki tilavet kayıtlarınız, okuduğunuz ayet, genel teknik detaylar (model sürümü, cihaz türü, sistem sürümü, gün) ve bu kuruluma özel rastgele bir kod ile birlikte Quran Lab'e gönderilir. İsminiz, hesabınız, kişileriniz, konumunuz veya cihaz tanımlayıcılarınız asla iletilmez. Kayıtlar AB sınırları içinde saklanır ve yalnızca ücretsiz Kur'an tilaveti modellerini geliştirmek için kullanılır. Paylaşımı kapattığınız anda gönderim durur ve paylaştığınız her şeyin silinmesini talep edebilirsiniz.",
315
+ "delete.confirm": "Paylaştığınız tilavetler {days} gün içinde Quran Lab sunucularından silinecektir.",
316
+ "delete.action": "Paylaşılanları sil",
317
+ "delete.cancel": "İptal et",
318
+ "delete.done": "Talebiniz alındı. Paylaştıklarınız {days} gün içinde silinecektir.",
319
+ "reask": "Bugünkü tilavetiniz yarın bir başka öğrenciye yardımcı olabilir. Paylaşmak ister misiniz?",
320
+ "impact.minutes": {
321
+ "one": "{minutes} dakika tilavet paylaştınız. Allah razı olsun.",
322
+ "other": "{minutes} dakika tilavet paylaştınız. Allah razı olsun."
323
+ }
324
+ },
325
+ "hadith_source": {
326
+ "collection": "Sahih Muslim",
327
+ "number": 1631,
328
+ "url": "https://sunnah.com/muslim:1631",
329
+ "translation_source": "https://hadeethenc.com/tr/browse/hadith/65566",
330
+ "translated": false,
331
+ "verified_at": "2026-11-07",
332
+ "note": "established translation published by HadeethEnc"
333
+ }
334
+ },
335
+ "ur": {
336
+ "format": "quranlab.sharing-copy/1",
337
+ "version": "2026-11-07.1",
338
+ "lang": "ur",
339
+ "dir": "rtl",
340
+ "consent_version": "share-2026-11-07",
341
+ "delete_days": 30,
342
+ "strings": {
343
+ "sheet.title": "اپنی تلاوت سے خیر کا سلسلہ جاری رکھیے",
344
+ "sheet.body": "جب آپ اپنی تلاوت شیئر کرتے ہیں تو اس سے اس ایپ کے مفت تجوید ماڈل کو بہتر طور پر سننے میں مدد ملتی ہے۔ آپ کے بعد جس بھی سیکھنے والے کی اس سے مدد ہوگی، وہ ان شاء اللہ آپ کی تلاوت مکمل ہو جانے کے بعد بھی ایک مسلسل نیکی بن سکتی ہے: صدقہ جاریہ۔",
345
+ "sheet.hadith": "نبی کریم صلی اللہ علیہ وسلم نے فرمایا: «جب انسان مر جاتا ہے تو اس کا عمل بھی منقطع ہو جاتا ہے، مگر تین قسم کے عمل (کا ثواب) جاری رہتا ہے۔ صدقۂ جاریہ، یا ایسا علم جس سے اس کے بعد بھی فائدہ اٹھایا جائے، یا نیک اولاد جو اس کے لیے دعا کرے۔»",
346
+ "sheet.hadith_ref": "صحیح مسلم 1631",
347
+ "sheet.mistakes": "آپ کی غلطیاں سب سے زیادہ قیمتی ہیں۔ ان سے ماڈل سیکھتا ہے کہ اگلے سیکھنے والے کی نرمی سے کیسے اصلاح کی جائے۔",
348
+ "sheet.what": "اس ایپ میں آپ کی تلاوت کی آڈیو جب ایپ سن رہی ہو، آپ کی پڑھی ہوئی آیات، اور بنیادی تکنیکی تفصیلات جیسے ماڈل کا ورژن اور ڈیوائس کی قسم۔ کوئی نام، رابطے یا لوکیشن شامل نہیں۔",
349
+ "sheet.who": "Quran Lab، جو کہ ایک وقف ہے۔ اس کے تیار کردہ ماڈلز ہمیشہ سب کے لیے مفت رہیں گے اور کبھی بیچے نہیں جائیں گے۔",
350
+ "sheet.control": "آپ کسی بھی وقت اسے روک سکتے ہیں اور سیٹنگز سے اپنا شیئر کردہ ڈیٹا ڈیلیٹ کر سکتے ہیں۔",
351
+ "sheet.share": "میری تلاوتیں شیئر کریں",
352
+ "sheet.not_now": "ابھی نہیں",
353
+ "sheet.guardian.title": "والدین یا سرپرست کے لیے ایک سوال",
354
+ "sheet.guardian.body": "یہ ایپ بچوں کے لیے بنائی گئی ہے۔ براہِ کرم اپنے بچے کے ساتھ اسے پڑھیں: شیئر کرنے سے ان کی تلاوت کی آڈیو Quran Lab کو بھیجی جائے گی تاکہ دوسرے سیکھنے والوں کی مدد ہو سکے۔ اسے صرف والدین یا سرپرست ہی فعال کر سکتے ہیں۔",
355
+ "sheet.guardian.confirm": "میں اس بچے کا والد، والدہ یا سرپرست ہوں اور تلاوت شیئر کرنے پر رضامند ہوں۔",
356
+ "sheet.guardian.share": "بطور سرپرست شیئر کریں",
357
+ "thanks": "جزاکم اللہ خیراً۔ آپ کی تلاوت سے اب دیگر سیکھنے والوں کو مدد مل رہی ہے۔ اللہ تعالیٰ آپ سے قبول فرمائے۔",
358
+ "settings.toggle": "میری تلاوتیں شیئر کریں",
359
+ "settings.what": "کیا شیئر کیا جاتا ہے",
360
+ "settings.delete": "شیئر کردہ مواد مٹائیں",
361
+ "what.detail": "جب شیئرنگ آن ہوتی ہے، تو اس ایپ میں آپ کی تلاوت کی آڈیو Quran Lab کو بھیجی جاتی ہے، ساتھ ہی پڑھی گئی آیت، بنیادی تکنیکی تفصیلات (ماڈل کا ورژن، ڈیوائس کی قسم، سسٹم ورژن اور دن) اور اس انسٹالیشن کا ایک بے ترتیب کوڈ شامل ہوتا ہے۔ آپ کا نام، اکاؤنٹ، رابطے، لوکیشن یا ڈیوائس کی مخصوص شناخت کبھی شیئر نہیں کی جاتی۔ یہ ڈیٹا یورپی یونین (EU) میں محفوظ کیا جاتا ہے اور اسے صرف قرآن کی تلاوت کے مفت ماڈلز کو بہتر بنانے کے لیے استعمال کیا جاتا ہے۔ شیئرنگ بند کرنے سے اپ لوڈز فوراً رک جاتے ہیں، اور آپ اپنا تمام شیئر کردہ ڈیٹا ڈیلیٹ کرنے کی درخواست بھی کر سکتے ہیں۔",
362
+ "delete.confirm": "آپ کی شیئر کردہ تلاوتیں {days} دن کے اندر Quran Lab کے سرورز سے ڈیلیٹ کر دی جائیں گی۔",
363
+ "delete.action": "ڈیلیٹ کریں",
364
+ "delete.cancel": "منسوخ کریں",
365
+ "delete.done": "آپ کی درخواست موصول ہو گئی ہے۔ آپ کا شیئر کردہ ڈیٹا {days} دن کے اندر ڈیلیٹ کر دیا جائے گا۔",
366
+ "reask": "آج آپ کی تلاوت سے کل کسی اور سیکھنے والے کی مدد ہو سکتی ہے۔ کیا آپ اسے شیئر کرنا چاہیں گے؟",
367
+ "impact.minutes": {
368
+ "one": "آپ نے تلاوت کا {minutes} منٹ شیئر کیا ہے۔ اللہ تعالیٰ آپ کو جزائے خیر عطا فرمائے۔",
369
+ "other": "آپ نے تلاوت کے {minutes} منٹ شیئر کیے ہیں۔ اللہ تعالیٰ آپ کو جزائے خیر عطا فرمائے۔"
370
+ }
371
+ },
372
+ "hadith_source": {
373
+ "collection": "Sahih Muslim",
374
+ "number": 1631,
375
+ "url": "https://sunnah.com/muslim:1631",
376
+ "translation_source": "https://hadeethenc.com/ur/browse/hadith/65566",
377
+ "translated": false,
378
+ "verified_at": "2026-11-07",
379
+ "note": "established translation published by HadeethEnc"
380
+ }
381
+ }
382
+ };
@@ -0,0 +1,164 @@
1
+ // <quranlab-consent> and <quranlab-sharing-settings>: the SDK's own sharing sheet and settings for
2
+ // the browser (SHARING_EXPERIENCE.md, PLATFORM_CLIENT.md section 9). Themable with CSS custom
3
+ // properties (--ql-bg, --ql-fg, --ql-muted, --ql-accent, --ql-accent-fg, --ql-radius, --ql-font) and
4
+ // ::part(sheet|title|body|primary|secondary|guardian), RTL from the deck's direction, keyboard and
5
+ // screen reader friendly (a modal <dialog>), two buttons of equal weight, nothing pre-ticked.
6
+ //
7
+ // import '@quran-lab/tajweedformer/consent'; // defines the elements
8
+ // const yes = await QuranLabConsent.ask({ lang: 'ar', variant: 'first', guardian: false });
9
+
10
+ import { copyFor } from './copy.js';
11
+
12
+ const STYLE = `
13
+ :host { all: initial; }
14
+ dialog { box-sizing: border-box; max-inline-size: min(34rem, calc(100vw - 2rem)); inline-size: 100%; border: none;
15
+ max-block-size: calc(100dvh - 2rem); overflow: auto; overscroll-behavior: contain;
16
+ padding: 1.5rem; border-radius: var(--ql-radius, 16px); background: var(--ql-bg, #fdfcf8); color: var(--ql-fg, #1c1b19);
17
+ font: 400 1rem/1.55 var(--ql-font, system-ui, -apple-system, "Segoe UI", "Noto Sans", "Noto Naskh Arabic", sans-serif);
18
+ box-shadow: 0 20px 60px rgb(0 0 0 / .25); }
19
+ dialog::backdrop { background: rgb(0 0 0 / .45); }
20
+
21
+ h2 { margin: 0 0 .75rem; font-size: 1.25rem; line-height: 1.3; font-weight: 650; }
22
+ p { margin: 0 0 .75rem; }
23
+ .muted { color: var(--ql-muted, inherit); opacity: .85; font-size: .95rem; }
24
+ ul { margin: 0 0 1rem; padding-inline-start: 1.1rem; }
25
+ li { margin-block-end: .35rem; }
26
+ label.guardian { display: flex; gap: .6rem; align-items: flex-start; margin: .5rem 0 1rem; font-weight: 600; }
27
+ label.guardian input { inline-size: 1.2rem; block-size: 1.2rem; margin: .15rem 0 0; accent-color: var(--ql-accent, #2a3b8f); }
28
+ .buttons { display: grid; grid-template-columns: 1fr 1fr; gap: .75rem; }
29
+ @media (max-width: 420px) { .buttons { grid-template-columns: 1fr; } }
30
+ button { font: inherit; font-weight: 600; min-block-size: 2.9rem; border-radius: calc(var(--ql-radius, 16px) * .6); cursor: pointer;
31
+ padding: .6rem 1rem; border: 2px solid var(--ql-accent, #2a3b8f); }
32
+ button.primary { background: var(--ql-accent, #2a3b8f); color: var(--ql-accent-fg, #fff); }
33
+ button.secondary { background: transparent; color: inherit; }
34
+ button:disabled { opacity: .5; cursor: not-allowed; }
35
+ button:focus-visible, input:focus-visible { outline: 3px solid var(--ql-accent, #2a3b8f); outline-offset: 2px; }
36
+ .row { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: .6rem 0; }
37
+ .code { font-family: ui-monospace, monospace; font-size: .85rem; word-break: break-all; }
38
+ @media (prefers-color-scheme: dark) {
39
+ dialog { background: var(--ql-bg, #1c1b19); color: var(--ql-fg, #f3f1ea); }
40
+ button { border-color: var(--ql-accent, #a9b6ff); }
41
+ button.primary { background: var(--ql-accent, #a9b6ff); color: var(--ql-accent-fg, #10142e); }
42
+ label.guardian input, button:focus-visible { accent-color: var(--ql-accent, #a9b6ff); outline-color: var(--ql-accent, #a9b6ff); }
43
+ }
44
+ h2:focus { outline: none; }
45
+ `;
46
+
47
+ const esc = (s) => String(s).replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
48
+
49
+ const Base = globalThis.HTMLElement || class {};
50
+
51
+ export class QuranLabConsent extends Base {
52
+ /**
53
+ * Show the sheet and resolve with the learner's answer (true = share). Dismissing (Escape) is "Not now".
54
+ * @param {{ lang?: string, variant?: 'first'|'reask'|'version', guardian?: boolean, parent?: Element }} o
55
+ * @returns {Promise<{ yes: boolean, guardianConfirmed: boolean }>}
56
+ */
57
+ static ask(o = {}) {
58
+ const el = document.createElement('quranlab-consent');
59
+ (o.parent || document.body).appendChild(el);
60
+ return el.show(o).finally(() => el.remove());
61
+ }
62
+
63
+ show({ lang = (globalThis.navigator && navigator.language) || 'en', variant = 'first', guardian = false } = {}) {
64
+ const c = copyFor(lang);
65
+ const t = c.t;
66
+ const root = this.shadowRoot || this.attachShadow({ mode: 'open' });
67
+ const lines = (guardian ? ['sheet.what', 'sheet.who', 'sheet.control'] : ['sheet.mistakes', 'sheet.what', 'sheet.who', 'sheet.control'])
68
+ .map((k) => `<li>${esc(t(k))}</li>`).join('');
69
+ const hadith = t('sheet.hadith') ? `<p class="muted">${esc(t('sheet.hadith'))}<br><small>${esc(t('sheet.hadith_ref'))}</small></p>` : '';
70
+ root.innerHTML = `<style>${STYLE}</style>
71
+ <dialog part="sheet" lang="${esc(c.lang)}" dir="${esc(c.dir)}" aria-labelledby="t" aria-describedby="b">
72
+ <h2 id="t" part="title" tabindex="-1">${esc(t(guardian ? 'sheet.guardian.title' : 'sheet.title'))}</h2>
73
+ <div id="b" part="body">
74
+ ${guardian ? `<p part="guardian">${esc(t('sheet.guardian.body'))}</p>` : ''}
75
+ ${variant === 'reask' ? `<p>${esc(t('reask'))}</p>` : ''}
76
+ ${guardian ? '' : `<p>${esc(t('sheet.body'))}</p>`}
77
+ ${hadith}
78
+ <ul>${lines}</ul>
79
+ </div>
80
+ ${guardian ? `<label class="guardian"><input type="checkbox" id="g"> <span>${esc(t('sheet.guardian.confirm'))}</span></label>` : ''}
81
+ <div class="buttons">
82
+ <button class="primary" part="primary" id="yes" ${guardian ? 'disabled' : ''}>${esc(t(guardian ? 'sheet.guardian.share' : 'sheet.share'))}</button>
83
+ <button class="secondary" part="secondary" id="no">${esc(t('sheet.not_now'))}</button>
84
+ </div>
85
+ </dialog>`;
86
+ const d = root.querySelector('dialog');
87
+ const g = root.querySelector('#g');
88
+ const yes = root.querySelector('#yes');
89
+ if (g) g.addEventListener('change', () => { yes.disabled = !g.checked; });
90
+ return new Promise((resolve) => {
91
+ let answered = false;
92
+ const done = (v) => { if (answered) return; answered = true; try { d.close(); } catch (_) { /* closed */ } resolve({ yes: v, guardianConfirmed: !!(g && g.checked) }); };
93
+ yes.addEventListener('click', () => done(true));
94
+ root.querySelector('#no').addEventListener('click', () => done(false));
95
+ d.addEventListener('close', () => done(false));
96
+ d.addEventListener('cancel', () => done(false));
97
+ if (typeof d.showModal === 'function') d.showModal(); else d.setAttribute('open', '');
98
+ // focus the title, not a button: neither choice is pre-selected
99
+ root.querySelector('#t').focus();
100
+ });
101
+ }
102
+
103
+ /** The one-time thank-you, as a small dismissable note. */
104
+ static thank({ lang, parent, ms = 6000 } = {}) {
105
+ const c = copyFor(lang || (globalThis.navigator && navigator.language) || 'en');
106
+ const el = document.createElement('div');
107
+ el.setAttribute('role', 'status');
108
+ el.lang = c.lang; el.dir = c.dir;
109
+ el.textContent = c.t('thanks');
110
+ el.style.cssText = 'position:fixed;inset-inline:1rem;inset-block-end:1rem;max-inline-size:32rem;margin-inline:auto;padding:1rem 1.25rem;border-radius:14px;background:var(--ql-accent,#2a3b8f);color:var(--ql-accent-fg,#fff);font:500 1rem/1.5 var(--ql-font,system-ui,sans-serif);z-index:2147483647;box-shadow:0 10px 30px rgb(0 0 0 / .25)';
111
+ (parent || document.body).appendChild(el);
112
+ setTimeout(() => el.remove(), ms);
113
+ return el;
114
+ }
115
+ }
116
+
117
+ /**
118
+ * <quranlab-sharing-settings>: the switch, "What is shared", "Delete what I shared" and the
119
+ * contribution code. Set `.controller` to the sharing controller (web/consent/index.js).
120
+ */
121
+ export class QuranLabSharingSettings extends Base {
122
+ set controller(c) { this._c = c; this.render(); }
123
+ get controller() { return this._c; }
124
+ connectedCallback() { this.render(); }
125
+ render() {
126
+ if (!this._c) return;
127
+ const c = copyFor(this.getAttribute('lang') || this._c.lang);
128
+ const t = c.t;
129
+ const root = this.shadowRoot || this.attachShadow({ mode: 'open' });
130
+ const on = this._c.state.sharing;
131
+ const code = this._c.recoveryCode();
132
+ root.innerHTML = `<style>${STYLE} :host{display:block} section{font:400 1rem/1.5 var(--ql-font,system-ui,sans-serif);color:inherit}</style>
133
+ <section lang="${esc(c.lang)}" dir="${esc(c.dir)}">
134
+ <div class="row"><label for="s">${esc(t('settings.toggle'))}</label>
135
+ <input type="checkbox" role="switch" id="s" ${on ? 'checked' : ''} aria-checked="${on}"></div>
136
+ ${this._c.state.guardianVariant ? `<label class="guardian"><input type="checkbox" id="g"> <span>${esc(t('sheet.guardian.confirm'))}</span></label>` : ''}
137
+ <details><summary>${esc(t('settings.what'))}</summary><p>${esc(t('what.detail'))}</p></details>
138
+ ${code ? `<details id="rc"><summary>Recovery code</summary><p class="code" id="code"></p></details>` : ''}
139
+ <div class="row"><button class="secondary" id="del">${esc(t('settings.delete'))}</button></div>
140
+ <p class="muted" id="msg" role="status"></p>
141
+ </section>`;
142
+ const rc = root.querySelector('#rc');
143
+ // the code is put in the page only when the learner opens it, never in the initial markup
144
+ if (rc) rc.addEventListener('toggle', () => { root.querySelector('#code').textContent = rc.open ? this._c.recoveryCode() : ''; });
145
+ const msg = root.querySelector('#msg');
146
+ root.querySelector('#s').addEventListener('change', (e) => {
147
+ const g = root.querySelector('#g');
148
+ try { this._c.setSharing(e.target.checked, { guardianConfirmed: !!(g && g.checked) }); } catch (_) { e.target.checked = false; }
149
+ this.render();
150
+ });
151
+ root.querySelector('#del').addEventListener('click', async () => {
152
+ if (!globalThis.confirm || globalThis.confirm(t('delete.confirm'))) {
153
+ const ok = await this._c.deleteShared().then(() => true, () => false);
154
+ msg.textContent = ok ? t('delete.done') : t('delete.confirm');
155
+ if (ok) this.render();
156
+ }
157
+ });
158
+ }
159
+ }
160
+
161
+ if (globalThis.customElements && !globalThis.customElements.get('quranlab-consent')) {
162
+ globalThis.customElements.define('quranlab-consent', QuranLabConsent);
163
+ globalThis.customElements.define('quranlab-sharing-settings', QuranLabSharingSettings);
164
+ }