euler314 commited on
Commit
58633fe
·
verified ·
1 Parent(s): 72c62aa

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +782 -193
app.py CHANGED
@@ -8,16 +8,24 @@ import shutil
8
  import io
9
  from PIL import Image
10
  import fitz # PyMuPDF
 
 
 
11
 
12
  # Set page configuration
13
- st.set_page_config(page_title="LaTeX Editor & Compiler", page_icon="📝", layout="wide")
 
 
 
 
 
14
 
15
  # Check if pdflatex is available
16
  def is_pdflatex_installed():
17
  return shutil.which("pdflatex") is not None
18
 
19
  # Function to convert LaTeX to PDF
20
- def latex_to_pdf(latex_code):
21
  # Check if pdflatex is installed
22
  if not is_pdflatex_installed():
23
  st.error("pdflatex not found. Debug info:")
@@ -27,9 +35,12 @@ def latex_to_pdf(latex_code):
27
  return None, "", "Error: pdflatex is not installed or not in PATH."
28
 
29
  with tempfile.TemporaryDirectory() as temp_dir:
 
 
 
30
  temp_path = Path(temp_dir)
31
  tex_file = temp_path / "document.tex"
32
- pdf_file = temp_path / "document.pdf"
33
 
34
  # Write LaTeX code to file
35
  with open(tex_file, "w") as f:
@@ -38,7 +49,7 @@ def latex_to_pdf(latex_code):
38
  try:
39
  # Run pdflatex to compile the LaTeX file
40
  process = subprocess.run(
41
- ["pdflatex", "-interaction=nonstopmode", "-output-directory", temp_dir, str(tex_file)],
42
  capture_output=True,
43
  text=True
44
  )
@@ -53,11 +64,6 @@ def latex_to_pdf(latex_code):
53
  except Exception as e:
54
  return None, "", str(e)
55
 
56
- # Function to create download link for PDF
57
- def get_download_link(pdf_data, filename="document.pdf"):
58
- b64_pdf = base64.b64encode(pdf_data).decode()
59
- return f'<a href="data:application/pdf;base64,{b64_pdf}" download="{filename}" class="download-button">Download PDF</a>'
60
-
61
  # Convert PDF to image for preview
62
  def render_pdf_preview(pdf_data):
63
  if not pdf_data:
@@ -72,7 +78,7 @@ def render_pdf_preview(pdf_data):
72
 
73
  # Render pages as images
74
  images = []
75
- for page_num in range(min(3, len(pdf_document))): # Preview first 3 pages max
76
  page = pdf_document.load_page(page_num)
77
  pix = page.get_pixmap(matrix=fitz.Matrix(2, 2)) # Zoom factor 2 for better resolution
78
  img_data = pix.tobytes("png")
@@ -85,6 +91,58 @@ def render_pdf_preview(pdf_data):
85
  st.error(f"Error rendering PDF preview: {str(e)}")
86
  return None
87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  # Default LaTeX template
89
  default_template = r"""\documentclass{article}
90
  \usepackage[utf8]{inputenc}
@@ -92,8 +150,9 @@ default_template = r"""\documentclass{article}
92
  \usepackage{amssymb}
93
  \usepackage{graphicx}
94
  \usepackage{hyperref}
 
95
 
96
- \title{LaTeX Document}
97
  \author{Your Name}
98
  \date{\today}
99
 
@@ -102,7 +161,7 @@ default_template = r"""\documentclass{article}
102
  \maketitle
103
 
104
  \section{Introduction}
105
- Your introduction here. Insert some text to demonstrate LaTeX.
106
 
107
  \section{Mathematical Expressions}
108
  \subsection{Equations}
@@ -125,9 +184,9 @@ An integral example:
125
  \section{Lists and Items}
126
  \subsection{Bullet Points}
127
  \begin{itemize}
128
- \item First item
129
- \item Second item
130
- \item Third item
131
  \end{itemize}
132
 
133
  \subsection{Numbered List}
@@ -151,79 +210,236 @@ An integral example:
151
  \label{tab:simple}
152
  \end{table}
153
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  \section{Conclusion}
155
  Your conclusion here.
156
 
 
 
 
 
 
157
  \end{document}
158
  """
159
 
160
- # Add VS Code-like styling
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  st.markdown("""
162
  <style>
163
- /* VS Code-like styling */
164
- .vscode-editor textarea {
165
- font-family: 'Consolas', 'Monaco', 'Courier New', monospace !important;
166
- font-size: 14px !important;
167
- line-height: 1.5 !important;
168
- background-color: #1e1e1e !important;
169
- color: #d4d4d4 !important;
170
- padding: 10px !important;
171
- border-radius: 4px !important;
172
- border: 1px solid #252526 !important;
173
- }
174
-
175
- /* Editor container styling */
176
- .vscode-container {
177
  background-color: #1e1e1e;
178
- border-radius: 6px;
179
- padding: 8px;
180
- border: 1px solid #333;
181
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
182
  }
183
 
184
- /* Make scrollbars VS Code style */
185
- .vscode-editor textarea::-webkit-scrollbar {
186
- width: 14px;
187
- height: 14px;
 
 
 
 
188
  }
189
 
190
- .vscode-editor textarea::-webkit-scrollbar-thumb {
191
- background-color: #424242;
192
- border-radius: 7px;
193
- border: 3px solid #1e1e1e;
194
  }
195
 
196
- .vscode-editor textarea::-webkit-scrollbar-track {
 
197
  background-color: #1e1e1e;
 
 
 
 
 
198
  }
199
 
200
- /* Button styling */
201
- .vscode-button {
202
- background-color: #0e639c;
 
 
 
 
 
 
 
 
 
 
 
203
  color: white;
204
- border: none;
205
- padding: 8px 12px;
206
- border-radius: 2px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  cursor: pointer;
 
 
 
 
 
 
 
 
 
 
 
 
 
208
  font-size: 13px;
209
- margin-right: 10px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  margin-top: 10px;
211
- transition: background-color 0.2s;
 
212
  }
213
 
214
- .vscode-button:hover {
215
- background-color: #1177bb;
 
 
 
 
 
 
 
 
 
 
 
216
  }
217
 
218
  /* Download button styling */
219
  .download-button {
220
  display: inline-block;
221
- padding: 0.7em 1.4em;
222
  background-color: #3d995e;
223
  color: white !important;
224
  text-align: center;
225
  text-decoration: none;
226
- font-size: 14px;
227
  border-radius: 2px;
228
  transition: background-color 0.3s;
229
  margin-top: 10px;
@@ -233,39 +449,59 @@ st.markdown("""
233
  background-color: #4eb772;
234
  }
235
 
236
- /* Status bar styling */
237
- .status-bar {
238
- background-color: #007acc;
239
- color: white;
240
- padding: 2px 8px;
241
- font-size: 12px;
242
- border-radius: 2px 2px 0 0;
 
 
 
 
 
 
 
 
 
 
 
 
243
  display: flex;
244
- justify-content: space-between;
245
  }
246
 
247
- /* Terminal/output styling */
248
- .terminal-output {
249
- background-color: #1e1e1e;
 
 
 
 
 
 
 
 
 
250
  color: #cccccc;
251
- padding: 10px;
252
- font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
253
- font-size: 12px;
254
- border-radius: 0 0 4px 4px;
255
- border-top: 1px solid #333;
256
- max-height: 200px;
257
- overflow-y: auto;
258
  }
259
 
260
- /* PDF preview container */
261
- .pdf-preview-container {
262
- border: 1px solid #333;
263
- border-radius: 4px;
264
- padding: 15px;
265
- background-color: #252526;
 
 
 
266
  }
267
 
268
- /* Info and error messages */
269
  .stInfo {
270
  background-color: #063b49;
271
  color: #bbbbbb;
@@ -284,161 +520,514 @@ st.markdown("""
284
  border: 1px solid #1e5a3a;
285
  }
286
 
287
- /* Hide Streamlit elements */
 
288
  #MainMenu {visibility: hidden;}
289
- footer {visibility: hidden;}
290
 
291
- /* Customize the rest of Streamlit UI */
292
- .stApp {
293
- background-color: #252526;
 
294
  }
295
 
296
- h1, h2, h3, h4, h5, h6, p, div {
 
297
  color: #cccccc;
 
 
 
 
298
  }
299
 
300
- .stTabs [data-baseweb="tab-list"] {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
  background-color: #2d2d2d;
 
 
 
302
  }
303
 
304
- .stTabs [data-baseweb="tab"] {
305
- color: #cccccc;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
  }
307
  </style>
308
  """, unsafe_allow_html=True)
309
 
310
- # Function to create a VS Code-like editor
311
- def vs_code_editor(key, height=500):
312
- editor_html = f"""
313
- <div class="vscode-container">
314
- <div class="status-bar">
315
- <span>document.tex - LaTeX</span>
316
- <span>UTF-8</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
  </div>
318
  </div>
319
  """
320
- st.markdown(editor_html, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
 
322
- # Create the actual editor with VS Code styling
323
- return st.text_area("", value=st.session_state.get(key, ""),
324
- height=height, key=key, label_visibility="collapsed",
325
- help="Type your LaTeX code here")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
 
327
  # Main application
328
  def main():
329
- # Set up a clean, dark theme
330
- st.markdown("<h1 style='color: #cccccc; margin-bottom: 20px;'>LaTeX Editor</h1>", unsafe_allow_html=True)
331
-
332
  # Initialize session state
333
  if 'latex_code' not in st.session_state:
334
  st.session_state.latex_code = default_template
335
  if 'show_preview' not in st.session_state:
336
  st.session_state.show_preview = False
 
 
 
 
 
 
337
 
338
- # Display installation status
339
  if not is_pdflatex_installed():
340
- st.warning("⚠️ LaTeX is not installed correctly. The PDF compilation feature will not work.")
341
 
342
- # Create layout - full width editor
343
  col1, col2 = st.columns([3, 2])
344
 
345
  with col1:
346
- # VS Code-like editor with custom class for styling
347
- st.markdown('<div class="vscode-editor">', unsafe_allow_html=True)
348
- latex_code = vs_code_editor("latex_editor", height=500)
349
- st.markdown('</div>', unsafe_allow_html=True)
350
- st.session_state.latex_code = latex_code
351
 
352
- # Control buttons with VS Code styling
353
- st.markdown("""
354
- <div style="display: flex; gap: 10px;">
355
- <button class="vscode-button" onclick="document.querySelector('[data-testid=\\\"stFormSubmitButton\\\"]').click()">
356
- Compile PDF
357
- </button>
358
- <button class="vscode-button" onclick="document.querySelector('[key=\\\"load_template\\\"]').click()">
359
- Load Template
360
- </button>
361
- <button class="vscode-button" onclick="document.querySelector('[key=\\\"clear_editor\\\"]').click()">
362
- Clear Editor
363
- </button>
364
- </div>
365
- """, unsafe_allow_html=True)
366
-
367
- # Hidden buttons to handle the clicks
368
- if st.button("Compile PDF", key="compile", help="Compile LaTeX to PDF"):
369
- st.session_state.compile_clicked = True
370
-
371
- if st.button("Load Template", key="load_template", help="Load default template"):
372
- st.session_state.latex_code = default_template
373
- st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
374
 
375
- if st.button("Clear Editor", key="clear_editor", help="Clear editor content"):
376
- st.session_state.latex_code = ""
377
- st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
378
 
379
  with col2:
380
- st.markdown("<h3 style='color: #cccccc; margin-bottom: 10px;'>Output</h3>", unsafe_allow_html=True)
 
381
 
382
- # PDF compilation
383
- if 'compile_clicked' in st.session_state and st.session_state.compile_clicked:
384
- with st.spinner("Compiling..."):
385
- pdf_data, stdout, stderr = latex_to_pdf(latex_code)
386
-
387
- if pdf_data:
388
- st.session_state.pdf_data = pdf_data
389
- st.success("Compilation successful")
390
-
391
- # Toggle button for preview
392
- if st.button("Toggle Preview", help="Show or hide the PDF preview"):
393
- st.session_state.show_preview = not st.session_state.show_preview
394
 
395
- # Download button always available
396
- st.markdown(get_download_link(pdf_data), unsafe_allow_html=True)
397
-
398
- # Optional preview
399
- if st.session_state.show_preview:
400
- st.markdown('<div class="pdf-preview-container">', unsafe_allow_html=True)
401
- preview_images = render_pdf_preview(pdf_data)
402
- if preview_images:
403
- for i, img in enumerate(preview_images):
404
- st.image(img, caption=f"Page {i+1}", use_container_width=True,
405
- output_format="PNG")
406
- st.markdown('</div>', unsafe_allow_html=True)
407
 
408
- # Terminal output in collapsible section
409
- with st.expander("Terminal Output"):
410
- st.markdown('<div class="terminal-output">', unsafe_allow_html=True)
411
- st.text(stdout)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
412
  st.markdown('</div>', unsafe_allow_html=True)
413
-
414
- st.session_state.compile_clicked = False
415
- else:
416
- st.error("Compilation failed")
417
- st.markdown('<div class="terminal-output">', unsafe_allow_html=True)
418
- st.text(stderr)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
419
  st.markdown('</div>', unsafe_allow_html=True)
420
- st.session_state.compile_clicked = False
 
421
 
422
- # Display previous PDF if available
423
- elif 'pdf_data' in st.session_state and st.session_state.pdf_data:
424
- # Toggle button for preview
425
- if st.button("Toggle Preview", help="Show or hide the PDF preview"):
426
- st.session_state.show_preview = not st.session_state.show_preview
427
-
428
- # Download button always available
429
- st.markdown(get_download_link(st.session_state.pdf_data), unsafe_allow_html=True)
430
-
431
- # Optional preview
432
- if st.session_state.show_preview:
433
- st.markdown('<div class="pdf-preview-container">', unsafe_allow_html=True)
434
- preview_images = render_pdf_preview(st.session_state.pdf_data)
435
- if preview_images:
436
- for i, img in enumerate(preview_images):
437
- st.image(img, caption=f"Page {i+1}", use_container_width=True,
438
- output_format="PNG")
439
- st.markdown('</div>', unsafe_allow_html=True)
440
- else:
441
- st.info("Click 'Compile PDF' to generate output")
442
 
443
  if __name__ == "__main__":
444
  main()
 
8
  import io
9
  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(
17
+ page_title="Advanced LaTeX Editor",
18
+ page_icon="📝",
19
+ layout="wide",
20
+ initial_sidebar_state="collapsed"
21
+ )
22
 
23
  # Check if pdflatex is available
24
  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, output_dir=None):
29
  # Check if pdflatex is installed
30
  if not is_pdflatex_installed():
31
  st.error("pdflatex not found. Debug info:")
 
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 = Path(output_dir) / "document.pdf"
44
 
45
  # Write LaTeX code to file
46
  with open(tex_file, "w") as f:
 
49
  try:
50
  # Run pdflatex to compile the LaTeX file
51
  process = subprocess.run(
52
+ ["pdflatex", "-interaction=nonstopmode", "-output-directory", output_dir, str(tex_file)],
53
  capture_output=True,
54
  text=True
55
  )
 
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
 
79
  # Render pages as images
80
  images = []
81
+ for page_num in range(min(5, len(pdf_document))): # Preview first 5 pages max
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
  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 = []
102
+ warnings = []
103
+
104
+ # Match errors
105
+ error_matches = re.finditer(r'! (.+?)\.[\r\n]', output)
106
+ for match in error_matches:
107
+ errors.append(match.group(1))
108
+
109
+ # Match warnings
110
+ warning_matches = re.finditer(r'LaTeX Warning: (.+?)[\r\n]', output)
111
+ for match in warning_matches:
112
+ warnings.append(match.group(1))
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)
123
+
124
+ for match in matches:
125
+ section_type = match.group(1)
126
+ section_title = match.group(2)
127
+
128
+ # Calculate indentation level based on section type
129
+ level = {
130
+ 'part': 0,
131
+ 'chapter': 1,
132
+ 'section': 2,
133
+ 'subsection': 3,
134
+ 'subsubsection': 4
135
+ }.get(section_type, 0)
136
+
137
+ structure.append({
138
+ 'type': section_type,
139
+ 'title': section_title,
140
+ 'level': level,
141
+ 'position': match.start() # Position in document for navigation
142
+ })
143
+
144
+ return structure
145
+
146
  # Default LaTeX template
147
  default_template = r"""\documentclass{article}
148
  \usepackage[utf8]{inputenc}
 
150
  \usepackage{amssymb}
151
  \usepackage{graphicx}
152
  \usepackage{hyperref}
153
+ \usepackage{xcolor}
154
 
155
+ \title{Advanced \LaTeX{} Document}
156
  \author{Your Name}
157
  \date{\today}
158
 
 
161
  \maketitle
162
 
163
  \section{Introduction}
164
+ This is the introduction to your document. You can write your content here.
165
 
166
  \section{Mathematical Expressions}
167
  \subsection{Equations}
 
184
  \section{Lists and Items}
185
  \subsection{Bullet Points}
186
  \begin{itemize}
187
+ \item First item with \textbf{bold text}
188
+ \item Second item with \textit{italic text}
189
+ \item Third item with \textcolor{blue}{colored text}
190
  \end{itemize}
191
 
192
  \subsection{Numbered List}
 
210
  \label{tab:simple}
211
  \end{table}
212
 
213
+ \section{Figures}
214
+ You can include figures using the following syntax:
215
+
216
+ % \begin{figure}[h]
217
+ % \centering
218
+ % \includegraphics[width=0.7\textwidth]{example-image}
219
+ % \caption{Example figure}
220
+ % \label{fig:example}
221
+ % \end{figure}
222
+
223
+ \section{Citations}
224
+ You can cite references using the \verb|\cite{}| command \cite{example}.
225
+
226
+ \section{Cross-References}
227
+ You can reference sections, figures, and tables using the \verb|\ref{}| command.
228
+ For example, see Table~\ref{tab:simple}.
229
+
230
  \section{Conclusion}
231
  Your conclusion here.
232
 
233
+ % Sample bibliography entry
234
+ \begin{thebibliography}{9}
235
+ \bibitem{example} Author, A. (2023). \textit{Title of the Work}. Publisher.
236
+ \end{thebibliography}
237
+
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>
295
+ /* Base theming - VS Code inspired */
296
+ html, body, .stApp {
 
 
 
 
 
 
 
 
 
 
 
 
297
  background-color: #1e1e1e;
298
+ color: #cccccc;
 
 
 
299
  }
300
 
301
+ /* Streamlit component overrides */
302
+ .stButton button {
303
+ background-color: #0e639c;
304
+ color: white;
305
+ border: none;
306
+ padding: 0.4rem 1rem;
307
+ font-size: 0.8rem;
308
+ border-radius: 2px;
309
  }
310
 
311
+ .stButton button:hover {
312
+ background-color: #1177bb;
 
 
313
  }
314
 
315
+ /* VS Code-like editor container */
316
+ .editor-container {
317
  background-color: #1e1e1e;
318
+ border-radius: 4px;
319
+ border: 1px solid #2d2d2d;
320
+ margin-bottom: 1rem;
321
+ display: flex;
322
+ flex-direction: column;
323
  }
324
 
325
+ /* Editor tab bar */
326
+ .tab-bar {
327
+ display: flex;
328
+ background-color: #252526;
329
+ border-bottom: 1px solid #2d2d2d;
330
+ padding: 0;
331
+ height: 36px;
332
+ }
333
+
334
+ .tab {
335
+ padding: 0 15px;
336
+ height: 36px;
337
+ line-height: 36px;
338
+ background-color: #2d2d2d;
339
  color: white;
340
+ border-right: 1px solid #252526;
341
+ font-size: 13px;
342
+ display: flex;
343
+ align-items: center;
344
+ }
345
+
346
+ .tab.active {
347
+ background-color: #1e1e1e;
348
+ border-top: 1px solid #0e639c;
349
+ }
350
+
351
+ .tab-icon {
352
+ margin-right: 6px;
353
+ opacity: 0.8;
354
+ }
355
+
356
+ /* Status bar */
357
+ .status-bar {
358
+ display: flex;
359
+ justify-content: space-between;
360
+ background-color: #007acc;
361
+ color: white;
362
+ padding: 3px 10px;
363
+ font-size: 12px;
364
+ }
365
+
366
+ /* Activity bar */
367
+ .activity-bar {
368
+ width: 50px;
369
+ background-color: #333333;
370
+ display: flex;
371
+ flex-direction: column;
372
+ align-items: center;
373
+ padding-top: 10px;
374
+ }
375
+
376
+ .activity-icon {
377
+ width: 30px;
378
+ height: 30px;
379
+ margin-bottom: 20px;
380
+ opacity: 0.7;
381
  cursor: pointer;
382
+ text-align: center;
383
+ }
384
+
385
+ .activity-icon:hover {
386
+ opacity: 1;
387
+ }
388
+
389
+ /* Terminal/console styling */
390
+ .terminal {
391
+ background-color: #1e1e1e;
392
+ color: #cccccc;
393
+ padding: 10px;
394
+ font-family: 'Cascadia Code', 'Consolas', monospace;
395
  font-size: 13px;
396
+ border-top: 1px solid #2d2d2d;
397
+ overflow-y: auto;
398
+ max-height: 200px;
399
+ }
400
+
401
+ .terminal-error {
402
+ color: #f48771;
403
+ }
404
+
405
+ .terminal-warning {
406
+ color: #cca700;
407
+ }
408
+
409
+ /* Outline view */
410
+ .outline-view {
411
+ background-color: #252526;
412
+ border: 1px solid #2d2d2d;
413
+ border-radius: 4px;
414
  margin-top: 10px;
415
+ max-height: 400px;
416
+ overflow-y: auto;
417
  }
418
 
419
+ .outline-item {
420
+ padding: 5px 10px;
421
+ cursor: pointer;
422
+ display: flex;
423
+ align-items: center;
424
+ }
425
+
426
+ .outline-item:hover {
427
+ background-color: #2a2d2e;
428
+ }
429
+
430
+ .outline-item-text {
431
+ margin-left: 5px;
432
  }
433
 
434
  /* Download button styling */
435
  .download-button {
436
  display: inline-block;
437
+ padding: 8px 16px;
438
  background-color: #3d995e;
439
  color: white !important;
440
  text-align: center;
441
  text-decoration: none;
442
+ font-size: 13px;
443
  border-radius: 2px;
444
  transition: background-color 0.3s;
445
  margin-top: 10px;
 
449
  background-color: #4eb772;
450
  }
451
 
452
+ /* PDF preview area */
453
+ .preview-container {
454
+ background-color: #252526;
455
+ border: 1px solid #2d2d2d;
456
+ border-radius: 4px;
457
+ padding: 15px;
458
+ margin-top: 10px;
459
+ }
460
+
461
+ /* Control panel */
462
+ .control-panel {
463
+ background-color: #252526;
464
+ border: 1px solid #2d2d2d;
465
+ border-radius: 4px;
466
+ padding: 10px;
467
+ margin-bottom: 10px;
468
+ }
469
+
470
+ .button-group {
471
  display: flex;
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;
507
  color: #bbbbbb;
 
520
  border: 1px solid #1e5a3a;
521
  }
522
 
523
+ /* Hide Streamlit footer and menu */
524
+ footer, header {display: none !important;}
525
  #MainMenu {visibility: hidden;}
 
526
 
527
+ /* Custom tabs */
528
+ .stTabs [data-baseweb="tab-list"] {
529
+ background-color: #2d2d2d;
530
+ gap: 0px !important;
531
  }
532
 
533
+ .stTabs [data-baseweb="tab"] {
534
+ background-color: #252526;
535
  color: #cccccc;
536
+ border-radius: 0;
537
+ border-right: 1px solid #1e1e1e;
538
+ padding: 0px 16px;
539
+ height: 36px;
540
  }
541
 
542
+ .stTabs [aria-selected="true"] {
543
+ background-color: #1e1e1e;
544
+ border-top: 2px solid #007acc !important;
545
+ color: white;
546
+ }
547
+
548
+ /* Custom file explorer */
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;
584
+ border-radius: 4px;
585
+ margin-top: 10px;
586
+ padding: 0;
587
+ }
588
+
589
+ .problems-header {
590
+ background-color: #2d2d2d;
591
+ padding: 5px 10px;
592
+ font-weight: bold;
593
+ font-size: 13px;
594
+ }
595
+
596
+ .problem-item {
597
+ padding: 8px 10px;
598
+ border-bottom: 1px solid #2d2d2d;
599
+ display: flex;
600
+ align-items: flex-start;
601
+ }
602
+
603
+ .problem-icon {
604
+ margin-right: 8px;
605
+ flex-shrink: 0;
606
+ }
607
+
608
+ .problem-message {
609
+ flex-grow: 1;
610
+ }
611
+
612
+ .problem-location {
613
+ font-size: 12px;
614
+ color: #8a8a8a;
615
+ margin-top: 2px;
616
+ }
617
+
618
+ /* Document info panel */
619
+ .document-info {
620
+ background-color: #252526;
621
+ border: 1px solid #2d2d2d;
622
+ border-radius: 4px;
623
+ padding: 10px;
624
+ margin-top: 10px;
625
+ font-size: 13px;
626
+ }
627
+
628
+ .info-row {
629
+ display: flex;
630
+ justify-content: space-between;
631
+ margin-bottom: 5px;
632
+ border-bottom: 1px solid #333;
633
+ padding-bottom: 5px;
634
+ }
635
+
636
+ .info-label {
637
+ font-weight: bold;
638
+ color: #8a8a8a;
639
+ }
640
+
641
+ .info-value {
642
+ text-align: right;
643
  }
644
  </style>
645
  """, unsafe_allow_html=True)
646
 
647
+ # Create editor with custom HTML component
648
+ def create_editor_component(key, height=600):
649
+ # Get the current value from session state or use default
650
+ content = st.session_state.get(key, "")
651
+
652
+ # Create Ace editor with proper LaTeX content escaping
653
+ escaped_content = json.dumps(content)
654
+ editor_html = create_ace_editor().replace('{latex_content}', escaped_content)
655
+
656
+ # Create component
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" onclick="insertTextAtCursor('\\\\textbf{}')">
667
+ <strong>B</strong>
668
+ </button>
669
+ <button class="toolbar-button" title="Italic" onclick="insertTextAtCursor('\\\\textit{}')">
670
+ <em>I</em>
671
+ </button>
672
+ <button class="toolbar-button" title="Math" onclick="insertTextAtCursor('$ $')">
673
+
674
+ </button>
675
+ <div class="toolbar-separator"></div>
676
+ <button class="toolbar-button" title="Section" onclick="insertTextAtCursor('\\\\section{}')">
677
+ §
678
+ </button>
679
+ <button class="toolbar-button" title="Subsection" onclick="insertTextAtCursor('\\\\subsection{}')">
680
+ §§
681
+ </button>
682
+ <div class="toolbar-separator"></div>
683
+ <button class="toolbar-button" title="Itemize" onclick="insertTextAtCursor('\\\\begin{itemize}\\n\\t\\\\item \\n\\\\end{itemize}')">
684
+
685
+ </button>
686
+ <button class="toolbar-button" title="Enumerate" onclick="insertTextAtCursor('\\\\begin{enumerate}\\n\\t\\\\item \\n\\\\end{enumerate}')">
687
+ 1.
688
+ </button>
689
+ <div class="toolbar-separator"></div>
690
+ <button class="toolbar-button" title="Equation" onclick="insertTextAtCursor('\\\\begin{equation}\\n \\n\\\\end{equation}')">
691
+ =
692
+ </button>
693
+ <button class="toolbar-button" title="Figure" onclick="insertTextAtCursor('\\\\begin{figure}\\n\\t\\\\centering\\n\\t\\\\includegraphics[width=0.8\\\\textwidth]{image}\\n\\t\\\\caption{Caption}\\n\\t\\\\label{fig:label}\\n\\\\end{figure}')">
694
+ 🖼
695
+ </button>
696
+ <button class="toolbar-button" title="Table" onclick="insertTextAtCursor('\\\\begin{table}\\n\\t\\\\centering\\n\\t\\\\begin{tabular}{ccc}\\n\\t\\tA & B & C \\\\\\\\\\n\\t\\t1 & 2 & 3 \\\\\\\\\\n\\t\\\\end{tabular}\\n\\t\\\\caption{Caption}\\n\\t\\\\label{tab:label}\\n\\\\end{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 = """
725
+ <div class="status-bar">
726
+ <span>LaTeX</span>
727
+ <span>Line: 1, Col: 1</span>
728
+ <span>UTF-8</span>
729
+ </div>
730
+ """
731
+ st.markdown(status_html, unsafe_allow_html=True)
732
+
733
+ # Render a document outline based on section hierarchy
734
+ def render_document_outline(structure):
735
+ if not structure:
736
+ return
737
+
738
+ st.markdown('<div class="outline-view">', unsafe_allow_html=True)
739
+
740
+ for item in structure:
741
+ level = item['level']
742
+ title = item['title']
743
+ type_icon = {
744
+ 'part': '📑',
745
+ 'chapter': '📕',
746
+ 'section': '📌',
747
+ 'subsection': '📎',
748
+ 'subsubsection': '📍'
749
+ }.get(item['type'], '📋')
750
+
751
+ # Indent based on level
752
+ indent = "&nbsp;" * (level * 4)
753
+
754
+ st.markdown(
755
+ f'<div class="outline-item" title="Go to {item["type"]}: {title}">'
756
+ f'<span>{type_icon}</span>'
757
+ f'<span class="outline-item-text">{indent}{title}</span>'
758
+ f'</div>',
759
+ unsafe_allow_html=True
760
+ )
761
 
762
+ st.markdown('</div>', unsafe_allow_html=True)
763
+
764
+ # Render errors and warnings in a VS Code style
765
+ def render_problems(errors, warnings):
766
+ if not errors and not warnings:
767
+ return
768
+
769
+ st.markdown('<div class="problems-panel">', unsafe_allow_html=True)
770
+ st.markdown('<div class="problems-header">Problems</div>', unsafe_allow_html=True)
771
+
772
+ # Show errors
773
+ for error in errors:
774
+ st.markdown(
775
+ f'<div class="problem-item">'
776
+ f'<div class="problem-icon">❌</div>'
777
+ f'<div class="problem-message">{error}'
778
+ f'<div class="problem-location">document.tex</div>'
779
+ f'</div></div>',
780
+ unsafe_allow_html=True
781
+ )
782
+
783
+ # Show warnings
784
+ for warning in warnings:
785
+ st.markdown(
786
+ f'<div class="problem-item">'
787
+ f'<div class="problem-icon">⚠️</div>'
788
+ f'<div class="problem-message">{warning}'
789
+ f'<div class="problem-location">document.tex</div>'
790
+ f'</div></div>',
791
+ unsafe_allow_html=True
792
+ )
793
+
794
+ st.markdown('</div>', unsafe_allow_html=True)
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)
801
+ line_count = len(latex_code.split('\n'))
802
+
803
+ # Find document class
804
+ doc_class_match = re.search(r'\\documentclass(?:\[.*?\])?\{(.*?)\}', latex_code)
805
+ doc_class = doc_class_match.group(1) if doc_class_match else "unknown"
806
+
807
+ # Count equations
808
+ equation_count = len(re.findall(r'\\begin\{equation', latex_code))
809
+
810
+ # Count figures
811
+ figure_count = len(re.findall(r'\\begin\{figure', latex_code))
812
+
813
+ # Count tables
814
+ table_count = len(re.findall(r'\\begin\{table', latex_code))
815
+
816
+ # Render the info panel
817
+ st.markdown('<div class="document-info">', unsafe_allow_html=True)
818
+
819
+ info_rows = [
820
+ ("Document Class", doc_class),
821
+ ("Word Count", word_count),
822
+ ("Character Count", char_count),
823
+ ("Line Count", line_count),
824
+ ("Equations", equation_count),
825
+ ("Figures", figure_count),
826
+ ("Tables", table_count)
827
+ ]
828
+
829
+ for label, value in info_rows:
830
+ st.markdown(
831
+ f'<div class="info-row">'
832
+ f'<span class="info-label">{label}:</span>'
833
+ f'<span class="info-value">{value}</span>'
834
+ f'</div>',
835
+ unsafe_allow_html=True
836
+ )
837
+
838
+ st.markdown('</div>', unsafe_allow_html=True)
839
 
840
  # Main application
841
  def main():
 
 
 
842
  # Initialize session state
843
  if 'latex_code' not in st.session_state:
844
  st.session_state.latex_code = default_template
845
  if 'show_preview' not in st.session_state:
846
  st.session_state.show_preview = False
847
+ if 'last_compiled' not in st.session_state:
848
+ st.session_state.last_compiled = None
849
+ if 'errors' not in st.session_state:
850
+ st.session_state.errors = []
851
+ if 'warnings' not in st.session_state:
852
+ st.session_state.warnings = []
853
 
854
+ # Check installation status
855
  if not is_pdflatex_installed():
856
+ st.warning("⚠️ LaTeX is not installed. The compilation feature will not work.")
857
 
858
+ # Create main layout
859
  col1, col2 = st.columns([3, 2])
860
 
861
  with col1:
862
+ # Create tabs for main editing area with VS Code style
863
+ editor_tabs = st.tabs(["Editor", "Settings"])
 
 
 
864
 
865
+ with editor_tabs[0]:
866
+ # VS Code-like editor interface
867
+ st.markdown('<div class="editor-container">', unsafe_allow_html=True)
868
+
869
+ # Tab bar
870
+ render_editor_tabs()
871
+
872
+ # Toolbar
873
+ render_toolbar()
874
+
875
+ # Initialize Ace editor
876
+ latex_code = create_editor_component("ace_editor", height=500)
877
+ if latex_code is not None:
878
+ st.session_state.latex_code = latex_code
879
+
880
+ # Status bar
881
+ render_status_bar()
882
+
883
+ st.markdown('</div>', unsafe_allow_html=True)
884
+
885
+ # Control buttons with VS Code styling
886
+ st.markdown('<div class="button-group">', unsafe_allow_html=True)
887
+ compile_btn = st.button("Compile", help="Compile LaTeX to PDF")
888
+ load_template_btn = st.button("Load Template", help="Load default template")
889
+ clear_btn = st.button("Clear", help="Clear editor content")
890
+ st.markdown('</div>', unsafe_allow_html=True)
891
+
892
+ # Handle button actions
893
+ if compile_btn:
894
+ st.session_state.compile_clicked = True
895
+ st.session_state.last_compiled = time.time()
896
+
897
+ if load_template_btn:
898
+ st.session_state.latex_code = default_template
899
+ st.rerun()
900
+
901
+ if clear_btn:
902
+ st.session_state.latex_code = ""
903
+ st.rerun()
904
 
905
+ with editor_tabs[1]:
906
+ st.markdown("<h3>LaTeX Settings</h3>", unsafe_allow_html=True)
907
+
908
+ st.markdown('<div class="control-panel">', unsafe_allow_html=True)
909
+ col_a, col_b = st.columns(2)
910
+
911
+ with col_a:
912
+ st.checkbox("Auto-compile on save", value=False, key="auto_compile")
913
+ st.checkbox("Use pdflatex", value=True, key="use_pdflatex")
914
+ st.checkbox("Enable BibTeX", value=False, key="use_bibtex")
915
+
916
+ with col_b:
917
+ st.selectbox("Document Class",
918
+ ["article", "report", "book", "letter", "beamer"],
919
+ index=0, key="doc_class")
920
+ st.selectbox("PDF Engine",
921
+ ["pdflatex", "xelatex", "lualatex"],
922
+ index=0, key="pdf_engine")
923
+
924
+ st.markdown('</div>', unsafe_allow_html=True)
925
 
926
  with col2:
927
+ # Output tabs with VS Code style
928
+ output_tabs = st.tabs(["Output", "Outline", "Problems", "Info"])
929
 
930
+ with output_tabs[0]:
931
+ # PDF compilation and output
932
+ if 'compile_clicked' in st.session_state and st.session_state.compile_clicked:
933
+ with st.spinner("Compiling..."):
934
+ pdf_data, stdout, stderr = latex_to_pdf(st.session_state.latex_code)
 
 
 
 
 
 
 
935
 
936
+ # Parse errors and warnings
937
+ errors, warnings = parse_latex_errors(stdout + stderr)
938
+ st.session_state.errors = errors
939
+ st.session_state.warnings = warnings
 
 
 
 
 
 
 
 
940
 
941
+ if pdf_data:
942
+ st.session_state.pdf_data = pdf_data
943
+ st.success("Compilation successful")
944
+
945
+ # Toggle button for preview
946
+ if st.button("Toggle Preview", help="Show or hide the PDF preview"):
947
+ st.session_state.show_preview = not st.session_state.show_preview
948
+
949
+ # Download button
950
+ st.markdown(get_download_link(pdf_data), unsafe_allow_html=True)
951
+
952
+ # Display compilation info
953
+ if st.session_state.last_compiled:
954
+ time_str = time.strftime("%H:%M:%S", time.localtime(st.session_state.last_compiled))
955
+ st.info(f"Last compiled: {time_str}")
956
+
957
+ # Optional preview
958
+ if st.session_state.show_preview:
959
+ st.markdown('<div class="preview-container">', unsafe_allow_html=True)
960
+ preview_images = render_pdf_preview(pdf_data)
961
+ if preview_images:
962
+ for i, img in enumerate(preview_images):
963
+ st.image(img, caption=f"Page {i+1}", use_container_width=True,
964
+ output_format="PNG")
965
+ st.markdown('</div>', unsafe_allow_html=True)
966
+
967
+ # Terminal output in collapsible section
968
+ with st.expander("Terminal Output"):
969
+ st.markdown('<div class="terminal">', unsafe_allow_html=True)
970
+ st.text(stdout)
971
+ st.markdown('</div>', unsafe_allow_html=True)
972
+
973
+ st.session_state.compile_clicked = False
974
+ else:
975
+ st.error("Compilation failed")
976
+ st.markdown('<div class="terminal">', unsafe_allow_html=True)
977
+
978
+ # Highlight errors in output
979
+ for line in stderr.split('\n'):
980
+ if "error" in line.lower():
981
+ st.markdown(f'<span class="terminal-error">{line}</span>', unsafe_allow_html=True)
982
+ elif "warning" in line.lower():
983
+ st.markdown(f'<span class="terminal-warning">{line}</span>', unsafe_allow_html=True)
984
+ else:
985
+ st.write(line)
986
+
987
  st.markdown('</div>', unsafe_allow_html=True)
988
+ st.session_state.compile_clicked = False
989
+
990
+ # Display previous PDF if available
991
+ elif 'pdf_data' in st.session_state and st.session_state.pdf_data:
992
+ # Toggle button for preview
993
+ if st.button("Toggle Preview", help="Show or hide the PDF preview"):
994
+ st.session_state.show_preview = not st.session_state.show_preview
995
+
996
+ # Download button
997
+ st.markdown(get_download_link(st.session_state.pdf_data), unsafe_allow_html=True)
998
+
999
+ # Display compilation info
1000
+ if st.session_state.last_compiled:
1001
+ time_str = time.strftime("%H:%M:%S", time.localtime(st.session_state.last_compiled))
1002
+ st.info(f"Last compiled: {time_str}")
1003
+
1004
+ # Optional preview
1005
+ if st.session_state.show_preview:
1006
+ st.markdown('<div class="preview-container">', unsafe_allow_html=True)
1007
+ preview_images = render_pdf_preview(st.session_state.pdf_data)
1008
+ if preview_images:
1009
+ for i, img in enumerate(preview_images):
1010
+ st.image(img, caption=f"Page {i+1}", use_container_width=True,
1011
+ output_format="PNG")
1012
  st.markdown('</div>', unsafe_allow_html=True)
1013
+ else:
1014
+ st.info("Click 'Compile' to generate PDF output")
1015
 
1016
+ with output_tabs[1]:
1017
+ # Document structure/outline view
1018
+ structure = extract_document_structure(st.session_state.latex_code)
1019
+ render_document_outline(structure)
1020
+
1021
+ with output_tabs[2]:
1022
+ # Problems panel (errors & warnings)
1023
+ if st.session_state.errors or st.session_state.warnings:
1024
+ render_problems(st.session_state.errors, st.session_state.warnings)
1025
+ else:
1026
+ st.info("No problems detected")
1027
+
1028
+ with output_tabs[3]:
1029
+ # Document information panel
1030
+ render_document_info(st.session_state.latex_code)
 
 
 
 
 
1031
 
1032
  if __name__ == "__main__":
1033
  main()