Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -10,6 +10,7 @@ from PIL import Image
|
|
10 |
import fitz # PyMuPDF
|
11 |
import time
|
12 |
import re
|
|
|
13 |
|
14 |
# Set page configuration
|
15 |
st.set_page_config(
|
@@ -191,6 +192,24 @@ def find_environments(latex_code):
|
|
191 |
|
192 |
return environments
|
193 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
194 |
# Default LaTeX template
|
195 |
default_template = r"""\documentclass{article}
|
196 |
\usepackage[utf8]{inputenc}
|
@@ -286,7 +305,7 @@ Your conclusion here.
|
|
286 |
\end{document}
|
287 |
"""
|
288 |
|
289 |
-
# Enhanced VSCode-like styling
|
290 |
st.markdown("""
|
291 |
<style>
|
292 |
/* Base theming - VS Code inspired */
|
@@ -309,48 +328,6 @@ st.markdown("""
|
|
309 |
background-color: #1177bb;
|
310 |
}
|
311 |
|
312 |
-
/* Custom editor with built-in syntax highlighting */
|
313 |
-
#latex-editor-container {
|
314 |
-
position: relative;
|
315 |
-
width: 100%;
|
316 |
-
height: 600px;
|
317 |
-
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
|
318 |
-
font-size: 14px;
|
319 |
-
line-height: 1.5;
|
320 |
-
background-color: #1e1e1e;
|
321 |
-
color: #d4d4d4;
|
322 |
-
border-radius: 4px;
|
323 |
-
border: 1px solid #252526;
|
324 |
-
padding: 10px;
|
325 |
-
}
|
326 |
-
|
327 |
-
/* Default coloring for textarea */
|
328 |
-
.stTextArea textarea {
|
329 |
-
font-family: 'Consolas', 'Monaco', 'Courier New', monospace !important;
|
330 |
-
font-size: 14px !important;
|
331 |
-
line-height: 1.5 !important;
|
332 |
-
background-color: #1e1e1e !important;
|
333 |
-
color: #d4d4d4 !important;
|
334 |
-
padding: 10px !important;
|
335 |
-
border-radius: 4px !important;
|
336 |
-
border: 1px solid #252526 !important;
|
337 |
-
}
|
338 |
-
|
339 |
-
/* Highlight LaTeX specific elements */
|
340 |
-
.stTextArea textarea::selection {
|
341 |
-
background-color: #264f78;
|
342 |
-
}
|
343 |
-
|
344 |
-
/* VS Code-like editor container */
|
345 |
-
.editor-container {
|
346 |
-
background-color: #1e1e1e;
|
347 |
-
border-radius: 4px;
|
348 |
-
border: 1px solid #2d2d2d;
|
349 |
-
margin-bottom: 1rem;
|
350 |
-
display: flex;
|
351 |
-
flex-direction: column;
|
352 |
-
}
|
353 |
-
|
354 |
/* Editor tab bar */
|
355 |
.tab-bar {
|
356 |
display: flex;
|
@@ -693,171 +670,15 @@ st.markdown("""
|
|
693 |
margin-right: 10px;
|
694 |
}
|
695 |
|
696 |
-
/*
|
697 |
-
|
698 |
-
|
|
|
|
|
699 |
}
|
700 |
</style>
|
701 |
-
|
702 |
-
<div id="syntax-styles">
|
703 |
-
<style>
|
704 |
-
.latex-command { color: #569cd6; }
|
705 |
-
.latex-environment { color: #4ec9b0; }
|
706 |
-
.latex-bracket { color: #d4d4d4; }
|
707 |
-
.latex-math { color: #c586c0; }
|
708 |
-
.latex-comment { color: #6a9955; }
|
709 |
-
.latex-package { color: #dcdcaa; }
|
710 |
-
</style>
|
711 |
-
</div>
|
712 |
-
|
713 |
-
<script>
|
714 |
-
// Add inline syntax highlighting support for the editor
|
715 |
-
document.addEventListener('DOMContentLoaded', function() {
|
716 |
-
// Function to apply syntax highlighting to the textarea
|
717 |
-
function applySyntaxHighlighting() {
|
718 |
-
const textarea = document.querySelector('textarea');
|
719 |
-
if (!textarea) return;
|
720 |
-
|
721 |
-
// Find text matches and colorize them
|
722 |
-
const text = textarea.value;
|
723 |
-
|
724 |
-
// Color LaTeX commands
|
725 |
-
textarea.style.color = '#d4d4d4'; // Default text color
|
726 |
-
|
727 |
-
// Apply specific colors for different LaTeX constructs
|
728 |
-
// We can't do this directly in the textarea, but we indicate the presence of these elements
|
729 |
-
if (text.match(/\\usepackage/g)) {
|
730 |
-
const packageButtons = document.querySelectorAll('.toolbar-button[title="Add Package"]');
|
731 |
-
packageButtons.forEach(button => button.style.color = '#dcdcaa');
|
732 |
-
}
|
733 |
-
|
734 |
-
if (text.match(/\\begin\{align\}/g)) {
|
735 |
-
const alignButtons = document.querySelectorAll('.toolbar-button[title="Align"]');
|
736 |
-
alignButtons.forEach(button => button.style.color = '#4ec9b0');
|
737 |
-
}
|
738 |
-
|
739 |
-
if (text.match(/\\\w+/g)) {
|
740 |
-
// Commands are present
|
741 |
-
}
|
742 |
-
|
743 |
-
if (text.match(/\$/g)) {
|
744 |
-
const mathButtons = document.querySelectorAll('.toolbar-button[title="Inline Math"]');
|
745 |
-
mathButtons.forEach(button => button.style.color = '#c586c0');
|
746 |
-
}
|
747 |
-
}
|
748 |
-
|
749 |
-
// Set up an interval to periodically check and update highlighting
|
750 |
-
setInterval(applySyntaxHighlighting, 1000);
|
751 |
-
});
|
752 |
-
</script>
|
753 |
""", unsafe_allow_html=True)
|
754 |
|
755 |
-
# Function to create a functional VS Code-like toolbar
|
756 |
-
def render_functional_toolbar():
|
757 |
-
toolbar_html = """
|
758 |
-
<div class="toolbar">
|
759 |
-
<button class="toolbar-button" title="Bold" onclick="insertAtCursor('\\\\textbf{', '}')">
|
760 |
-
<strong>B</strong>
|
761 |
-
</button>
|
762 |
-
<button class="toolbar-button" title="Italic" onclick="insertAtCursor('\\\\textit{', '}')">
|
763 |
-
<em>I</em>
|
764 |
-
</button>
|
765 |
-
<button class="toolbar-button" title="Inline Math" onclick="insertAtCursor('$', '$')">
|
766 |
-
∑
|
767 |
-
</button>
|
768 |
-
<div class="toolbar-separator"></div>
|
769 |
-
<button class="toolbar-button" title="Section" onclick="insertAtCursor('\\\\section{', '}')">
|
770 |
-
§
|
771 |
-
</button>
|
772 |
-
<button class="toolbar-button" title="Subsection" onclick="insertAtCursor('\\\\subsection{', '}')">
|
773 |
-
§§
|
774 |
-
</button>
|
775 |
-
<div class="toolbar-separator"></div>
|
776 |
-
<button class="toolbar-button" title="Itemize" onclick="insertAtCursor('\\\\begin{itemize}\\n \\\\item ', '\\n\\\\end{itemize}')">
|
777 |
-
•
|
778 |
-
</button>
|
779 |
-
<button class="toolbar-button" title="Enumerate" onclick="insertAtCursor('\\\\begin{enumerate}\\n \\\\item ', '\\n\\\\end{enumerate}')">
|
780 |
-
1.
|
781 |
-
</button>
|
782 |
-
<div class="toolbar-separator"></div>
|
783 |
-
<button class="toolbar-button" title="Equation" onclick="insertAtCursor('\\\\begin{equation}\\n ', '\\n\\\\end{equation}')">
|
784 |
-
=
|
785 |
-
</button>
|
786 |
-
<button class="toolbar-button" title="Align" onclick="insertAtCursor('\\\\begin{align}\\n ', '\\n\\\\end{align}')">
|
787 |
-
≡
|
788 |
-
</button>
|
789 |
-
<button class="toolbar-button" title="Figure" onclick="insertAtCursor('\\\\begin{figure}[h]\\n \\\\centering\\n \\\\includegraphics[width=0.8\\\\textwidth]{', '}\\n \\\\caption{Caption}\\n \\\\label{fig:label}\\n\\\\end{figure}')">
|
790 |
-
🖼
|
791 |
-
</button>
|
792 |
-
<button class="toolbar-button" title="Table" onclick="insertAtCursor('\\\\begin{table}[h]\\n \\\\centering\\n \\\\begin{tabular}{ccc}\\n ', ' & B & C \\\\\\\\\\n 1 & 2 & 3 \\\\\\\\\\n \\\\end{tabular}\\n \\\\caption{Caption}\\n \\\\label{tab:label}\\n\\\\end{table}')">
|
793 |
-
⊞
|
794 |
-
</button>
|
795 |
-
<div class="toolbar-separator"></div>
|
796 |
-
<button class="toolbar-button" title="Fraction" onclick="insertAtCursor('\\\\frac{', '}{denominator}')">
|
797 |
-
⅟
|
798 |
-
</button>
|
799 |
-
<button class="toolbar-button" title="Square Root" onclick="insertAtCursor('\\\\sqrt{', '}')">
|
800 |
-
√
|
801 |
-
</button>
|
802 |
-
<button class="toolbar-button" title="Add Package" onclick="insertAtCursor('\\\\usepackage{', '}')">
|
803 |
-
📦
|
804 |
-
</button>
|
805 |
-
</div>
|
806 |
-
|
807 |
-
<script>
|
808 |
-
function insertAtCursor(before, after) {
|
809 |
-
// Find the textarea element
|
810 |
-
var textarea = document.querySelector('textarea');
|
811 |
-
if (!textarea) return;
|
812 |
-
|
813 |
-
// Get current cursor position
|
814 |
-
var startPos = textarea.selectionStart;
|
815 |
-
var endPos = textarea.selectionEnd;
|
816 |
-
|
817 |
-
// Get text before and after selection
|
818 |
-
var textBefore = textarea.value.substring(0, startPos);
|
819 |
-
var textSelected = textarea.value.substring(startPos, endPos);
|
820 |
-
var textAfter = textarea.value.substring(endPos);
|
821 |
-
|
822 |
-
// Insert the text
|
823 |
-
var newText;
|
824 |
-
if (textSelected) {
|
825 |
-
// If text is selected, wrap it with before and after
|
826 |
-
newText = textBefore + before + textSelected + after + textAfter;
|
827 |
-
} else {
|
828 |
-
// If no text is selected, just insert before and after
|
829 |
-
newText = textBefore + before + after + textAfter;
|
830 |
-
// Put cursor between before and after
|
831 |
-
var newCursorPos = startPos + before.length;
|
832 |
-
}
|
833 |
-
|
834 |
-
// Update textarea value
|
835 |
-
textarea.value = newText;
|
836 |
-
|
837 |
-
// Set selection range to position cursor appropriately
|
838 |
-
if (textSelected) {
|
839 |
-
// If text was selected, select the wrapped text
|
840 |
-
textarea.setSelectionRange(startPos, endPos + before.length + after.length);
|
841 |
-
} else {
|
842 |
-
// Otherwise position cursor between before and after
|
843 |
-
textarea.setSelectionRange(newCursorPos, newCursorPos);
|
844 |
-
}
|
845 |
-
|
846 |
-
// Focus on textarea
|
847 |
-
textarea.focus();
|
848 |
-
|
849 |
-
// Trigger an input event to update Streamlit
|
850 |
-
var event = new Event('input', { bubbles: true });
|
851 |
-
textarea.dispatchEvent(event);
|
852 |
-
|
853 |
-
// Trigger change event to update Streamlit session state
|
854 |
-
var changeEvent = new Event('change', { bubbles: true });
|
855 |
-
textarea.dispatchEvent(changeEvent);
|
856 |
-
}
|
857 |
-
</script>
|
858 |
-
"""
|
859 |
-
st.markdown(toolbar_html, unsafe_allow_html=True)
|
860 |
-
|
861 |
# VS Code-style editor tabs
|
862 |
def render_editor_tabs(active_tab="document.tex"):
|
863 |
tab_html = f"""
|
@@ -880,6 +701,26 @@ def render_status_bar():
|
|
880 |
"""
|
881 |
st.markdown(status_html, unsafe_allow_html=True)
|
882 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
883 |
# Render a document outline based on section hierarchy
|
884 |
def render_document_outline(structure):
|
885 |
if not structure:
|
@@ -1052,7 +893,7 @@ def render_environments_panel(environments):
|
|
1052 |
|
1053 |
st.markdown('</div>', unsafe_allow_html=True)
|
1054 |
|
1055 |
-
# Display a simple F5
|
1056 |
def render_f5_shortcut():
|
1057 |
st.markdown(
|
1058 |
'<div class="shortcut-item">'
|
@@ -1062,6 +903,120 @@ def render_f5_shortcut():
|
|
1062 |
unsafe_allow_html=True
|
1063 |
)
|
1064 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1065 |
# Main application
|
1066 |
def main():
|
1067 |
# Initialize session state
|
@@ -1094,20 +1049,74 @@ def main():
|
|
1094 |
# Tab bar
|
1095 |
render_editor_tabs()
|
1096 |
|
1097 |
-
# Toolbar
|
1098 |
-
|
|
|
|
|
|
|
|
|
1099 |
|
1100 |
-
|
1101 |
-
|
1102 |
-
|
1103 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1104 |
value=st.session_state.latex_code,
|
1105 |
height=500,
|
1106 |
-
|
1107 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1108 |
)
|
|
|
1109 |
st.markdown('</div>', unsafe_allow_html=True)
|
1110 |
-
|
|
|
|
|
1111 |
|
1112 |
# Status bar
|
1113 |
render_status_bar()
|
|
|
10 |
import fitz # PyMuPDF
|
11 |
import time
|
12 |
import re
|
13 |
+
from streamlit_monaco import st_monaco
|
14 |
|
15 |
# Set page configuration
|
16 |
st.set_page_config(
|
|
|
192 |
|
193 |
return environments
|
194 |
|
195 |
+
# Define LaTeX code snippets for toolbar actions
|
196 |
+
latex_snippets = {
|
197 |
+
"bold": "\\textbf{$0}",
|
198 |
+
"italic": "\\textit{$0}",
|
199 |
+
"math_inline": "$\n$0\n$",
|
200 |
+
"section": "\\section{$0}",
|
201 |
+
"subsection": "\\subsection{$0}",
|
202 |
+
"itemize": "\\begin{itemize}\n\t\\item $0\n\\end{itemize}",
|
203 |
+
"enumerate": "\\begin{enumerate}\n\t\\item $0\n\\end{enumerate}",
|
204 |
+
"equation": "\\begin{equation}\n\t$0\n\\end{equation}",
|
205 |
+
"align": "\\begin{align}\n\t$0\n\\end{align}",
|
206 |
+
"figure": "\\begin{figure}[h]\n\t\\centering\n\t\\includegraphics[width=0.8\\textwidth]{$0}\n\t\\caption{Caption}\n\t\\label{fig:label}\n\\end{figure}",
|
207 |
+
"table": "\\begin{table}[h]\n\t\\centering\n\t\\begin{tabular}{ccc}\n\t\t$0 & B & C \\\\\n\t\t1 & 2 & 3 \\\\\n\t\\end{tabular}\n\t\\caption{Caption}\n\t\\label{tab:label}\n\\end{table}",
|
208 |
+
"fraction": "\\frac{$0}{denominator}",
|
209 |
+
"sqrt": "\\sqrt{$0}",
|
210 |
+
"package": "\\usepackage{$0}"
|
211 |
+
}
|
212 |
+
|
213 |
# Default LaTeX template
|
214 |
default_template = r"""\documentclass{article}
|
215 |
\usepackage[utf8]{inputenc}
|
|
|
305 |
\end{document}
|
306 |
"""
|
307 |
|
308 |
+
# Enhanced VSCode-like styling
|
309 |
st.markdown("""
|
310 |
<style>
|
311 |
/* Base theming - VS Code inspired */
|
|
|
328 |
background-color: #1177bb;
|
329 |
}
|
330 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
331 |
/* Editor tab bar */
|
332 |
.tab-bar {
|
333 |
display: flex;
|
|
|
670 |
margin-right: 10px;
|
671 |
}
|
672 |
|
673 |
+
/* Monaco editor container */
|
674 |
+
.monaco-editor-container {
|
675 |
+
border: 1px solid #2d2d2d;
|
676 |
+
border-radius: 4px;
|
677 |
+
overflow: hidden;
|
678 |
}
|
679 |
</style>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
680 |
""", unsafe_allow_html=True)
|
681 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
682 |
# VS Code-style editor tabs
|
683 |
def render_editor_tabs(active_tab="document.tex"):
|
684 |
tab_html = f"""
|
|
|
701 |
"""
|
702 |
st.markdown(status_html, unsafe_allow_html=True)
|
703 |
|
704 |
+
# VS Code-style toolbar buttons
|
705 |
+
def render_toolbar_buttons():
|
706 |
+
col1, col2, col3, col4, col5 = st.columns([1, 1, 1, 1, 5])
|
707 |
+
|
708 |
+
with col1:
|
709 |
+
st.button("Bold", key="btn_bold", help="Insert bold text")
|
710 |
+
st.button("Italic", key="btn_italic", help="Insert italic text")
|
711 |
+
|
712 |
+
with col2:
|
713 |
+
st.button("Section", key="btn_section", help="Insert section heading")
|
714 |
+
st.button("Subsection", key="btn_subsection", help="Insert subsection heading")
|
715 |
+
|
716 |
+
with col3:
|
717 |
+
st.button("Itemize", key="btn_itemize", help="Insert bulleted list")
|
718 |
+
st.button("Enumerate", key="btn_enumerate", help="Insert numbered list")
|
719 |
+
|
720 |
+
with col4:
|
721 |
+
st.button("Equation", key="btn_equation", help="Insert equation environment")
|
722 |
+
st.button("Table", key="btn_table", help="Insert table environment")
|
723 |
+
|
724 |
# Render a document outline based on section hierarchy
|
725 |
def render_document_outline(structure):
|
726 |
if not structure:
|
|
|
893 |
|
894 |
st.markdown('</div>', unsafe_allow_html=True)
|
895 |
|
896 |
+
# Display a simple F5 shortcut reminder
|
897 |
def render_f5_shortcut():
|
898 |
st.markdown(
|
899 |
'<div class="shortcut-item">'
|
|
|
903 |
unsafe_allow_html=True
|
904 |
)
|
905 |
|
906 |
+
# Define LaTeX language for Monaco editor
|
907 |
+
def get_latex_language_definition():
|
908 |
+
return {
|
909 |
+
"defaultToken": "",
|
910 |
+
"tokenPostfix": ".tex",
|
911 |
+
"ignoreCase": True,
|
912 |
+
|
913 |
+
"brackets": [
|
914 |
+
{ "token": "delimiter.curly", "open": "{", "close": "}" },
|
915 |
+
{ "token": "delimiter.square", "open": "[", "close": "]" },
|
916 |
+
{ "token": "delimiter.parenthesis", "open": "(", "close": ")" }
|
917 |
+
],
|
918 |
+
|
919 |
+
"keywords": [
|
920 |
+
"documentclass", "usepackage", "begin", "end", "section", "subsection",
|
921 |
+
"subsubsection", "chapter", "paragraph", "subparagraph", "part", "appendix",
|
922 |
+
"maketitle", "tableofcontents", "figure", "table", "equation", "align",
|
923 |
+
"itemize", "enumerate", "description", "center", "verbatim", "minipage",
|
924 |
+
"textbf", "textit", "texttt", "textrm", "textsc", "textsf", "emph", "cite",
|
925 |
+
"ref", "label", "caption", "includegraphics", "input", "include", "hline",
|
926 |
+
"vspace", "hspace", "newpage", "clearpage", "pagebreak", "newcommand",
|
927 |
+
"renewcommand", "providecommand", "newenvironment", "renewenvironment",
|
928 |
+
"provideenvironment", "newtheorem", "footnote", "item", "multicolumn"
|
929 |
+
],
|
930 |
+
|
931 |
+
"operators": [
|
932 |
+
"&", "_", "^", "~", "'", "\"", "`"
|
933 |
+
],
|
934 |
+
|
935 |
+
"tokenizer": {
|
936 |
+
"root": [
|
937 |
+
[/\\([a-zA-Z]+)/, {
|
938 |
+
cases: {
|
939 |
+
"@keywords": { token: "keyword.$1" },
|
940 |
+
"@default": "keyword"
|
941 |
+
}
|
942 |
+
}],
|
943 |
+
[/\{/, "delimiter.curly"],
|
944 |
+
[/\}/, "delimiter.curly"],
|
945 |
+
[/\[/, "delimiter.square"],
|
946 |
+
[/\]/, "delimiter.square"],
|
947 |
+
[/\(/, "delimiter.parenthesis"],
|
948 |
+
[/\)/, "delimiter.parenthesis"],
|
949 |
+
[/\$\$/, { token: "delimiter.dollar", next: "@math_block" }],
|
950 |
+
[/\$/, { token: "delimiter.dollar", next: "@math_inline" }],
|
951 |
+
[/%.*$/, "comment"],
|
952 |
+
[/[^\\{}\[\]$%&_\^~'"` ]+/, ""]
|
953 |
+
],
|
954 |
+
|
955 |
+
"math_block": [
|
956 |
+
[/\$\$/, { token: "delimiter.dollar", next: "@pop" }],
|
957 |
+
[/[^\\$]+/, "predefined.mathematical"],
|
958 |
+
[/\\[a-zA-Z]+/, "predefined.mathematical"],
|
959 |
+
[/[\\$]/, "predefined.mathematical"]
|
960 |
+
],
|
961 |
+
|
962 |
+
"math_inline": [
|
963 |
+
[/\$/, { token: "delimiter.dollar", next: "@pop" }],
|
964 |
+
[/[^\\$]+/, "predefined.mathematical"],
|
965 |
+
[/\\[a-zA-Z]+/, "predefined.mathematical"],
|
966 |
+
[/[\\$]/, "predefined.mathematical"]
|
967 |
+
]
|
968 |
+
}
|
969 |
+
}
|
970 |
+
|
971 |
+
# Define LaTeX autocompletion suggestions for Monaco editor
|
972 |
+
def get_latex_completions():
|
973 |
+
return [
|
974 |
+
# Document structure
|
975 |
+
{"label": "\\documentclass", "kind": 14, "insertText": "\\documentclass{${1:article}}", "documentation": "Defines the document class"},
|
976 |
+
{"label": "\\usepackage", "kind": 14, "insertText": "\\usepackage{${1:package}}", "documentation": "Imports a package"},
|
977 |
+
{"label": "\\begin", "kind": 14, "insertText": "\\begin{${1:environment}}\n\t$0\n\\end{$1}", "documentation": "Creates an environment"},
|
978 |
+
{"label": "\\end", "kind": 14, "insertText": "\\end{${1:environment}}", "documentation": "Ends an environment"},
|
979 |
+
|
980 |
+
# Sections
|
981 |
+
{"label": "\\section", "kind": 14, "insertText": "\\section{${1:title}}", "documentation": "Creates a section"},
|
982 |
+
{"label": "\\subsection", "kind": 14, "insertText": "\\subsection{${1:title}}", "documentation": "Creates a subsection"},
|
983 |
+
{"label": "\\subsubsection", "kind": 14, "insertText": "\\subsubsection{${1:title}}", "documentation": "Creates a subsubsection"},
|
984 |
+
|
985 |
+
# Formatting
|
986 |
+
{"label": "\\textbf", "kind": 14, "insertText": "\\textbf{${1:text}}", "documentation": "Makes text bold"},
|
987 |
+
{"label": "\\textit", "kind": 14, "insertText": "\\textit{${1:text}}", "documentation": "Makes text italic"},
|
988 |
+
{"label": "\\underline", "kind": 14, "insertText": "\\underline{${1:text}}", "documentation": "Underlines text"},
|
989 |
+
{"label": "\\emph", "kind": 14, "insertText": "\\emph{${1:text}}", "documentation": "Emphasizes text"},
|
990 |
+
|
991 |
+
# Math
|
992 |
+
{"label": "\\begin{equation}", "kind": 14, "insertText": "\\begin{equation}\n\t${1}\n\\end{equation}", "documentation": "Creates an equation environment"},
|
993 |
+
{"label": "\\begin{align}", "kind": 14, "insertText": "\\begin{align}\n\t${1}\n\\end{align}", "documentation": "Creates an align environment"},
|
994 |
+
{"label": "\\frac", "kind": 14, "insertText": "\\frac{${1:numerator}}{${2:denominator}}", "documentation": "Creates a fraction"},
|
995 |
+
{"label": "\\sqrt", "kind": 14, "insertText": "\\sqrt{${1:expression}}", "documentation": "Creates a square root"},
|
996 |
+
{"label": "\\sum", "kind": 14, "insertText": "\\sum_{${1:lower}}^{${2:upper}}", "documentation": "Creates a summation"},
|
997 |
+
{"label": "\\int", "kind": 14, "insertText": "\\int_{${1:lower}}^{${2:upper}}", "documentation": "Creates an integral"},
|
998 |
+
|
999 |
+
# Lists
|
1000 |
+
{"label": "\\begin{itemize}", "kind": 14, "insertText": "\\begin{itemize}\n\t\\item ${1}\n\\end{itemize}", "documentation": "Creates a bulleted list"},
|
1001 |
+
{"label": "\\begin{enumerate}", "kind": 14, "insertText": "\\begin{enumerate}\n\t\\item ${1}\n\\end{enumerate}", "documentation": "Creates a numbered list"},
|
1002 |
+
{"label": "\\item", "kind": 14, "insertText": "\\item ${1}", "documentation": "Creates a list item"},
|
1003 |
+
|
1004 |
+
# Tables
|
1005 |
+
{"label": "\\begin{table}", "kind": 14, "insertText": "\\begin{table}[${1:h}]\n\t\\centering\n\t\\begin{tabular}{${2:ccc}}\n\t\t${3}\n\t\\end{tabular}\n\t\\caption{${4:caption}}\n\t\\label{tab:${5:label}}\n\\end{table}", "documentation": "Creates a table environment"},
|
1006 |
+
|
1007 |
+
# Figures
|
1008 |
+
{"label": "\\begin{figure}", "kind": 14, "insertText": "\\begin{figure}[${1:h}]\n\t\\centering\n\t\\includegraphics[width=${2:0.8}\\textwidth]{${3:filename}}\n\t\\caption{${4:caption}}\n\t\\label{fig:${5:label}}\n\\end{figure}", "documentation": "Creates a figure environment"},
|
1009 |
+
|
1010 |
+
# Common environments
|
1011 |
+
{"label": "\\begin{center}", "kind": 14, "insertText": "\\begin{center}\n\t${1}\n\\end{center}", "documentation": "Creates centered text"},
|
1012 |
+
{"label": "\\begin{verbatim}", "kind": 14, "insertText": "\\begin{verbatim}\n${1}\n\\end{verbatim}", "documentation": "Creates verbatim text"},
|
1013 |
+
|
1014 |
+
# References
|
1015 |
+
{"label": "\\label", "kind": 14, "insertText": "\\label{${1:label}}", "documentation": "Creates a label for cross-referencing"},
|
1016 |
+
{"label": "\\ref", "kind": 14, "insertText": "\\ref{${1:label}}", "documentation": "References a label"},
|
1017 |
+
{"label": "\\cite", "kind": 14, "insertText": "\\cite{${1:key}}", "documentation": "Cites a reference"}
|
1018 |
+
]
|
1019 |
+
|
1020 |
# Main application
|
1021 |
def main():
|
1022 |
# Initialize session state
|
|
|
1049 |
# Tab bar
|
1050 |
render_editor_tabs()
|
1051 |
|
1052 |
+
# Toolbar buttons
|
1053 |
+
render_toolbar_buttons()
|
1054 |
+
|
1055 |
+
# Check for toolbar button clicks
|
1056 |
+
button_clicked = False
|
1057 |
+
snippet_to_insert = ""
|
1058 |
|
1059 |
+
if st.session_state.get("btn_bold", False):
|
1060 |
+
snippet_to_insert = latex_snippets["bold"]
|
1061 |
+
button_clicked = True
|
1062 |
+
elif st.session_state.get("btn_italic", False):
|
1063 |
+
snippet_to_insert = latex_snippets["italic"]
|
1064 |
+
button_clicked = True
|
1065 |
+
elif st.session_state.get("btn_section", False):
|
1066 |
+
snippet_to_insert = latex_snippets["section"]
|
1067 |
+
button_clicked = True
|
1068 |
+
elif st.session_state.get("btn_subsection", False):
|
1069 |
+
snippet_to_insert = latex_snippets["subsection"]
|
1070 |
+
button_clicked = True
|
1071 |
+
elif st.session_state.get("btn_itemize", False):
|
1072 |
+
snippet_to_insert = latex_snippets["itemize"]
|
1073 |
+
button_clicked = True
|
1074 |
+
elif st.session_state.get("btn_enumerate", False):
|
1075 |
+
snippet_to_insert = latex_snippets["enumerate"]
|
1076 |
+
button_clicked = True
|
1077 |
+
elif st.session_state.get("btn_equation", False):
|
1078 |
+
snippet_to_insert = latex_snippets["equation"]
|
1079 |
+
button_clicked = True
|
1080 |
+
elif st.session_state.get("btn_table", False):
|
1081 |
+
snippet_to_insert = latex_snippets["table"]
|
1082 |
+
button_clicked = True
|
1083 |
+
|
1084 |
+
# Monaco Editor with LaTeX configuration
|
1085 |
+
st.markdown('<div class="monaco-editor-container">', unsafe_allow_html=True)
|
1086 |
+
|
1087 |
+
# Define LaTeX language configuration for Monaco
|
1088 |
+
latex_language = get_latex_language_definition()
|
1089 |
+
latex_completions = get_latex_completions()
|
1090 |
+
|
1091 |
+
# Create Monaco editor component
|
1092 |
+
latex_code = st_monaco(
|
1093 |
value=st.session_state.latex_code,
|
1094 |
height=500,
|
1095 |
+
language="latex",
|
1096 |
+
theme="vs-dark",
|
1097 |
+
options={
|
1098 |
+
"scrollBeyondLastLine": False,
|
1099 |
+
"minimap": {"enabled": True},
|
1100 |
+
"lineNumbers": "on",
|
1101 |
+
"tabSize": 2,
|
1102 |
+
"insertSpaces": True,
|
1103 |
+
"fontSize": 14,
|
1104 |
+
"wordWrap": "on",
|
1105 |
+
"automaticLayout": True,
|
1106 |
+
"suggestOnTriggerCharacters": True,
|
1107 |
+
"quickSuggestions": True,
|
1108 |
+
"snippetSuggestions": "inline",
|
1109 |
+
"folding": True,
|
1110 |
+
"bracketPairColorization.enabled": True,
|
1111 |
+
"renderWhitespace": "boundary"
|
1112 |
+
},
|
1113 |
+
key="monaco_editor"
|
1114 |
)
|
1115 |
+
|
1116 |
st.markdown('</div>', unsafe_allow_html=True)
|
1117 |
+
|
1118 |
+
if latex_code is not None:
|
1119 |
+
st.session_state.latex_code = latex_code
|
1120 |
|
1121 |
# Status bar
|
1122 |
render_status_bar()
|