euler314 commited on
Commit
f282d19
·
verified ·
1 Parent(s): bc4b8de

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +163 -78
app.py CHANGED
@@ -8,6 +8,7 @@ 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")
@@ -85,6 +86,36 @@ def render_pdf_preview(pdf_data):
85
  st.error(f"Error rendering PDF preview: {str(e)}")
86
  return None
87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  # LaTeX package reference
89
  latex_packages = {
90
  "Document": {
@@ -194,6 +225,11 @@ latex_commands = {
194
  }
195
  }
196
 
 
 
 
 
 
197
  # Default LaTeX template
198
  default_template = r"""\documentclass{article}
199
  \usepackage[utf8]{inputenc}
@@ -378,10 +414,31 @@ st.markdown("""
378
  background-color: #f8f9fa;
379
  }
380
 
381
- /* PDF page image */
382
- .pdf-page-image {
 
383
  border: 1px solid #dee2e6;
384
- margin-bottom: 15px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
385
  }
386
  </style>
387
  """, unsafe_allow_html=True)
@@ -390,6 +447,14 @@ st.markdown("""
390
  def main():
391
  st.title("LaTeX Editor & PDF Compiler")
392
 
 
 
 
 
 
 
 
 
393
  # Display installation status
394
  if not is_pdflatex_installed():
395
  st.warning("⚠️ LaTeX is not installed correctly. The PDF compilation feature will not work.")
@@ -406,25 +471,57 @@ python3-dev
406
  python3-pip
407
  poppler-utils""", language="text")
408
 
409
- # Create layout with sidebar
410
  col1, col2 = st.columns([3, 2])
411
 
412
  with col1:
413
  st.subheader("LaTeX Editor")
414
 
415
- # Initialize session state
416
- if 'latex_code' not in st.session_state:
417
- st.session_state.latex_code = default_template
 
 
 
 
 
 
 
 
 
 
 
 
 
 
418
 
419
  # LaTeX editor
420
  latex_code = st.text_area(
421
  "Edit your LaTeX document:",
422
  value=st.session_state.latex_code,
423
  height=500,
424
- key="latex_editor"
 
425
  )
426
  st.session_state.latex_code = latex_code
427
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
428
  # Control buttons
429
  col1_1, col1_2, col1_3 = st.columns(3)
430
 
@@ -443,9 +540,9 @@ poppler-utils""", language="text")
443
  st.rerun()
444
 
445
  with col2:
446
- st.subheader("PDF Preview")
447
 
448
- # PDF compilation and preview
449
  if 'compile_clicked' in st.session_state and st.session_state.compile_clicked:
450
  with st.spinner("Compiling LaTeX to PDF..."):
451
  pdf_data, stdout, stderr = latex_to_pdf(latex_code)
@@ -454,17 +551,22 @@ poppler-utils""", language="text")
454
  st.session_state.pdf_data = pdf_data
455
  st.success("PDF compiled successfully!")
456
 
457
- # Render preview
458
- preview_images = render_pdf_preview(pdf_data)
459
- if preview_images:
460
- st.write("PDF Preview (First Pages):")
461
- for i, img in enumerate(preview_images):
462
- st.image(img, caption=f"Page {i+1}", use_column_width=True,
463
- clamp=True, output_format="PNG")
464
 
465
- # Download button
466
  st.markdown(get_download_link(pdf_data), unsafe_allow_html=True)
467
 
 
 
 
 
 
 
 
 
 
468
  st.session_state.compile_clicked = False
469
  else:
470
  st.error("Compilation Error")
@@ -475,71 +577,54 @@ poppler-utils""", language="text")
475
 
476
  # Display previous PDF if available
477
  elif 'pdf_data' in st.session_state and st.session_state.pdf_data:
478
- preview_images = render_pdf_preview(st.session_state.pdf_data)
479
- if preview_images:
480
- st.write("PDF Preview (First Pages):")
481
- for i, img in enumerate(preview_images):
482
- st.image(img, caption=f"Page {i+1}", use_column_width=True,
483
- clamp=True, output_format="PNG")
484
 
485
- # Download button
486
  st.markdown(get_download_link(st.session_state.pdf_data), unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
487
  else:
488
- st.info("Compile your LaTeX document to see the PDF preview")
489
 
490
  # LaTeX Reference Sidebar
491
  st.sidebar.title("LaTeX Reference")
492
 
493
- # Search functionality
494
- search_query = st.sidebar.text_input("Search commands or packages", "")
495
 
496
- if search_query:
497
- st.sidebar.subheader("Search Results")
498
- found = False
499
-
500
- # Search commands
501
  for category, commands in latex_commands.items():
502
- filtered_commands = {cmd: desc for cmd, desc in commands.items()
503
- if search_query.lower() in cmd.lower() or search_query.lower() in desc.lower()}
504
-
505
- if filtered_commands:
506
- found = True
507
- with st.sidebar.expander(f"{category} ({len(filtered_commands)} results)"):
508
- st.markdown('<div class="command-list">', unsafe_allow_html=True)
509
- for cmd, desc in filtered_commands.items():
510
- col1, col2 = st.columns([3, 1])
511
- with col1:
512
- st.markdown(f'<div><span class="latex-command">{cmd}</span> <span class="command-description">{desc}</span></div>', unsafe_allow_html=True)
513
- with col2:
514
- if st.button("Insert", key=f"btn_{cmd}", help=f"Insert {cmd} into editor"):
515
- st.session_state.latex_code += f"\n{cmd}"
516
- st.rerun()
517
- st.markdown('</div>', unsafe_allow_html=True)
518
-
519
- # Search packages
520
- for category, packages in latex_packages.items():
521
- filtered_packages = {pkg: desc for pkg, desc in packages.items()
522
- if search_query.lower() in pkg.lower() or search_query.lower() in desc.lower()}
523
-
524
- if filtered_packages:
525
- found = True
526
- with st.sidebar.expander(f"Packages: {category} ({len(filtered_packages)} results)"):
527
- st.markdown('<div class="command-list">', unsafe_allow_html=True)
528
- for pkg, desc in filtered_packages.items():
529
- col1, col2 = st.columns([3, 1])
530
- with col1:
531
- st.markdown(f'<div><span class="latex-command">{pkg}</span> <span class="command-description">{desc}</span></div>', unsafe_allow_html=True)
532
- with col2:
533
- if st.button("Insert", key=f"btn_{pkg}", help=f"Insert {pkg} into editor"):
534
- st.session_state.latex_code += f"\n{pkg}"
535
- st.rerun()
536
- st.markdown('</div>', unsafe_allow_html=True)
537
 
538
- if not found:
539
- st.sidebar.info("No matching commands or packages found")
 
 
 
 
 
 
 
 
 
 
540
 
541
- else:
542
- # Display full reference when not searching
543
  tab1, tab2 = st.sidebar.tabs(["Commands", "Packages"])
544
 
545
  with tab1:
@@ -547,11 +632,11 @@ poppler-utils""", language="text")
547
  with st.expander(category, expanded=category=="Math"):
548
  st.markdown('<div class="command-list">', unsafe_allow_html=True)
549
  for cmd, desc in commands.items():
550
- col1, col2 = st.columns([3, 1])
551
  with col1:
552
- st.markdown(f'<div><span class="latex-command">{cmd}</span> <span class="command-description">{desc}</span></div>', unsafe_allow_html=True)
553
  with col2:
554
- if st.button("Insert", key=f"btn_{cmd}", help=f"Insert {cmd} into editor"):
555
  st.session_state.latex_code += f"\n{cmd}"
556
  st.rerun()
557
  st.markdown('</div>', unsafe_allow_html=True)
@@ -561,11 +646,11 @@ poppler-utils""", language="text")
561
  with st.expander(category):
562
  st.markdown('<div class="command-list">', unsafe_allow_html=True)
563
  for pkg, desc in packages.items():
564
- col1, col2 = st.columns([3, 1])
565
  with col1:
566
- st.markdown(f'<div><span class="latex-command">{pkg}</span> <span class="command-description">{desc}</span></div>', unsafe_allow_html=True)
567
  with col2:
568
- if st.button("Insert", key=f"btn_{pkg}", help=f"Insert {pkg} into editor"):
569
  st.session_state.latex_code += f"\n{pkg}"
570
  st.rerun()
571
  st.markdown('</div>', unsafe_allow_html=True)
 
8
  import io
9
  from PIL import Image
10
  import fitz # PyMuPDF
11
+ import re
12
 
13
  # Set page configuration
14
  st.set_page_config(page_title="LaTeX Editor & Compiler", page_icon="📝", layout="wide")
 
86
  st.error(f"Error rendering PDF preview: {str(e)}")
87
  return None
88
 
89
+ # Extract partial command from cursor position
90
+ def get_current_command(text, cursor_pos):
91
+ if cursor_pos <= 0:
92
+ return ""
93
+
94
+ # Look for the last backslash before cursor
95
+ start_pos = text[:cursor_pos].rfind("\\")
96
+ if start_pos == -1:
97
+ return ""
98
+
99
+ # Extract the partial command (from \ to cursor)
100
+ partial_cmd = text[start_pos:cursor_pos]
101
+ return partial_cmd
102
+
103
+ # Find matching commands for autocomplete
104
+ def find_matching_commands(partial_cmd, all_commands):
105
+ if not partial_cmd.startswith("\\"):
106
+ return []
107
+
108
+ partial_without_backslash = partial_cmd[1:].lower()
109
+ matches = []
110
+
111
+ for cmd in all_commands:
112
+ cmd_without_backslash = cmd[1:].lower()
113
+ # Check if command contains the partial input (without \)
114
+ if cmd_without_backslash.startswith(partial_without_backslash):
115
+ matches.append(cmd)
116
+
117
+ return matches
118
+
119
  # LaTeX package reference
120
  latex_packages = {
121
  "Document": {
 
225
  }
226
  }
227
 
228
+ # Create a flat list of all LaTeX commands for autocomplete
229
+ all_latex_commands = []
230
+ for category, commands in latex_commands.items():
231
+ all_latex_commands.extend(list(commands.keys()))
232
+
233
  # Default LaTeX template
234
  default_template = r"""\documentclass{article}
235
  \usepackage[utf8]{inputenc}
 
414
  background-color: #f8f9fa;
415
  }
416
 
417
+ /* Autocomplete suggestions */
418
+ .autocomplete-container {
419
+ background-color: #f8f9fa;
420
  border: 1px solid #dee2e6;
421
+ border-radius: 4px;
422
+ padding: 10px;
423
+ margin-top: 10px;
424
+ max-height: 200px;
425
+ overflow-y: auto;
426
+ }
427
+
428
+ .suggestion-item {
429
+ padding: 5px;
430
+ cursor: pointer;
431
+ border-radius: 3px;
432
+ }
433
+
434
+ .suggestion-item:hover {
435
+ background-color: #e9ecef;
436
+ }
437
+
438
+ /* Preview toggle button */
439
+ .preview-button {
440
+ margin-top: 15px;
441
+ width: 100%;
442
  }
443
  </style>
444
  """, unsafe_allow_html=True)
 
447
  def main():
448
  st.title("LaTeX Editor & PDF Compiler")
449
 
450
+ # Initialize session state variables
451
+ if 'latex_code' not in st.session_state:
452
+ st.session_state.latex_code = default_template
453
+ if 'cursor_pos' not in st.session_state:
454
+ st.session_state.cursor_pos = 0
455
+ if 'show_preview' not in st.session_state:
456
+ st.session_state.show_preview = False
457
+
458
  # Display installation status
459
  if not is_pdflatex_installed():
460
  st.warning("⚠️ LaTeX is not installed correctly. The PDF compilation feature will not work.")
 
471
  python3-pip
472
  poppler-utils""", language="text")
473
 
474
+ # Create layout
475
  col1, col2 = st.columns([3, 2])
476
 
477
  with col1:
478
  st.subheader("LaTeX Editor")
479
 
480
+ # Detect current command for autocomplete
481
+ if 'last_input' in st.session_state:
482
+ current_command = get_current_command(st.session_state.latex_code, st.session_state.cursor_pos)
483
+ matching_commands = find_matching_commands(current_command, all_latex_commands)
484
+
485
+ # Display autocomplete suggestions when typing a command
486
+ if current_command and matching_commands:
487
+ st.markdown("### Command Suggestions")
488
+ st.markdown('<div class="autocomplete-container">', unsafe_allow_html=True)
489
+ for cmd in matching_commands[:10]: # Limit to 10 suggestions
490
+ if st.button(cmd, key=f"suggest_{cmd}"):
491
+ # Replace partial command with the full command
492
+ text_before = st.session_state.latex_code[:st.session_state.cursor_pos - len(current_command)]
493
+ text_after = st.session_state.latex_code[st.session_state.cursor_pos:]
494
+ st.session_state.latex_code = text_before + cmd + text_after
495
+ st.rerun()
496
+ st.markdown('</div>', unsafe_allow_html=True)
497
 
498
  # LaTeX editor
499
  latex_code = st.text_area(
500
  "Edit your LaTeX document:",
501
  value=st.session_state.latex_code,
502
  height=500,
503
+ key="latex_editor",
504
+ on_change=lambda: setattr(st.session_state, 'last_input', True)
505
  )
506
  st.session_state.latex_code = latex_code
507
 
508
+ # Get cursor position for autocomplete (using JS - note this is limited in Streamlit)
509
+ # This is a workaround since Streamlit doesn't directly expose cursor position
510
+ st.markdown("""
511
+ <script>
512
+ document.addEventListener('DOMContentLoaded', function() {
513
+ const textArea = document.querySelector('textarea');
514
+ if (textArea) {
515
+ textArea.addEventListener('keyup', function(e) {
516
+ const cursorPos = this.selectionStart;
517
+ // Would need a way to communicate this to Python
518
+ console.log("Cursor position:", cursorPos);
519
+ });
520
+ }
521
+ });
522
+ </script>
523
+ """, unsafe_allow_html=True)
524
+
525
  # Control buttons
526
  col1_1, col1_2, col1_3 = st.columns(3)
527
 
 
540
  st.rerun()
541
 
542
  with col2:
543
+ st.subheader("PDF Output")
544
 
545
+ # PDF compilation
546
  if 'compile_clicked' in st.session_state and st.session_state.compile_clicked:
547
  with st.spinner("Compiling LaTeX to PDF..."):
548
  pdf_data, stdout, stderr = latex_to_pdf(latex_code)
 
551
  st.session_state.pdf_data = pdf_data
552
  st.success("PDF compiled successfully!")
553
 
554
+ # Toggle button for preview
555
+ if st.button("Show/Hide Preview", use_container_width=True):
556
+ st.session_state.show_preview = not st.session_state.show_preview
 
 
 
 
557
 
558
+ # Download button always available
559
  st.markdown(get_download_link(pdf_data), unsafe_allow_html=True)
560
 
561
+ # Optional preview
562
+ if st.session_state.show_preview:
563
+ preview_images = render_pdf_preview(pdf_data)
564
+ if preview_images:
565
+ st.write("PDF Preview (First Pages):")
566
+ for i, img in enumerate(preview_images):
567
+ st.image(img, caption=f"Page {i+1}", use_container_width=True,
568
+ output_format="PNG")
569
+
570
  st.session_state.compile_clicked = False
571
  else:
572
  st.error("Compilation Error")
 
577
 
578
  # Display previous PDF if available
579
  elif 'pdf_data' in st.session_state and st.session_state.pdf_data:
580
+ # Toggle button for preview
581
+ if st.button("Show/Hide Preview", use_container_width=True):
582
+ st.session_state.show_preview = not st.session_state.show_preview
 
 
 
583
 
584
+ # Download button always available
585
  st.markdown(get_download_link(st.session_state.pdf_data), unsafe_allow_html=True)
586
+
587
+ # Optional preview
588
+ if st.session_state.show_preview:
589
+ preview_images = render_pdf_preview(st.session_state.pdf_data)
590
+ if preview_images:
591
+ st.write("PDF Preview (First Pages):")
592
+ for i, img in enumerate(preview_images):
593
+ st.image(img, caption=f"Page {i+1}", use_container_width=True,
594
+ output_format="PNG")
595
  else:
596
+ st.info("Compile your LaTeX document to generate a PDF for download")
597
 
598
  # LaTeX Reference Sidebar
599
  st.sidebar.title("LaTeX Reference")
600
 
601
+ # Command quick search
602
+ quick_search = st.sidebar.text_input("Type to find commands (e.g. 'fr' for '\\frac')")
603
 
604
+ if quick_search:
605
+ # Find and display matching commands
606
+ matching_cmds = []
 
 
607
  for category, commands in latex_commands.items():
608
+ for cmd, desc in commands.items():
609
+ cmd_without_backslash = cmd.replace("\\", "").lower()
610
+ if quick_search.lower() in cmd_without_backslash:
611
+ matching_cmds.append((cmd, desc, category))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
612
 
613
+ if matching_cmds:
614
+ st.sidebar.markdown("### Matching Commands")
615
+ for cmd, desc, category in matching_cmds[:15]: # Limit to 15 results
616
+ col1, col2 = st.sidebar.columns([4, 1])
617
+ with col1:
618
+ st.markdown(f"<div><span class='latex-command'>{cmd}</span> <small>{category}</small></div>", unsafe_allow_html=True)
619
+ with col2:
620
+ if st.button("Insert", key=f"quick_{cmd}"):
621
+ st.session_state.latex_code += f"\n{cmd}"
622
+ st.rerun()
623
+ else:
624
+ st.sidebar.info("No matching commands found")
625
 
626
+ # Regular categories
627
+ if not quick_search:
628
  tab1, tab2 = st.sidebar.tabs(["Commands", "Packages"])
629
 
630
  with tab1:
 
632
  with st.expander(category, expanded=category=="Math"):
633
  st.markdown('<div class="command-list">', unsafe_allow_html=True)
634
  for cmd, desc in commands.items():
635
+ col1, col2 = st.sidebar.columns([4, 1])
636
  with col1:
637
+ st.markdown(f"<div><span class='latex-command'>{cmd}</span></div>", unsafe_allow_html=True)
638
  with col2:
639
+ if st.button("Insert", key=f"btn_{cmd}"):
640
  st.session_state.latex_code += f"\n{cmd}"
641
  st.rerun()
642
  st.markdown('</div>', unsafe_allow_html=True)
 
646
  with st.expander(category):
647
  st.markdown('<div class="command-list">', unsafe_allow_html=True)
648
  for pkg, desc in packages.items():
649
+ col1, col2 = st.sidebar.columns([4, 1])
650
  with col1:
651
+ st.markdown(f"<div><span class='latex-command'>{pkg}</span></div>", unsafe_allow_html=True)
652
  with col2:
653
+ if st.button("Insert", key=f"btn_{pkg}"):
654
  st.session_state.latex_code += f"\n{pkg}"
655
  st.rerun()
656
  st.markdown('</div>', unsafe_allow_html=True)