Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
|
@@ -10,7 +10,6 @@ from PIL import Image
|
|
| 10 |
import fitz # PyMuPDF
|
| 11 |
import time
|
| 12 |
import re
|
| 13 |
-
import json
|
| 14 |
|
| 15 |
# Set page configuration
|
| 16 |
st.set_page_config(
|
|
@@ -25,7 +24,7 @@ def is_pdflatex_installed():
|
|
| 25 |
return shutil.which("pdflatex") is not None
|
| 26 |
|
| 27 |
# Function to convert LaTeX to PDF
|
| 28 |
-
def latex_to_pdf(latex_code
|
| 29 |
# Check if pdflatex is installed
|
| 30 |
if not is_pdflatex_installed():
|
| 31 |
st.error("pdflatex not found. Debug info:")
|
|
@@ -35,12 +34,9 @@ def latex_to_pdf(latex_code, output_dir=None):
|
|
| 35 |
return None, "", "Error: pdflatex is not installed or not in PATH."
|
| 36 |
|
| 37 |
with tempfile.TemporaryDirectory() as temp_dir:
|
| 38 |
-
if output_dir is None:
|
| 39 |
-
output_dir = temp_dir
|
| 40 |
-
|
| 41 |
temp_path = Path(temp_dir)
|
| 42 |
tex_file = temp_path / "document.tex"
|
| 43 |
-
pdf_file =
|
| 44 |
|
| 45 |
# Write LaTeX code to file
|
| 46 |
with open(tex_file, "w") as f:
|
|
@@ -49,7 +45,7 @@ def latex_to_pdf(latex_code, output_dir=None):
|
|
| 49 |
try:
|
| 50 |
# Run pdflatex to compile the LaTeX file
|
| 51 |
process = subprocess.run(
|
| 52 |
-
["pdflatex", "-interaction=nonstopmode", "-output-directory",
|
| 53 |
capture_output=True,
|
| 54 |
text=True
|
| 55 |
)
|
|
@@ -64,6 +60,11 @@ def latex_to_pdf(latex_code, output_dir=None):
|
|
| 64 |
except Exception as e:
|
| 65 |
return None, "", str(e)
|
| 66 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
# Convert PDF to image for preview
|
| 68 |
def render_pdf_preview(pdf_data):
|
| 69 |
if not pdf_data:
|
|
@@ -78,7 +79,7 @@ def render_pdf_preview(pdf_data):
|
|
| 78 |
|
| 79 |
# Render pages as images
|
| 80 |
images = []
|
| 81 |
-
for page_num in range(min(
|
| 82 |
page = pdf_document.load_page(page_num)
|
| 83 |
pix = page.get_pixmap(matrix=fitz.Matrix(2, 2)) # Zoom factor 2 for better resolution
|
| 84 |
img_data = pix.tobytes("png")
|
|
@@ -91,11 +92,6 @@ def render_pdf_preview(pdf_data):
|
|
| 91 |
st.error(f"Error rendering PDF preview: {str(e)}")
|
| 92 |
return None
|
| 93 |
|
| 94 |
-
# Function to create download link for PDF
|
| 95 |
-
def get_download_link(pdf_data, filename="document.pdf"):
|
| 96 |
-
b64_pdf = base64.b64encode(pdf_data).decode()
|
| 97 |
-
return f'<a href="data:application/pdf;base64,{b64_pdf}" download="{filename}" class="download-button">Download PDF</a>'
|
| 98 |
-
|
| 99 |
# Function to parse LaTeX errors
|
| 100 |
def parse_latex_errors(output):
|
| 101 |
errors = []
|
|
@@ -113,10 +109,20 @@ def parse_latex_errors(output):
|
|
| 113 |
|
| 114 |
return errors, warnings
|
| 115 |
|
| 116 |
-
# Function to extract document structure
|
| 117 |
def extract_document_structure(latex_code):
|
| 118 |
structure = []
|
| 119 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
# Find sections, subsections, etc.
|
| 121 |
section_pattern = r'\\(section|subsection|subsubsection|chapter|part)\{([^}]+)\}'
|
| 122 |
matches = re.finditer(section_pattern, latex_code)
|
|
@@ -238,57 +244,6 @@ Your conclusion here.
|
|
| 238 |
\end{document}
|
| 239 |
"""
|
| 240 |
|
| 241 |
-
# Ace Editor component for syntax highlighting
|
| 242 |
-
def create_ace_editor():
|
| 243 |
-
ace_editor_html = """
|
| 244 |
-
<div id="editor" style="height: 600px; width: 100%; border-radius: 4px;"></div>
|
| 245 |
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.24.1/ace.js"></script>
|
| 246 |
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.24.1/ext-language_tools.js"></script>
|
| 247 |
-
<script>
|
| 248 |
-
// Create the editor
|
| 249 |
-
var editor = ace.edit("editor");
|
| 250 |
-
editor.setTheme("ace/theme/tomorrow_night_eighties");
|
| 251 |
-
editor.session.setMode("ace/mode/latex");
|
| 252 |
-
editor.setValue(JSON.parse('{latex_content}'));
|
| 253 |
-
editor.clearSelection();
|
| 254 |
-
|
| 255 |
-
// Enable advanced features
|
| 256 |
-
editor.setOptions({
|
| 257 |
-
enableBasicAutocompletion: true,
|
| 258 |
-
enableSnippets: true,
|
| 259 |
-
enableLiveAutocompletion: true,
|
| 260 |
-
showPrintMargin: false,
|
| 261 |
-
fontSize: 14,
|
| 262 |
-
fontFamily: "'Fira Code', 'Courier New', monospace",
|
| 263 |
-
scrollPastEnd: 0.5
|
| 264 |
-
});
|
| 265 |
-
|
| 266 |
-
// Function to update Streamlit with the content
|
| 267 |
-
function updateStreamlit() {
|
| 268 |
-
const content = editor.getValue();
|
| 269 |
-
if (window.Streamlit) {
|
| 270 |
-
window.Streamlit.setComponentValue(content);
|
| 271 |
-
}
|
| 272 |
-
}
|
| 273 |
-
|
| 274 |
-
// Add change event listener
|
| 275 |
-
editor.session.on('change', function() {
|
| 276 |
-
updateStreamlit();
|
| 277 |
-
});
|
| 278 |
-
|
| 279 |
-
// Initialize Streamlit component communication
|
| 280 |
-
function onStreamlitLoad() {
|
| 281 |
-
updateStreamlit();
|
| 282 |
-
}
|
| 283 |
-
|
| 284 |
-
if (window.Streamlit) {
|
| 285 |
-
window.Streamlit.setComponentReady();
|
| 286 |
-
window.Streamlit.onCustomComponentReady(onStreamlitLoad);
|
| 287 |
-
}
|
| 288 |
-
</script>
|
| 289 |
-
"""
|
| 290 |
-
return ace_editor_html
|
| 291 |
-
|
| 292 |
# Enhanced VSCode-like advanced styling
|
| 293 |
st.markdown("""
|
| 294 |
<style>
|
|
@@ -312,6 +267,18 @@ st.markdown("""
|
|
| 312 |
background-color: #1177bb;
|
| 313 |
}
|
| 314 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 315 |
/* VS Code-like editor container */
|
| 316 |
.editor-container {
|
| 317 |
background-color: #1e1e1e;
|
|
@@ -363,27 +330,33 @@ st.markdown("""
|
|
| 363 |
font-size: 12px;
|
| 364 |
}
|
| 365 |
|
| 366 |
-
/*
|
| 367 |
-
.
|
| 368 |
-
|
| 369 |
-
|
| 370 |
display: flex;
|
| 371 |
-
|
| 372 |
align-items: center;
|
| 373 |
-
padding-top: 10px;
|
| 374 |
}
|
| 375 |
|
| 376 |
-
.
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
cursor: pointer;
|
| 382 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 383 |
}
|
| 384 |
|
| 385 |
-
.
|
| 386 |
-
|
|
|
|
|
|
|
|
|
|
| 387 |
}
|
| 388 |
|
| 389 |
/* Terminal/console styling */
|
|
@@ -472,35 +445,6 @@ st.markdown("""
|
|
| 472 |
gap: 5px;
|
| 473 |
}
|
| 474 |
|
| 475 |
-
/* Toolbar */
|
| 476 |
-
.toolbar {
|
| 477 |
-
background-color: #2d2d2d;
|
| 478 |
-
padding: 5px;
|
| 479 |
-
display: flex;
|
| 480 |
-
gap: 5px;
|
| 481 |
-
align-items: center;
|
| 482 |
-
}
|
| 483 |
-
|
| 484 |
-
.toolbar-button {
|
| 485 |
-
background-color: transparent;
|
| 486 |
-
border: none;
|
| 487 |
-
color: #cccccc;
|
| 488 |
-
padding: 5px;
|
| 489 |
-
cursor: pointer;
|
| 490 |
-
border-radius: 2px;
|
| 491 |
-
}
|
| 492 |
-
|
| 493 |
-
.toolbar-button:hover {
|
| 494 |
-
background-color: #3c3c3c;
|
| 495 |
-
}
|
| 496 |
-
|
| 497 |
-
.toolbar-separator {
|
| 498 |
-
width: 1px;
|
| 499 |
-
height: 20px;
|
| 500 |
-
background-color: #555;
|
| 501 |
-
margin: 0 5px;
|
| 502 |
-
}
|
| 503 |
-
|
| 504 |
/* Messages */
|
| 505 |
.stInfo {
|
| 506 |
background-color: #063b49;
|
|
@@ -545,39 +489,7 @@ st.markdown("""
|
|
| 545 |
color: white;
|
| 546 |
}
|
| 547 |
|
| 548 |
-
/*
|
| 549 |
-
.file-explorer {
|
| 550 |
-
background-color: #252526;
|
| 551 |
-
border: 1px solid #2d2d2d;
|
| 552 |
-
border-radius: 4px;
|
| 553 |
-
padding: 0;
|
| 554 |
-
margin-top: 10px;
|
| 555 |
-
}
|
| 556 |
-
|
| 557 |
-
.file-header {
|
| 558 |
-
background-color: #2d2d2d;
|
| 559 |
-
padding: 5px 10px;
|
| 560 |
-
font-weight: bold;
|
| 561 |
-
font-size: 13px;
|
| 562 |
-
}
|
| 563 |
-
|
| 564 |
-
.file-item {
|
| 565 |
-
padding: 5px 10px;
|
| 566 |
-
display: flex;
|
| 567 |
-
align-items: center;
|
| 568 |
-
cursor: pointer;
|
| 569 |
-
}
|
| 570 |
-
|
| 571 |
-
.file-item:hover {
|
| 572 |
-
background-color: #2a2d2e;
|
| 573 |
-
}
|
| 574 |
-
|
| 575 |
-
.file-icon {
|
| 576 |
-
margin-right: 5px;
|
| 577 |
-
opacity: 0.7;
|
| 578 |
-
}
|
| 579 |
-
|
| 580 |
-
/* Enhanced styles for problems panel */
|
| 581 |
.problems-panel {
|
| 582 |
background-color: #252526;
|
| 583 |
border: 1px solid #2d2d2d;
|
|
@@ -644,81 +556,58 @@ st.markdown("""
|
|
| 644 |
</style>
|
| 645 |
""", unsafe_allow_html=True)
|
| 646 |
|
| 647 |
-
#
|
| 648 |
-
def
|
| 649 |
-
|
| 650 |
-
|
| 651 |
-
|
| 652 |
-
|
| 653 |
-
|
| 654 |
-
|
| 655 |
-
|
| 656 |
-
|
| 657 |
-
component = st.components.v1.html(editor_html, height=height, scrolling=False)
|
| 658 |
-
|
| 659 |
-
# Return the latest value
|
| 660 |
-
return component if component is not None else content
|
| 661 |
|
| 662 |
# Build a VS Code-like toolbar
|
| 663 |
def render_toolbar():
|
| 664 |
toolbar_html = """
|
| 665 |
<div class="toolbar">
|
| 666 |
-
<button class="toolbar-button" title="Bold"
|
| 667 |
<strong>B</strong>
|
| 668 |
</button>
|
| 669 |
-
<button class="toolbar-button" title="Italic"
|
| 670 |
<em>I</em>
|
| 671 |
</button>
|
| 672 |
-
<button class="toolbar-button" title="Math"
|
| 673 |
∑
|
| 674 |
</button>
|
| 675 |
<div class="toolbar-separator"></div>
|
| 676 |
-
<button class="toolbar-button" title="Section"
|
| 677 |
§
|
| 678 |
</button>
|
| 679 |
-
<button class="toolbar-button" title="Subsection"
|
| 680 |
§§
|
| 681 |
</button>
|
| 682 |
<div class="toolbar-separator"></div>
|
| 683 |
-
<button class="toolbar-button" title="Itemize"
|
| 684 |
•
|
| 685 |
</button>
|
| 686 |
-
<button class="toolbar-button" title="Enumerate"
|
| 687 |
1.
|
| 688 |
</button>
|
| 689 |
<div class="toolbar-separator"></div>
|
| 690 |
-
<button class="toolbar-button" title="Equation"
|
| 691 |
=
|
| 692 |
</button>
|
| 693 |
-
<button class="toolbar-button" title="Figure"
|
| 694 |
🖼
|
| 695 |
</button>
|
| 696 |
-
<button class="toolbar-button" title="Table"
|
| 697 |
⊞
|
| 698 |
</button>
|
| 699 |
</div>
|
| 700 |
-
|
| 701 |
-
<script>
|
| 702 |
-
function insertTextAtCursor(text) {
|
| 703 |
-
var editor = ace.edit("editor");
|
| 704 |
-
editor.insert(text);
|
| 705 |
-
editor.focus();
|
| 706 |
-
}
|
| 707 |
-
</script>
|
| 708 |
"""
|
| 709 |
st.markdown(toolbar_html, unsafe_allow_html=True)
|
| 710 |
|
| 711 |
-
# VS Code-style editor tabs
|
| 712 |
-
def render_editor_tabs(active_tab="document.tex"):
|
| 713 |
-
tab_html = f"""
|
| 714 |
-
<div class="tab-bar">
|
| 715 |
-
<div class="tab active">
|
| 716 |
-
<span class="tab-icon">📄</span> {active_tab}
|
| 717 |
-
</div>
|
| 718 |
-
</div>
|
| 719 |
-
"""
|
| 720 |
-
st.markdown(tab_html, unsafe_allow_html=True)
|
| 721 |
-
|
| 722 |
# VS Code-style status bar
|
| 723 |
def render_status_bar():
|
| 724 |
status_html = """
|
|
@@ -752,7 +641,7 @@ def render_document_outline(structure):
|
|
| 752 |
indent = " " * (level * 4)
|
| 753 |
|
| 754 |
st.markdown(
|
| 755 |
-
f'<div class="outline-item"
|
| 756 |
f'<span>{type_icon}</span>'
|
| 757 |
f'<span class="outline-item-text">{indent}{title}</span>'
|
| 758 |
f'</div>',
|
|
@@ -795,6 +684,13 @@ def render_problems(errors, warnings):
|
|
| 795 |
|
| 796 |
# Render document information panel
|
| 797 |
def render_document_info(latex_code):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 798 |
# Calculate basic document stats
|
| 799 |
word_count = len(re.findall(r'\b\w+\b', latex_code))
|
| 800 |
char_count = len(latex_code)
|
|
@@ -872,10 +768,15 @@ def main():
|
|
| 872 |
# Toolbar
|
| 873 |
render_toolbar()
|
| 874 |
|
| 875 |
-
#
|
| 876 |
-
latex_code =
|
| 877 |
-
|
| 878 |
-
st.session_state.latex_code
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 879 |
|
| 880 |
# Status bar
|
| 881 |
render_status_bar()
|
|
@@ -1014,8 +915,11 @@ def main():
|
|
| 1014 |
st.info("Click 'Compile' to generate PDF output")
|
| 1015 |
|
| 1016 |
with output_tabs[1]:
|
| 1017 |
-
# Document structure/outline view
|
| 1018 |
-
|
|
|
|
|
|
|
|
|
|
| 1019 |
render_document_outline(structure)
|
| 1020 |
|
| 1021 |
with output_tabs[2]:
|
|
|
|
| 10 |
import fitz # PyMuPDF
|
| 11 |
import time
|
| 12 |
import re
|
|
|
|
| 13 |
|
| 14 |
# Set page configuration
|
| 15 |
st.set_page_config(
|
|
|
|
| 24 |
return shutil.which("pdflatex") is not None
|
| 25 |
|
| 26 |
# Function to convert LaTeX to PDF
|
| 27 |
+
def latex_to_pdf(latex_code):
|
| 28 |
# Check if pdflatex is installed
|
| 29 |
if not is_pdflatex_installed():
|
| 30 |
st.error("pdflatex not found. Debug info:")
|
|
|
|
| 34 |
return None, "", "Error: pdflatex is not installed or not in PATH."
|
| 35 |
|
| 36 |
with tempfile.TemporaryDirectory() as temp_dir:
|
|
|
|
|
|
|
|
|
|
| 37 |
temp_path = Path(temp_dir)
|
| 38 |
tex_file = temp_path / "document.tex"
|
| 39 |
+
pdf_file = temp_path / "document.pdf"
|
| 40 |
|
| 41 |
# Write LaTeX code to file
|
| 42 |
with open(tex_file, "w") as f:
|
|
|
|
| 45 |
try:
|
| 46 |
# Run pdflatex to compile the LaTeX file
|
| 47 |
process = subprocess.run(
|
| 48 |
+
["pdflatex", "-interaction=nonstopmode", "-output-directory", temp_dir, str(tex_file)],
|
| 49 |
capture_output=True,
|
| 50 |
text=True
|
| 51 |
)
|
|
|
|
| 60 |
except Exception as e:
|
| 61 |
return None, "", str(e)
|
| 62 |
|
| 63 |
+
# Function to create download link for PDF
|
| 64 |
+
def get_download_link(pdf_data, filename="document.pdf"):
|
| 65 |
+
b64_pdf = base64.b64encode(pdf_data).decode()
|
| 66 |
+
return f'<a href="data:application/pdf;base64,{b64_pdf}" download="{filename}" class="download-button">Download PDF</a>'
|
| 67 |
+
|
| 68 |
# Convert PDF to image for preview
|
| 69 |
def render_pdf_preview(pdf_data):
|
| 70 |
if not pdf_data:
|
|
|
|
| 79 |
|
| 80 |
# Render pages as images
|
| 81 |
images = []
|
| 82 |
+
for page_num in range(min(3, len(pdf_document))): # Preview first 3 pages max
|
| 83 |
page = pdf_document.load_page(page_num)
|
| 84 |
pix = page.get_pixmap(matrix=fitz.Matrix(2, 2)) # Zoom factor 2 for better resolution
|
| 85 |
img_data = pix.tobytes("png")
|
|
|
|
| 92 |
st.error(f"Error rendering PDF preview: {str(e)}")
|
| 93 |
return None
|
| 94 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
# Function to parse LaTeX errors
|
| 96 |
def parse_latex_errors(output):
|
| 97 |
errors = []
|
|
|
|
| 109 |
|
| 110 |
return errors, warnings
|
| 111 |
|
| 112 |
+
# Function to extract document structure - fixed to handle non-string input
|
| 113 |
def extract_document_structure(latex_code):
|
| 114 |
structure = []
|
| 115 |
|
| 116 |
+
# Ensure latex_code is a string
|
| 117 |
+
if latex_code is None:
|
| 118 |
+
return structure
|
| 119 |
+
|
| 120 |
+
if not isinstance(latex_code, str):
|
| 121 |
+
try:
|
| 122 |
+
latex_code = str(latex_code)
|
| 123 |
+
except:
|
| 124 |
+
return structure
|
| 125 |
+
|
| 126 |
# Find sections, subsections, etc.
|
| 127 |
section_pattern = r'\\(section|subsection|subsubsection|chapter|part)\{([^}]+)\}'
|
| 128 |
matches = re.finditer(section_pattern, latex_code)
|
|
|
|
| 244 |
\end{document}
|
| 245 |
"""
|
| 246 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 247 |
# Enhanced VSCode-like advanced styling
|
| 248 |
st.markdown("""
|
| 249 |
<style>
|
|
|
|
| 267 |
background-color: #1177bb;
|
| 268 |
}
|
| 269 |
|
| 270 |
+
/* VS Code-like editor */
|
| 271 |
+
.stTextArea textarea {
|
| 272 |
+
font-family: 'Consolas', 'Monaco', 'Courier New', monospace !important;
|
| 273 |
+
font-size: 14px !important;
|
| 274 |
+
line-height: 1.5 !important;
|
| 275 |
+
background-color: #1e1e1e !important;
|
| 276 |
+
color: #d4d4d4 !important;
|
| 277 |
+
padding: 10px !important;
|
| 278 |
+
border-radius: 4px !important;
|
| 279 |
+
border: 1px solid #252526 !important;
|
| 280 |
+
}
|
| 281 |
+
|
| 282 |
/* VS Code-like editor container */
|
| 283 |
.editor-container {
|
| 284 |
background-color: #1e1e1e;
|
|
|
|
| 330 |
font-size: 12px;
|
| 331 |
}
|
| 332 |
|
| 333 |
+
/* Toolbar */
|
| 334 |
+
.toolbar {
|
| 335 |
+
background-color: #2d2d2d;
|
| 336 |
+
padding: 5px;
|
| 337 |
display: flex;
|
| 338 |
+
gap: 5px;
|
| 339 |
align-items: center;
|
|
|
|
| 340 |
}
|
| 341 |
|
| 342 |
+
.toolbar-button {
|
| 343 |
+
background-color: transparent;
|
| 344 |
+
border: none;
|
| 345 |
+
color: #cccccc;
|
| 346 |
+
padding: 5px;
|
| 347 |
cursor: pointer;
|
| 348 |
+
border-radius: 2px;
|
| 349 |
+
}
|
| 350 |
+
|
| 351 |
+
.toolbar-button:hover {
|
| 352 |
+
background-color: #3c3c3c;
|
| 353 |
}
|
| 354 |
|
| 355 |
+
.toolbar-separator {
|
| 356 |
+
width: 1px;
|
| 357 |
+
height: 20px;
|
| 358 |
+
background-color: #555;
|
| 359 |
+
margin: 0 5px;
|
| 360 |
}
|
| 361 |
|
| 362 |
/* Terminal/console styling */
|
|
|
|
| 445 |
gap: 5px;
|
| 446 |
}
|
| 447 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 448 |
/* Messages */
|
| 449 |
.stInfo {
|
| 450 |
background-color: #063b49;
|
|
|
|
| 489 |
color: white;
|
| 490 |
}
|
| 491 |
|
| 492 |
+
/* Problems panel */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 493 |
.problems-panel {
|
| 494 |
background-color: #252526;
|
| 495 |
border: 1px solid #2d2d2d;
|
|
|
|
| 556 |
</style>
|
| 557 |
""", unsafe_allow_html=True)
|
| 558 |
|
| 559 |
+
# VS Code-style editor tabs
|
| 560 |
+
def render_editor_tabs(active_tab="document.tex"):
|
| 561 |
+
tab_html = f"""
|
| 562 |
+
<div class="tab-bar">
|
| 563 |
+
<div class="tab active">
|
| 564 |
+
<span class="tab-icon">📄</span> {active_tab}
|
| 565 |
+
</div>
|
| 566 |
+
</div>
|
| 567 |
+
"""
|
| 568 |
+
st.markdown(tab_html, unsafe_allow_html=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 569 |
|
| 570 |
# Build a VS Code-like toolbar
|
| 571 |
def render_toolbar():
|
| 572 |
toolbar_html = """
|
| 573 |
<div class="toolbar">
|
| 574 |
+
<button class="toolbar-button" title="Bold">
|
| 575 |
<strong>B</strong>
|
| 576 |
</button>
|
| 577 |
+
<button class="toolbar-button" title="Italic">
|
| 578 |
<em>I</em>
|
| 579 |
</button>
|
| 580 |
+
<button class="toolbar-button" title="Math">
|
| 581 |
∑
|
| 582 |
</button>
|
| 583 |
<div class="toolbar-separator"></div>
|
| 584 |
+
<button class="toolbar-button" title="Section">
|
| 585 |
§
|
| 586 |
</button>
|
| 587 |
+
<button class="toolbar-button" title="Subsection">
|
| 588 |
§§
|
| 589 |
</button>
|
| 590 |
<div class="toolbar-separator"></div>
|
| 591 |
+
<button class="toolbar-button" title="Itemize">
|
| 592 |
•
|
| 593 |
</button>
|
| 594 |
+
<button class="toolbar-button" title="Enumerate">
|
| 595 |
1.
|
| 596 |
</button>
|
| 597 |
<div class="toolbar-separator"></div>
|
| 598 |
+
<button class="toolbar-button" title="Equation">
|
| 599 |
=
|
| 600 |
</button>
|
| 601 |
+
<button class="toolbar-button" title="Figure">
|
| 602 |
🖼
|
| 603 |
</button>
|
| 604 |
+
<button class="toolbar-button" title="Table">
|
| 605 |
⊞
|
| 606 |
</button>
|
| 607 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 608 |
"""
|
| 609 |
st.markdown(toolbar_html, unsafe_allow_html=True)
|
| 610 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 611 |
# VS Code-style status bar
|
| 612 |
def render_status_bar():
|
| 613 |
status_html = """
|
|
|
|
| 641 |
indent = " " * (level * 4)
|
| 642 |
|
| 643 |
st.markdown(
|
| 644 |
+
f'<div class="outline-item">'
|
| 645 |
f'<span>{type_icon}</span>'
|
| 646 |
f'<span class="outline-item-text">{indent}{title}</span>'
|
| 647 |
f'</div>',
|
|
|
|
| 684 |
|
| 685 |
# Render document information panel
|
| 686 |
def render_document_info(latex_code):
|
| 687 |
+
# Ensure latex_code is a string
|
| 688 |
+
if not isinstance(latex_code, str):
|
| 689 |
+
try:
|
| 690 |
+
latex_code = str(latex_code)
|
| 691 |
+
except:
|
| 692 |
+
latex_code = ""
|
| 693 |
+
|
| 694 |
# Calculate basic document stats
|
| 695 |
word_count = len(re.findall(r'\b\w+\b', latex_code))
|
| 696 |
char_count = len(latex_code)
|
|
|
|
| 768 |
# Toolbar
|
| 769 |
render_toolbar()
|
| 770 |
|
| 771 |
+
# Simple text area with VS Code styling
|
| 772 |
+
latex_code = st.text_area(
|
| 773 |
+
"",
|
| 774 |
+
value=st.session_state.latex_code,
|
| 775 |
+
height=500,
|
| 776 |
+
key="latex_editor",
|
| 777 |
+
label_visibility="collapsed"
|
| 778 |
+
)
|
| 779 |
+
st.session_state.latex_code = latex_code
|
| 780 |
|
| 781 |
# Status bar
|
| 782 |
render_status_bar()
|
|
|
|
| 915 |
st.info("Click 'Compile' to generate PDF output")
|
| 916 |
|
| 917 |
with output_tabs[1]:
|
| 918 |
+
# Document structure/outline view (with type check)
|
| 919 |
+
if isinstance(st.session_state.latex_code, str):
|
| 920 |
+
structure = extract_document_structure(st.session_state.latex_code)
|
| 921 |
+
else:
|
| 922 |
+
structure = []
|
| 923 |
render_document_outline(structure)
|
| 924 |
|
| 925 |
with output_tabs[2]:
|