Download index.html from Ghada-99-Ragab/Islamic-Content-Verifier: direct link, hf CLI and curl.
- Browser
- Download file 17.8 kB
-
https://huggingface.co/spaces/Ghada-99-Ragab/Islamic-Content-Verifier/resolve/main/index.html
- Command line
-
hf download hf://spaces/Ghada-99-Ragab/Islamic-Content-Verifier/index.html
-
curl -L -o index.html https://huggingface.co/spaces/Ghada-99-Ragab/Islamic-Content-Verifier/resolve/main/index.html
17.8 kB
| <html lang="ar" dir="rtl"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <title>Islamic Content Verifier</title> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Cairo:wght@400;600;700&family=Amiri:wght@400;700&display=swap'); | |
| .gradio-container { | |
| --body-background-fill:#0a1128; --block-background-fill:#101a38; --block-border-color:#1e2d57; | |
| --input-background-fill:#0d1631; --body-text-color:#e8eefc; --block-label-text-color:#9fb3da; | |
| --button-primary-background-fill:#14b8a6; --button-primary-background-fill-hover:#22d3ee; --button-primary-text-color:#04202a; | |
| --button-secondary-background-fill:#16244a; --button-secondary-text-color:#e8eefc; --button-secondary-border-color:#2a3d73; | |
| background:#0a1128 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='80' height='80' viewBox='0 0 80 80'%3E%3Cg fill='none' stroke='%2322d3ee' stroke-opacity='0.07' stroke-width='1'%3E%3Crect x='20' y='20' width='40' height='40'/%3E%3Crect x='20' y='20' width='40' height='40' transform='rotate(45 40 40)'/%3E%3C/g%3E%3C/svg%3E"); font-family:'Cairo','Segoe UI',Tahoma,sans-serif; max-width:1080px ; color:#e8eefc; | |
| } | |
| .hero { text-align:center; padding:34px 12px 18px; direction:rtl; } | |
| .hero .eyebrow { color:#22d3ee; letter-spacing:.14em; font-size:.8rem; text-transform:uppercase; } | |
| .hero h1 { font-size:2.3rem; margin:.3rem 0 .1rem; color:#fff; } | |
| .hero h2 { font-size:1.05rem; font-weight:400; color:#9fb3da; margin:0 0 .9rem; direction:ltr; } | |
| .hero p { max-width:760px; margin:.3rem auto; color:#c7d4f0; line-height:1.9; } | |
| .hero .en { direction:ltr; color:#8ea3cf; font-size:.92rem; } | |
| .input-area textarea { direction:rtl; text-align:right; font-family:'Amiri','Cairo',serif ; font-size:1.2rem ; line-height:2 ; } | |
| .btn-row button { font-weight:700 ; border-radius:12px ; } | |
| .disclaimer { font-size:.82rem; color:#8ea3cf; text-align:center; padding:12px 8px; direction:rtl; line-height:1.8; } | |
| .disclaimer .en { display:block; direction:ltr; } | |
| .results { direction:rtl; text-align:right; } | |
| .results .en, .notice .en { display:block; direction:ltr; text-align:left; color:#8ea3cf; font-size:.88rem; font-weight:400; } | |
| .summary { display:grid; grid-template-columns:repeat(auto-fit,minmax(130px,1fr)); gap:10px; margin:6px 0 14px; } | |
| .tile { background:#101a38; border:1px solid #1e2d57; border-radius:14px; padding:12px; text-align:center; } | |
| .tile b { display:block; font-size:1.8rem; color:#fff; } .tile span { display:block; font-size:.9rem; } .tile small { color:#8ea3cf; } | |
| .tile.verified { border-color:#14b8a6; } .tile.verified b { color:#2dd4bf; } | |
| .tile.mismatch { border-color:#f43f5e; } .tile.mismatch b { color:#fb7185; } | |
| .tile.review { border-color:#f59e0b; } .tile.review b { color:#fbbf24; } | |
| .highlight { background:#0d1631; border:1px solid #1e2d57; border-radius:14px; padding:14px 16px; margin-bottom:14px; } | |
| .highlight label, .field label { display:block; color:#7e93c0; font-size:.78rem; margin-bottom:4px; } | |
| .highlight p { font-family:'Amiri','Cairo',serif; font-size:1.15rem; line-height:2.1; margin:0; white-space:pre-wrap; } | |
| mark { color:#fff; border-radius:6px; padding:1px 4px; } | |
| mark.verified { background:rgba(20,184,166,.35); } mark.mismatch { background:rgba(244,63,94,.35); } mark.review { background:rgba(245,158,11,.35); } | |
| .qcard { background:#101a38; border:1px solid #1e2d57; border-inline-start:5px solid #3b4b7a; border-radius:16px; padding:16px 18px; margin:12px 0; } | |
| .qcard.verified { border-inline-start-color:#14b8a6; } .qcard.mismatch { border-inline-start-color:#f43f5e; } .qcard.review { border-inline-start-color:#f59e0b; } | |
| .qhead { display:flex; flex-wrap:wrap; gap:8px; align-items:center; margin-bottom:10px; } | |
| .idx { background:#1b2a55; border-radius:50%; width:28px; height:28px; display:inline-flex; align-items:center; justify-content:center; font-weight:700; } | |
| .badge { padding:3px 12px; border-radius:999px; font-size:.82rem; background:#16244a; border:1px solid #2a3d73; } | |
| .badge.st.verified { background:rgba(20,184,166,.18); border-color:#14b8a6; color:#5eead4; } | |
| .badge.st.mismatch { background:rgba(244,63,94,.16); border-color:#f43f5e; color:#fda4af; } | |
| .badge.st.review { background:rgba(245,158,11,.16); border-color:#f59e0b; color:#fcd34d; } | |
| .conf { margin-inline-start:auto; color:#9fb3da; font-size:.88rem; } .conf b { color:#22d3ee; } | |
| .field { margin:8px 0; } .field p { margin:0; line-height:1.8; } | |
| .quote { font-family:'Amiri','Cairo',serif; font-size:1.2rem; line-height:2.1; color:#fff; } .quote.small { font-size:1.05rem; color:#d6e0f7; } | |
| .diff { font-family:'Amiri','Cairo',serif; font-size:1.1rem; line-height:2.1; } | |
| .w-extra { background:rgba(244,63,94,.25); border-radius:4px; padding:0 3px; text-decoration:line-through; } | |
| .w-missing { background:rgba(20,184,166,.28); border-radius:4px; padding:0 3px; } | |
| .legend { color:#8ea3cf; font-size:.82rem; margin:4px 0 0; } .legend .w-extra { text-decoration:none; } | |
| .tile.zero { opacity:.5; } | |
| .det-chip { text-align:center; color:#8ea3cf; font-size:.82rem; margin:2px 0 8px; } .det-chip b { color:#22d3ee; font-weight:600; } | |
| .hl-legend { display:flex; flex-wrap:wrap; gap:8px; margin-top:10px; font-size:.78rem; } .hl-legend mark { padding:2px 10px; } | |
| .flow { display:flex; flex-wrap:wrap; justify-content:center; align-items:center; gap:8px; margin:16px 0 4px; direction:rtl; } | |
| .flow span { background:#101a38; border:1px solid #1e2d57; border-radius:999px; padding:5px 14px; font-size:.85rem; color:#cfe0ff; } | |
| .flow span small { color:#7e93c0; margin-inline-start:6px; direction:ltr; display:inline-block; } | |
| .flow i { color:#22d3ee; font-style:normal; } | |
| .hero .mark { width:52px; height:52px; margin:0 auto 6px; display:block; } | |
| .hero h1 { background:linear-gradient(90deg,#fff,#a5f3fc); -webkit-background-clip:text; background-clip:text; color:transparent; } | |
| .action { border-radius:12px; padding:10px 14px; margin-top:10px; line-height:1.8; } | |
| .action.ok { background:rgba(20,184,166,.12); border:1px solid #14b8a6; } | |
| .action.warn { background:rgba(245,158,11,.12); border:1px solid #f59e0b; } | |
| .action.bad { background:rgba(244,63,94,.12); border:1px solid #f43f5e; } | |
| .evidence { margin-top:12px; border-top:1px dashed #2a3d73; padding-top:8px; } | |
| .evidence summary { cursor:pointer; color:#22d3ee; font-weight:700; } | |
| .ev-row { margin:10px 0; } .ev-row b { color:#9fb3da; font-size:.85rem; } .ev-row p { margin:2px 0; } | |
| .indicators { display:grid; gap:6px; margin-top:6px; } | |
| .ind { display:grid; grid-template-columns:150px 1fr 48px; gap:10px; align-items:center; font-size:.85rem; } | |
| .ind-name small { display:block; color:#7e93c0; font-size:.72rem; direction:ltr; text-align:right; } | |
| .bar { background:#0d1631; border-radius:999px; height:8px; overflow:hidden; } .bar span { display:block; height:100%; background:linear-gradient(90deg,#14b8a6,#22d3ee); } | |
| .ind-val { text-align:left; direction:ltr; color:#cfe0ff; } .ind-val.wide { grid-column:2 / span 2; text-align:right; } | |
| .notice { background:#101a38; border:1px solid #1e2d57; border-radius:14px; padding:16px; direction:rtl; line-height:1.9; } | |
| .notice.warn { border-color:#f59e0b; } .notice.bad { border-color:#f43f5e; } | |
| @media (max-width:640px){ .hero h1{font-size:1.7rem;} .ind{grid-template-columns:110px 1fr 40px;} } | |
| html,body{margin:0;background:#0a1128;} | |
| .gradio-container{min-height:100vh;box-sizing:border-box;padding:8px 16px 24px;margin:0 auto;} | |
| .input-area textarea{width:100%;box-sizing:border-box;min-height:220px;background:#0d1631;color:#e8eefc;border:1px solid #1e2d57;border-radius:14px;padding:14px;resize:vertical;outline:none;} | |
| .input-area textarea:focus{border-color:#22d3ee;} | |
| .input-area label{display:block;color:#9fb3da;font-size:.85rem;margin:0 0 6px;direction:rtl;text-align:right;} | |
| .btn-row{display:flex;gap:12px;margin:12px 0 18px;flex-wrap:wrap;} | |
| .btn-row button{flex:1 1 220px;border:0;border-radius:12px;padding:13px 18px;font-size:1rem;font-weight:700;cursor:pointer;font-family:inherit;} | |
| #verify{background:#14b8a6;color:#04202a;} #verify:hover{background:#22d3ee;} | |
| #example{background:#16244a;color:#e8eefc;border:1px solid #2a3d73;} #example:hover{border-color:#22d3ee;} | |
| .btn-row button:disabled{opacity:.45;cursor:not-allowed;} | |
| #status{direction:rtl;text-align:center;color:#9fb3da;font-size:.92rem;margin:0 0 10px;min-height:1.4em;} | |
| #status .en{display:block;direction:ltr;color:#7e93c0;font-size:.82rem;} | |
| .spinner{display:inline-block;width:14px;height:14px;border:2px solid #2a3d73;border-top-color:#22d3ee;border-radius:50%;animation:spin 1s linear infinite;vertical-align:middle;margin-inline-end:8px;} | |
| @keyframes spin{to{transform:rotate(360deg)}} | |
| </style> | |
| </head> | |
| <body> | |
| <div class="gradio-container"> | |
| <div class="hero"> | |
| <svg class="mark" viewBox="0 0 52 52" fill="none" stroke="#22d3ee" stroke-width="1.6" aria-hidden="true"> | |
| <rect x="11" y="11" width="30" height="30"/><rect x="11" y="11" width="30" height="30" transform="rotate(45 26 26)"/> | |
| <circle cx="26" cy="26" r="6" fill="#14b8a6" stroke="none"/></svg> | |
| <div class="eyebrow">Islamic Content Verifier</div> | |
| <h1>مُدقِّق المحتوى الإسلامي</h1> | |
| <h2>Verify Quranic and Hadith quotations with evidence.</h2> | |
| <p>الصق نصًا ولّده نموذج لغوي، وسيكتشف النظام الآيات والأحاديث الواردة فيه، ويسترجع نصوصها من المصادر، ويقارنها كلمةً بكلمة، | |
| ثم يعرض الدليل. وإذا لم تكفِ الأدلة فلن يختلق تصحيحًا، بل يحيل الحالة إلى المراجعة البشرية.</p> | |
| <p class="en">Paste text generated by a language model. The system detects Quran and Hadith quotations, retrieves the source texts, | |
| compares them word by word and shows the evidence. When evidence is insufficient it never invents a correction: it recommends human review.</p> | |
| <div class="flow"><span>كشف<small>Detect</small></span><i>←</i><span>استرجاع<small>Retrieve</small></span><i>←</i> | |
| <span>تحقق<small>Verify</small></span><i>←</i><span>دليل<small>Evidence</small></span><i>←</i><span>قرار<small>Decide</small></span></div> | |
| </div> | |
| <div class="input-area"> | |
| <label for="text">النص المراد التحقق منه · Text to verify</label> | |
| <textarea id="text" dir="rtl" placeholder="الصق هنا الرد الذي ولّده النموذج اللغوي، ويفضَّل أن تكون الاقتباسات بين علامات تنصيص بعد عبارة مثل «قال الله تعالى» أو «قال رسول الله ﷺ»…"></textarea> | |
| </div> | |
| <div class="btn-row"> | |
| <button id="verify" disabled>تحقق من النص · Verify Text</button> | |
| <button id="example" disabled>جرّب مثالًا · Try an Example</button> | |
| </div> | |
| <div id="status"></div> | |
| <div id="results" class="results"></div> | |
| <div class="disclaimer">أداة مساعدة للتدقيق النصي وليست فتوى ولا بديلًا عن المراجعة المتخصصة. النتائج مبنية على مراجع القرآن والكتب الستة المضمّنة فقط. | |
| <span class="en">A text-verification aid, not a religious ruling and not a substitute for expert review. Results rely only on the bundled Quran and Six Books corpora.</span></div> | |
| </div> | |
| <script type="text/plain" id="worker-src"> | |
| // Runs Pyodide and the verification code off the main thread so the page stays responsive while the index builds. | |
| let verify = null; | |
| const post = (message) => self.postMessage(message); | |
| const status = (ar, en) => post({ type: "status", ar, en }); | |
| async function fetchBytes(url, onProgress) { | |
| const response = await fetch(url); | |
| if (!response.ok) throw new Error("Could not download " + url + " (" + response.status + ")"); | |
| const total = Number(response.headers.get("Content-Length")) || 0; | |
| if (!response.body || !onProgress) return new Uint8Array(await response.arrayBuffer()); | |
| const reader = response.body.getReader(), chunks = []; let received = 0; | |
| for (;;) { | |
| const { done, value } = await reader.read(); | |
| if (done) break; | |
| chunks.push(value); received += value.length; onProgress(received, total); | |
| } | |
| const bytes = new Uint8Array(received); let offset = 0; | |
| for (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.length; } | |
| return bytes; | |
| } | |
| // Accept both layouts: files inside data/ and demo/ folders, or uploaded flat next to index.html. | |
| async function fetchAny(names, base, onProgress) { | |
| let lastError = null; | |
| for (const name of names) { | |
| for (const path of [name, name.split("/").pop()]) { | |
| try { return await fetchBytes(new URL(path, base).href, onProgress); } catch (error) { lastError = error; } | |
| } | |
| } | |
| throw lastError; | |
| } | |
| async function boot(message) { | |
| const url = (file) => new URL(file, message.base).href; | |
| status("جارٍ تحميل بيئة بايثون…", "Loading the Python runtime…"); | |
| importScripts("https://cdn.jsdelivr.net/pyodide/v" + message.pyodideVersion + "/full/pyodide.js"); | |
| const pyodide = await loadPyodide(); | |
| status("جارٍ تحميل الكود والمراجع…", "Downloading code and corpora…"); | |
| for (const dir of ["/app", "/app/data", "/app/demo"]) pyodide.FS.mkdir(dir); | |
| for (const file of message.files) pyodide.FS.writeFile("/app/" + file, await fetchAny([file], message.base)); | |
| const hadith = await fetchAny(["data/hadith.json.gz", "data/hadith.json"], message.base, (got, total) => { | |
| const mb = (got / 1048576).toFixed(1), of = total ? " / " + (total / 1048576).toFixed(1) : ""; | |
| status("جارٍ تحميل كتب الحديث… " + mb + of + " MB", "Downloading the Hadith corpus… " + mb + of + " MB"); | |
| }); | |
| const isGzip = hadith[0] === 0x1f && hadith[1] === 0x8b; // the file may be the compressed or the plain JSON copy | |
| pyodide.FS.writeFile(isGzip ? "/app/data/hadith.json.gz" : "/app/data/hadith.json", hadith); | |
| const examples = JSON.parse(new TextDecoder().decode(pyodide.FS.readFile("/app/demo/examples.json"))); | |
| status("جارٍ بناء فهرس المصادر (مرة واحدة، قد يستغرق دقيقة)…", "Building the source index (once, may take up to a minute)…"); | |
| pyodide.runPython("import sys; sys.path.insert(0, '/app')"); | |
| pyodide.runPython("from app import get_pipeline, verify_text; get_pipeline()"); | |
| verify = pyodide.globals.get("verify_text"); | |
| post({ type: "ready", examples }); | |
| } | |
| self.onmessage = async (event) => { | |
| const message = event.data; | |
| if (message.type === "boot") { | |
| try { await boot(message); } | |
| catch (error) { post({ type: "error", message: String((error && error.message) || error) }); } | |
| } else if (message.type === "verify") { | |
| try { post({ type: "result", id: message.id, html: verify(message.text) }); } | |
| catch (error) { post({ type: "result", id: message.id, html: null }); } | |
| } | |
| }; | |
| </script> | |
| <script> | |
| const PYODIDE_VERSION = "0.26.4"; | |
| const PYTHON_FILES = ["app.py", "verifier.py", "retrieval.py"]; | |
| const $ = (id) => document.getElementById(id); | |
| let examples = [], exampleIndex = 0, nextId = 0, worker = null; | |
| const pending = new Map(); | |
| function setStatus(ar, en, busy = true) { | |
| $("status").innerHTML = (busy ? '<span class="spinner"></span>' : "") + ar + '<span class="en">' + en + "</span>"; | |
| } | |
| function fail(message) { | |
| console.error(message); | |
| setStatus("تعذّر تشغيل النظام. جرّب متصفح كمبيوتر حديثًا ثم أعد تحميل الصفحة.", | |
| "The verifier could not start (" + message + "). Try a modern desktop browser and reload the page.", false); | |
| } | |
| function ask(text) { | |
| return new Promise((resolve) => { | |
| const id = ++nextId; | |
| pending.set(id, resolve); | |
| worker.postMessage({ type: "verify", id, text }); | |
| }); | |
| } | |
| async function run() { | |
| if (!worker) return; | |
| $("verify").disabled = $("example").disabled = true; | |
| setStatus("جارٍ التحقق…", "Verifying…"); | |
| const html = await ask($("text").value); | |
| $("results").innerHTML = html !== null ? html | |
| : '<div class="notice bad">حدث خطأ غير متوقع.<span class="en">An unexpected error occurred.</span></div>'; | |
| setStatus("", "", false); | |
| $("verify").disabled = $("example").disabled = false; | |
| } | |
| function start() { | |
| try { | |
| const source = $("worker-src").textContent; | |
| worker = new Worker(URL.createObjectURL(new Blob([source], { type: "text/javascript" }))); | |
| } catch (error) { return fail(error.message); } | |
| worker.onerror = (event) => fail(event.message || "worker error"); | |
| worker.onmessage = (event) => { | |
| const message = event.data; | |
| if (message.type === "status") setStatus(message.ar, message.en); | |
| else if (message.type === "error") fail(message.message); | |
| else if (message.type === "result") { const resolve = pending.get(message.id); pending.delete(message.id); resolve(message.html); } | |
| else if (message.type === "ready") { | |
| examples = message.examples; | |
| $("verify").disabled = $("example").disabled = false; | |
| setStatus("جاهز. الكود يعمل داخل متصفحك ولا يُرسل النص إلى أي خادم.", | |
| "Ready. The code runs in your browser; your text is not sent to any server.", false); | |
| } | |
| }; | |
| worker.postMessage({ type: "boot", base: location.href, pyodideVersion: PYODIDE_VERSION, | |
| files: PYTHON_FILES.concat(["demo/examples.json", "data/quran.json"]) }); | |
| } | |
| $("verify").addEventListener("click", run); | |
| $("example").addEventListener("click", () => { | |
| if (!examples.length) return; | |
| $("text").value = examples[exampleIndex % examples.length].text; | |
| exampleIndex += 1; | |
| run(); | |
| }); | |
| start(); | |
| </script> | |
| </body> | |
| </html> | |