| | const dropZone = document.getElementById('dropZone');
|
| | const fileInput = document.getElementById('fileInput');
|
| | const preview = document.getElementById('preview');
|
| | const solveBtn = document.getElementById('solveBtn');
|
| | const cluesInput = document.getElementById('cluesInput');
|
| | const resultsArea = document.getElementById('resultsArea');
|
| | const loading = document.getElementById('loading');
|
| | const statusIndicator = document.getElementById('statusIndicator');
|
| |
|
| | let selectedFile = null;
|
| |
|
| |
|
| | dropZone.addEventListener('dragover', (e) => {
|
| | e.preventDefault();
|
| | dropZone.classList.add('dragover');
|
| | });
|
| |
|
| | dropZone.addEventListener('dragleave', () => {
|
| | dropZone.classList.remove('dragover');
|
| | });
|
| |
|
| | dropZone.addEventListener('drop', (e) => {
|
| | e.preventDefault();
|
| | dropZone.classList.remove('dragover');
|
| | if (e.dataTransfer.files.length) {
|
| | handleFile(e.dataTransfer.files[0]);
|
| | }
|
| | });
|
| |
|
| | dropZone.addEventListener('click', () => fileInput.click());
|
| |
|
| | fileInput.addEventListener('change', (e) => {
|
| | if (e.target.files.length) {
|
| | handleFile(e.target.files[0]);
|
| | }
|
| | });
|
| |
|
| | function handleFile(file) {
|
| | if (!file.type.startsWith('image/')) {
|
| | alert('Please upload an image file.');
|
| | return;
|
| | }
|
| | selectedFile = file;
|
| |
|
| | const reader = new FileReader();
|
| | reader.onload = (e) => {
|
| | preview.src = e.target.result;
|
| | preview.style.display = 'block';
|
| |
|
| |
|
| | const zoneContent = dropZone.querySelector('.zone-content');
|
| | if (zoneContent) zoneContent.style.display = 'none';
|
| | };
|
| | reader.readAsDataURL(file);
|
| | }
|
| |
|
| |
|
| | solveBtn.addEventListener('click', async () => {
|
| | if (!selectedFile) {
|
| | alert('Please upload an image first.');
|
| | return;
|
| | }
|
| |
|
| | const clues = cluesInput.value.trim();
|
| | if (!clues) {
|
| | alert('Please paste the challenge text / clues.');
|
| | return;
|
| | }
|
| |
|
| |
|
| | solveBtn.disabled = true;
|
| | solveBtn.innerHTML = '<span class="btn-icon">⏳</span> SCANNING...';
|
| |
|
| |
|
| | resultsArea.innerHTML = '';
|
| | loading.style.display = 'block';
|
| |
|
| |
|
| | if (statusIndicator) {
|
| | statusIndicator.innerHTML = '<span class="dot active"></span> ANALYZING';
|
| | }
|
| |
|
| | const formData = new FormData();
|
| | formData.append('image', selectedFile);
|
| | formData.append('clues', clues);
|
| |
|
| | try {
|
| | const response = await fetch('/api/solve', {
|
| | method: 'POST',
|
| | body: formData
|
| | });
|
| |
|
| | const data = await response.json();
|
| |
|
| | if (data.error) {
|
| | renderError(data.error);
|
| | if (statusIndicator) {
|
| | statusIndicator.innerHTML = '<span class="dot" style="background: #ef4444; box-shadow: none;"></span> ERROR';
|
| | }
|
| | } else {
|
| | renderResults(data);
|
| | if (statusIndicator) {
|
| | statusIndicator.innerHTML = '<span class="dot active" style="background: #4ade80; box-shadow: 0 0 10px #4ade80;"></span> COMPLETE';
|
| | }
|
| | }
|
| | } catch (err) {
|
| | renderError("Network or Server Error: " + err.message);
|
| | if (statusIndicator) {
|
| | statusIndicator.innerHTML = '<span class="dot" style="background: #ef4444; box-shadow: none;"></span> ERROR';
|
| | }
|
| | } finally {
|
| | solveBtn.disabled = false;
|
| | solveBtn.innerHTML = '<span class="btn-icon">⚡</span> INITIATE SCAN';
|
| | loading.style.display = 'none';
|
| |
|
| |
|
| | if (selectedFile) {
|
| | const zoneContent = dropZone.querySelector('.zone-content');
|
| | if (zoneContent) zoneContent.style.display = 'none';
|
| | }
|
| | }
|
| | });
|
| |
|
| | function renderResults(data) {
|
| | let html = '';
|
| |
|
| |
|
| | if (data.grid && data.grid.length > 0) {
|
| | html += '<div style="margin-bottom: 2rem; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 1rem;">';
|
| | html += '<div style="font-weight: bold; margin-bottom: 0.5rem; color: var(--text-secondary); letter-spacing: 0.1em; font-size: 0.8rem;">DETECTED MATRIX:</div>';
|
| | html += '<div style="letter-spacing: 0.5em; line-height: 1.5em; font-family: monospace; color: var(--text-primary);">';
|
| | data.grid.forEach(row => {
|
| | html += `<div>${row.join('')}</div>`;
|
| | });
|
| | html += '</div></div>';
|
| | }
|
| |
|
| |
|
| | html += '<div style="font-weight: bold; margin-bottom: 1rem; color: var(--text-secondary); letter-spacing: 0.1em; font-size: 0.8rem;">IDENTIFIED PATTERNS:</div>';
|
| |
|
| | if (data.solutions && data.solutions.length > 0) {
|
| | data.solutions.forEach(item => {
|
| | const hasMatch = item.found && item.found.length > 0;
|
| | const matchClass = hasMatch ? 'matches' : 'no-match';
|
| |
|
| | let matchHtml = '[Not Found]';
|
| | if (hasMatch) {
|
| | matchHtml = item.found.map(word =>
|
| | `<span class="found-word" onclick="copyToClipboard('${word}', this)" title="Click to copy">${word}</span>`
|
| | ).join(', ');
|
| | }
|
| |
|
| | html += `
|
| | <div class="result-item">
|
| | <span class="pattern">${item.pattern}</span>
|
| | <span style="color: #444;">➜</span>
|
| | <span class="${matchClass}">${matchHtml}</span>
|
| | </div>
|
| | `;
|
| | });
|
| | } else {
|
| | html += '<div style="color: var(--text-muted);">No patterns found in input text.</div>';
|
| | }
|
| |
|
| | resultsArea.innerHTML = html;
|
| | }
|
| |
|
| |
|
| | async function copyToClipboard(text, element) {
|
| | try {
|
| | await navigator.clipboard.writeText(text);
|
| |
|
| |
|
| | const originalText = element.innerText;
|
| | element.innerText = 'COPIED!';
|
| | element.classList.add('copied');
|
| |
|
| | setTimeout(() => {
|
| | element.innerText = originalText;
|
| | element.classList.remove('copied');
|
| | }, 800);
|
| |
|
| | } catch (err) {
|
| | console.error('Failed to copy:', err);
|
| | }
|
| | }
|
| |
|
| | function renderError(msg) {
|
| | resultsArea.innerHTML = `
|
| | <div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: #ef4444;">
|
| | <div style="font-size: 2rem; margin-bottom: 0.5rem;">⚠️</div>
|
| | <div style="font-weight: 700;">SYSTEM ERROR</div>
|
| | <div style="font-size: 0.9rem; margin-top: 0.2rem; opacity: 0.8;">${msg}</div>
|
| | </div>
|
| | `;
|
| | }
|
| |
|