| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Advanced Text Processor</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| .dropzone { |
| border: 2px dashed #ccc; |
| transition: all 0.3s; |
| } |
| .dropzone.active { |
| border-color: #4f46e5; |
| background-color: #f0f7ff; |
| } |
| .chunk-box { |
| position: relative; |
| } |
| .chunk-box:hover .copy-btn { |
| opacity: 1; |
| } |
| .copy-btn { |
| opacity: 0; |
| transition: opacity 0.2s; |
| } |
| .progress-bar { |
| transition: width 0.3s ease; |
| } |
| .glow { |
| animation: glow 1.5s ease-in-out infinite alternate; |
| } |
| @keyframes glow { |
| from { |
| box-shadow: 0 0 5px rgba(79, 70, 229, 0.5); |
| } |
| to { |
| box-shadow: 0 0 15px rgba(79, 70, 229, 0.8); |
| } |
| } |
| </style> |
| </head> |
| <body class="bg-gray-50 min-h-screen"> |
| <div class="container mx-auto px-4 py-8 max-w-4xl"> |
| <div class="text-center mb-8"> |
| <h1 class="text-3xl font-bold text-indigo-700 mb-2">Advanced Text Processor</h1> |
| <p class="text-gray-600">Upload a TXT file to clean text (removes punctuation and numbers)</p> |
| </div> |
|
|
| <div class="bg-white rounded-xl shadow-md p-6 mb-8 glow"> |
| <div id="dropzone" class="dropzone rounded-lg p-12 text-center cursor-pointer"> |
| <div class="flex flex-col items-center justify-center"> |
| <i class="fas fa-file-alt text-4xl text-indigo-500 mb-4"></i> |
| <h3 class="text-lg font-medium text-gray-700 mb-2">Drag & drop your TXT file here</h3> |
| <p class="text-gray-500 mb-4">or</p> |
| <label for="fileInput" class="bg-indigo-600 text-white px-4 py-2 rounded-md hover:bg-indigo-700 transition cursor-pointer"> |
| <i class="fas fa-upload mr-2"></i> Select File |
| </label> |
| <input id="fileInput" type="file" accept=".txt" class="hidden"> |
| </div> |
| </div> |
|
|
| <div id="progressContainer" class="mt-4 hidden"> |
| <div class="flex justify-between mb-1"> |
| <span class="text-sm font-medium text-indigo-700">Processing...</span> |
| <span id="progressPercent" class="text-sm font-medium text-indigo-700">0%</span> |
| </div> |
| <div class="w-full bg-gray-200 rounded-full h-2.5"> |
| <div id="progressBar" class="progress-bar bg-indigo-600 h-2.5 rounded-full" style="width: 0%"></div> |
| </div> |
| </div> |
| </div> |
|
|
| <div id="resultsSection" class="hidden"> |
| <div class="flex justify-between items-center mb-4"> |
| <h2 class="text-xl font-semibold text-gray-800">Processed Results</h2> |
| <div class="flex space-x-2"> |
| <button id="copyAllBtn" class="bg-indigo-100 text-indigo-700 px-3 py-1 rounded-md text-sm hover:bg-indigo-200 transition flex items-center"> |
| <i class="fas fa-copy mr-1"></i> Copy All |
| </button> |
| <button id="downloadBtn" class="bg-green-100 text-green-700 px-3 py-1 rounded-md text-sm hover:bg-green-200 transition flex items-center"> |
| <i class="fas fa-download mr-1"></i> Download |
| </button> |
| </div> |
| </div> |
|
|
| <div id="fileInfo" class="bg-blue-50 border-l-4 border-blue-500 p-4 mb-6 rounded"> |
| <div class="flex justify-between"> |
| <div> |
| <h3 class="font-medium text-blue-800" id="fileName"></h3> |
| <p class="text-sm text-blue-600" id="fileSize"></p> |
| </div> |
| <div class="text-right"> |
| <p class="text-sm text-blue-600" id="chunkCount"></p> |
| <p class="text-sm text-blue-600" id="processingTime"></p> |
| </div> |
| </div> |
| </div> |
|
|
| <div id="chunksContainer" class="space-y-4"></div> |
| </div> |
| </div> |
|
|
| <script> |
| document.addEventListener('DOMContentLoaded', function() { |
| const dropzone = document.getElementById('dropzone'); |
| const fileInput = document.getElementById('fileInput'); |
| const resultsSection = document.getElementById('resultsSection'); |
| const chunksContainer = document.getElementById('chunksContainer'); |
| const progressContainer = document.getElementById('progressContainer'); |
| const progressBar = document.getElementById('progressBar'); |
| const progressPercent = document.getElementById('progressPercent'); |
| const copyAllBtn = document.getElementById('copyAllBtn'); |
| const downloadBtn = document.getElementById('downloadBtn'); |
| const fileName = document.getElementById('fileName'); |
| const fileSize = document.getElementById('fileSize'); |
| const chunkCount = document.getElementById('chunkCount'); |
| const processingTime = document.getElementById('processingTime'); |
| |
| |
| ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { |
| dropzone.addEventListener(eventName, preventDefaults, false); |
| }); |
| |
| function preventDefaults(e) { |
| e.preventDefault(); |
| e.stopPropagation(); |
| } |
| |
| ['dragenter', 'dragover'].forEach(eventName => { |
| dropzone.addEventListener(eventName, highlight, false); |
| }); |
| |
| ['dragleave', 'drop'].forEach(eventName => { |
| dropzone.addEventListener(eventName, unhighlight, false); |
| }); |
| |
| function highlight() { |
| dropzone.classList.add('active'); |
| } |
| |
| function unhighlight() { |
| dropzone.classList.remove('active'); |
| } |
| |
| |
| dropzone.addEventListener('drop', handleDrop, false); |
| fileInput.addEventListener('change', handleFiles); |
| |
| function handleDrop(e) { |
| const dt = e.dataTransfer; |
| const files = dt.files; |
| handleFiles({ target: { files } }); |
| } |
| |
| function handleFiles(e) { |
| const file = e.target.files[0]; |
| if (!file) return; |
| |
| if (!file.name.endsWith('.txt')) { |
| alert('Please upload a .txt file'); |
| return; |
| } |
| |
| processFile(file); |
| } |
| |
| function processFile(file) { |
| const startTime = performance.now(); |
| progressContainer.classList.remove('hidden'); |
| resultsSection.classList.add('hidden'); |
| chunksContainer.innerHTML = ''; |
| |
| const reader = new FileReader(); |
| |
| reader.onload = function(e) { |
| let content = e.target.result; |
| |
| |
| |
| |
| |
| |
| |
| |
| content = content.replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,\-.\/:;<=>?@[\]^_`{|}~\uFF0C-\uFF0E\uFF08-\uFF09\uFF01-\uFF1F\u3002]/g, ''); |
| |
| |
| content = content.replace(/\b\d+\.?\s*/g, ''); |
| |
| |
| content = content.replace(/\b\d+\b/g, ''); |
| |
| |
| content = content.replace(/\s+/g, ' '); |
| |
| |
| content = content.trim(); |
| |
| |
| const chunks = []; |
| for (let i = 0; i < content.length; i += 128) { |
| const chunk = content.substr(i, 128).trim(); |
| if (chunk.length > 0) { |
| chunks.push(chunk); |
| } |
| } |
| |
| |
| let processed = 0; |
| const totalChunks = chunks.length; |
| |
| |
| fileName.textContent = file.name; |
| fileSize.textContent = `${(file.size / 1024).toFixed(2)} KB`; |
| chunkCount.textContent = `${totalChunks} chunks`; |
| |
| |
| const processChunk = (index) => { |
| if (index >= totalChunks) { |
| const endTime = performance.now(); |
| processingTime.textContent = `Processed in ${((endTime - startTime) / 1000).toFixed(2)}s`; |
| progressContainer.classList.add('hidden'); |
| resultsSection.classList.remove('hidden'); |
| |
| |
| document.cleanedContent = chunks.join('\n\n'); |
| return; |
| } |
| |
| const chunk = chunks[index]; |
| createChunkElement(chunk, index + 1); |
| |
| processed++; |
| const percent = Math.round((processed / totalChunks) * 100); |
| progressBar.style.width = `${percent}%`; |
| progressPercent.textContent = `${percent}%`; |
| |
| setTimeout(() => processChunk(index + 1), 10); |
| }; |
| |
| processChunk(0); |
| }; |
| |
| reader.readAsText(file); |
| } |
| |
| function createChunkElement(chunk, index) { |
| const chunkElement = document.createElement('div'); |
| chunkElement.className = 'chunk-box bg-white border border-gray-200 rounded-lg p-4 hover:shadow-sm transition'; |
| |
| chunkElement.innerHTML = ` |
| <div class="flex justify-between items-start mb-2"> |
| <span class="text-xs font-medium bg-indigo-100 text-indigo-800 px-2 py-1 rounded">Chunk ${index}</span> |
| <span class="text-xs text-gray-500">${chunk.length} bytes</span> |
| </div> |
| <div class="relative"> |
| <pre class="text-gray-800 whitespace-pre-wrap break-words font-mono text-sm">${chunk}</pre> |
| <button class="copy-btn absolute top-0 right-0 bg-gray-100 hover:bg-gray-200 p-1 rounded text-gray-600" data-chunk="${index}"> |
| <i class="fas fa-copy text-xs"></i> |
| </button> |
| </div> |
| `; |
| |
| chunksContainer.appendChild(chunkElement); |
| } |
| |
| |
| chunksContainer.addEventListener('click', function(e) { |
| if (e.target.closest('.copy-btn')) { |
| const btn = e.target.closest('.copy-btn'); |
| const chunkIndex = btn.getAttribute('data-chunk'); |
| const chunkText = btn.closest('.chunk-box').querySelector('pre').textContent; |
| |
| navigator.clipboard.writeText(chunkText).then(() => { |
| const originalIcon = btn.innerHTML; |
| btn.innerHTML = '<i class="fas fa-check text-xs text-green-500"></i>'; |
| setTimeout(() => { |
| btn.innerHTML = originalIcon; |
| }, 2000); |
| }); |
| } |
| }); |
| |
| |
| copyAllBtn.addEventListener('click', function() { |
| const allChunks = Array.from(document.querySelectorAll('#chunksContainer pre')) |
| .map(pre => pre.textContent) |
| .join('\n\n'); |
| |
| navigator.clipboard.writeText(allChunks).then(() => { |
| const originalText = copyAllBtn.innerHTML; |
| copyAllBtn.innerHTML = '<i class="fas fa-check mr-1"></i> Copied!'; |
| setTimeout(() => { |
| copyAllBtn.innerHTML = originalText; |
| }, 2000); |
| }); |
| }); |
| |
| |
| downloadBtn.addEventListener('click', function() { |
| if (!document.cleanedContent) return; |
| |
| const blob = new Blob([document.cleanedContent], { type: 'text/plain' }); |
| const url = URL.createObjectURL(blob); |
| const a = document.createElement('a'); |
| a.href = url; |
| a.download = 'cleaned_' + fileName.textContent; |
| document.body.appendChild(a); |
| a.click(); |
| document.body.removeChild(a); |
| URL.revokeObjectURL(url); |
| |
| |
| const originalText = downloadBtn.innerHTML; |
| downloadBtn.innerHTML = '<i class="fas fa-check mr-1"></i> Downloaded!'; |
| setTimeout(() => { |
| downloadBtn.innerHTML = originalText; |
| }, 2000); |
| }); |
| }); |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=xacwutao/advanced-text-processor" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |