Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
|
@@ -5,6 +5,7 @@ import base64
|
|
| 5 |
from pathlib import Path
|
| 6 |
import os
|
| 7 |
import shutil
|
|
|
|
| 8 |
|
| 9 |
# Set page configuration
|
| 10 |
st.set_page_config(page_title="LaTeX Editor & Compiler", page_icon="📝", layout="wide")
|
|
@@ -21,8 +22,6 @@ def latex_to_pdf(latex_code):
|
|
| 21 |
st.code(f"PATH: {os.environ.get('PATH')}")
|
| 22 |
result = subprocess.run(["which", "pdflatex"], capture_output=True, text=True)
|
| 23 |
st.code(f"which pdflatex: {result.stdout} {result.stderr}")
|
| 24 |
-
result = subprocess.run(["ls", "-la", "/usr/bin/pdflatex"], capture_output=True, text=True)
|
| 25 |
-
st.code(f"ls -la /usr/bin/pdflatex: {result.stdout} {result.stderr}")
|
| 26 |
return None, "", "Error: pdflatex is not installed or not in PATH."
|
| 27 |
|
| 28 |
with tempfile.TemporaryDirectory() as temp_dir:
|
|
@@ -52,24 +51,30 @@ def latex_to_pdf(latex_code):
|
|
| 52 |
except Exception as e:
|
| 53 |
return None, "", str(e)
|
| 54 |
|
| 55 |
-
# Function to display PDF
|
| 56 |
-
def display_pdf(pdf_data):
|
| 57 |
-
base64_pdf = base64.b64encode(pdf_data).decode('utf-8')
|
| 58 |
-
pdf_display = f"""
|
| 59 |
-
<iframe
|
| 60 |
-
src="data:application/pdf;base64,{base64_pdf}"
|
| 61 |
-
width="100%"
|
| 62 |
-
height="600"
|
| 63 |
-
style="border: none;"
|
| 64 |
-
></iframe>
|
| 65 |
-
"""
|
| 66 |
-
st.markdown(pdf_display, unsafe_allow_html=True)
|
| 67 |
-
|
| 68 |
# Function to create download link for PDF
|
| 69 |
def get_download_link(pdf_data, filename="document.pdf"):
|
| 70 |
b64_pdf = base64.b64encode(pdf_data).decode()
|
| 71 |
return f'<a href="data:application/pdf;base64,{b64_pdf}" download="{filename}" class="download-button">Download PDF</a>'
|
| 72 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
# LaTeX package reference
|
| 74 |
latex_packages = {
|
| 75 |
"Document": {
|
|
@@ -262,18 +267,21 @@ st.markdown("""
|
|
| 262 |
}
|
| 263 |
.download-button {
|
| 264 |
display: inline-block;
|
| 265 |
-
padding: 0.
|
| 266 |
background-color: #4CAF50;
|
| 267 |
color: white !important;
|
| 268 |
text-align: center;
|
| 269 |
text-decoration: none;
|
| 270 |
-
font-size:
|
| 271 |
border-radius: 4px;
|
| 272 |
transition: background-color 0.3s;
|
| 273 |
margin-top: 10px;
|
|
|
|
|
|
|
| 274 |
}
|
| 275 |
.download-button:hover {
|
| 276 |
background-color: #45a049;
|
|
|
|
| 277 |
}
|
| 278 |
.stTextArea textarea {
|
| 279 |
font-family: 'Courier New', Courier, monospace !important;
|
|
@@ -302,6 +310,29 @@ st.markdown("""
|
|
| 302 |
</style>
|
| 303 |
""", unsafe_allow_html=True)
|
| 304 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 305 |
# Main application
|
| 306 |
def main():
|
| 307 |
st.title("LaTeX Editor & PDF Compiler")
|
|
@@ -310,8 +341,13 @@ def main():
|
|
| 310 |
if not is_pdflatex_installed():
|
| 311 |
st.warning("⚠️ LaTeX is not installed correctly. The PDF compilation feature will not work.")
|
| 312 |
st.info("For Hugging Face Spaces, make sure you have a packages.txt file with the necessary LaTeX packages.")
|
| 313 |
-
|
| 314 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 315 |
|
| 316 |
# Create layout with sidebar
|
| 317 |
col1, col2 = st.columns([3, 2])
|
|
@@ -350,19 +386,17 @@ def main():
|
|
| 350 |
st.rerun()
|
| 351 |
|
| 352 |
with col2:
|
| 353 |
-
st.subheader("PDF
|
| 354 |
|
| 355 |
-
# PDF
|
| 356 |
if 'compile_clicked' in st.session_state and st.session_state.compile_clicked:
|
| 357 |
with st.spinner("Compiling LaTeX to PDF..."):
|
| 358 |
pdf_data, stdout, stderr = latex_to_pdf(latex_code)
|
| 359 |
|
| 360 |
if pdf_data:
|
| 361 |
st.session_state.pdf_data = pdf_data
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
# Download button
|
| 365 |
-
st.markdown(get_download_link(pdf_data), unsafe_allow_html=True)
|
| 366 |
st.session_state.compile_clicked = False
|
| 367 |
else:
|
| 368 |
st.error("Compilation Error")
|
|
@@ -373,10 +407,9 @@ def main():
|
|
| 373 |
|
| 374 |
# Display previous PDF if available
|
| 375 |
elif 'pdf_data' in st.session_state and st.session_state.pdf_data:
|
| 376 |
-
|
| 377 |
-
st.markdown(get_download_link(st.session_state.pdf_data), unsafe_allow_html=True)
|
| 378 |
else:
|
| 379 |
-
st.info("Compile your LaTeX document to
|
| 380 |
|
| 381 |
# LaTeX Reference Sidebar
|
| 382 |
st.sidebar.title("LaTeX Reference")
|
|
@@ -397,7 +430,12 @@ def main():
|
|
| 397 |
found = True
|
| 398 |
with st.sidebar.expander(f"{category} ({len(filtered_commands)} results)"):
|
| 399 |
for cmd, desc in filtered_commands.items():
|
| 400 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 401 |
|
| 402 |
# Search packages
|
| 403 |
for category, packages in latex_packages.items():
|
|
@@ -408,7 +446,11 @@ def main():
|
|
| 408 |
found = True
|
| 409 |
with st.sidebar.expander(f"Packages: {category} ({len(filtered_packages)} results)"):
|
| 410 |
for pkg, desc in filtered_packages.items():
|
| 411 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 412 |
|
| 413 |
if not found:
|
| 414 |
st.sidebar.info("No matching commands or packages found")
|
|
@@ -422,12 +464,18 @@ def main():
|
|
| 422 |
with st.expander(category):
|
| 423 |
for cmd, desc in commands.items():
|
| 424 |
st.markdown(f"<div class='latex-command'>{cmd}</div> - {desc}", unsafe_allow_html=True)
|
|
|
|
|
|
|
|
|
|
| 425 |
|
| 426 |
with tabs[1]:
|
| 427 |
for category, packages in latex_packages.items():
|
| 428 |
with st.expander(category):
|
| 429 |
for pkg, desc in packages.items():
|
| 430 |
st.markdown(f"<div class='latex-command'>{pkg}</div> - {desc}", unsafe_allow_html=True)
|
|
|
|
|
|
|
|
|
|
| 431 |
|
| 432 |
if __name__ == "__main__":
|
| 433 |
main()
|
|
|
|
| 5 |
from pathlib import Path
|
| 6 |
import os
|
| 7 |
import shutil
|
| 8 |
+
import time
|
| 9 |
|
| 10 |
# Set page configuration
|
| 11 |
st.set_page_config(page_title="LaTeX Editor & Compiler", page_icon="📝", layout="wide")
|
|
|
|
| 22 |
st.code(f"PATH: {os.environ.get('PATH')}")
|
| 23 |
result = subprocess.run(["which", "pdflatex"], capture_output=True, text=True)
|
| 24 |
st.code(f"which pdflatex: {result.stdout} {result.stderr}")
|
|
|
|
|
|
|
| 25 |
return None, "", "Error: pdflatex is not installed or not in PATH."
|
| 26 |
|
| 27 |
with tempfile.TemporaryDirectory() as temp_dir:
|
|
|
|
| 51 |
except Exception as e:
|
| 52 |
return None, "", str(e)
|
| 53 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
# Function to create download link for PDF
|
| 55 |
def get_download_link(pdf_data, filename="document.pdf"):
|
| 56 |
b64_pdf = base64.b64encode(pdf_data).decode()
|
| 57 |
return f'<a href="data:application/pdf;base64,{b64_pdf}" download="{filename}" class="download-button">Download PDF</a>'
|
| 58 |
|
| 59 |
+
# Convert PDF to images for preview (safer than embedding PDF)
|
| 60 |
+
def pdf_preview(pdf_data):
|
| 61 |
+
# Save PDF to a temporary file
|
| 62 |
+
if not pdf_data:
|
| 63 |
+
return st.error("No PDF data to preview")
|
| 64 |
+
|
| 65 |
+
st.warning("PDF preview is not available due to browser security restrictions.")
|
| 66 |
+
st.info("Please download the PDF using the button below to view it.")
|
| 67 |
+
|
| 68 |
+
# Provide download button with more prominent styling
|
| 69 |
+
st.markdown(
|
| 70 |
+
f"""
|
| 71 |
+
<div style="text-align: center; margin: 30px 0;">
|
| 72 |
+
{get_download_link(pdf_data)}
|
| 73 |
+
</div>
|
| 74 |
+
""",
|
| 75 |
+
unsafe_allow_html=True
|
| 76 |
+
)
|
| 77 |
+
|
| 78 |
# LaTeX package reference
|
| 79 |
latex_packages = {
|
| 80 |
"Document": {
|
|
|
|
| 267 |
}
|
| 268 |
.download-button {
|
| 269 |
display: inline-block;
|
| 270 |
+
padding: 0.7em 1.4em;
|
| 271 |
background-color: #4CAF50;
|
| 272 |
color: white !important;
|
| 273 |
text-align: center;
|
| 274 |
text-decoration: none;
|
| 275 |
+
font-size: 18px;
|
| 276 |
border-radius: 4px;
|
| 277 |
transition: background-color 0.3s;
|
| 278 |
margin-top: 10px;
|
| 279 |
+
font-weight: bold;
|
| 280 |
+
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
| 281 |
}
|
| 282 |
.download-button:hover {
|
| 283 |
background-color: #45a049;
|
| 284 |
+
box-shadow: 0 4px 8px rgba(0,0,0,0.3);
|
| 285 |
}
|
| 286 |
.stTextArea textarea {
|
| 287 |
font-family: 'Courier New', Courier, monospace !important;
|
|
|
|
| 310 |
</style>
|
| 311 |
""", unsafe_allow_html=True)
|
| 312 |
|
| 313 |
+
# JavaScript for copying commands (optional, may not work in all environments)
|
| 314 |
+
st.markdown("""
|
| 315 |
+
<script>
|
| 316 |
+
document.addEventListener('DOMContentLoaded', (event) => {
|
| 317 |
+
// Add click handlers to copy LaTeX commands
|
| 318 |
+
document.querySelectorAll('.latex-command').forEach(element => {
|
| 319 |
+
element.addEventListener('click', function() {
|
| 320 |
+
const textToCopy = this.textContent;
|
| 321 |
+
const textArea = document.querySelector('.stTextArea textarea');
|
| 322 |
+
if (textArea) {
|
| 323 |
+
const start = textArea.selectionStart;
|
| 324 |
+
const end = textArea.selectionEnd;
|
| 325 |
+
const value = textArea.value;
|
| 326 |
+
textArea.value = value.substring(0, start) + textToCopy + value.substring(end);
|
| 327 |
+
textArea.selectionStart = textArea.selectionEnd = start + textToCopy.length;
|
| 328 |
+
textArea.focus();
|
| 329 |
+
}
|
| 330 |
+
});
|
| 331 |
+
});
|
| 332 |
+
});
|
| 333 |
+
</script>
|
| 334 |
+
""", unsafe_allow_html=True)
|
| 335 |
+
|
| 336 |
# Main application
|
| 337 |
def main():
|
| 338 |
st.title("LaTeX Editor & PDF Compiler")
|
|
|
|
| 341 |
if not is_pdflatex_installed():
|
| 342 |
st.warning("⚠️ LaTeX is not installed correctly. The PDF compilation feature will not work.")
|
| 343 |
st.info("For Hugging Face Spaces, make sure you have a packages.txt file with the necessary LaTeX packages.")
|
| 344 |
+
|
| 345 |
+
# Show packages.txt content suggestion
|
| 346 |
+
with st.expander("Required packages.txt content"):
|
| 347 |
+
st.code("""texlive
|
| 348 |
+
texlive-latex-extra
|
| 349 |
+
texlive-fonts-recommended
|
| 350 |
+
texlive-science""", language="text")
|
| 351 |
|
| 352 |
# Create layout with sidebar
|
| 353 |
col1, col2 = st.columns([3, 2])
|
|
|
|
| 386 |
st.rerun()
|
| 387 |
|
| 388 |
with col2:
|
| 389 |
+
st.subheader("PDF Output")
|
| 390 |
|
| 391 |
+
# PDF compilation and download
|
| 392 |
if 'compile_clicked' in st.session_state and st.session_state.compile_clicked:
|
| 393 |
with st.spinner("Compiling LaTeX to PDF..."):
|
| 394 |
pdf_data, stdout, stderr = latex_to_pdf(latex_code)
|
| 395 |
|
| 396 |
if pdf_data:
|
| 397 |
st.session_state.pdf_data = pdf_data
|
| 398 |
+
st.success("PDF compiled successfully!")
|
| 399 |
+
pdf_preview(pdf_data)
|
|
|
|
|
|
|
| 400 |
st.session_state.compile_clicked = False
|
| 401 |
else:
|
| 402 |
st.error("Compilation Error")
|
|
|
|
| 407 |
|
| 408 |
# Display previous PDF if available
|
| 409 |
elif 'pdf_data' in st.session_state and st.session_state.pdf_data:
|
| 410 |
+
pdf_preview(st.session_state.pdf_data)
|
|
|
|
| 411 |
else:
|
| 412 |
+
st.info("Compile your LaTeX document to generate a PDF for download")
|
| 413 |
|
| 414 |
# LaTeX Reference Sidebar
|
| 415 |
st.sidebar.title("LaTeX Reference")
|
|
|
|
| 430 |
found = True
|
| 431 |
with st.sidebar.expander(f"{category} ({len(filtered_commands)} results)"):
|
| 432 |
for cmd, desc in filtered_commands.items():
|
| 433 |
+
cmd_display = cmd.replace("\\", "\\\\") # Escape backslashes for display
|
| 434 |
+
st.markdown(f"<div class='latex-command' title='Click to copy'>{cmd}</div> - {desc}", unsafe_allow_html=True)
|
| 435 |
+
if st.button(f"Insert '{cmd}'", key=f"btn_{cmd}"):
|
| 436 |
+
# Insert at cursor not supported directly, but we can append
|
| 437 |
+
st.session_state.latex_code += f"\n{cmd}"
|
| 438 |
+
st.rerun()
|
| 439 |
|
| 440 |
# Search packages
|
| 441 |
for category, packages in latex_packages.items():
|
|
|
|
| 446 |
found = True
|
| 447 |
with st.sidebar.expander(f"Packages: {category} ({len(filtered_packages)} results)"):
|
| 448 |
for pkg, desc in filtered_packages.items():
|
| 449 |
+
pkg_display = pkg.replace("\\", "\\\\") # Escape backslashes for display
|
| 450 |
+
st.markdown(f"<div class='latex-command' title='Click to copy'>{pkg}</div> - {desc}", unsafe_allow_html=True)
|
| 451 |
+
if st.button(f"Insert '{pkg}'", key=f"btn_{pkg}"):
|
| 452 |
+
st.session_state.latex_code += f"\n{pkg}"
|
| 453 |
+
st.rerun()
|
| 454 |
|
| 455 |
if not found:
|
| 456 |
st.sidebar.info("No matching commands or packages found")
|
|
|
|
| 464 |
with st.expander(category):
|
| 465 |
for cmd, desc in commands.items():
|
| 466 |
st.markdown(f"<div class='latex-command'>{cmd}</div> - {desc}", unsafe_allow_html=True)
|
| 467 |
+
if st.button(f"Insert '{cmd}'", key=f"btn_{cmd}"):
|
| 468 |
+
st.session_state.latex_code += f"\n{cmd}"
|
| 469 |
+
st.rerun()
|
| 470 |
|
| 471 |
with tabs[1]:
|
| 472 |
for category, packages in latex_packages.items():
|
| 473 |
with st.expander(category):
|
| 474 |
for pkg, desc in packages.items():
|
| 475 |
st.markdown(f"<div class='latex-command'>{pkg}</div> - {desc}", unsafe_allow_html=True)
|
| 476 |
+
if st.button(f"Insert '{pkg}'", key=f"btn_{pkg}"):
|
| 477 |
+
st.session_state.latex_code += f"\n{pkg}"
|
| 478 |
+
st.rerun()
|
| 479 |
|
| 480 |
if __name__ == "__main__":
|
| 481 |
main()
|