diff --git a/index.html b/index.html index c535abf..d1a3aeb 100644 --- a/index.html +++ b/index.html @@ -78,6 +78,38 @@

Is This URL
Safe to Visit?

malware test + +
+
+ or +
+
+ +
Drag & drop a screenshot here
+
or click to upload ยท JPG, PNG, WEBP
+ +
+
+
+
@@ -168,6 +200,7 @@

Resources

+ diff --git a/script.js b/script.js index 2c902f0..2ba0235 100644 --- a/script.js +++ b/script.js @@ -40,7 +40,126 @@ window.addEventListener('pageshow', (e) => { if (main) { main.classList.remove('hidden'); main.style.opacity = '1'; } } }); +// โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +// SCREENSHOT URL DETECTION +// โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +function handleDragOver(e) { + e.preventDefault(); + document.getElementById('screenshotDropzone').classList.add('dragover'); +} + +function handleDragLeave(e) { + document.getElementById('screenshotDropzone').classList.remove('dragover'); +} + +function handleDrop(e) { + e.preventDefault(); + document.getElementById('screenshotDropzone').classList.remove('dragover'); + const file = e.dataTransfer.files[0]; + if (file && file.type.startsWith('image/')) { + processScreenshot(file); + } +} + +function handleScreenshotUpload(e) { + const file = e.target.files[0]; + if (file) processScreenshot(file); +} + +function processScreenshot(file) { + const resultEl = document.getElementById('screenshotResult'); + resultEl.innerHTML = `
๐Ÿ” Extracting URLs from screenshot...
`; + + const reader = new FileReader(); + reader.onload = function(e) { + const img = new Image(); + img.onload = function() { + // Draw image to canvas for Tesseract + const canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + const ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0); + + // Use Tesseract.js to extract text + Tesseract.recognize(canvas, 'eng', { + logger: m => { + if (m.status === 'recognizing text') { + resultEl.innerHTML = `
๐Ÿ” Scanning image... ${Math.round(m.progress * 100)}%
`; + } + } + }).then(({ data: { text } }) => { + const urls = extractUrlsFromText(text); + showScreenshotUrls(urls, text); + }).catch(err => { + resultEl.innerHTML = `
โŒ OCR failed: ${err.message}
`; + }); + }; + img.src = e.target.result; + }; + reader.readAsDataURL(file); +} + +function extractUrlsFromText(text) { + // Match URLs including http, https, and bare domains + const urlRegex = /(?:https?:\/\/)?(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,6}\b(?:[-a-zA-Z0-9@:%_+.~#?&/=]*)/g; + const matches = text.match(urlRegex) || []; + + // Filter out noise โ€” must have a dot and reasonable length + return [...new Set( + matches + .filter(u => u.includes('.') && u.length > 4 && u.length < 200) + .filter(u => !/^\d+\.\d+$/.test(u)) // exclude version numbers like 1.0 + .map(u => u.trim().replace(/[.,;:'")\]>]+$/, '')) // strip trailing punctuation + )].slice(0, 10); // max 10 URLs +} + +function showScreenshotUrls(urls, rawText) { + const resultEl = document.getElementById('screenshotResult'); + + if (urls.length === 0) { + resultEl.innerHTML = ` +
+ + No URLs detected in the screenshot. Try a clearer image. +
+ `; + return; + } + + resultEl.innerHTML = ` +
+
+ + ${urls.length} URL${urls.length > 1 ? 's' : ''} found โ€” click to scan +
+
+ ${urls.map(url => ` +
+ ${url} + +
+ `).join('')} +
+
+ `; +} + +function scanExtractedUrl(url) { + // Fill the URL input and trigger scan + const input = document.getElementById('urlInput'); + input.value = url; + input.scrollIntoView({ behavior: 'smooth', block: 'center' }); + checkSecurity(); +} // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // THEME TOGGLE // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• diff --git a/style.css b/style.css index 49e1885..084e54f 100644 --- a/style.css +++ b/style.css @@ -1295,3 +1295,139 @@ html.light-mode .result-url { background: rgba(0,0,0,0.06); } @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } } +/* โ”€โ”€ Screenshot URL Detection โ”€โ”€ */ +.screenshot-divider { + display: flex; + align-items: center; + gap: 12px; + margin: 16px 0; + color: #475569; + font-size: 13px; +} + +.screenshot-divider::before, +.screenshot-divider::after { + content: ''; + flex: 1; + height: 1px; + background: rgba(255,255,255,0.07); +} + +.screenshot-dropzone { + border: 2px dashed rgba(0,255,180,0.25); + border-radius: 12px; + padding: 28px 20px; + text-align: center; + cursor: pointer; + transition: border-color 0.2s, background 0.2s; + background: rgba(0,255,180,0.02); +} + +.screenshot-dropzone:hover, +.screenshot-dropzone.dragover { + border-color: #00ffb4; + background: rgba(0,255,180,0.06); +} + +.screenshot-dropzone:focus-visible { + outline: 2px solid #00ffb4; + outline-offset: 3px; +} + +.dropzone-icon { + font-size: 28px; + margin-bottom: 8px; +} + +.dropzone-text { + font-size: 14px; + font-weight: 600; + color: var(--text-primary, #f1f5f9); + margin-bottom: 4px; +} + +.dropzone-sub { + font-size: 12px; + color: #64748b; +} + +.screenshot-result { + margin-top: 12px; +} + +.screenshot-processing { + font-size: 13px; + color: #94a3b8; + padding: 10px; + text-align: center; +} + +.screenshot-no-urls { + font-size: 13px; + color: #64748b; + padding: 10px; + text-align: center; +} + +.screenshot-error { + font-size: 13px; + color: #f87171; + padding: 10px; + text-align: center; +} + +.screenshot-urls-found { + background: var(--card-bg, #1e293b); + border: 1px solid rgba(255,255,255,0.07); + border-radius: 12px; + padding: 16px; +} + +.screenshot-urls-header { + font-size: 13px; + font-weight: 600; + color: #00ffb4; + margin-bottom: 12px; + display: flex; + align-items: center; + gap: 8px; +} + +.screenshot-urls-list { + display: flex; + flex-direction: column; + gap: 8px; +} + +.screenshot-url-item { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + background: rgba(255,255,255,0.04); + border-radius: 8px; + padding: 10px 12px; +} + +.screenshot-url-text { + font-size: 12px; + color: #94a3b8; + word-break: break-all; + flex: 1; +} + +.screenshot-scan-btn { + background: rgba(0,255,180,0.1); + border: 1px solid rgba(0,255,180,0.3); + color: #00ffb4; + padding: 5px 14px; + border-radius: 6px; + font-size: 12px; + cursor: pointer; + white-space: nowrap; + transition: background 0.2s; +} + +.screenshot-scan-btn:hover { + background: rgba(0,255,180,0.2); +} \ No newline at end of file