tex2lab / templates /base.html
Zunayedthebot's picture
Upload 132 files
3e6b063 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}TexLab - Professional LaTeX Converter{% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
<style>
:root {
--primary: #4361ee;
--primary-dark: #3a56d4;
--secondary: #7209b7;
--accent: #4cc9f0;
--success: #4caf50;
--dark: #212529;
--light: #f8f9fa;
--gray: #6c757d;
--border-radius: 12px;
--box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
--transition: all 0.3s ease;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
height: 100%;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background-color: #fafbff;
color: #333;
line-height: 1.6;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* Header & Navigation */
.navbar {
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
padding: 1rem 0;
box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1);
flex-shrink: 0;
}
.navbar-brand {
font-weight: 700;
font-size: 1.8rem;
color: white !important;
display: flex;
align-items: center;
}
.navbar-brand i {
margin-right: 10px;
}
.nav-link {
color: rgba(255, 255, 255, 0.85) !important;
font-weight: 500;
padding: 0.5rem 1rem !important;
border-radius: 50px;
transition: var(--transition);
margin: 0 5px;
}
.nav-link:hover, .nav-link.active {
color: white !important;
background: rgba(255, 255, 255, 0.15) !important;
}
.user-info {
display: flex;
align-items: center;
color: white;
font-weight: 500;
padding: 0.5rem 1rem;
border-radius: 50px;
background: rgba(255, 255, 255, 0.15);
margin-left: 1rem;
}
.user-info i {
margin-right: 0.5rem;
}
/* Main Content */
.main-content {
flex: 1;
padding: 2rem 0;
}
.page-header {
text-align: center;
margin-bottom: 2.5rem;
padding: 0 1rem;
}
.page-title {
font-size: 2.2rem;
font-weight: 800;
margin-bottom: 1rem;
color: var(--dark);
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.page-subtitle {
font-size: 1.1rem;
color: var(--gray);
max-width: 700px;
margin: 0 auto;
}
/* Cards */
.feature-card {
background: white;
border-radius: var(--border-radius);
padding: 2rem;
box-shadow: var(--box-shadow);
transition: var(--transition);
height: 100%;
border: 1px solid rgba(0, 0, 0, 0.05);
}
.feature-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.12);
}
.feature-icon {
width: 70px;
height: 70px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 1.5rem;
font-size: 1.8rem;
}
.feature-title {
font-size: 1.4rem;
font-weight: 700;
margin-bottom: 1rem;
color: var(--dark);
}
.feature-description {
color: var(--gray);
margin-bottom: 1.5rem;
font-size: 0.95rem;
}
/* Buttons */
.btn-primary-gradient {
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
color: white;
border: none;
padding: 10px 22px;
border-radius: 50px;
font-weight: 600;
transition: var(--transition);
font-size: 0.95rem;
box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
}
.btn-primary-gradient:hover {
transform: translateY(-3px);
box-shadow: 0 7px 14px rgba(50, 50, 93, 0.1), 0 3px 6px rgba(0, 0, 0, 0.08);
}
.btn-secondary-gradient {
background: linear-gradient(135deg, #f72585 0%, #b5179e 100%);
color: white;
border: none;
padding: 10px 22px;
border-radius: 50px;
font-weight: 600;
transition: var(--transition);
font-size: 0.95rem;
box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
}
.btn-secondary-gradient:hover {
transform: translateY(-3px);
box-shadow: 0 7px 14px rgba(50, 50, 93, 0.1), 0 3px 6px rgba(0, 0, 0, 0.08);
}
.btn-accent-gradient {
background: linear-gradient(135deg, var(--accent) 0%, #4895ef 100%);
color: white;
border: none;
padding: 10px 22px;
border-radius: 50px;
font-weight: 600;
transition: var(--transition);
font-size: 0.95rem;
box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
}
.btn-accent-gradient:hover {
transform: translateY(-3px);
box-shadow: 0 7px 14px rgba(50, 50, 93, 0.1), 0 3px 6px rgba(0, 0, 0, 0.08);
}
.btn-dark-gradient {
background: linear-gradient(135deg, #3a0ca3 0%, #250070 100%);
color: white;
border: none;
padding: 10px 22px;
border-radius: 50px;
font-weight: 600;
transition: var(--transition);
font-size: 0.95rem;
box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
}
.btn-dark-gradient:hover {
transform: translateY(-3px);
box-shadow: 0 7px 14px rgba(50, 50, 93, 0.1), 0 3px 6px rgba(0, 0, 0, 0.08);
}
/* Notification */
.notification {
position: fixed;
top: 20px;
right: 20px;
padding: 15px 25px;
border-radius: 10px;
background: var(--success);
color: white;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
transform: translateX(200%);
transition: transform 0.3s ease;
z-index: 1000;
display: flex;
align-items: center;
}
.notification.show {
transform: translateX(0);
}
/* Footer */
.footer {
background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
color: #cbd5e1;
padding: 3rem 0 2rem;
flex-shrink: 0;
margin-top: auto;
}
.footer-content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem;
margin-bottom: 2rem;
}
.footer-column h4 {
color: white;
font-size: 1.2rem;
margin-bottom: 1.5rem;
position: relative;
padding-bottom: 0.5rem;
}
.footer-column h4::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 50px;
height: 3px;
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
border-radius: 3px;
}
.footer-column p {
margin-bottom: 1rem;
line-height: 1.7;
}
.footer-links {
list-style: none;
padding: 0;
}
.footer-links li {
margin-bottom: 0.8rem;
}
.footer-links a {
color: #94a3b8;
text-decoration: none;
transition: var(--transition);
display: flex;
align-items: center;
}
.footer-links a:hover {
color: white;
transform: translateX(5px);
}
.footer-links a i {
margin-right: 10px;
font-size: 0.8rem;
}
.social-links {
display: flex;
gap: 1rem;
margin-top: 1rem;
}
.social-link {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.05);
color: white;
transition: var(--transition);
}
.social-link:hover {
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
transform: translateY(-3px);
}
.copyright {
border-top: 1px solid rgba(255, 255, 255, 0.1);
padding-top: 2rem;
text-align: center;
color: #94a3b8;
font-size: 0.9rem;
}
.copyright a {
color: #94a3b8;
text-decoration: none;
}
.copyright a:hover {
color: white;
}
/* Chat Panel */
.chat-toggle {
position: fixed;
bottom: 30px;
right: 30px;
width: 60px;
height: 60px;
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
color: white;
border: none;
border-radius: 50%;
cursor: pointer;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
z-index: 999;
transition: var(--transition);
}
.chat-toggle:hover {
transform: scale(1.1);
}
/* LaTeX to Text Toggle Button */
#latexToTextToggle {
bottom: 100px;
background: linear-gradient(135deg, #f72585 0%, #b5179e 100%);
}
.chat-panel {
position: fixed;
bottom: 100px;
right: 30px;
width: 380px;
height: 500px;
background: white;
border-radius: var(--border-radius);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
display: flex;
flex-direction: column;
z-index: 1000;
overflow: hidden;
transform: translateY(20px);
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
}
.chat-panel.active {
transform: translateY(0);
opacity: 1;
visibility: visible;
}
.chat-header {
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
color: white;
padding: 1rem 1.5rem;
font-weight: 600;
display: flex;
justify-content: space-between;
align-items: center;
}
.chat-close {
background: none;
border: none;
color: white;
font-size: 1.2rem;
cursor: pointer;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: background 0.2s;
}
.chat-close:hover {
background: rgba(255, 255, 255, 0.2);
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 1rem;
display: flex;
flex-direction: column;
gap: 12px;
background: #f8f9fa;
}
.message {
max-width: 85%;
padding: 12px 16px;
border-radius: 18px;
position: relative;
font-size: 0.9rem;
line-height: 1.5;
}
.user-message {
align-self: flex-end;
background: var(--primary);
color: white;
border-bottom-right-radius: 5px;
}
.assistant-message {
align-self: flex-start;
background: white;
color: var(--dark);
border: 1px solid #e9ecef;
border-bottom-left-radius: 5px;
}
.message-info {
font-size: 0.7rem;
opacity: 0.7;
margin-top: 5px;
text-align: right;
}
.chat-input {
display: flex;
padding: 1rem;
background: white;
border-top: 1px solid #e9ecef;
gap: 10px;
}
.chat-input textarea {
flex: 1;
padding: 12px 15px;
border: 1px solid #dee2e6;
border-radius: 50px;
resize: none;
font-family: inherit;
height: 44px;
font-size: 0.9rem;
}
.chat-input textarea:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(67, 97, 238, 0.1);
}
.chat-send {
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
color: white;
border: none;
border-radius: 50%;
width: 44px;
height: 44px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.2s;
}
.chat-send:hover {
transform: scale(1.05);
}
.chat-send:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.loading {
display: none;
align-self: flex-start;
background: white;
color: var(--dark);
padding: 12px 16px;
border-radius: 18px;
border: 1px solid #e9ecef;
border-bottom-left-radius: 5px;
font-size: 0.9rem;
}
.loading-dots {
display: flex;
gap: 4px;
}
.loading-dot {
width: 8px;
height: 8px;
background: var(--primary);
border-radius: 50%;
animation: bounce 1.5s infinite;
}
.loading-dot:nth-child(2) {
animation-delay: 0.2s;
}
.loading-dot:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-5px); }
}
.welcome-message {
text-align: center;
padding: 20px;
color: var(--gray);
font-size: 0.9rem;
}
.welcome-message h6 {
color: var(--dark);
margin-bottom: 10px;
font-size: 1.1rem;
}
/* Responsive */
@media (max-width: 992px) {
.page-title {
font-size: 2rem;
}
.page-subtitle {
font-size: 1rem;
}
}
@media (max-width: 768px) {
.main-content {
padding: 1.5rem 0;
}
.page-header {
margin-bottom: 2rem;
}
.page-title {
font-size: 1.8rem;
}
.feature-card {
padding: 1.5rem;
}
.chat-panel {
width: calc(100% - 40px);
height: 70vh;
right: 20px;
bottom: 90px;
}
.navbar-nav {
text-align: center;
margin-top: 1rem;
}
.nav-link {
margin: 5px 0;
}
.footer-content {
grid-template-columns: 1fr;
gap: 1.5rem;
}
}
@media (max-width: 576px) {
.page-title {
font-size: 1.6rem;
}
.feature-icon {
width: 60px;
height: 60px;
font-size: 1.5rem;
}
.feature-title {
font-size: 1.2rem;
}
}
/* Neutral Button */
.btn-neutral {
background: #f8f9fa;
color: #333;
border: 1px solid #ddd;
padding: 12px 25px;
border-radius: 50px;
font-weight: 600;
transition: var(--transition);
font-size: 1rem;
cursor: pointer;
box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
}
.btn-neutral:hover {
background: #e9ecef;
transform: translateY(-2px);
box-shadow: 0 7px 14px rgba(50, 50, 93, 0.1), 0 3px 6px rgba(0, 0, 0, 0.08);
}
/* Buttons for LaTeX to Text panel */
#convertLatexBtn, #copyTextBtn {
border: none;
border-radius: 6px;
font-weight: 600;
transition: var(--transition);
cursor: pointer;
box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
}
#convertLatexBtn {
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
color: white;
}
#convertLatexBtn:hover {
transform: translateY(-2px);
box-shadow: 0 7px 14px rgba(50, 50, 93, 0.1), 0 3px 6px rgba(0, 0, 0, 0.08);
}
#copyTextBtn:hover {
background: #e9ecef;
transform: translateY(-2px);
box-shadow: 0 7px 14px rgba(50, 50, 93, 0.1), 0 3px 6px rgba(0, 0, 0, 0.08);
}
</style>
{% block extra_css %}{% endblock %}
</head>
<body>
{% if request.endpoint != 'auth.login' %}
<nav class="navbar navbar-expand-lg navbar-dark">
<div class="container">
<a class="navbar-brand" href="/">
<i class="fas fa-calculator"></i>
<span>TexLab</span>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto">
<li class="nav-item">
<a class="nav-link" href="/math">Math</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/scribble">Scribble</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/camera">Camera</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/table">Table</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/pdffly">PDFly</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/graph">Graph</a>
</li>
</ul>
<div class="d-flex align-items-center">
<div id="authContainer">
<!-- Auth content will be loaded here by JavaScript -->
</div>
</div>
</div>
</div>
</nav>
{% endif %}
<div class="container main-content">
{% block content %}{% endblock %}
</div>
{% if request.endpoint != 'auth.login' %}
{% if request.path == '/' %}
<footer class="site-footer">
<!-- Footer -->
<footer class="footer" style="width: 100%; background: #1e293b; color: #fff; padding: 2rem 1rem;">
<div class="container">
<div class="footer-content">
<div class="footer-column">
<h4>About TexLab</h4>
<p>
TexLab is a cutting-edge platform that transforms handwritten mathematical expressions,
scribbles, tables, and camera captures into clean, editable LaTeX code using advanced AI models.
</p>
<div class="social-links">
<a href="#" class="social-link"><i class="fab fa-twitter"></i></a>
<a href="#" class="social-link"><i class="fab fa-facebook-f"></i></a>
<a href="#" class="social-link"><i class="fab fa-linkedin-in"></i></a>
<a href="#" class="social-link"><i class="fab fa-github"></i></a>
</div>
</div>
<div class="footer-column">
<h4>Quick Links</h4>
<ul class="footer-links">
<li><a href="/"><i class="fas fa-chevron-right"></i> Home</a></li>
<li><a href="/math"><i class="fas fa-chevron-right"></i> Math Equations</a></li>
<li><a href="/scribble"><i class="fas fa-chevron-right"></i> Scribble Converter</a></li>
<li><a href="/camera"><i class="fas fa-chevron-right"></i> Camera Capture</a></li>
<li><a href="/table"><i class="fas fa-chevron-right"></i> Table Detection</a></li>
</ul>
</div>
<div class="footer-column">
<h4>Resources</h4>
<ul class="footer-links">
<li><a href="/help"><i class="fas fa-chevron-right"></i> Documentation</a></li>
<li><a href="/tutorials"><i class="fas fa-chevron-right"></i> Tutorials</a></li>
<li><a href="/api"><i class="fas fa-chevron-right"></i> API Reference</a></li>
<li><a href="/blog"><i class="fas fa-chevron-right"></i> Blog</a></li>
<li><a href="/support"><i class="fas fa-chevron-right"></i> Support Center</a></li>
</ul>
</div>
<div class="footer-column">
<h4>Legal</h4>
<ul class="footer-links">
<li><a href="/privacy"><i class="fas fa-chevron-right"></i> Privacy Policy</a></li>
<li><a href="/terms"><i class="fas fa-chevron-right"></i> Terms of Service</a></li>
<li><a href="/cookies"><i class="fas fa-chevron-right"></i> Cookie Policy</a></li>
<li><a href="/gdpr"><i class="fas fa-chevron-right"></i> GDPR Compliance</a></li>
<li><a href="/contact"><i class="fas fa-chevron-right"></i> Contact Us</a></li>
</ul>
</div>
</div>
<div class="copyright">
<p>&copy; 2025 TexLab. All rights reserved. Transforming handwritten content into beautiful LaTeX since 2025.</p>
<p>Designed with <i class="fas fa-heart" style="color: #f72585;"></i> for mathematicians, scientists, and educators worldwide.</p>
</div>
</div>
</footer>
</footer>
{% endif %}
{% endif %}
{% if request.endpoint != 'auth.login' %}
<!-- Chat Toggle Button -->
<button class="chat-toggle" id="chatToggle">
<i class="fas fa-robot"></i>
</button>
<!-- LaTeX to Text Toggle Button -->
<button class="chat-toggle" id="latexToTextToggle" style="bottom: 100px;">
<i class="fas fa-exchange-alt"></i>
</button>
<!-- Chat Panel -->
<div class="chat-panel" id="chatPanel">
<div class="chat-header">
<h5><i class="fas fa-robot me-2"></i>TexLab Assistant</h5>
<button class="chat-close" id="chatClose">
<i class="fas fa-times"></i>
</button>
</div>
<div class="chat-messages" id="chatMessages">
<div class="welcome-message">
<h6>Welcome to TexLab Assistant!</h6>
<p>I can help you with LaTeX, mathematics, and document conversion.</p>
<p>Try asking: "How do I write a fraction in LaTeX?"</p>
</div>
</div>
<div class="chat-input">
<textarea id="messageInput" placeholder="Type your message... (Enter to send)"></textarea>
<button class="chat-send" id="sendButton" disabled>
<i class="fas fa-paper-plane"></i>
</button>
</div>
</div>
<!-- LaTeX to Text Panel -->
<div class="chat-panel" id="latexToTextPanel" style="bottom: 140px;">
<div class="chat-header">
<h5><i class="fas fa-exchange-alt me-2"></i>LaTeX to Text Converter</h5>
<button class="chat-close" id="latexToTextClose">
<i class="fas fa-times"></i>
</button>
</div>
<div class="chat-messages" id="latexToTextMessages">
<div class="welcome-message">
<h6>Convert LaTeX to Readable Text</h6>
<p>Enter LaTeX code below to convert it to human-readable text.</p>
</div>
</div>
<div style="padding: 1rem; border-top: 1px solid #e9ecef;">
<textarea id="latexInput" placeholder="Enter LaTeX code..." style="width: 100%; height: 80px; padding: 10px; border-radius: 8px; border: 1px solid #ddd; resize: vertical; font-family: 'Consolas', monospace;"></textarea>
<div style="display: flex; gap: 10px; margin-top: 10px;">
<button class="btn btn-primary-gradient" id="convertLatexBtn" style="flex: 1; padding: 8px; font-size: 0.9rem;">
<i class="fas fa-exchange-alt me-1"></i>Convert
</button>
<button class="btn btn-neutral" id="copyTextBtn" style="flex: 1; padding: 8px; font-size: 0.9rem;">
<i class="fas fa-copy me-1"></i>Copy Text
</button>
</div>
<div style="margin-top: 15px;">
<h6 style="margin-bottom: 8px; color: #4361ee;">Converted Text:</h6>
<div id="convertedTextOutput" style="background: white; border: 1px solid #e9ecef; border-radius: 8px; padding: 12px; min-height: 60px; font-size: 0.95rem;">
Your converted text will appear here...
</div>
</div>
</div>
</div>
<div class="notification" id="notification">
<i class="fas fa-check-circle me-2"></i>LaTeX code copied to clipboard!
</div>
{% endif %}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
{% block extra_js %}{% endblock %}
<script>
// Check if we're on the login page
const isLoginPage = document.querySelector('.login-container') !== null;
if (!isLoginPage) {
// Load user authentication status
document.addEventListener('DOMContentLoaded', function() {
fetch('/auth/user')
.then(response => response.json())
.then(data => {
const authContainer = document.getElementById('authContainer');
if (data.authenticated) {
// Show user info with appropriate icon
if (data.user.email && data.user.email.includes('@')) {
// Google user
authContainer.innerHTML = `
<div class="user-info">
<i class="fab fa-google"></i>
<span>${data.user.name}</span>
</div>
<a href="/auth/logout" class="btn btn-signin ms-2">
<i class="fas fa-sign-out-alt me-1"></i>Sign Out
</a>
`;
} else {
// Guest user
authContainer.innerHTML = `
<div class="user-info">
<i class="fas fa-user"></i>
<span>Guest: ${data.user.name}</span>
</div>
<a href="/auth/logout" class="btn btn-signin ms-2">
<i class="fas fa-sign-out-alt me-1"></i>Sign Out
</a>
`;
}
} else {
authContainer.innerHTML = `
<a href="/auth/login" class="btn btn-signin">
<i class="fas fa-sign-in-alt me-1"></i>Sign In
</a>
`;
}
})
.catch(error => {
console.error('Error checking auth status:', error);
// Fallback to show sign in button
document.getElementById('authContainer').innerHTML = `
<a href="/auth/login" class="btn btn-signin">
<i class="fas fa-sign-in-alt me-1"></i>Sign In
</a>
`;
});
});
// Chat panel functionality
const chatToggle = document.getElementById('chatToggle');
const chatPanel = document.getElementById('chatPanel');
const chatClose = document.getElementById('chatClose');
const chatMessages = document.getElementById('chatMessages');
const messageInput = document.getElementById('messageInput');
const sendButton = document.getElementById('sendButton');
// Toggle chat panel
chatToggle.addEventListener('click', () => {
chatPanel.classList.toggle('active');
});
// Close chat panel
chatClose.addEventListener('click', () => {
chatPanel.classList.remove('active');
});
// Auto-resize textarea
messageInput.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = (this.scrollHeight > 120 ? 120 : this.scrollHeight) + 'px';
});
// Enable send button when there's text
messageInput.addEventListener('input', function() {
sendButton.disabled = !this.value.trim();
});
// Send message on Enter key
messageInput.addEventListener('keydown', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
if (!sendButton.disabled) {
sendMessage();
}
}
});
// Send button click
sendButton.addEventListener('click', sendMessage);
// Generate a simple session ID
const sessionId = 'session_' + Date.now();
// Send message function
async function sendMessage() {
const message = messageInput.value.trim();
if (!message) return;
// Clear input and disable button
messageInput.value = '';
messageInput.style.height = '44px';
sendButton.disabled = true;
// Add user message to chat
addMessageToChat(message, 'user');
// Show loading indicator
const loadingElement = document.createElement('div');
loadingElement.className = 'loading';
loadingElement.id = 'loadingIndicator';
loadingElement.innerHTML = `
<div class="loading-dots">
<div class="loading-dot"></div>
<div class="loading-dot"></div>
<div class="loading-dot"></div>
</div>
`;
chatMessages.appendChild(loadingElement);
chatMessages.scrollTop = chatMessages.scrollHeight;
try {
// Simulate AI response (in a real implementation, you would call your AI model)
// For now, we'll generate a simple response based on common LaTeX questions
setTimeout(() => {
// Remove loading indicator
document.getElementById('loadingIndicator').remove();
// Generate response based on user input
const response = generateResponse(message);
// Add assistant response to chat
addMessageToChat(response, 'assistant');
}, 1000);
} catch (error) {
// Remove loading indicator
document.getElementById('loadingIndicator').remove();
console.error('Error:', error);
addMessageToChat('Sorry, I encountered an error. Please try again.', 'assistant');
}
}
// Generate a response based on user input
function generateResponse(userMessage) {
const lowerMessage = userMessage.toLowerCase();
if (lowerMessage.includes('fraction') || lowerMessage.includes('frac')) {
return 'To write a fraction in LaTeX, use \\\\frac{numerator}{denominator}. For example: \\\\frac{1}{2} produces ½.';
} else if (lowerMessage.includes('integral') || lowerMessage.includes('int')) {
return 'To write an integral in LaTeX, use \\\\int. For example: \\\\int_0^1 x^2 dx. For definite integrals, specify limits with _ and ^.';
} else if (lowerMessage.includes('sum') || lowerMessage.includes('sigma')) {
return 'To write a summation in LaTeX, use \\\\sum. For example: \\\\sum_{i=1}^{n} i. Use _ for lower limit and ^ for upper limit.';
} else if (lowerMessage.includes('limit') || lowerMessage.includes('lim')) {
return 'To write a limit in LaTeX, use \\\\lim. For example: \\\\lim_{x \\\\to 0} \\\\frac{\\\\sin x}{x} = 1.';
} else if (lowerMessage.includes('matrix') || lowerMessage.includes('array')) {
return 'To create a matrix in LaTeX, use \\\\begin{matrix} ... \\\\end{matrix}. For example:\\n\\\\begin{matrix}\\na & b \\\\\\nc & d\\n\\\\end{matrix}';
} else if (lowerMessage.includes('table') || lowerMessage.includes('tabular')) {
return 'To create a table in LaTeX, use the tabular environment. For example:\\n\\\\begin{tabular}{|c|c|}\\n\\\\hline\\nColumn 1 & Column 2 \\\\\\n\\\\hline\\nItem 1 & Item 2 \\\\\\n\\\\hline\\n\\\\end{tabular}';
} else if (lowerMessage.includes('equation') || lowerMessage.includes('align')) {
return 'To write equations in LaTeX, you can use:\\n- Inline: $E = mc^2$\\n- Display: $$E = mc^2$$\\n- Aligned: \\\\begin{align} x &= y \\\\ y &= z \\\\end{align}';
} else if (lowerMessage.includes('texlab') || lowerMessage.includes('help')) {
return "I'm the TexLab Assistant! I can help you with:\\n- LaTeX syntax and commands\\n- Mathematical notation\\n- Document conversion tips\\n- Using TexLab features\\n\\nJust ask me any LaTeX or math question!";
} else {
// Default response
return "I'm the TexLab Assistant. I can help you with LaTeX syntax, mathematical notation, and document conversion. Try asking me something like 'How do I write a fraction in LaTeX?' or 'How do I create a matrix?'";
}
}
// Add message to chat UI
function addMessageToChat(content, sender) {
// Remove welcome message if it exists
const welcomeMessage = chatMessages.querySelector('.welcome-message');
if (welcomeMessage) {
welcomeMessage.remove();
}
const messageElement = document.createElement('div');
messageElement.className = `message ${sender}-message`;
const time = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
messageElement.innerHTML = `
<div>${content}</div>
<div class="message-info">${sender === 'user' ? 'You' : 'Assistant'}${time}</div>
`;
chatMessages.appendChild(messageElement);
chatMessages.scrollTop = chatMessages.scrollHeight;
// Re-enable send button
sendButton.disabled = false;
}
// Global notification function
function showNotification(message) {
const notification = document.getElementById("notification");
if (notification) {
notification.innerHTML = `<i class="fas fa-check-circle me-2"></i>${message}`;
notification.classList.add('show');
setTimeout(() => {
notification.classList.remove('show');
}, 3000);
}
}
// LaTeX to Text functionality
const latexToTextToggle = document.getElementById('latexToTextToggle');
const latexToTextPanel = document.getElementById('latexToTextPanel');
const latexToTextClose = document.getElementById('latexToTextClose');
const latexInput = document.getElementById('latexInput');
const convertLatexBtn = document.getElementById('convertLatexBtn');
const copyTextBtn = document.getElementById('copyTextBtn');
const convertedTextOutput = document.getElementById('convertedTextOutput');
// Toggle LaTeX to Text panel
latexToTextToggle.addEventListener('click', () => {
latexToTextPanel.classList.toggle('active');
// Close chat panel if it's open
chatPanel.classList.remove('active');
});
// Close LaTeX to Text panel
latexToTextClose.addEventListener('click', () => {
latexToTextPanel.classList.remove('active');
});
// Convert LaTeX to text function
function convertLatexToText(latexStr) {
if (!latexStr) return "";
// Make a copy to work with
let text = latexStr;
// Remove common LaTeX commands
text = text.replace(/\\begin\{.*?\}/g, '');
text = text.replace(/\\end\{.*?\}/g, '');
// Replace common math symbols with readable text
const replacements = {
'\\\\alpha': 'alpha',
'\\\\beta': 'beta',
'\\\\gamma': 'gamma',
'\\\\delta': 'delta',
'\\\\epsilon': 'epsilon',
'\\\\theta': 'theta',
'\\\\lambda': 'lambda',
'\\\\mu': 'mu',
'\\\\pi': 'pi',
'\\\\sigma': 'sigma',
'\\\\phi': 'phi',
'\\\\omega': 'omega',
'\\\\infty': 'infinity',
'\\\\int': 'integral',
'\\\\sum': 'sum',
'\\\\prod': 'product',
'\\\\lim': 'limit',
'\\\\sqrt': 'square root',
'\\\\frac': 'fraction',
'\\\\times': 'times',
'\\\\cdot': 'cdot',
'\\\\div': 'divided by',
'\\\\pm': 'plus or minus',
'\\\\mp': 'minus or plus',
'\\\\leq': 'less than or equal to',
'\\\\geq': 'greater than or equal to',
'\\\\neq': 'not equal to',
'\\\\approx': 'approximately',
'\\\\equiv': 'equivalent to',
'\\\\subset': 'subset of',
'\\\\subseteq': 'subset or equal to',
'\\\\cup': 'union',
'\\\\cap': 'intersection',
'\\\\in': 'element of',
'\\\\notin': 'not element of',
'\\\\forall': 'for all',
'\\\\exists': 'there exists',
'\\\\nexists': 'there does not exist',
'\\\\emptyset': 'empty set',
'\\\\varnothing': 'empty set',
'\\\\mathbb{R}': 'real numbers',
'\\\\mathbb{N}': 'natural numbers',
'\\\\mathbb{Z}': 'integers',
'\\\\mathbb{Q}': 'rational numbers',
'\\\\mathbb{C}': 'complex numbers',
'\\\\to': 'to',
'\\\\rightarrow': 'rightarrow',
'\\\\left': '',
'\\\\right': '',
'\\\\big': '',
'\\\\Big': '',
'\\\\bigg': '',
'\\\\Bigg': '',
};
for (const [pattern, replacement] of Object.entries(replacements)) {
const regex = new RegExp(pattern, 'g');
text = text.replace(regex, replacement);
}
// Handle fractions specifically: \frac{a}{b} -> (a over b)
text = text.replace(/\\frac\{([^}]*)\}\{([^}]*)\}/g, '($1 over $2)');
// Handle subscripts: _{text} -> _text or _a -> _a
text = text.replace(/_\{([^}]*)\}/g, '_$1');
// Handle superscripts: ^{text} -> ^text or ^a -> ^a
text = text.replace(/\^\{([^}]*)\}/g, '^$1');
// Handle square roots: \sqrt{text} -> square root of (text)
text = text.replace(/\\sqrt\{([^}]*)\}/g, 'square root of ($1)');
// Remove remaining braces
text = text.replace(/\{/g, '(').replace(/\}/g, ')');
// Clean up extra spaces
text = text.replace(/\s+/g, ' ').trim();
return text;
}
// Convert button click handler
convertLatexBtn.addEventListener('click', () => {
const latexCode = latexInput.value.trim();
if (!latexCode) {
convertedTextOutput.textContent = 'Please enter some LaTeX code to convert.';
return;
}
try {
const convertedText = convertLatexToText(latexCode);
convertedTextOutput.textContent = convertedText;
} catch (error) {
console.error('Conversion error:', error);
convertedTextOutput.textContent = 'An error occurred during conversion. Please try again.';
}
});
// Copy text button click handler
copyTextBtn.addEventListener('click', () => {
const text = convertedTextOutput.textContent;
if (!text || text === 'Your converted text will appear here...') {
showNotification('Nothing to copy!');
return;
}
navigator.clipboard.writeText(text).then(() => {
showNotification('Text copied to clipboard!');
}).catch(err => {
console.error('Failed to copy:', err);
showNotification('Failed to copy text. Please try again.');
});
});
}
</script>
</body>
</html>