Spaces:
Running
Running
Приложение для распознавания выделенной область на экране и автомотическго запись в текстовый файл. Приложение работает поверх других приложений
749cd9e verified | <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>ScreenSnip Scribe</title> | |
| <link rel="icon" type="image/x-icon" href="/static/favicon.ico"> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://unpkg.com/feather-icons"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/tesseract.js/4.1.1/tesseract.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.waves.min.js"></script> | |
| <style> | |
| .selection-box { | |
| position: absolute; | |
| border: 2px dashed rgba(59, 130, 246, 0.8); | |
| background-color: rgba(59, 130, 246, 0.2); | |
| pointer-events: none; | |
| z-index: 9999; | |
| } | |
| .overlay { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background-color: rgba(0, 0, 0, 0.5); | |
| z-index: 9998; | |
| display: none; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-900 text-gray-100 min-h-screen"> | |
| <div id="vanta-bg" class="fixed inset-0 z-0"></div> | |
| <div class="relative z-10 container mx-auto px-4 py-12"> | |
| <header class="mb-12 text-center"> | |
| <h1 class="text-5xl font-bold mb-4 bg-clip-text text-transparent bg-gradient-to-r from-blue-400 to-purple-600">ScreenSnip Scribe</h1> | |
| <p class="text-xl text-gray-300 max-w-2xl mx-auto">Capture any screen area and automatically save the text to a file with this magical OCR tool ✨</p> | |
| </header> | |
| <main class="bg-gray-800 bg-opacity-70 backdrop-blur-lg rounded-xl p-8 max-w-3xl mx-auto shadow-2xl border border-gray-700"> | |
| <div class="flex flex-col items-center"> | |
| <div class="w-full mb-8"> | |
| <div class="flex items-center justify-center gap-4 mb-6"> | |
| <button id="startCaptureBtn" class="px-6 py-3 bg-blue-600 hover:bg-blue-700 rounded-lg font-medium flex items-center gap-2 transition-all transform hover:scale-105"> | |
| <i data-feather="crop"></i> Start Capture | |
| </button> | |
| <button id="saveBtn" disabled class="px-6 py-3 bg-purple-600 hover:bg-purple-700 rounded-lg font-medium flex items-center gap-2 transition-all opacity-70"> | |
| <i data-feather="save"></i> Save Text | |
| </button> | |
| </div> | |
| <div class="bg-gray-900 rounded-lg p-4 min-h-40 max-h-96 overflow-y-auto"> | |
| <h3 class="text-lg font-medium mb-3 text-blue-400">Extracted Text:</h3> | |
| <div id="extractedText" class="text-gray-300 whitespace-pre-wrap"></div> | |
| </div> | |
| </div> | |
| <div class="w-full"> | |
| <div class="bg-gray-900 rounded-lg p-4"> | |
| <div class="flex items-center justify-between mb-3"> | |
| <h3 class="text-lg font-medium text-blue-400">Settings</h3> | |
| <i data-feather="settings" class="text-gray-400"></i> | |
| </div> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-300 mb-1">Output Format</label> | |
| <select id="outputFormat" class="w-full bg-gray-800 border border-gray-700 rounded-md px-3 py-2 text-gray-200 focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
| <option value="txt">Text File (.txt)</option> | |
| <option value="doc">Word Document (.doc)</option> | |
| <option value="pdf">PDF (.pdf)</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-300 mb-1">Auto Save Location</label> | |
| <div class="flex gap-2"> | |
| <input type="text" id="savePath" class="flex-1 bg-gray-800 border border-gray-700 rounded-md px-3 py-2 text-gray-200 focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="C:/ScreenCaptures/" readonly> | |
| <button id="changePathBtn" class="px-3 py-2 bg-gray-700 hover:bg-gray-600 rounded-md text-sm font-medium transition-colors"> | |
| <i data-feather="folder"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| <div id="overlay" class="overlay"></div> | |
| <div id="selectionBox" class="selection-box"></div> | |
| </div> | |
| <script> | |
| // Initialize Vanta.js background | |
| VANTA.WAVES({ | |
| el: "#vanta-bg", | |
| mouseControls: true, | |
| touchControls: true, | |
| gyroControls: false, | |
| minHeight: 200.00, | |
| minWidth: 200.00, | |
| scale: 1.00, | |
| scaleMobile: 1.00, | |
| color: 0x1e3a8a, | |
| shininess: 50.00, | |
| waveHeight: 20.00, | |
| waveSpeed: 0.50, | |
| zoom: 0.75 | |
| }); | |
| // Initialize feather icons | |
| feather.replace(); | |
| // App functionality | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const startCaptureBtn = document.getElementById('startCaptureBtn'); | |
| const saveBtn = document.getElementById('saveBtn'); | |
| const extractedText = document.getElementById('extractedText'); | |
| const overlay = document.getElementById('overlay'); | |
| const selectionBox = document.getElementById('selectionBox'); | |
| const outputFormat = document.getElementById('outputFormat'); | |
| const savePath = document.getElementById('savePath'); | |
| const changePathBtn = document.getElementById('changePathBtn'); | |
| let isSelecting = false; | |
| let startX, startY, endX, endY; | |
| // Set default save path (electron would handle this differently) | |
| savePath.value = "Documents/ScreenSnips/"; | |
| startCaptureBtn.addEventListener('click', () => { | |
| isSelecting = true; | |
| overlay.style.display = 'block'; | |
| document.body.style.cursor = 'crosshair'; | |
| startCaptureBtn.disabled = true; | |
| startCaptureBtn.classList.add('opacity-70'); | |
| }); | |
| overlay.addEventListener('mousedown', (e) => { | |
| if (!isSelecting) return; | |
| startX = e.clientX; | |
| startY = e.clientY; | |
| selectionBox.style.left = startX + 'px'; | |
| selectionBox.style.top = startY + 'px'; | |
| selectionBox.style.width = '0'; | |
| selectionBox.style.height = '0'; | |
| selectionBox.style.display = 'block'; | |
| }); | |
| overlay.addEventListener('mousemove', (e) => { | |
| if (!isSelecting || !startX) return; | |
| endX = e.clientX; | |
| endY = e.clientY; | |
| selectionBox.style.left = Math.min(startX, endX) + 'px'; | |
| selectionBox.style.top = Math.min(startY, endY) + 'px'; | |
| selectionBox.style.width = Math.abs(endX - startX) + 'px'; | |
| selectionBox.style.height = Math.abs(endY - startY) + 'px'; | |
| }); | |
| overlay.addEventListener('mouseup', async () => { | |
| if (!isSelecting || !startX) return; | |
| isSelecting = false; | |
| document.body.style.cursor = ''; | |
| // Hide overlay and selection box | |
| overlay.style.display = 'none'; | |
| selectionBox.style.display = 'none'; | |
| // Get the selected area coordinates | |
| const left = Math.min(startX, endX); | |
| const top = Math.min(startY, endY); | |
| const width = Math.abs(endX - startX); | |
| const height = Math.abs(endY - startY); | |
| // In a real Electron app, we would use the desktopCapturer API here | |
| // For this demo, we'll simulate with html2canvas | |
| try { | |
| // Show loading state | |
| extractedText.textContent = "Processing..."; | |
| // In a real app, we would capture the screen here | |
| // For demo purposes, we'll just simulate with some sample text | |
| setTimeout(() => { | |
| // Simulate OCR processing | |
| const sampleText = `Sample extracted text from screen area: | |
| Left: ${left}px | |
| Top: ${top}px | |
| Width: ${width}px | |
| Height: ${height}px | |
| This is where the actual OCR text would appear. In a real app, this would be the text extracted from the selected screen area using Tesseract.js or similar OCR technology. | |
| The text would then be saved to a file in the selected format (${outputFormat.value}) at the specified location.`; | |
| extractedText.textContent = sampleText; | |
| saveBtn.disabled = false; | |
| saveBtn.classList.remove('opacity-70'); | |
| startCaptureBtn.disabled = false; | |
| startCaptureBtn.classList.remove('opacity-70'); | |
| }, 1500); | |
| } catch (error) { | |
| console.error('Capture error:', error); | |
| extractedText.textContent = "Error capturing screen area. Please try again."; | |
| startCaptureBtn.disabled = false; | |
| startCaptureBtn.classList.remove('opacity-70'); | |
| } | |
| }); | |
| saveBtn.addEventListener('click', () => { | |
| if (!extractedText.textContent.trim()) return; | |
| // In a real Electron app, we would use the fs module here | |
| // For demo, we'll just show a success message | |
| saveBtn.disabled = true; | |
| saveBtn.classList.add('opacity-70'); | |
| const format = outputFormat.value; | |
| const path = savePath.value; | |
| extractedText.textContent += `\n\nFile saved to: ${path}capture_${Date.now()}.${format}`; | |
| setTimeout(() => { | |
| saveBtn.disabled = false; | |
| saveBtn.classList.remove('opacity-70'); | |
| }, 2000); | |
| }); | |
| changePathBtn.addEventListener('click', () => { | |
| // In a real Electron app, we would use dialog.showOpenDialog here | |
| // For demo, we'll just show a simulated path change | |
| savePath.value = "Documents/ScreenSnips/NewFolder/"; | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |