Download public/index.html from veela4/luna_ocr: direct link, hf CLI and curl.
- Browser
- Download file 5.95 kB
-
https://huggingface.co/veela4/luna_ocr/resolve/main/public/index.html
- Command line
-
hf download hf://veela4/luna_ocr/public/index.html
-
curl -L -o index.html https://huggingface.co/veela4/luna_ocr/resolve/main/public/index.html
5.95 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <link rel="icon" | |
| href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>ππ</text></svg>" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| <meta name="theme-color" content="#0a0a0f" /> | |
| <meta name="description" content="Luna OCR - Advanced Text Extraction from PDFs and Images" /> | |
| <title>ππ Luna OCR - Text Extraction</title> | |
| <!-- Preload fonts --> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> | |
| <link href="https://fonts.googleapis.com/css2?family=Martian+Mono:wght@100..800&display=swap" rel="stylesheet"> | |
| <!-- Meta tags for better SEO --> | |
| <meta property="og:title" content="Luna OCR - Text Extraction" /> | |
| <meta property="og:description" | |
| content="Extract text from PDFs and images using advanced Luna processing. Support for Thai language and structured formatting." /> | |
| <meta property="og:type" content="website" /> | |
| <meta name="twitter:card" content="summary_large_image" /> | |
| <style> | |
| /* Prevent flash of unstyled content */ | |
| body { | |
| margin: 0; | |
| background: #141414 ; | |
| color: #ffffff; | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', sans-serif; | |
| overflow: hidden; | |
| } | |
| /* Loading screen styles - applied immediately */ | |
| #loading { | |
| position: fixed ; | |
| top: 0 ; | |
| left: 0 ; | |
| width: 100vw ; | |
| height: 100vh ; | |
| background: #141414 ; | |
| z-index: 99999 ; | |
| display: flex ; | |
| align-items: center; | |
| justify-content: center; | |
| opacity: 1 ; | |
| } | |
| /* Hide React root initially */ | |
| #root { | |
| opacity: 0; | |
| transition: opacity 0.3s ease; | |
| } | |
| /* Show root when loading is done */ | |
| body.loaded #root { | |
| opacity: 1; | |
| } | |
| body.loaded #loading { | |
| display: none ; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <noscript>You need to enable JavaScript to run this app.</noscript> | |
| <!-- Loading screen --> | |
| <div id="loading" class="loading-container"> | |
| <div class="a-hole"> | |
| <canvas class="js-canvas"></canvas> | |
| <div class="aura"></div> | |
| </div> | |
| </div> | |
| <div id="root"></div> | |
| <script> | |
| // Simple loading screen that won't interfere with React | |
| document.addEventListener('DOMContentLoaded', function () { | |
| const canvas = document.querySelector('.js-canvas'); | |
| if (!canvas) return; | |
| const ctx = canvas.getContext('2d'); | |
| let animationId; | |
| // Set canvas size | |
| function resizeCanvas() { | |
| const rect = canvas.parentElement.getBoundingClientRect(); | |
| canvas.width = rect.width; | |
| canvas.height = rect.height; | |
| } | |
| resizeCanvas(); | |
| window.addEventListener('resize', resizeCanvas); | |
| // Optimized particle animation - fewer particles for better performance | |
| const particles = []; | |
| for (let i = 0; i < 25; i++) { | |
| particles.push({ | |
| x: Math.random() * canvas.width, | |
| y: Math.random() * canvas.height, | |
| vx: (Math.random() - 0.5) * 1.5, | |
| vy: (Math.random() - 0.5) * 1.5, | |
| size: Math.random() * 2 + 1, | |
| opacity: Math.random() * 0.6 + 0.3 | |
| }); | |
| } | |
| function animate() { | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| // Draw particles - all visible immediately | |
| particles.forEach(particle => { | |
| particle.x += particle.vx; | |
| particle.y += particle.vy; | |
| // Wrap around edges | |
| if (particle.x < 0) particle.x = canvas.width; | |
| if (particle.x > canvas.width) particle.x = 0; | |
| if (particle.y < 0) particle.y = canvas.height; | |
| if (particle.y > canvas.height) particle.y = 0; | |
| ctx.fillStyle = `rgba(255, 255, 255, ${particle.opacity})`; | |
| ctx.beginPath(); | |
| ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2); | |
| ctx.fill(); | |
| }); | |
| animationId = requestAnimationFrame(animate); | |
| } | |
| // Start animation after 500ms delay | |
| setTimeout(() => { | |
| animate(); | |
| }, 500); | |
| // Hide loading screen when React loads | |
| function hideLoading() { | |
| cancelAnimationFrame(animationId); | |
| document.body.classList.add('loaded'); | |
| } | |
| // Fast loading screen removal | |
| let reactLoaded = false; | |
| // Check for actual app content (header with Luna OCR) | |
| const checkReactLoaded = () => { | |
| const root = document.getElementById('root'); | |
| const appHeader = root?.querySelector('.app-header'); | |
| const logoContent = root?.querySelector('.logo'); | |
| if (root && root.children.length > 0 && (appHeader || logoContent) && !reactLoaded) { | |
| reactLoaded = true; | |
| // Small delay to ensure orb and other components are ready | |
| setTimeout(hideLoading, 200); | |
| } | |
| }; | |
| // Fast polling every 100ms | |
| const pollInterval = setInterval(() => { | |
| checkReactLoaded(); | |
| if (reactLoaded) { | |
| clearInterval(pollInterval); | |
| } | |
| }, 100); | |
| // Much faster fallback - hide after 800ms max | |
| setTimeout(() => { | |
| if (!reactLoaded) { | |
| hideLoading(); | |
| } | |
| }, 800); | |
| // Immediate check on DOM ready | |
| if (document.readyState === 'complete') { | |
| setTimeout(checkReactLoaded, 100); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |