texcomp / index.html
Samuel
update
18e1c52
<!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">
<!-- Animated Background Pattern -->
<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">
<!-- Header -->
<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>
<!-- Main Card -->
<div class="glass-effect rounded-2xl shadow-2xl overflow-hidden">
<!-- Tab Navigation -->
<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">
<!-- Code Editor Section -->
<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>
<!-- File Upload Section -->
<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>
<!-- Compile Button -->
<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>
<!-- Status Messages -->
<div id="statusArea" class="hidden mt-6 animate-fade-in"></div>
<!-- Results -->
<div id="resultArea" class="hidden mt-6 animate-fade-in"></div>
</div>
</div>
<!-- Features Section -->
<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>
<!-- Footer -->
<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>