| | <!DOCTYPE html> |
| | <html lang="en"> |
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>LaTeX Compiler - Modern PDF Generator</title> |
| | <script src="https://cdn.tailwindcss.com"></script> |
| | <style> |
| | @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); |
| | |
| | * { |
| | font-family: 'Inter', sans-serif; |
| | } |
| | |
| | .glass-effect { |
| | background: rgba(255, 255, 255, 0.95); |
| | backdrop-filter: blur(10px); |
| | border: 1px solid rgba(255, 255, 255, 0.3); |
| | } |
| | |
| | .gradient-bg { |
| | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | } |
| | |
| | .code-editor { |
| | font-family: 'Courier New', monospace; |
| | background: #1e1e1e; |
| | color: #d4d4d4; |
| | } |
| | |
| | .animate-fade-in { |
| | animation: fadeIn 0.3s ease-in; |
| | } |
| | |
| | @keyframes fadeIn { |
| | from { opacity: 0; transform: translateY(-10px); } |
| | to { opacity: 1; transform: translateY(0); } |
| | } |
| | |
| | .btn-primary { |
| | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | transition: all 0.3s ease; |
| | } |
| | |
| | .btn-primary:hover { |
| | transform: translateY(-2px); |
| | box-shadow: 0 10px 25px rgba(102, 126, 234, 0.4); |
| | } |
| | |
| | .tab-active { |
| | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | color: white; |
| | } |
| | |
| | .tab-inactive { |
| | background: #f3f4f6; |
| | color: #6b7280; |
| | } |
| | </style> |
| | </head> |
| | <body class="min-h-screen gradient-bg"> |
| | |
| | <div class="fixed inset-0 opacity-10"> |
| | <div class="absolute inset-0" style="background-image: radial-gradient(circle at 2px 2px, white 1px, transparent 0); background-size: 40px 40px;"></div> |
| | </div> |
| |
|
| | <div class="relative z-10 container mx-auto px-4 py-8 max-w-6xl"> |
| | |
| | <div class="text-center mb-10"> |
| | <div class="inline-block mb-4"> |
| | <div class="text-6xl mb-2">π</div> |
| | </div> |
| | <h1 class="text-5xl font-bold text-white mb-3 tracking-tight">LaTeX Compiler</h1> |
| | <p class="text-xl text-purple-100 mb-4">Transform your LaTeX code into beautiful PDFs instantly</p> |
| | <a href="/docs" class="inline-flex items-center text-white hover:text-purple-200 transition font-medium"> |
| | <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path> |
| | </svg> |
| | API Documentation |
| | </a> |
| | </div> |
| |
|
| | |
| | <div class="glass-effect rounded-2xl shadow-2xl overflow-hidden"> |
| | |
| | <div class="flex border-b border-gray-200 bg-gray-50"> |
| | <button onclick="showTextarea()" id="btnTextarea" class="flex-1 px-6 py-4 font-semibold transition tab-active"> |
| | <div class="flex items-center justify-center"> |
| | <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"></path> |
| | </svg> |
| | Write Code |
| | </div> |
| | </button> |
| | <button onclick="showFileUpload()" id="btnFile" class="flex-1 px-6 py-4 font-semibold transition tab-inactive"> |
| | <div class="flex items-center justify-center"> |
| | <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"></path> |
| | </svg> |
| | Upload File |
| | </div> |
| | </button> |
| | </div> |
| |
|
| | <div class="p-8"> |
| | |
| | <div id="textareaSection"> |
| | <div class="mb-4 flex justify-between items-center"> |
| | <label class="text-sm font-semibold text-gray-700">LaTeX Source Code</label> |
| | <button onclick="clearCode()" class="text-sm text-gray-500 hover:text-gray-700 transition">Clear</button> |
| | </div> |
| | <textarea id="latexCode" class="code-editor w-full h-96 p-4 rounded-xl border-2 border-gray-200 focus:border-purple-500 focus:ring-4 focus:ring-purple-200 transition-all resize-none" placeholder="% Start typing your LaTeX code...">% Beautiful LaTeX Document |
| | \documentclass[12pt]{article} |
| | \usepackage[utf8]{inputenc} |
| | \usepackage{amsmath} |
| | \usepackage{graphicx} |
| | \usepackage{xcolor} |
| |
|
| | \title{\textbf{Professional LaTeX Document}} |
| | \author{LaTeX Compiler} |
| | \date{\today} |
| |
|
| | \begin{document} |
| |
|
| | \maketitle |
| |
|
| | \begin{abstract} |
| | This is a professionally formatted LaTeX document demonstrating advanced typesetting capabilities. |
| | \end{abstract} |
| |
|
| | \section{Introduction} |
| | Welcome to the world of \LaTeX! This powerful typesetting system enables you to create documents with exceptional quality. |
| |
|
| | \section{Mathematical Excellence} |
| | Einstein's groundbreaking equation: |
| | \[ |
| | E = mc^2 |
| | \] |
| |
|
| | \subsection{Advanced Mathematics} |
| | The Gaussian integral: |
| | \[ |
| | \int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi} |
| | \] |
| |
|
| | Euler's identity, often called the most beautiful equation: |
| | \[ |
| | e^{i\pi} + 1 = 0 |
| | \] |
| |
|
| | \section{Structured Content} |
| | \subsection{Key Features} |
| | \begin{itemize} |
| | \item \textbf{Professional Typography:} Beautiful fonts and spacing |
| | \item \textbf{Mathematical Typesetting:} Perfect equations every time |
| | \item \textbf{Cross-referencing:} Automatic numbering and references |
| | \item \textbf{Bibliography Support:} Manage citations effortlessly |
| | \end{itemize} |
| |
|
| | \subsection{Quality Metrics} |
| | \begin{enumerate} |
| | \item Document structure and organization |
| | \item Visual consistency throughout |
| | \item Professional appearance |
| | \item Academic standards compliance |
| | \end{enumerate} |
| |
|
| | \section{Conclusion} |
| | \LaTeX\ remains the gold standard for technical and academic document preparation, delivering unmatched quality and precision. |
| |
|
| | \end{document}</textarea> |
| | </div> |
| |
|
| | |
| | <div id="fileSection" class="hidden"> |
| | <div class="border-3 border-dashed border-purple-300 rounded-2xl p-12 text-center hover:border-purple-500 hover:bg-purple-50 transition-all cursor-pointer" onclick="document.getElementById('fileInput').click()"> |
| | <input type="file" id="fileInput" accept=".tex" class="hidden" onchange="handleFileSelect(event)"> |
| | <div class="mb-4"> |
| | <svg class="mx-auto h-20 w-20 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"></path> |
| | </svg> |
| | </div> |
| | <p class="text-lg font-semibold text-gray-700 mb-2">Drop your .tex file here</p> |
| | <p class="text-sm text-gray-500 mb-4">or click to browse</p> |
| | <div class="inline-block px-6 py-2 bg-purple-100 text-purple-700 rounded-lg font-medium"> |
| | Choose File |
| | </div> |
| | <p id="fileName" class="mt-6 text-base text-gray-700 font-semibold"></p> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div class="mt-8"> |
| | <button onclick="compileLatex()" id="compileBtn" class="w-full btn-primary text-white py-4 px-8 rounded-xl font-bold text-lg shadow-xl flex items-center justify-center"> |
| | <svg class="w-6 h-6 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path> |
| | </svg> |
| | Compile to PDF |
| | </button> |
| | </div> |
| |
|
| | |
| | <div id="statusArea" class="hidden mt-6 animate-fade-in"></div> |
| |
|
| | |
| | <div id="resultArea" class="hidden mt-6 animate-fade-in"></div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div class="grid md:grid-cols-3 gap-6 mt-12"> |
| | <div class="glass-effect rounded-xl p-6 text-center"> |
| | <div class="text-4xl mb-3">β‘</div> |
| | <h3 class="text-lg font-bold text-gray-800 mb-2">Lightning Fast</h3> |
| | <p class="text-sm text-gray-600">Compile your documents in seconds with optimized pdflatex</p> |
| | </div> |
| | <div class="glass-effect rounded-xl p-6 text-center"> |
| | <div class="text-4xl mb-3">π</div> |
| | <h3 class="text-lg font-bold text-gray-800 mb-2">Secure & Private</h3> |
| | <p class="text-sm text-gray-600">Your documents are processed securely and deleted immediately</p> |
| | </div> |
| | <div class="glass-effect rounded-xl p-6 text-center"> |
| | <div class="text-4xl mb-3">π¦</div> |
| | <h3 class="text-lg font-bold text-gray-800 mb-2">Full TeX Live</h3> |
| | <p class="text-sm text-gray-600">Access to complete LaTeX distribution with all packages</p> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div class="text-center mt-12 text-white text-sm opacity-80"> |
| | <p>Powered by pdflatex β’ Open Source β’ Free Forever</p> |
| | </div> |
| | </div> |
| |
|
| | <script> |
| | let selectedFile = null; |
| | |
| | function showTextarea() { |
| | document.getElementById('textareaSection').classList.remove('hidden'); |
| | document.getElementById('fileSection').classList.add('hidden'); |
| | document.getElementById('btnTextarea').className = 'flex-1 px-6 py-4 font-semibold transition tab-active'; |
| | document.getElementById('btnFile').className = 'flex-1 px-6 py-4 font-semibold transition tab-inactive'; |
| | selectedFile = null; |
| | } |
| | |
| | function showFileUpload() { |
| | document.getElementById('textareaSection').classList.add('hidden'); |
| | document.getElementById('fileSection').classList.remove('hidden'); |
| | document.getElementById('btnFile').className = 'flex-1 px-6 py-4 font-semibold transition tab-active'; |
| | document.getElementById('btnTextarea').className = 'flex-1 px-6 py-4 font-semibold transition tab-inactive'; |
| | } |
| | |
| | function clearCode() { |
| | document.getElementById('latexCode').value = ''; |
| | } |
| | |
| | function handleFileSelect(event) { |
| | selectedFile = event.target.files[0]; |
| | if (selectedFile) { |
| | document.getElementById('fileName').innerHTML = ` |
| | <span class="inline-flex items-center"> |
| | <svg class="w-5 h-5 mr-2 text-green-500" fill="currentColor" viewBox="0 0 20 20"> |
| | <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path> |
| | </svg> |
| | ${selectedFile.name} |
| | </span> |
| | `; |
| | } |
| | } |
| | |
| | function showStatus(message, type = 'info') { |
| | const statusArea = document.getElementById('statusArea'); |
| | statusArea.classList.remove('hidden'); |
| | |
| | const styles = { |
| | info: 'bg-blue-50 border-blue-200 text-blue-800', |
| | success: 'bg-green-50 border-green-200 text-green-800', |
| | error: 'bg-red-50 border-red-200 text-red-800', |
| | warning: 'bg-yellow-50 border-yellow-200 text-yellow-800' |
| | }; |
| | |
| | const icons = { |
| | info: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>', |
| | success: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>', |
| | error: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>', |
| | warning: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>' |
| | }; |
| | |
| | statusArea.innerHTML = ` |
| | <div class="border-2 ${styles[type]} rounded-xl p-4 flex items-start"> |
| | <svg class="w-6 h-6 mr-3 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| | ${icons[type]} |
| | </svg> |
| | <p class="font-medium flex-1">${message}</p> |
| | </div> |
| | `; |
| | } |
| | |
| | function showResult(pdfBlob) { |
| | const resultArea = document.getElementById('resultArea'); |
| | resultArea.classList.remove('hidden'); |
| | |
| | const url = URL.createObjectURL(pdfBlob); |
| | |
| | resultArea.innerHTML = ` |
| | <div class="bg-gradient-to-r from-green-50 to-emerald-50 border-2 border-green-200 rounded-xl p-8 text-center"> |
| | <div class="mb-6"> |
| | <svg class="mx-auto h-16 w-16 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path> |
| | </svg> |
| | </div> |
| | <h3 class="text-2xl font-bold text-green-800 mb-2">Success!</h3> |
| | <p class="text-green-700 mb-6">Your PDF has been compiled successfully</p> |
| | <div class="flex flex-wrap gap-4 justify-center"> |
| | <a href="${url}" download="compiled.pdf" class="inline-flex items-center px-8 py-3 bg-green-600 text-white rounded-lg hover:bg-green-700 transition font-semibold shadow-lg hover:shadow-xl"> |
| | <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path> |
| | </svg> |
| | Download PDF |
| | </a> |
| | <a href="${url}" target="_blank" class="inline-flex items-center px-8 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition font-semibold shadow-lg hover:shadow-xl"> |
| | <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path> |
| | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path> |
| | </svg> |
| | Preview |
| | </a> |
| | </div> |
| | </div> |
| | `; |
| | } |
| | |
| | function showError(errorMsg, log) { |
| | const resultArea = document.getElementById('resultArea'); |
| | resultArea.classList.remove('hidden'); |
| | |
| | resultArea.innerHTML = ` |
| | <div class="bg-red-50 border-2 border-red-200 rounded-xl p-8"> |
| | <div class="flex items-start mb-4"> |
| | <svg class="h-8 w-8 text-red-500 mr-3 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path> |
| | </svg> |
| | <div class="flex-1"> |
| | <h3 class="text-xl font-bold text-red-800 mb-2">Compilation Failed</h3> |
| | <p class="text-red-700 mb-4">${errorMsg}</p> |
| | ${log ? ` |
| | <details class="bg-white rounded-lg border-2 border-red-200 p-4 cursor-pointer hover:border-red-300 transition"> |
| | <summary class="font-semibold text-red-800 hover:text-red-900">View Error Log</summary> |
| | <pre class="mt-4 text-xs text-gray-700 overflow-x-auto whitespace-pre-wrap max-h-80 overflow-y-auto p-3 bg-gray-50 rounded">${log}</pre> |
| | </details> |
| | ` : ''} |
| | </div> |
| | </div> |
| | </div> |
| | `; |
| | } |
| | |
| | async function compileLatex() { |
| | const compileBtn = document.getElementById('compileBtn'); |
| | compileBtn.disabled = true; |
| | compileBtn.innerHTML = ` |
| | <svg class="animate-spin w-6 h-6 mr-3" fill="none" viewBox="0 0 24 24"> |
| | <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> |
| | <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> |
| | </svg> |
| | Compiling... |
| | `; |
| | |
| | showStatus('π Compiling your LaTeX document...', 'info'); |
| | document.getElementById('resultArea').classList.add('hidden'); |
| | |
| | try { |
| | let response; |
| | |
| | if (!document.getElementById('textareaSection').classList.contains('hidden')) { |
| | const code = document.getElementById('latexCode').value; |
| | |
| | if (!code.trim()) { |
| | showStatus('β οΈ Please enter some LaTeX code', 'warning'); |
| | compileBtn.disabled = false; |
| | compileBtn.innerHTML = '<svg class="w-6 h-6 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path></svg>Compile to PDF'; |
| | return; |
| | } |
| | |
| | response = await fetch('/compile', { |
| | method: 'POST', |
| | headers: { 'Content-Type': 'application/json' }, |
| | body: JSON.stringify({ code: code }) |
| | }); |
| | } else { |
| | if (!selectedFile) { |
| | showStatus('β οΈ Please select a .tex file', 'warning'); |
| | compileBtn.disabled = false; |
| | compileBtn.innerHTML = '<svg class="w-6 h-6 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path></svg>Compile to PDF'; |
| | return; |
| | } |
| | |
| | const formData = new FormData(); |
| | formData.append('file', selectedFile); |
| | response = await fetch('/compile', { method: 'POST', body: formData }); |
| | } |
| | |
| | if (response.ok) { |
| | const blob = await response.blob(); |
| | showStatus('β
Compilation successful!', 'success'); |
| | showResult(blob); |
| | } else { |
| | const error = await response.json(); |
| | showStatus('β Compilation failed', 'error'); |
| | showError(error.error, error.log); |
| | } |
| | } catch (error) { |
| | showStatus('β Network error', 'error'); |
| | showError('Failed to connect to server: ' + error.message); |
| | } finally { |
| | compileBtn.disabled = false; |
| | compileBtn.innerHTML = '<svg class="w-6 h-6 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path></svg>Compile to PDF'; |
| | } |
| | } |
| | </script> |
| | </body> |
| | </html> |