Spaces:
Running
Running
| <html lang="es"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Node URL Interceptor Script</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet"> | |
| <link | |
| href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Inter:wght@300;400;600;800&display=swap" | |
| rel="stylesheet"> | |
| <script> | |
| tailwind.config = { | |
| theme: { | |
| extend: { | |
| fontFamily: { | |
| sans: ['Inter', 'sans-serif'], | |
| mono: ['JetBrains Mono', 'monospace'], | |
| }, | |
| colors: { | |
| brand: { | |
| dark: '#0f172a', | |
| card: '#1e293b', | |
| accent: '#38bdf8', | |
| success: '#4ade80', | |
| warning: '#facc15', | |
| danger: '#f87171' | |
| } | |
| }, | |
| animation: { | |
| 'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite', | |
| 'float': 'float 6s ease-in-out infinite', | |
| 'scan': 'scan 2s linear infinite', | |
| }, | |
| keyframes: { | |
| float: { | |
| '0%, 100%': { transform: 'translateY(0)' }, | |
| '50%': { transform: 'translateY(-10px)' }, | |
| }, | |
| scan: { | |
| '0%': { transform: 'translateY(-100%)' }, | |
| '100%': { transform: 'translateY(100%)' }, | |
| } | |
| } | |
| } | |
| } | |
| } | |
| </script> | |
| <style> | |
| body { | |
| background-color: #0f172a; | |
| background-image: | |
| radial-gradient(at 0% 0%, rgba(56, 189, 248, 0.1) 0px, transparent 50%), | |
| radial-gradient(at 100% 0%, rgba(168, 85, 247, 0.1) 0px, transparent 50%), | |
| radial-gradient(at 100% 100%, rgba(56, 189, 248, 0.1) 0px, transparent 50%), | |
| radial-gradient(at 0% 100%, rgba(168, 85, 247, 0.1) 0px, transparent 50%); | |
| background-attachment: fixed; | |
| color: #e2e8f0; | |
| } | |
| .glass-panel { | |
| background: rgba(30, 41, 59, 0.7); | |
| backdrop-filter: blur(12px); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37); | |
| } | |
| .code-block { | |
| background: #0d1117; | |
| border: 1px solid #30363d; | |
| border-radius: 0.5rem; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| /* Custom Scrollbar */ | |
| ::-webkit-scrollbar { | |
| width: 8px; | |
| height: 8px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: #0f172a; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: #334155; | |
| border-radius: 4px; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: #475569; | |
| } | |
| .glow-text { | |
| text-shadow: 0 0 10px rgba(56, 189, 248, 0.5); | |
| } | |
| .terminal-line { | |
| opacity: 0; | |
| animation: fadeIn 0.3s forwards; | |
| } | |
| @keyframes fadeIn { | |
| to { | |
| opacity: 1; | |
| } | |
| } | |
| .toggle-checkbox:checked { | |
| right: 0; | |
| border-color: #38bdf8; | |
| } | |
| .toggle-checkbox:checked + .toggle-label { | |
| background-color: #38bdf8; | |
| } | |
| </style> | |
| </head> | |
| <body class="min-h-screen flex flex-col"> | |
| <!-- Navbar --> | |
| <nav class="glass-panel border-b border-white/10 sticky top-0 z-50"> | |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
| <div class="flex items-center justify-between h-16"> | |
| <div class="flex items-center gap-3"> | |
| <div | |
| class="w-8 h-8 rounded bg-gradient-to-br from-cyan-500 to-blue-600 flex items-center justify-center shadow-lg shadow-cyan-500/20"> | |
| <i class="fa-solid fa-link text-white text-sm"></i> | |
| </div> | |
| <span class="font-bold text-xl tracking-tight text-white">Clip<span class="text-cyan-400">Snatch</span></span> | |
| </div> | |
| <div class="flex items-center gap-4"> | |
| <span class="text-xs font-mono text-slate-400 bg-slate-800 px-2 py-1 rounded border border-slate-700">v2.0.0</span> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" | |
| class="text-xs text-slate-400 hover:text-cyan-400 transition-colors">Built with anycoder</a> | |
| </div> | |
| </div> | |
| </div> | |
| </nav> | |
| <!-- Main Content --> | |
| <main class="flex-grow container mx-auto px-4 py-8 grid grid-cols-1 lg:grid-cols-12 gap-8"> | |
| <!-- Left Column: Configuration & Code --> | |
| <div class="lg:col-span-5 space-y-6"> | |
| <!-- Intro Card --> | |
| <div class="glass-panel rounded-2xl p-6 animate-float"> | |
| <h1 class="text-2xl font-bold text-white mb-2 glow-text">Interceptor de URLs</h1> | |
| <p class="text-slate-400 text-sm mb-4"> | |
| Genera un script de Node.js que monitorea el portapapeles en tiempo real. | |
| Detecta automáticamente URLs y ejecuta acciones personalizadas. | |
| Ahora compatible con <span class="text-cyan-400 font-semibold">ES Modules</span>. | |
| </p> | |
| <div class="flex gap-2"> | |
| <span class="px-2 py-1 rounded bg-blue-500/10 text-blue-400 text-xs border border-blue-500/20">Node.js</span> | |
| <span class="px-2 py-1 rounded bg-yellow-500/10 text-yellow-400 text-xs border border-yellow-500/20">ESM</span> | |
| <span class="px-2 py-1 rounded bg-purple-500/10 text-purple-400 text-xs border border-purple-500/20">Clipboard</span> | |
| <span class="px-2 py-1 rounded bg-green-500/10 text-green-400 text-xs border border-green-500/20">Automation</span> | |
| </div> | |
| </div> | |
| <!-- Configuration Form --> | |
| <div class="glass-panel rounded-2xl p-6"> | |
| <h2 class="text-lg font-semibold text-white mb-4 flex items-center gap-2"> | |
| <i class="fa-solid fa-sliders text-cyan-400"></i> Configuración | |
| </h2> | |
| <div class="space-y-4"> | |
| <!-- Module Type Toggle --> | |
| <div class="flex items-center justify-between p-3 bg-slate-900/50 rounded-lg border border-slate-700"> | |
| <div> | |
| <label class="block text-sm font-medium text-white">Tipo de Módulo</label> | |
| <p class="text-xs text-slate-400">ESM (.mjs) recomendado para proyectos modernos</p> | |
| </div> | |
| <div class="relative inline-block w-12 mr-2 align-middle select-none transition duration-200 ease-in"> | |
| <input type="checkbox" name="toggle" id="moduleToggle" class="toggle-checkbox absolute block w-6 h-6 rounded-full bg-white border-4 appearance-none cursor-pointer transition-all duration-300 ease-in-out right-6 checked:right-0 checked:border-cyan-500" checked onchange="toggleModuleType()"> | |
| <label for="moduleToggle" class="toggle-label block overflow-hidden h-6 rounded-full bg-slate-700 cursor-pointer transition-colors duration-300 ease-in-out"></label> | |
| </div> | |
| </div> | |
| <div id="moduleTypeDisplay" class="text-xs font-mono text-cyan-400 bg-cyan-500/10 px-2 py-1 rounded border border-cyan-500/20"> | |
| <i class="fa-solid fa-check-circle mr-1"></i> ES Modules (import/export) | |
| </div> | |
| <!-- Check Interval --> | |
| <div> | |
| <label class="block text-xs font-medium text-slate-400 mb-1">Intervalo de Escaneo (ms)</label> | |
| <div class="relative"> | |
| <input type="number" id="intervalInput" value="1000" min="500" step="100" | |
| class="w-full bg-slate-900/50 border border-slate-700 rounded-lg px-4 py-2 text-sm text-white focus:outline-none focus:border-cyan-500 focus:ring-1 focus:ring-cyan-500 transition-all font-mono"> | |
| <div class="absolute right-3 top-2 text-xs text-slate-500">ms</div> | |
| </div> | |
| </div> | |
| <!-- Regex Pattern --> | |
| <div> | |
| <label class="block text-xs font-medium text-slate-400 mb-1">Patrón Regex (URL)</label> | |
| <input type="text" id="regexInput" value="https?://[^\s]+" | |
| class="w-full bg-slate-900/50 border border-slate-700 rounded-lg px-4 py-2 text-sm text-white focus:outline-none focus:border-cyan-500 focus:ring-1 focus:ring-cyan-500 transition-all font-mono"> | |
| </div> | |
| <!-- Action Mode --> | |
| <div> | |
| <label class="block text-xs font-medium text-slate-400 mb-2">Acción al Detectar</label> | |
| <div class="grid grid-cols-3 gap-2"> | |
| <button onclick="setAction('log')" id="btn-log" class="action-btn active ring-1 ring-cyan-500 bg-cyan-500/20 text-cyan-300 px-3 py-2 rounded-lg text-xs font-medium transition-all hover:bg-cyan-500/30"> | |
| <i class="fa-solid fa-terminal mb-1 block"></i> Log | |
| </button> | |
| <button onclick="setAction('save')" id="btn-save" class="action-btn bg-slate-800 text-slate-400 px-3 py-2 rounded-lg text-xs font-medium transition-all hover:bg-slate-700 hover:text-white"> | |
| <i class="fa-solid fa-file-arrow-down mb-1 block"></i> Guardar | |
| </button> | |
| <button onclick="setAction('open')" id="btn-open" class="action-btn bg-slate-800 text-slate-400 px-3 py-2 rounded-lg text-xs font-medium transition-all hover:bg-slate-700 hover:text-white"> | |
| <i class="fa-solid fa-globe mb-1 block"></i> Abrir | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Custom Action Code (Hidden by default or context aware) --> | |
| <div id="customActionContainer" class="hidden"> | |
| <label class="block text-xs font-medium text-slate-400 mb-1">Comando Personalizado</label> | |
| <textarea id="customCode" rows="2" class="w-full bg-slate-900/50 border border-slate-700 rounded-lg px-4 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500"></textarea> | |
| </div> | |
| <button onclick="generateScript()" class="w-full bg-gradient-to-r from-cyan-500 to-blue-600 hover:from-cyan-400 hover:to-blue-500 text-white font-bold py-3 rounded-xl shadow-lg shadow-cyan-500/25 transition-all transform hover:scale-[1.02] active:scale-[0.98] flex items-center justify-center gap-2"> | |
| <i class="fa-solid fa-rotate"></i> Generar Script | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Installation Instructions --> | |
| <div class="glass-panel rounded-2xl p-6"> | |
| <h3 class="text-sm font-semibold text-white mb-3">Requisitos</h3> | |
| <div | |
| class="bg-slate-950 rounded-lg p-3 font-mono text-xs text-slate-300 border border-slate-800 mb-2 relative group"> | |
| <span class="text-purple-400">npm</span> install clipboardy | |
| <button onclick="copyToClipboard('npm install clipboardy')" class="absolute right-2 top-2 text-slate-500 hover:text-white transition-colors"> | |
| <i class="fa-regular fa-copy"></i> | |
| </button> | |
| </div> | |
| <p class="text-xs text-slate-500"> | |
| Para ES Modules, asegúrate de tener <code>"type": "module"</code> en tu package.json o usa extensión .mjs | |
| </p> | |
| </div> | |
| </div> | |
| <!-- Right Column: Code Preview & Simulation --> | |
| <div class="lg:col-span-7 space-y-6"> | |
| <!-- Code Editor Window --> | |
| <div class="glass-panel rounded-2xl overflow-hidden flex flex-col h-[400px] lg:h-[500px]"> | |
| <div class="bg-slate-900/80 px-4 py-2 border-b border-slate-700 flex items-center justify-between"> | |
| <div class="flex items-center gap-2"> | |
| <div class="flex gap-1.5"> | |
| <div class="w-3 h-3 rounded-full bg-red-500"></div> | |
| <div class="w-3 h-3 rounded-full bg-yellow-500"></div> | |
| <div class="w-3 h-3 rounded-full bg-green-500"></div> | |
| </div> | |
| <span class="ml-3 text-xs text-slate-400 font-mono" id="filenameDisplay">interceptor.mjs</span> | |
| </div> | |
| <button onclick="downloadScript()" class="text-xs bg-slate-800 hover:bg-slate-700 text-white px-3 py-1 rounded border border-slate-600 transition-colors flex items-center gap-2"> | |
| <i class="fa-solid fa-download"></i> Descargar | |
| </button> | |
| </div> | |
| <div class="flex-grow relative bg-[#0d1117] overflow-auto"> | |
| <pre | |
| class="p-4 text-sm font-mono leading-relaxed"><code id="codeDisplay" class="language-javascript text-slate-300"></code></pre> | |
| </div> | |
| </div> | |
| <!-- Live Simulation --> | |
| <div class="glass-panel rounded-2xl p-6 border-t-4 border-t-cyan-500"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <h2 class="text-lg font-semibold text-white flex items-center gap-2"> | |
| <i class="fa-solid fa-vial text-cyan-400"></i> Simulador en Vivo | |
| </h2> | |
| <div class="flex items-center gap-2"> | |
| <div id="statusIndicator" class="w-2 h-2 rounded-full bg-slate-500"></div> | |
| <span id="statusText" class="text-xs text-slate-400 font-mono">DETENIDO</span> | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <!-- Controls --> | |
| <div class="space-y-3"> | |
| <p class="text-xs text-slate-400 mb-2">Simula copiar una URL al portapapeles:</p> | |
| <div class="flex gap-2"> | |
| <input type="text" id="simInput" placeholder="https://ejemplo.com" | |
| class="flex-grow bg-slate-900/50 border border-slate-700 rounded-lg px-3 py-2 text-sm text-white focus:border-cyan-500 focus:outline-none font-mono"> | |
| <button onclick="simulateCopy()" class="bg-cyan-600 hover:bg-cyan-500 text-white px-4 py-2 rounded-lg transition-colors"> | |
| <i class="fa-solid fa-copy"></i> | |
| </button> | |
| </div> | |
| <div class="flex gap-2 mt-2"> | |
| <button onclick="startSimulation()" id="btnStart" class="flex-1 bg-green-600/20 text-green-400 border border-green-600/50 hover:bg-green-600/30 py-2 rounded-lg text-xs font-bold transition-all"> | |
| INICIAR | |
| </button> | |
| <button onclick="stopSimulation()" id="btnStop" class="flex-1 bg-red-600/20 text-red-400 border border-red-600/50 hover:bg-red-600/30 py-2 rounded-lg text-xs font-bold transition-all opacity-50 cursor-not-allowed" disabled> | |
| DETENER | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Console Output --> | |
| <div | |
| class="bg-black rounded-lg p-3 font-mono text-xs h-40 overflow-y-auto border border-slate-800 shadow-inner" | |
| id="consoleOutput"> | |
| <div class="text-slate-500 italic">// Terminal lista...</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| <!-- Footer --> | |
| <footer class="border-t border-slate-800 mt-auto py-6 bg-slate-900/50"> | |
| <div class="container mx-auto px-4 text-center text-slate-500 text-xs"> | |
| <p>Generado para Node.js • Compatible con CommonJS y ES Modules</p> | |
| </div> | |
| </footer> | |
| <script> | |
| // --- State Management --- | |
| let currentAction = 'log'; | |
| let isSimulating = false; | |
| let simInterval = null; | |
| let lastSimClipboard = ""; | |
| let isESM = true; // Default to ES Modules | |
| // --- Template Logic --- | |
| const getScriptTemplate = (config) => { | |
| const { interval, regex, action, customCode, esm } = config; | |
| let actionLogic = ''; | |
| let imports = ''; | |
| let extraVars = ''; | |
| // Generate imports based on module type | |
| if (esm) { | |
| imports = `import clipboardy from 'clipboardy';\n`; | |
| } else { | |
| imports = `const clipboardy = require('clipboardy');\n`; | |
| } | |
| if (action === 'log') { | |
| actionLogic = ` | |
| console.log('\\x1b[36m[URL Detectada]\\x1b[0m', url);`; | |
| } else if (action === 'save') { | |
| if (esm) { | |
| imports += `import fs from 'fs';\n`; | |
| } else { | |
| imports += `const fs = require('fs');\n`; | |
| } | |
| extraVars = `const FILE_PATH = './captured_urls.txt';\n`; | |
| actionLogic = ` | |
| fs.appendFileSync(FILE_PATH, url + '\\n'); | |
| console.log('\\x1b[32m[Guardado]\\x1b[0m URL añadida a', FILE_PATH);`; | |
| } else if (action === 'open') { | |
| if (esm) { | |
| imports += `import { exec } from 'child_process';\n`; | |
| } else { | |
| imports += `const { exec } = require('child_process');\n`; | |
| } | |
| actionLogic = ` | |
| console.log('\\x1b[33m[Abriendo]\\x1b[0m Navegador...'); | |
| exec(\`start \${url}\`); // Windows | |
| // exec(\`open "\${url}"\`); // Mac | |
| // exec(\`xdg-open "\${url}"\`); // Linux`; | |
| } | |
| return `${imports}${extraVars} | |
| // Configuración | |
| const CHECK_INTERVAL = ${interval}; // ms | |
| const URL_REGEX = /${regex}/; | |
| let lastClipboard = ""; | |
| console.log('\\x1b[35m[ClipSnatch]\\x1b[0m Iniciando interceptor...'); | |
| console.log('\\x1b[35m[ClipSnatch]\\x1b[0m Esperando URLs...\\n'); | |
| setInterval(async () => { | |
| try { | |
| const current = await clipboardy.read(); | |
| // Detectar cambios | |
| if (current !== lastClipboard) { | |
| lastClipboard = current; | |
| // Validar URL | |
| if (URL_REGEX.test(current)) { | |
| const url = current.match(URL_REGEX)[0]; | |
| ${actionLogic} | |
| } | |
| } | |
| } catch (err) { | |
| console.error('\\x1b[31m[Error]\\x1b[0m', err.message); | |
| } | |
| }, CHECK_INTERVAL);`; | |
| }; | |
| // --- UI Logic --- | |
| function toggleModuleType() { | |
| isESM = document.getElementById('moduleToggle').checked; | |
| const display = document.getElementById('moduleTypeDisplay'); | |
| const filename = document.getElementById('filenameDisplay'); | |
| if (isESM) { | |
| display.innerHTML = '<i class="fa-solid fa-check-circle mr-1"></i> ES Modules (import/export)'; | |
| display.className = 'text-xs font-mono text-cyan-400 bg-cyan-500/10 px-2 py-1 rounded border border-cyan-500/20'; | |
| filename.textContent = 'interceptor.mjs'; | |
| } else { | |
| display.innerHTML = '<i class="fa-solid fa-circle mr-1"></i> CommonJS (require/module.exports)'; | |
| display.className = 'text-xs font-mono text-yellow-400 bg-yellow-500/10 px-2 py-1 rounded border border-yellow-500/20'; | |
| filename.textContent = 'interceptor.js'; | |
| } | |
| generateScript(); | |
| } | |
| function setAction(action) { | |
| currentAction = action; | |
| // Update buttons | |
| document.querySelectorAll('.action-btn').forEach(btn => { | |
| btn.classList.remove('ring-1', 'ring-cyan-500', 'bg-cyan-500/20', 'text-cyan-300'); | |
| btn.classList.add('bg-slate-800', 'text-slate-400'); | |
| }); | |
| const activeBtn = document.getElementById(`btn-${action}`); | |
| activeBtn.classList.remove('bg-slate-800', 'text-slate-400'); | |
| activeBtn.classList.add('ring-1', 'ring-cyan-500', 'bg-cyan-500/20', 'text-cyan-300'); | |
| // Show/Hide custom code | |
| const customContainer = document.getElementById('customActionContainer'); | |
| if(action === 'custom') { | |
| customContainer.classList.remove('hidden'); | |
| } else { | |
| customContainer.classList.add('hidden'); | |
| } | |
| generateScript(); | |
| } | |
| function generateScript() { | |
| const interval = document.getElementById('intervalInput').value; | |
| const regex = document.getElementById('regexInput').value; | |
| const customCode = document.getElementById('customCode').value; | |
| const config = { | |
| interval, | |
| regex, | |
| action: currentAction, | |
| customCode, | |
| esm: isESM | |
| }; | |
| const code = getScriptTemplate(config); | |
| const codeBlock = document.getElementById('codeDisplay'); | |
| codeBlock.textContent = code; | |
| } | |
| function downloadScript() { | |
| const code = document.getElementById('codeDisplay').textContent; | |
| const extension = isESM ? 'mjs' : 'js'; | |
| const blob = new Blob([code], { type: 'text/javascript' }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `interceptor.${extension}`; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| URL.revokeObjectURL(url); | |
| } | |
| function copyToClipboard(text) { | |
| navigator.clipboard.writeText(text).then(() => { | |
| alert('Comando copiado al portapapeles'); | |
| }); | |
| } | |
| // --- Simulation Logic --- | |
| function logToConsole(text, type = 'info') { | |
| const consoleEl = document.getElementById('consoleOutput'); | |
| const line = document.createElement('div'); | |
| line.className = 'terminal-line mb-1 font-mono text-xs break-all'; | |
| const timestamp = new Date().toLocaleTimeString('es-ES', { hour12: false }); | |
| if (type === 'success') line.classList.add('text-green-400'); | |
| else if (type === 'error') line.classList.add('text-red-400'); | |
| else if (type === 'warn') line.classList.add('text-yellow-400'); | |
| else line.classList.add('text-slate-300'); | |
| line.innerHTML = `<span class="text-slate-600">[${timestamp}]</span> ${text}`; | |
| consoleEl.appendChild(line); | |
| consoleEl.scrollTop = consoleEl.scrollHeight; | |
| } | |
| function startSimulation() { | |
| if (isSimulating) return; | |
| isSimulating = true; | |
| // UI Updates | |
| document.getElementById('btnStart').disabled = true; | |
| document.getElementById('btnStart').classList.add('opacity-50', 'cursor-not-allowed'); | |
| document.getElementById('btnStop').disabled = false; | |
| document.getElementById('btnStop').classList.remove('opacity-50', 'cursor-not-allowed'); | |
| const statusInd = document.getElementById('statusIndicator'); | |
| statusInd.classList.remove('bg-slate-500'); | |
| statusInd.classList.add('bg-green-500', 'animate-pulse'); | |
| document.getElementById('statusText').textContent = "ESCUCHANDO..."; | |
| document.getElementById('statusText').classList.add('text-green-400'); | |
| logToConsole('Iniciando servicio de monitoreo...', 'warn'); | |
| logToConsole(`Modulo: ${isESM ? 'ESM' : 'CommonJS'}`, 'info'); | |
| logToConsole(`Intervalo configurado: ${document.getElementById('intervalInput').value}ms`, 'info'); | |
| } | |
| function stopSimulation() { | |
| isSimulating = false; | |
| document.getElementById('btnStart').disabled = false; | |
| document.getElementById('btnStart').classList.remove('opacity-50', 'cursor-not-allowed'); | |
| document.getElementById('btnStop').disabled = true; | |
| document.getElementById('btnStop').classList.add('opacity-50', 'cursor-not-allowed'); | |
| const statusInd = document.getElementById('statusIndicator'); | |
| statusInd.classList.remove('bg-green-500', 'animate-pulse'); | |
| statusInd.classList.add('bg-slate-500'); | |
| document.getElementById('statusText').textContent = "DETENIDO"; | |
| document.getElementById('statusText').classList.remove('text-green-400'); | |
| logToConsole('Servicio detenido.', 'error'); | |
| } | |
| function simulateCopy() { | |
| const input = document.getElementById('simInput'); | |
| const text = input.value; | |
| if (!text) return; | |
| if (!isSimulating) { | |
| logToConsole('Error: El interceptor no está activo. Presiona INICIAR.', 'error'); | |
| return; | |
| } | |
| // Simulate Clipboard Read | |
| logToConsole(`Portapapeles actualizado: "${text}"`, 'info'); | |
| // Check Regex | |
| const regexStr = document.getElementById('regexInput').value; | |
| try { | |
| const regex = new RegExp(regexStr); | |
| if (regex.test(text)) { | |
| const match = text.match(regex)[0]; | |
| handleDetection(match); | |
| } else { | |
| logToConsole('Contenido ignorado (no es URL).', 'info'); | |
| } | |
| } catch (e) { | |
| logToConsole('Error en Regex: ' + e.message, 'error'); | |
| } | |
| input.value = ''; | |
| } | |
| function handleDetection(url) { | |
| const action = currentAction; | |
| if (action === 'log') { | |
| logToConsole(`[URL Detectada] ${url}`, 'success'); | |
| } else if (action === 'save') { | |
| logToConsole(`[Guardado] URL añadida a captured_urls.txt`, 'success'); | |
| logToConsole(` -> ${url}`, 'info'); | |
| } else if (action === 'open') { | |
| logToConsole(`[Abriendo] Navegador...`, 'warn'); | |
| setTimeout(() => { | |
| window.open(url, '_blank'); | |
| }, 500); | |
| } | |
| } | |
| // Initialize | |
| document.addEventListener('DOMContentLoaded', () => { | |
| generateScript(); | |
| // Add enter key support for simulation | |
| document.getElementById('simInput').addEventListener('keypress', function (e) { | |
| if (e.key === 'Enter') { | |
| simulateCopy(); | |
| } | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> |