From 1795a858401196c0bb65532b904ca07df7543630 Mon Sep 17 00:00:00 2001 From: Riyanshi Gupta Date: Thu, 2 Jul 2026 02:27:19 +0530 Subject: [PATCH] feat: add drag-and-drop screenshot URL scanner with OCR --- README.md | 1 + index.html | 19 +++++++++- script.js | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++- style.css | 20 ++++++++++ 4 files changed, 144 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 59df00f..d02ccff 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ https://cybershield-url.netlify.app * ⚡ **Fast and responsive UI** * 🌐 **Uses Google Safe Browsing API** * ✅ **Simple and user-friendly interface** +* 📸 **Screenshot URL Scanner** --- diff --git a/index.html b/index.html index 47a8041..be3924d 100644 --- a/index.html +++ b/index.html @@ -59,6 +59,23 @@

Is This URL
Safe to Visit?

Scan URL +
+
+ + + + + + +
+ Drag & drop a screenshot or click to upload + We'll extract the URL and scan it automatically +
+ +
+
+
Try: @@ -159,7 +176,7 @@

Resources

- + \ No newline at end of file diff --git a/script.js b/script.js index 22e1e07..a5c9e02 100644 --- a/script.js +++ b/script.js @@ -231,4 +231,108 @@ async function checkSecurity() { document.getElementById('urlInput').addEventListener('keydown', e => { if (e.key === 'Enter') checkSecurity(); -}); \ No newline at end of file +}); +// SCREENSHOT UPLOAD — OCR URL EXTRACTION + +const dropZone = document.getElementById('dropZone'); +const fileInput = document.getElementById('fileInput'); +const previewImg = document.getElementById('previewImg'); +const ocrStatus = document.getElementById('ocrStatus'); + +const URL_REGEX = /\b((?:https?:\/\/)?(?:www\.)?[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+(?:\/[^\s"'<>]*)?)\b/gi; + +function extractUrlsFromText(text) { + const matches = text.match(URL_REGEX) || []; + return [...new Set(matches.map(m => m.trim()))]; +} + +function setOcrStatus(msg, isError = false) { + ocrStatus.textContent = msg; + ocrStatus.style.color = isError ? '#ff5c7a' : '#94a3b8'; +} + +dropZone.addEventListener('click', () => fileInput.click()); + +dropZone.addEventListener('keydown', e => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + fileInput.click(); + } +}); + +['dragenter', 'dragover'].forEach(evt => + dropZone.addEventListener(evt, e => { + e.preventDefault(); + dropZone.classList.add('dragover'); + }) +); + +['dragleave', 'drop'].forEach(evt => + dropZone.addEventListener(evt, e => { + e.preventDefault(); + dropZone.classList.remove('dragover'); + }) +); + +dropZone.addEventListener('drop', e => { + const file = e.dataTransfer.files[0]; + if (file) handleImageFile(file); +}); + +fileInput.addEventListener('change', e => { + const file = e.target.files[0]; + if (file) handleImageFile(file); +}); + +function handleImageFile(file) { + if (!file.type.startsWith('image/')) { + setOcrStatus('Please upload an image file (PNG, JPG, etc.)', true); + return; + } + + const reader = new FileReader(); + reader.onload = e => { + previewImg.src = e.target.result; + previewImg.classList.remove('hidden'); + runOcr(file); + }; + reader.readAsDataURL(file); +} + +async function runOcr(file) { + if (typeof Tesseract === 'undefined') { + setOcrStatus('OCR engine failed to load. Check your connection.', true); + return; + } + + setOcrStatus('Reading text from screenshot…'); + + try { + const { data: { text } } = await Tesseract.recognize(file, 'eng', { + logger: m => { + if (m.status === 'recognizing text') { + setOcrStatus(`Reading screenshot… ${Math.round(m.progress * 100)}%`); + } + } + }); + + const urls = extractUrlsFromText(text); + + if (urls.length === 0) { + setOcrStatus('No URL found in that image. Try a clearer screenshot.', true); + return; + } + + const chosenUrl = urls[0]; + document.getElementById('urlInput').value = chosenUrl; + setOcrStatus( + urls.length > 1 + ? `Found ${urls.length} URLs — scanning the first: ${chosenUrl}` + : `Found URL: ${chosenUrl} — scanning…` + ); + + checkSecurity(); + } catch (err) { + setOcrStatus('Could not read text from that image. Try a clearer screenshot.', true); + } +} \ No newline at end of file diff --git a/style.css b/style.css index bf982b2..a65f476 100644 --- a/style.css +++ b/style.css @@ -1073,3 +1073,23 @@ input[type="text"]:focus { @media (max-width: 380px) { .team-grid { grid-template-columns: repeat(2, 1fr); } } +.upload-row { margin-top: 16px; } +.drop-zone { + border: 2px dashed rgba(0,255,180,0.35); + border-radius: 14px; + padding: 28px 20px; + text-align: center; + cursor: pointer; + transition: border-color .2s, background .2s; +} +.drop-zone:hover, +.drop-zone.dragover { + border-color: #00ffb4; + background: rgba(0,255,180,0.06); +} +.drop-zone .upload-icon { width: 28px; height: 28px; color: #00ffb4; margin-bottom: 8px; } +.drop-zone-text { color: #cbd5e1; font-size: 14px; } +.drop-zone-sub { display: block; font-size: 12px; color: #64748b; margin-top: 4px; } +.preview-img { max-width: 100%; max-height: 160px; border-radius: 10px; margin-top: 12px; display: block; } +.hidden { display: none; } +.ocr-status { text-align: center; font-size: 13px; color: #94a3b8; margin-top: 8px; min-height: 18px; } \ No newline at end of file