euler314 commited on
Commit
189b68e
·
verified ·
1 Parent(s): 10222d3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +153 -179
app.py CHANGED
@@ -26,6 +26,10 @@ import pandas as pd
26
  import plotly.express as px
27
  import markdown
28
  import zipfile
 
 
 
 
29
 
30
  # Set up enhanced logging
31
  logging.basicConfig(
@@ -63,7 +67,6 @@ def ensure_packages():
63
  'matplotlib': '3.5.0', # For Python script runner
64
  'seaborn': '0.11.2', # For enhanced visualizations
65
  'scipy': '1.7.3', # For scientific computations
66
- 'accelerate': '0.20.0',
67
  }
68
 
69
  with st.spinner("Checking required packages..."):
@@ -302,7 +305,7 @@ QUALITY_PRESETS = {
302
  "720p": {"resolution": "720p", "fps": "30"},
303
  "1080p": {"resolution": "1080p", "fps": "60"},
304
  "4K": {"resolution": "2160p", "fps": "60"},
305
- "8K": {"resolution": "4320p", "fps": "60"}
306
  }
307
 
308
  # Animation speeds
@@ -510,7 +513,7 @@ def generate_manim_video(python_code, format_type, quality_preset, animation_spe
510
  "720p": "-qm", # Medium quality
511
  "1080p": "-qh", # High quality
512
  "4K": "-qk", # 4K quality
513
- "8K": "-qu" # Ultra quality (8K)
514
  }
515
  quality_flag = quality_map.get(quality_preset, "-qm")
516
 
@@ -1170,7 +1173,7 @@ def create_timeline_editor(code):
1170
  animation_steps = df.to_dict('records')
1171
  new_code = generate_code_from_timeline(animation_steps, code)
1172
 
1173
- st.success("Timeline updated! Click 'Refresh App' to see changes.")
1174
  return new_code
1175
 
1176
  # Visual keyframe editor
@@ -1467,32 +1470,6 @@ def export_to_educational_format(video_data, format_type, animation_title, expla
1467
  logger.error(traceback.format_exc())
1468
  return None, None
1469
 
1470
- def process_multiple_images(uploaded_images):
1471
- """Process multiple uploaded images and return information about them"""
1472
- processed_images = []
1473
-
1474
- # Create a unique image directory if it doesn't exist
1475
- image_dir = os.path.join(os.getcwd(), "manim_assets", "images")
1476
- os.makedirs(image_dir, exist_ok=True)
1477
-
1478
- for img in uploaded_images:
1479
- # Generate a unique filename and save the image
1480
- file_extension = img.name.split(".")[-1]
1481
- unique_filename = f"image_{int(time.time())}_{len(processed_images)}.{file_extension}"
1482
- image_path = os.path.join(image_dir, unique_filename)
1483
-
1484
- with open(image_path, "wb") as f:
1485
- f.write(img.getvalue())
1486
-
1487
- # Store image info
1488
- processed_images.append({
1489
- "name": img.name,
1490
- "path": image_path,
1491
- "preview": img
1492
- })
1493
-
1494
- return processed_images
1495
-
1496
  def main():
1497
  # Initialize session state variables if they don't exist
1498
  if 'init' not in st.session_state:
@@ -1511,13 +1488,12 @@ def main():
1511
  st.session_state.custom_library_result = ""
1512
  st.session_state.python_script = "import matplotlib.pyplot as plt\nimport numpy as np\n\n# Example: Create a simple plot\nx = np.linspace(0, 10, 100)\ny = np.sin(x)\n\nplt.figure(figsize=(10, 6))\nplt.plot(x, y, 'b-', label='sin(x)')\nplt.title('Sine Wave')\nplt.xlabel('x')\nplt.ylabel('sin(x)')\nplt.grid(True)\nplt.legend()\n"
1513
  st.session_state.python_result = None
 
1514
  st.session_state.settings = {
1515
  "quality": "720p",
1516
  "format_type": "mp4",
1517
  "animation_speed": "Normal"
1518
  }
1519
- st.session_state.pending_changes = False # Track if there are changes that need refresh
1520
- st.session_state.pending_temp_code = "" # Store code changes before refresh
1521
 
1522
  # Page configuration with improved layout
1523
  st.set_page_config(
@@ -1625,41 +1601,6 @@ def main():
1625
  margin-top: 1rem;
1626
  border-left: 4px solid #ef4444;
1627
  }
1628
-
1629
- .refresh-button {
1630
- background-color: #4CAF50;
1631
- color: white;
1632
- padding: 10px 20px;
1633
- border: none;
1634
- border-radius: 5px;
1635
- cursor: pointer;
1636
- font-weight: bold;
1637
- margin: 10px 0;
1638
- }
1639
-
1640
- .refresh-button:hover {
1641
- background-color: #45a049;
1642
- }
1643
-
1644
- .image-grid {
1645
- display: grid;
1646
- grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
1647
- gap: 10px;
1648
- margin-top: 10px;
1649
- }
1650
-
1651
- .image-item {
1652
- border: 1px solid #ddd;
1653
- border-radius: 5px;
1654
- padding: 5px;
1655
- text-align: center;
1656
- }
1657
-
1658
- .image-item img {
1659
- max-width: 100%;
1660
- height: auto;
1661
- border-radius: 3px;
1662
- }
1663
  </style>
1664
  """, unsafe_allow_html=True)
1665
 
@@ -1684,18 +1625,11 @@ def main():
1684
  st.session_state.ai_models = init_ai_models()
1685
 
1686
  # Create main tabs
1687
- tabs = st.tabs(["✨ Editor", "🤖 AI Assistant", "📚 LaTeX Formulas", "🎨 Assets", "🎞️ Timeline", "🎓 Educational Export", "🐍 Python Runner"])
 
1688
 
1689
- # Sidebar for rendering settings, custom libraries, and refresh button
1690
  with st.sidebar:
1691
- # Add refresh button at the top of the sidebar
1692
- if st.button("🔄 Refresh App", key="refresh_button", help="Refresh the app to apply all changes", use_container_width=True):
1693
- st.success("App refreshed!")
1694
- if st.session_state.pending_changes:
1695
- st.session_state.code = st.session_state.pending_temp_code
1696
- st.session_state.temp_code = st.session_state.pending_temp_code
1697
- st.session_state.pending_changes = False
1698
-
1699
  # Rendering settings section
1700
  st.markdown("## ⚙️ Rendering Settings")
1701
 
@@ -1775,36 +1709,31 @@ def main():
1775
  if uploaded_file:
1776
  code_content = uploaded_file.getvalue().decode("utf-8")
1777
  if code_content.strip(): # Only update if file has content
1778
- st.session_state.pending_temp_code = code_content
1779
- st.session_state.pending_changes = True
1780
- st.info("Code loaded from file. Click 'Refresh App' to apply changes.")
1781
 
1782
  # Code editor
1783
  if ACE_EDITOR_AVAILABLE:
1784
  current_code = st.session_state.code if hasattr(st.session_state, 'code') and st.session_state.code else ""
1785
- temp_code = st_ace(
1786
  value=current_code,
1787
  language="python",
1788
  theme="monokai",
1789
  min_lines=20,
1790
  key="ace_editor"
1791
  )
1792
- if temp_code != current_code:
1793
- st.session_state.pending_temp_code = temp_code
1794
- st.session_state.pending_changes = True
1795
- st.info("Code changes detected. Click 'Refresh App' to apply changes.")
1796
  else:
1797
  current_code = st.session_state.code if hasattr(st.session_state, 'code') and st.session_state.code else ""
1798
- temp_code = st.text_area(
1799
  "Manim Python Code",
1800
  value=current_code,
1801
  height=400,
1802
  key="code_textarea"
1803
  )
1804
- if temp_code != current_code:
1805
- st.session_state.pending_temp_code = temp_code
1806
- st.session_state.pending_changes = True
1807
- st.info("Code changes detected. Click 'Refresh App' to apply changes.")
1808
 
1809
  # Generate button (use a form to prevent page reloads)
1810
  generate_btn = st.button("🚀 Generate Animation", use_container_width=True, key="generate_btn")
@@ -1825,9 +1754,8 @@ class MyScene(Scene):
1825
  self.wait(2)
1826
  """
1827
  st.session_state.code += default_scene
1828
- st.session_state.pending_temp_code = st.session_state.code
1829
- st.session_state.pending_changes = True
1830
- st.warning("No scene class found. Added a default scene. Click 'Refresh App' to see changes.")
1831
 
1832
  with st.spinner("Generating animation..."):
1833
  video_data, status = generate_manim_video(
@@ -1915,7 +1843,7 @@ class MyScene(Scene):
1915
  1. **Syntax Errors**: Check your Python code for any syntax issues
1916
  2. **Missing Scene Class**: Ensure your code contains a scene class that extends Scene
1917
  3. **High Resolution Issues**: Try a lower quality preset for complex animations
1918
- 4. **Memory Issues**: For 4K or 8K animations, reduce complexity or try again
1919
  5. **Format Issues**: Some formats require specific Manim configurations
1920
  6. **GIF Generation**: If GIF doesn't work, try MP4 and we'll convert it automatically
1921
 
@@ -1987,9 +1915,11 @@ class MyScene(Scene):
1987
  col_ai1, col_ai2 = st.columns(2)
1988
  with col_ai1:
1989
  if st.button("Use This Code", key="use_gen_code"):
1990
- st.session_state.pending_temp_code = st.session_state.generated_code
1991
- st.session_state.pending_changes = True
1992
- st.info("AI code ready to use. Click 'Refresh App' to apply it to the editor.")
 
 
1993
 
1994
  with col_ai2:
1995
  if st.button("Render Preview", key="render_preview"):
@@ -2032,9 +1962,8 @@ class MyScene(Scene):
2032
  key="latex_input"
2033
  )
2034
 
2035
- # Update session state without auto-refresh
2036
- if latex_input != st.session_state.latex_formula:
2037
- st.session_state.latex_formula = latex_input
2038
 
2039
  # Common LaTeX formulas library
2040
  st.markdown("#### Formula Library")
@@ -2082,9 +2011,10 @@ class MyScene(Scene):
2082
  with formula_tabs[i]:
2083
  for formula in formulas:
2084
  if st.button(formula["name"], key=f"latex_{formula['name']}"):
 
2085
  st.session_state.latex_formula = formula["latex"]
2086
- # No automatic rerun - user needs to click refresh
2087
- st.info(f"Added '{formula['name']}'. Click 'Refresh App' to update the preview.")
2088
 
2089
  # LaTeX code snippet
2090
  st.markdown("#### Manim Code Snippet")
@@ -2122,10 +2052,13 @@ self.wait(2)
2122
  # If we didn't find content, append to the end with default indentation
2123
  lines.append(" " + "\n ".join(manim_latex_code.strip().split("\n")))
2124
 
2125
- new_code = "\n".join(lines)
2126
- st.session_state.pending_temp_code = new_code
2127
- st.session_state.pending_changes = True
2128
- st.success("LaTeX formula prepared for insertion. Click 'Refresh App' to update the editor.")
 
 
 
2129
  else:
2130
  st.warning("Could not find 'construct' method in your code. Please add a scene class first.")
2131
  else:
@@ -2139,9 +2072,13 @@ class LatexScene(Scene):
2139
  self.play(Write(formula))
2140
  self.wait(2)
2141
  """
2142
- st.session_state.pending_temp_code = basic_scene
2143
- st.session_state.pending_changes = True
2144
- st.success("Created new scene with your LaTeX formula. Click 'Refresh App' to update the editor.")
 
 
 
 
2145
 
2146
  with col_latex2:
2147
  # LaTeX preview
@@ -2183,79 +2120,102 @@ class LatexScene(Scene):
2183
  st.markdown("#### 📸 Image Assets")
2184
  st.markdown("Upload images to use in your animations:")
2185
 
2186
- # Multiple image uploader
2187
- uploaded_images = st.file_uploader("Upload Images", type=["jpg", "png", "jpeg", "svg"], accept_multiple_files=True, key="image_uploader_tab")
 
 
 
 
 
2188
 
2189
  if uploaded_images:
2190
- # Process all uploaded images
2191
- with st.spinner("Processing uploaded images..."):
2192
- processed_images = process_multiple_images(uploaded_images)
 
 
 
 
 
 
 
2193
 
2194
- # Add to session state
 
 
 
2195
  if "image_paths" not in st.session_state:
2196
  st.session_state.image_paths = []
2197
 
2198
- st.session_state.image_paths.extend(processed_images)
2199
-
2200
- # Show preview of uploaded images
2201
- st.success(f"Successfully uploaded {len(processed_images)} images")
 
 
 
2202
 
2203
- # Display image grid
2204
- st.markdown("#### New Images")
2205
- image_cols = st.columns(min(4, len(processed_images)))
2206
- for i, img_info in enumerate(processed_images):
2207
- with image_cols[i % len(image_cols)]:
2208
- st.image(img_info["preview"], caption=img_info["name"], width=150)
 
 
 
 
 
 
 
 
 
2209
 
2210
- # Show code to use images
2211
- if processed_images:
2212
- st.markdown("#### Code to use these images:")
2213
- image_code_samples = []
2214
- for i, img_info in enumerate(processed_images):
2215
- image_code = f"""
2216
- # Load and display image {i+1}: {img_info['name']}
2217
- image{i+1} = ImageMobject(r"{img_info['path']}")
2218
- image{i+1}.scale(2) # Adjust size as needed
2219
- self.play(FadeIn(image{i+1}))
2220
  self.wait(1)
2221
  """
2222
- image_code_samples.append(image_code)
2223
-
2224
- all_images_code = "\n".join(image_code_samples)
2225
- st.code(all_images_code, language="python")
2226
-
2227
- if st.button("Insert All Images Code", key="insert_images_code_btn"):
2228
- if not st.session_state.code:
2229
- base_code = """from manim import *
2230
 
2231
  class ImageScene(Scene):
2232
  def construct(self):
2233
  """
2234
- indented_code = "\n ".join(all_images_code.split("\n"))
2235
- new_code = base_code + "\n " + indented_code
2236
- else:
2237
- new_code = st.session_state.code + "\n" + all_images_code
2238
-
2239
- st.session_state.pending_temp_code = new_code
2240
- st.session_state.pending_changes = True
2241
- st.success("Image code prepared for insertion. Click 'Refresh App' to update the editor.")
 
 
 
 
2242
 
2243
  # Display previously uploaded images
2244
  if st.session_state.image_paths:
2245
- st.markdown("#### All Available Images")
2246
-
2247
- # Display in a grid format
2248
- st.markdown('<div class="image-grid">', unsafe_allow_html=True)
2249
-
2250
- for idx, img_info in enumerate(st.session_state.image_paths):
2251
- st.markdown(f"""
2252
- <div class="asset-card">
2253
- <p><strong>{img_info['name']}</strong></p>
2254
- <p class="small-text">Path: {img_info['path']}</p>
2255
- </div>
2256
- """, unsafe_allow_html=True)
2257
-
2258
- st.markdown('</div>', unsafe_allow_html=True)
 
2259
 
2260
  with asset_col2:
2261
  # Audio uploader section
@@ -2341,8 +2301,8 @@ class YourScene(Scene):
2341
 
2342
  # If code was modified by the timeline editor, update the session state
2343
  if updated_code != st.session_state.code:
2344
- st.session_state.pending_temp_code = updated_code
2345
- st.session_state.pending_changes = True
2346
 
2347
  # EDUCATIONAL EXPORT TAB
2348
  with tabs[5]:
@@ -2626,11 +2586,13 @@ plt.legend()
2626
 
2627
  # Python code editor
2628
  if selected_example != "Select an example..." and selected_example in example_scripts:
2629
- st.session_state.python_script = example_scripts[selected_example]
 
 
2630
 
2631
  if ACE_EDITOR_AVAILABLE:
2632
  python_code = st_ace(
2633
- value=st.session_state.python_script,
2634
  language="python",
2635
  theme="monokai",
2636
  min_lines=15,
@@ -2639,12 +2601,12 @@ plt.legend()
2639
  else:
2640
  python_code = st.text_area(
2641
  "Python Code",
2642
- value=st.session_state.python_script,
2643
  height=400,
2644
  key="python_textarea"
2645
  )
2646
 
2647
- # Update session state
2648
  st.session_state.python_script = python_code
2649
 
2650
  # Check for input() calls
@@ -2707,20 +2669,25 @@ self.wait(1)
2707
  """
2708
  # Insert into editor code
2709
  if st.session_state.code:
2710
- new_code = st.session_state.code + "\n" + plot_code
2711
- st.session_state.pending_temp_code = new_code
2712
- st.session_state.pending_changes = True
2713
- st.success(f"Plot {i+1} added to your animation code! Click 'Refresh App' to update the editor.")
 
 
2714
  else:
2715
  basic_scene = f"""from manim import *
2716
 
2717
  class PlotScene(Scene):
2718
  def construct(self):
2719
- {plot_code}
2720
  """
2721
- st.session_state.pending_temp_code = basic_scene
2722
- st.session_state.pending_changes = True
2723
- st.success(f"Created new scene with Plot {i+1}! Click 'Refresh App' to update the editor.")
 
 
 
2724
 
2725
  # Provide option to save the script
2726
  if st.button("📄 Save This Script", key="save_script_btn"):
@@ -2795,6 +2762,13 @@ class PlotScene(Scene):
2795
  self.wait(2)
2796
  ```
2797
  """)
 
 
 
 
 
 
 
2798
 
2799
  if __name__ == "__main__":
2800
  main()
 
26
  import plotly.express as px
27
  import markdown
28
  import zipfile
29
+ import contextlib
30
+ import threading
31
+ import traceback
32
+ from io import StringIO, BytesIO
33
 
34
  # Set up enhanced logging
35
  logging.basicConfig(
 
67
  'matplotlib': '3.5.0', # For Python script runner
68
  'seaborn': '0.11.2', # For enhanced visualizations
69
  'scipy': '1.7.3', # For scientific computations
 
70
  }
71
 
72
  with st.spinner("Checking required packages..."):
 
305
  "720p": {"resolution": "720p", "fps": "30"},
306
  "1080p": {"resolution": "1080p", "fps": "60"},
307
  "4K": {"resolution": "2160p", "fps": "60"},
308
+ "8K": {"resolution": "4320p", "fps": "60"} # Added 8K option
309
  }
310
 
311
  # Animation speeds
 
513
  "720p": "-qm", # Medium quality
514
  "1080p": "-qh", # High quality
515
  "4K": "-qk", # 4K quality
516
+ "8K": "-qp" # 8K quality (production quality)
517
  }
518
  quality_flag = quality_map.get(quality_preset, "-qm")
519
 
 
1173
  animation_steps = df.to_dict('records')
1174
  new_code = generate_code_from_timeline(animation_steps, code)
1175
 
1176
+ st.success("Timeline updated! Code has been regenerated.")
1177
  return new_code
1178
 
1179
  # Visual keyframe editor
 
1470
  logger.error(traceback.format_exc())
1471
  return None, None
1472
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1473
  def main():
1474
  # Initialize session state variables if they don't exist
1475
  if 'init' not in st.session_state:
 
1488
  st.session_state.custom_library_result = ""
1489
  st.session_state.python_script = "import matplotlib.pyplot as plt\nimport numpy as np\n\n# Example: Create a simple plot\nx = np.linspace(0, 10, 100)\ny = np.sin(x)\n\nplt.figure(figsize=(10, 6))\nplt.plot(x, y, 'b-', label='sin(x)')\nplt.title('Sine Wave')\nplt.xlabel('x')\nplt.ylabel('sin(x)')\nplt.grid(True)\nplt.legend()\n"
1490
  st.session_state.python_result = None
1491
+ st.session_state.active_tab = 0 # Track currently active tab
1492
  st.session_state.settings = {
1493
  "quality": "720p",
1494
  "format_type": "mp4",
1495
  "animation_speed": "Normal"
1496
  }
 
 
1497
 
1498
  # Page configuration with improved layout
1499
  st.set_page_config(
 
1601
  margin-top: 1rem;
1602
  border-left: 4px solid #ef4444;
1603
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1604
  </style>
1605
  """, unsafe_allow_html=True)
1606
 
 
1625
  st.session_state.ai_models = init_ai_models()
1626
 
1627
  # Create main tabs
1628
+ tab_names = ["✨ Editor", "🤖 AI Assistant", "📚 LaTeX Formulas", "🎨 Assets", "🎞️ Timeline", "🎓 Educational Export", "🐍 Python Runner"]
1629
+ tabs = st.tabs(tab_names)
1630
 
1631
+ # Sidebar for rendering settings and custom libraries
1632
  with st.sidebar:
 
 
 
 
 
 
 
 
1633
  # Rendering settings section
1634
  st.markdown("## ⚙️ Rendering Settings")
1635
 
 
1709
  if uploaded_file:
1710
  code_content = uploaded_file.getvalue().decode("utf-8")
1711
  if code_content.strip(): # Only update if file has content
1712
+ st.session_state.code = code_content
1713
+ st.session_state.temp_code = code_content
 
1714
 
1715
  # Code editor
1716
  if ACE_EDITOR_AVAILABLE:
1717
  current_code = st.session_state.code if hasattr(st.session_state, 'code') and st.session_state.code else ""
1718
+ st.session_state.temp_code = st_ace(
1719
  value=current_code,
1720
  language="python",
1721
  theme="monokai",
1722
  min_lines=20,
1723
  key="ace_editor"
1724
  )
 
 
 
 
1725
  else:
1726
  current_code = st.session_state.code if hasattr(st.session_state, 'code') and st.session_state.code else ""
1727
+ st.session_state.temp_code = st.text_area(
1728
  "Manim Python Code",
1729
  value=current_code,
1730
  height=400,
1731
  key="code_textarea"
1732
  )
1733
+
1734
+ # Update code in session state if it changed
1735
+ if st.session_state.temp_code != st.session_state.code:
1736
+ st.session_state.code = st.session_state.temp_code
1737
 
1738
  # Generate button (use a form to prevent page reloads)
1739
  generate_btn = st.button("🚀 Generate Animation", use_container_width=True, key="generate_btn")
 
1754
  self.wait(2)
1755
  """
1756
  st.session_state.code += default_scene
1757
+ st.session_state.temp_code = st.session_state.code
1758
+ st.warning("No scene class found. Added a default scene.")
 
1759
 
1760
  with st.spinner("Generating animation..."):
1761
  video_data, status = generate_manim_video(
 
1843
  1. **Syntax Errors**: Check your Python code for any syntax issues
1844
  2. **Missing Scene Class**: Ensure your code contains a scene class that extends Scene
1845
  3. **High Resolution Issues**: Try a lower quality preset for complex animations
1846
+ 4. **Memory Issues**: For 4K animations, reduce complexity or try again
1847
  5. **Format Issues**: Some formats require specific Manim configurations
1848
  6. **GIF Generation**: If GIF doesn't work, try MP4 and we'll convert it automatically
1849
 
 
1915
  col_ai1, col_ai2 = st.columns(2)
1916
  with col_ai1:
1917
  if st.button("Use This Code", key="use_gen_code"):
1918
+ st.session_state.code = st.session_state.generated_code
1919
+ st.session_state.temp_code = st.session_state.generated_code
1920
+ # Switch to Editor tab
1921
+ st.session_state.active_tab = 0
1922
+ st.rerun()
1923
 
1924
  with col_ai2:
1925
  if st.button("Render Preview", key="render_preview"):
 
1962
  key="latex_input"
1963
  )
1964
 
1965
+ # Update session state
1966
+ st.session_state.latex_formula = latex_input
 
1967
 
1968
  # Common LaTeX formulas library
1969
  st.markdown("#### Formula Library")
 
2011
  with formula_tabs[i]:
2012
  for formula in formulas:
2013
  if st.button(formula["name"], key=f"latex_{formula['name']}"):
2014
+ # Insert formula into the text area
2015
  st.session_state.latex_formula = formula["latex"]
2016
+ # Rerun to update the text area and preview
2017
+ st.rerun()
2018
 
2019
  # LaTeX code snippet
2020
  st.markdown("#### Manim Code Snippet")
 
2052
  # If we didn't find content, append to the end with default indentation
2053
  lines.append(" " + "\n ".join(manim_latex_code.strip().split("\n")))
2054
 
2055
+ st.session_state.code = "\n".join(lines)
2056
+ st.session_state.temp_code = st.session_state.code
2057
+ st.success("LaTeX formula inserted into the editor!")
2058
+
2059
+ # Switch to Editor tab
2060
+ st.session_state.active_tab = 0
2061
+ st.rerun()
2062
  else:
2063
  st.warning("Could not find 'construct' method in your code. Please add a scene class first.")
2064
  else:
 
2072
  self.play(Write(formula))
2073
  self.wait(2)
2074
  """
2075
+ st.session_state.code = basic_scene
2076
+ st.session_state.temp_code = basic_scene
2077
+ st.success("Created new scene with your LaTeX formula!")
2078
+
2079
+ # Switch to Editor tab
2080
+ st.session_state.active_tab = 0
2081
+ st.rerun()
2082
 
2083
  with col_latex2:
2084
  # LaTeX preview
 
2120
  st.markdown("#### 📸 Image Assets")
2121
  st.markdown("Upload images to use in your animations:")
2122
 
2123
+ # Allow multiple image uploads
2124
+ uploaded_images = st.file_uploader(
2125
+ "Upload Images",
2126
+ type=["jpg", "png", "jpeg", "svg"],
2127
+ accept_multiple_files=True,
2128
+ key="image_uploader_tab"
2129
+ )
2130
 
2131
  if uploaded_images:
2132
+ # Create a unique image directory if it doesn't exist
2133
+ image_dir = os.path.join(os.getcwd(), "manim_assets", "images")
2134
+ os.makedirs(image_dir, exist_ok=True)
2135
+
2136
+ # Process each uploaded image
2137
+ for uploaded_image in uploaded_images:
2138
+ # Generate a unique filename and save the image
2139
+ file_extension = uploaded_image.name.split(".")[-1]
2140
+ unique_filename = f"image_{int(time.time())}_{uuid.uuid4().hex[:8]}.{file_extension}"
2141
+ image_path = os.path.join(image_dir, unique_filename)
2142
 
2143
+ with open(image_path, "wb") as f:
2144
+ f.write(uploaded_image.getvalue())
2145
+
2146
+ # Store the path in session state
2147
  if "image_paths" not in st.session_state:
2148
  st.session_state.image_paths = []
2149
 
2150
+ # Check if this image was already added
2151
+ # Check if this image was already added
2152
+ image_already_added = False
2153
+ for img in st.session_state.image_paths:
2154
+ if img["name"] == uploaded_image.name:
2155
+ image_already_added = True
2156
+ break
2157
 
2158
+ if not image_already_added:
2159
+ st.session_state.image_paths.append({
2160
+ "name": uploaded_image.name,
2161
+ "path": image_path
2162
+ })
2163
+
2164
+ # Display uploaded images in a grid
2165
+ st.markdown("##### Uploaded Images:")
2166
+ image_cols = st.columns(3)
2167
+
2168
+ for i, img_info in enumerate(st.session_state.image_paths[-len(uploaded_images):]):
2169
+ with image_cols[i % 3]:
2170
+ try:
2171
+ img = Image.open(img_info["path"])
2172
+ st.image(img, caption=img_info["name"], width=150)
2173
 
2174
+ # Show code snippet for this specific image
2175
+ if st.button(f"Use {img_info['name']}", key=f"use_img_{i}"):
2176
+ image_code = f"""
2177
+ # Load and display image
2178
+ image = ImageMobject(r"{img_info['path']}")
2179
+ image.scale(2) # Adjust size as needed
2180
+ self.play(FadeIn(image))
 
 
 
2181
  self.wait(1)
2182
  """
2183
+ if not st.session_state.code:
2184
+ base_code = """from manim import *
 
 
 
 
 
 
2185
 
2186
  class ImageScene(Scene):
2187
  def construct(self):
2188
  """
2189
+ st.session_state.code = base_code + "\n " + image_code.replace("\n", "\n ")
2190
+ else:
2191
+ st.session_state.code += "\n" + image_code
2192
+
2193
+ st.session_state.temp_code = st.session_state.code
2194
+ st.success(f"Added {img_info['name']} to your code!")
2195
+
2196
+ # Switch to Editor tab
2197
+ st.session_state.active_tab = 0
2198
+ st.rerun()
2199
+ except Exception as e:
2200
+ st.error(f"Error loading image {img_info['name']}: {e}")
2201
 
2202
  # Display previously uploaded images
2203
  if st.session_state.image_paths:
2204
+ with st.expander("Previously Uploaded Images"):
2205
+ # Group images by 3 in each row
2206
+ for i in range(0, len(st.session_state.image_paths), 3):
2207
+ prev_cols = st.columns(3)
2208
+ for j in range(3):
2209
+ if i+j < len(st.session_state.image_paths):
2210
+ img_info = st.session_state.image_paths[i+j]
2211
+ with prev_cols[j]:
2212
+ try:
2213
+ img = Image.open(img_info["path"])
2214
+ st.image(img, caption=img_info["name"], width=100)
2215
+ st.markdown(f"<div class='small-text'>Path: {img_info['path']}</div>", unsafe_allow_html=True)
2216
+ except:
2217
+ st.markdown(f"**{img_info['name']}**")
2218
+ st.markdown(f"<div class='small-text'>Path: {img_info['path']}</div>", unsafe_allow_html=True)
2219
 
2220
  with asset_col2:
2221
  # Audio uploader section
 
2301
 
2302
  # If code was modified by the timeline editor, update the session state
2303
  if updated_code != st.session_state.code:
2304
+ st.session_state.code = updated_code
2305
+ st.session_state.temp_code = updated_code
2306
 
2307
  # EDUCATIONAL EXPORT TAB
2308
  with tabs[5]:
 
2586
 
2587
  # Python code editor
2588
  if selected_example != "Select an example..." and selected_example in example_scripts:
2589
+ python_code = example_scripts[selected_example]
2590
+ else:
2591
+ python_code = st.session_state.python_script
2592
 
2593
  if ACE_EDITOR_AVAILABLE:
2594
  python_code = st_ace(
2595
+ value=python_code,
2596
  language="python",
2597
  theme="monokai",
2598
  min_lines=15,
 
2601
  else:
2602
  python_code = st.text_area(
2603
  "Python Code",
2604
+ value=python_code,
2605
  height=400,
2606
  key="python_textarea"
2607
  )
2608
 
2609
+ # Store script in session state (without clearing existing code)
2610
  st.session_state.python_script = python_code
2611
 
2612
  # Check for input() calls
 
2669
  """
2670
  # Insert into editor code
2671
  if st.session_state.code:
2672
+ st.session_state.code += "\n" + plot_code
2673
+ st.session_state.temp_code = st.session_state.code
2674
+ st.success(f"Plot {i+1} added to your animation code!")
2675
+ # Switch to Editor tab
2676
+ st.session_state.active_tab = 0
2677
+ st.rerun()
2678
  else:
2679
  basic_scene = f"""from manim import *
2680
 
2681
  class PlotScene(Scene):
2682
  def construct(self):
2683
+ {plot_code}
2684
  """
2685
+ st.session_state.code = basic_scene
2686
+ st.session_state.temp_code = basic_scene
2687
+ st.success(f"Created new scene with Plot {i+1}!")
2688
+ # Switch to Editor tab
2689
+ st.session_state.active_tab = 0
2690
+ st.rerun()
2691
 
2692
  # Provide option to save the script
2693
  if st.button("📄 Save This Script", key="save_script_btn"):
 
2762
  self.wait(2)
2763
  ```
2764
  """)
2765
+
2766
+ # Restore active tab if specified
2767
+ if st.session_state.active_tab is not None and st.session_state.active_tab < len(tabs):
2768
+ for i, tab in enumerate(tabs):
2769
+ if i == st.session_state.active_tab:
2770
+ tab.active = True
2771
+ break
2772
 
2773
  if __name__ == "__main__":
2774
  main()