", unsafe_allow_html=True)
+
+ # Generated output display
+ if st.session_state.video_data:
+ # Different handling based on format type
+ format_type = st.session_state.settings["format_type"]
+
+ if format_type == "png_sequence":
+ st.info("PNG sequence generated successfully. Use the download button to get the ZIP file.")
+
+ # Add download button for ZIP
+ st.download_button(
+ label="β¬οΈ Download PNG Sequence (ZIP)",
+ data=st.session_state.video_data,
+ file_name=f"manim_pngs_{datetime.now().strftime('%Y%m%d_%H%M%S')}.zip",
+ mime="application/zip",
+ use_container_width=True
+ )
+ elif format_type == "svg":
+ # Display SVG preview
+ try:
+ svg_data = st.session_state.video_data.decode('utf-8')
+ components.html(svg_data, height=400)
+ except Exception as e:
+ st.error(f"Error displaying SVG: {str(e)}")
+
+ # Download button for SVG
+ st.download_button(
+ label="β¬οΈ Download SVG",
+ data=st.session_state.video_data,
+ file_name=f"manim_animation_{datetime.now().strftime('%Y%m%d_%H%M%S')}.svg",
+ mime="image/svg+xml",
+ use_container_width=True
+ )
+ else:
+ # Standard video display for MP4, GIF, WebM
+ try:
+ st.video(st.session_state.video_data, format=format_type)
+ except Exception as e:
+ st.error(f"Error displaying video: {str(e)}")
+ # Fallback for GIF if st.video fails
+ if format_type == "gif":
+ st.markdown("GIF preview:")
+ gif_b64 = base64.b64encode(st.session_state.video_data).decode()
+ st.markdown(f'', unsafe_allow_html=True)
+
+ # Add download button
+ st.download_button(
+ label=f"β¬οΈ Download {format_type.upper()}",
+ data=st.session_state.video_data,
+ file_name=f"manim_animation_{datetime.now().strftime('%Y%m%d_%H%M%S')}.{format_type}",
+ mime=f"{'image' if format_type == 'gif' else 'video'}/{format_type}",
+ use_container_width=True
+ )
+
+ if st.session_state.status:
+ if "Error" in st.session_state.status:
+ st.error(st.session_state.status)
+
+ # Show troubleshooting tips
+ with st.expander("π Troubleshooting Tips"):
+ st.markdown("""
+ ### Common Issues:
+ 1. **Syntax Errors**: Check your Python code for any syntax issues
+ 2. **Missing Scene Class**: Ensure your code contains a scene class that extends Scene
+ 3. **High Resolution Issues**: Try a lower quality preset for complex animations
+ 4. **Memory Issues**: For 4K or 8K animations, reduce complexity or try again
+ 5. **Format Issues**: Some formats require specific Manim configurations
+ 6. **GIF Generation**: If GIF doesn't work, try MP4 and we'll convert it automatically
+
+ ### Example Code:
+ ```python
+ from manim import *
+
+ class MyScene(Scene):
+ def construct(self):
+ circle = Circle(color=RED)
+ self.play(Create(circle))
+ self.wait(1)
+ ```
+ """)
+ else:
+ st.success(st.session_state.status)
+
+ # AI ASSISTANT TAB
+ with tabs[1]:
+ st.markdown("### π€ AI Animation Assistant")
+
+ if st.session_state.ai_models:
+ # AI code generation
+ st.markdown("
", unsafe_allow_html=True)
+ st.markdown("#### Generate Animation from Description")
+ st.write("Describe the animation you want to create, or provide partial code to complete.")
+
+ # Predefined animation ideas dropdown
+ animation_ideas = [
+ "Select an idea...",
+ "Create a 3D animation showing a sphere morphing into a torus",
+ "Show a visual proof of the Pythagorean theorem",
+ "Visualize a Fourier transform converting a signal from time domain to frequency domain",
+ "Create an animation explaining neural network forward propagation",
+ "Illustrate the concept of integration with area under a curve"
+ ]
+
+ selected_idea = st.selectbox(
+ "Try one of these ideas",
+ options=animation_ideas
+ )
+
+ prompt_value = selected_idea if selected_idea != "Select an idea..." else ""
+
+ code_input = st.text_area(
+ "Your Prompt or Code",
+ value=prompt_value,
+ placeholder="Example: Create an animation that shows a circle morphing into a square while changing color from red to blue",
+ height=150
+ )
+
+ if st.button("Generate Animation Code", key="gen_ai_code"):
+ if code_input:
+ with st.spinner("AI is generating your animation code..."):
+ response = suggest_code_completion(code_input, st.session_state.ai_models)
+ if response:
+ st.session_state.generated_code = response
+ else:
+ st.warning("Please enter a description or prompt first")
+
+ st.markdown("
", unsafe_allow_html=True)
+
+ # AI generated code display and actions
+ if st.session_state.generated_code:
+ st.markdown("
", unsafe_allow_html=True)
+ st.markdown("#### Generated Animation Code")
+ st.code(st.session_state.generated_code, language="python")
+
+ col_ai1, col_ai2 = st.columns(2)
+ with col_ai1:
+ if st.button("Use This Code", key="use_gen_code"):
+ st.session_state.pending_temp_code = st.session_state.generated_code
+ st.session_state.pending_changes = True
+ st.info("AI code ready to use. Click 'Refresh App' to apply it to the editor.")
+
+ with col_ai2:
+ if st.button("Render Preview", key="render_preview"):
+ with st.spinner("Rendering preview..."):
+ video_data, status = generate_manim_video(
+ st.session_state.generated_code,
+ "mp4",
+ "480p", # Use lowest quality for preview
+ ANIMATION_SPEEDS["Normal"]
+ )
+
+ if video_data:
+ st.video(video_data)
+ st.download_button(
+ label="Download Preview",
+ data=video_data,
+ file_name=f"manim_preview_{int(time.time())}.mp4",
+ mime="video/mp4"
+ )
+ else:
+ st.error(f"Failed to generate preview: {status}")
+ st.markdown("
", unsafe_allow_html=True)
+ else:
+ st.warning("AI models failed to load. Generation features are unavailable.")
+
+ # LATEX FORMULAS TAB
+ with tabs[2]:
+ st.markdown("### π LaTeX Formula Builder")
+
+ col_latex1, col_latex2 = st.columns([3, 2])
+
+ with col_latex1:
+ # LaTeX formula input
+ st.markdown("#### Enter LaTeX Formula")
+ latex_input = st.text_area(
+ "LaTeX Formula",
+ value=st.session_state.latex_formula,
+ height=100,
+ placeholder=r"e^{i\pi} + 1 = 0",
+ key="latex_input"
+ )
+
+ # Update session state without auto-refresh
+ if latex_input != st.session_state.latex_formula:
+ st.session_state.latex_formula = latex_input
+
+ # Common LaTeX formulas library
+ st.markdown("#### Formula Library")
+
+ # Categorized formulas
+ latex_categories = {
+ "Basic Math": [
+ {"name": "Fractions", "latex": r"\frac{a}{b}"},
+ {"name": "Square Root", "latex": r"\sqrt{x}"},
+ {"name": "Nth Root", "latex": r"\sqrt[n]{x}"},
+ {"name": "Powers", "latex": r"x^{n}"},
+ {"name": "Subscript", "latex": r"x_{i}"},
+ ],
+ "Algebra": [
+ {"name": "Quadratic Formula", "latex": r"x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}"},
+ {"name": "Binomial Coefficient", "latex": r"\binom{n}{k}"},
+ {"name": "Sum", "latex": r"\sum_{i=1}^{n} i = \frac{n(n+1)}{2}"},
+ {"name": "Product", "latex": r"\prod_{i=1}^{n} i = n!"},
+ ],
+ "Calculus": [
+ {"name": "Derivative", "latex": r"\frac{d}{dx}f(x)"},
+ {"name": "Partial Derivative", "latex": r"\frac{\partial f}{\partial x}"},
+ {"name": "Integral", "latex": r"\int_{a}^{b} f(x) \, dx"},
+ {"name": "Double Integral", "latex": r"\iint_{D} f(x,y) \, dx \, dy"},
+ {"name": "Limit", "latex": r"\lim_{x \to \infty} f(x)"},
+ ],
+ "Linear Algebra": [
+ {"name": "Matrix", "latex": r"\begin{pmatrix} a & b \\ c & d \end{pmatrix}"},
+ {"name": "Determinant", "latex": r"\begin{vmatrix} a & b \\ c & d \end{vmatrix}"},
+ {"name": "Vector", "latex": r"\vec{v} = (v_1, v_2, v_3)"},
+ {"name": "Dot Product", "latex": r"\vec{a} \cdot \vec{b} = |a||b|\cos\theta"},
+ ],
+ "Famous Equations": [
+ {"name": "Euler's Identity", "latex": r"e^{i\pi} + 1 = 0"},
+ {"name": "Einstein's Mass-Energy", "latex": r"E = mc^2"},
+ {"name": "SchrΓΆdinger Equation", "latex": r"i\hbar\frac{\partial}{\partial t}\Psi = \hat{H}\Psi"},
+ {"name": "Maxwell's Equations", "latex": r"\nabla \cdot \vec{E} = \frac{\rho}{\varepsilon_0}"},
+ ]
+ }
+
+ # Create tabs for formula categories
+ formula_tabs = st.tabs(list(latex_categories.keys()))
+
+ for i, (category, formulas) in enumerate(latex_categories.items()):
+ with formula_tabs[i]:
+ for formula in formulas:
+ if st.button(formula["name"], key=f"latex_{formula['name']}"):
+ st.session_state.latex_formula = formula["latex"]
+ # No automatic rerun - user needs to click refresh
+ st.info(f"Added '{formula['name']}'. Click 'Refresh App' to update the preview.")
+
+ # LaTeX code snippet
+ st.markdown("#### Manim Code Snippet")
+ if latex_input:
+ manim_latex_code = f"""
+# LaTeX formula
+formula = MathTex(r"{latex_input}")
+self.play(Write(formula))
+self.wait(2)
+"""
+ st.code(manim_latex_code, language="python")
+
+ if st.button("Insert into Editor", key="insert_latex_btn"):
+ if st.session_state.code:
+ # Find the construct method and insert after it
+ if "def construct(self):" in st.session_state.code:
+ lines = st.session_state.code.split("\n")
+ construct_index = -1
+
+ for i, line in enumerate(lines):
+ if "def construct(self):" in line:
+ construct_index = i
+ break
+
+ if construct_index >= 0:
+ # Find the first line with non-whitespace content after construct
+ for i in range(construct_index + 1, len(lines)):
+ if lines[i].strip() and not lines[i].strip().startswith("#"):
+ # Insert before this line
+ indent = re.match(r"(\s*)", lines[i]).group(1)
+ indented_code = "\n".join([indent + line for line in manim_latex_code.strip().split("\n")])
+ lines.insert(i, indented_code)
+ break
+ else:
+ # If we didn't find content, append to the end with default indentation
+ lines.append(" " + "\n ".join(manim_latex_code.strip().split("\n")))
+
+ new_code = "\n".join(lines)
+ st.session_state.pending_temp_code = new_code
+ st.session_state.pending_changes = True
+ st.success("LaTeX formula prepared for insertion. Click 'Refresh App' to update the editor.")
+ else:
+ st.warning("Could not find 'construct' method in your code. Please add a scene class first.")
+ else:
+ # Create a new basic scene with the LaTeX formula
+ basic_scene = f"""from manim import *
+
+class LatexScene(Scene):
+ def construct(self):
+ # LaTeX formula
+ formula = MathTex(r"{latex_input}")
+ self.play(Write(formula))
+ self.wait(2)
+"""
+ st.session_state.pending_temp_code = basic_scene
+ st.session_state.pending_changes = True
+ st.success("Created new scene with your LaTeX formula. Click 'Refresh App' to update the editor.")
+
+ with col_latex2:
+ # LaTeX preview
+ st.markdown("#### Formula Preview")
+ latex_preview_html = render_latex_preview(latex_input)
+ components.html(latex_preview_html, height=300)
+
+ # LaTeX tips
+ with st.expander("LaTeX Tips & Tricks"):
+ st.markdown("""
+ ### LaTeX Tips
+
+ - Use `\\frac{a}{b}` for fractions
+ - Use `\\sum_{i=1}^{n}` for summation
+ - Use `\\int_{a}^{b}` for integration
+ - Use `\\{` and `\\}` for curly braces
+ - Enclose equations in `$...$` or `\\[...\\]`
+
+ ### Manim LaTeX
+
+ In Manim, use `MathTex` for inline math and `Tex` for text with LaTeX:
+
+ ```python
+ formula = MathTex(r"\\sum_{i=1}^{n} i = \\frac{n(n+1)}{2}")
+ text = Tex(r"This is a binomial coefficient: $\\binom{n}{k}$")
+ ```
+
+ The `r` before the string creates a raw string, which is recommended to avoid escaping backslashes.
+ """)
+
+ # ASSETS TAB
+ with tabs[3]:
+ st.markdown("### π¨ Asset Management")
+
+ asset_col1, asset_col2 = st.columns([1, 1])
+
+ with asset_col1:
+ # Image uploader section
+ st.markdown("#### πΈ Image Assets")
+ st.markdown("Upload images to use in your animations:")
+
+ # Multiple image uploader
+ uploaded_images = st.file_uploader("Upload Images", type=["jpg", "png", "jpeg", "svg"], accept_multiple_files=True, key="image_uploader_tab")
+
+ if uploaded_images:
+ # Process all uploaded images
+ with st.spinner("Processing uploaded images..."):
+ processed_images = process_multiple_images(uploaded_images)
+
+ # Add to session state
+ if "image_paths" not in st.session_state:
+ st.session_state.image_paths = []
+
+ st.session_state.image_paths.extend(processed_images)
+
+ # Show preview of uploaded images
+ st.success(f"Successfully uploaded {len(processed_images)} images")
+
+ # Display image grid
+ st.markdown("#### New Images")
+ image_cols = st.columns(min(4, len(processed_images)))
+ for i, img_info in enumerate(processed_images):
+ with image_cols[i % len(image_cols)]:
+ st.image(img_info["preview"], caption=img_info["name"], width=150)
+
+ # Show code to use images
+ if processed_images:
+ st.markdown("#### Code to use these images:")
+ image_code_samples = []
+ for i, img_info in enumerate(processed_images):
+ image_code = f"""
+# Load and display image {i+1}: {img_info['name']}
+image{i+1} = ImageMobject(r"{img_info['path']}")
+image{i+1}.scale(2) # Adjust size as needed
+self.play(FadeIn(image{i+1}))
+self.wait(1)
+"""
+ image_code_samples.append(image_code)
+
+ all_images_code = "\n".join(image_code_samples)
+ st.code(all_images_code, language="python")
+
+ if st.button("Insert All Images Code", key="insert_images_code_btn"):
+ if not st.session_state.code:
+ base_code = """from manim import *
+
+class ImageScene(Scene):
+ def construct(self):
+"""
+ indented_code = "\n ".join(all_images_code.split("\n"))
+ new_code = base_code + "\n " + indented_code
+ else:
+ new_code = st.session_state.code + "\n" + all_images_code
+
+ st.session_state.pending_temp_code = new_code
+ st.session_state.pending_changes = True
+ st.success("Image code prepared for insertion. Click 'Refresh App' to update the editor.")
+
+ # Display previously uploaded images
+ if st.session_state.image_paths:
+ st.markdown("#### All Available Images")
+
+ # Display in a grid format
+ st.markdown('
', unsafe_allow_html=True)
+
+ for idx, img_info in enumerate(st.session_state.image_paths):
+ st.markdown(f"""
+
+
{img_info['name']}
+
Path: {img_info['path']}
+
+ """, unsafe_allow_html=True)
+
+ st.markdown('
', unsafe_allow_html=True)
+
+ with asset_col2:
+ # Audio uploader section
+ st.markdown("#### π΅ Audio Assets")
+ st.markdown("Upload audio files for background or narration:")
+
+ uploaded_audio = st.file_uploader("Upload Audio", type=["mp3", "wav", "ogg"], key="audio_uploader")
+
+ if uploaded_audio:
+ # Create a unique audio directory if it doesn't exist
+ audio_dir = os.path.join(os.getcwd(), "manim_assets", "audio")
+ os.makedirs(audio_dir, exist_ok=True)
+
+ # Generate a unique filename and save the audio
+ file_extension = uploaded_audio.name.split(".")[-1]
+ unique_filename = f"audio_{int(time.time())}.{file_extension}"
+ audio_path = os.path.join(audio_dir, unique_filename)
+
+ with open(audio_path, "wb") as f:
+ f.write(uploaded_audio.getvalue())
+
+ # Store the path in session state
+ st.session_state.audio_path = audio_path
+
+ # Display audio player
+ st.audio(uploaded_audio)
+
+ st.markdown(f"""
+
+
Audio: {uploaded_audio.name}
+
Path: {audio_path}
+
+ """, unsafe_allow_html=True)
+
+ # Two options for audio usage
+ st.markdown("#### Add Audio to Your Animation")
+
+ option = st.radio(
+ "Choose how to use audio:",
+ ["Background Audio", "Generate Audio from Text"]
+ )
+
+ if option == "Background Audio":
+ st.markdown("##### Code to add background audio:")
+
+ # For with_sound decorator
+ audio_code1 = f"""
+# Add this import at the top of your file
+from manim.scene.scene_file_writer import SceneFileWriter
+
+# Add this decorator before your scene class
+@with_sound("{audio_path}")
+class YourScene(Scene):
+ def construct(self):
+ # Your animation code here
+"""
+ st.code(audio_code1, language="python")
+
+ if st.button("Use This Audio in Animation", key="use_audio_btn"):
+ st.success("Audio set for next render!")
+
+ elif option == "Generate Audio from Text":
+ # Text-to-speech input
+ tts_text = st.text_area(
+ "Enter text for narration",
+ placeholder="Type the narration text here...",
+ height=100
+ )
+
+ if st.button("Create Narration", key="create_narration_btn"):
+ try:
+ # Use basic TTS (placeholder for actual implementation)
+ st.warning("Text-to-speech feature requires additional setup. Using uploaded audio instead.")
+ st.session_state.audio_path = audio_path
+ st.success("Audio set for next render!")
+ except Exception as e:
+ st.error(f"Error creating narration: {str(e)}")
+
+ # TIMELINE EDITOR TAB
+ with tabs[4]:
+ # New code for reordering animation steps
+ updated_code = create_timeline_editor(st.session_state.code)
+
+ # If code was modified by the timeline editor, update the session state
+ if updated_code != st.session_state.code:
+ st.session_state.pending_temp_code = updated_code
+ st.session_state.pending_changes = True
+
+ # EDUCATIONAL EXPORT TAB
+ with tabs[5]:
+ st.markdown("### π Educational Export Options")
+
+ # Check if we have an animation to export
+ if not st.session_state.video_data:
+ st.warning("Generate an animation first before using educational export features.")
+ else:
+ st.markdown("Create various educational assets from your animation:")
+
+ # Animation title and explanation
+ animation_title = st.text_input("Animation Title", value="Manim Animation", key="edu_title")
+
+ st.markdown("#### Explanation Text")
+ st.markdown("Add explanatory text to accompany your animation. Use markdown formatting.")
+ st.markdown("Use ## to separate explanation sections for step-by-step sequence export.")
+
+ explanation_text = st.text_area(
+ "Explanation (markdown supported)",
+ height=150,
+ placeholder="Explain your animation here...\n\n## Step 1\nIntroduction to the concept...\n\n## Step 2\nNext, we demonstrate..."
+ )
+
+ # Export format selection
+ edu_format = st.selectbox(
+ "Export Format",
+ options=["PowerPoint Presentation", "Interactive HTML", "Explanation Sequence PDF"]
+ )
+
+ # Format-specific options
+ if edu_format == "PowerPoint Presentation":
+ st.info("Creates a PowerPoint file with your animation and explanation text.")
+
+ elif edu_format == "Interactive HTML":
+ st.info("Creates an interactive HTML webpage with playback controls and explanation.")
+ include_controls = st.checkbox("Include interactive controls", value=True)
+
+ elif edu_format == "Explanation Sequence PDF":
+ st.info("Creates a PDF with key frames and step-by-step explanations.")
+ frame_count = st.slider("Number of key frames", min_value=3, max_value=10, value=5)
+
+ # Export button
+ if st.button("Export Educational Material", key="export_edu_btn"):
+ with st.spinner(f"Creating {edu_format}..."):
+ # Map selected format to internal format type
+ format_map = {
+ "PowerPoint Presentation": "powerpoint",
+ "Interactive HTML": "html",
+ "Explanation Sequence PDF": "sequence"
+ }
+
+ # Create a temporary directory for export
+ temp_export_dir = tempfile.mkdtemp(prefix="manim_edu_export_")
+
+ # Process the export
+ exported_data, file_type = export_to_educational_format(
+ st.session_state.video_data,
+ format_map[edu_format],
+ animation_title,
+ explanation_text,
+ temp_export_dir
+ )
+
+ if exported_data:
+ # File extension mapping
+ ext_map = {
+ "powerpoint": "pptx",
+ "html": "html",
+ "pdf": "pdf"
+ }
+
+ # Download button
+ ext = ext_map.get(file_type, "zip")
+ filename = f"{animation_title.replace(' ', '_')}.{ext}"
+
+ st.success(f"{edu_format} created successfully!")
+ st.download_button(
+ label=f"β¬οΈ Download {edu_format}",
+ data=exported_data,
+ file_name=filename,
+ mime=f"application/{ext}",
+ use_container_width=True
+ )
+
+ # For HTML, also offer to open in browser
+ if file_type == "html":
+ html_path = os.path.join(temp_export_dir, filename)
+ st.markdown(f"[π Open in browser](file://{html_path})", unsafe_allow_html=True)
+ else:
+ st.error(f"Failed to create {edu_format}. Check logs for details.")
+
+ # Show usage examples and tips
+ with st.expander("Usage Tips"):
+ st.markdown("""
+ ### Educational Export Tips
+
+ **PowerPoint Presentations**
+ - Great for lectures and classroom presentations
+ - Animation will autoplay when clicked
+ - Add detailed explanations in notes section
+
+ **Interactive HTML**
+ - Perfect for websites and online learning platforms
+ - Students can control playback speed and navigation
+ - Mobile-friendly for learning on any device
+
+ **Explanation Sequence**
+ - Ideal for printed materials and study guides
+ - Use ## headers to mark different explanation sections
+ - Each section will be paired with a key frame
+ """)
+
+ # PYTHON RUNNER TAB
+ with tabs[6]:
+ st.markdown("### π Python Script Runner")
+ st.markdown("Execute Python scripts and visualize the results directly.")
+
+ # Predefined example scripts
+ example_scripts = {
+ "Select an example...": "",
+ "Basic Matplotlib Plot": """import matplotlib.pyplot as plt
+import numpy as np
+
+# Create data
+x = np.linspace(0, 10, 100)
+y = np.sin(x)
+
+# Create plot
+plt.figure(figsize=(10, 6))
+plt.plot(x, y, 'b-', label='sin(x)')
+plt.title('Sine Wave')
+plt.xlabel('x')
+plt.ylabel('sin(x)')
+plt.grid(True)
+plt.legend()
+""",
+ "User Input Example": """# This example demonstrates how to handle user input
+name = input("Enter your name: ")
+age = int(input("Enter your age: "))
+
+print(f"Hello, {name}! In 10 years, you'll be {age + 10} years old.")
+
+# Let's get some numbers and calculate the average
+num_count = int(input("How many numbers would you like to average? "))
+total = 0
+
+for i in range(num_count):
+ num = float(input(f"Enter number {i+1}: "))
+ total += num
+
+average = total / num_count
+print(f"The average of your {num_count} numbers is: {average}")
+""",
+ "Pandas DataFrame": """import pandas as pd
+import numpy as np
+
+# Create a sample dataframe
+data = {
+ 'Name': ['Alice', 'Bob', 'Charlie', 'David', 'Emma'],
+ 'Age': [25, 30, 35, 40, 45],
+ 'Salary': [50000, 60000, 70000, 80000, 90000],
+ 'Department': ['HR', 'IT', 'Finance', 'Marketing', 'Engineering']
+}
+
+df = pd.DataFrame(data)
+
+# Display the dataframe
+print("Sample DataFrame:")
+print(df)
+
+# Basic statistics
+print("\\nSummary Statistics:")
+print(df.describe())
+
+# Filtering
+print("\\nEmployees older than 30:")
+print(df[df['Age'] > 30])
+""",
+ "Seaborn Visualization": """import matplotlib.pyplot as plt
+import seaborn as sns
+import numpy as np
+import pandas as pd
+
+# Set the style
+sns.set_style("whitegrid")
+
+# Create sample data
+np.random.seed(42)
+data = np.random.randn(100, 3)
+df = pd.DataFrame(data, columns=['A', 'B', 'C'])
+df['category'] = pd.Categorical(['Group 1'] * 50 + ['Group 2'] * 50)
+
+# Create a paired plot
+sns.pairplot(df, hue='category', palette='viridis')
+
+# Create another plot
+plt.figure(figsize=(10, 6))
+sns.violinplot(x='category', y='A', data=df, palette='magma')
+plt.title('Distribution of A by Category')
+""",
+ "NumPy Computation": """import numpy as np
+
+# Create arrays
+arr1 = np.array([1, 2, 3, 4, 5])
+arr2 = np.array([5, 4, 3, 2, 1])
+
+print("Array 1:", arr1)
+print("Array 2:", arr2)
+
+# Basic operations
+print("\\nBasic Operations:")
+print("Addition:", arr1 + arr2)
+print("Multiplication:", arr1 * arr2)
+print("Division:", arr1 / arr2)
+
+# Statistics
+print("\\nStatistics:")
+print("Mean of arr1:", np.mean(arr1))
+print("Standard deviation of arr2:", np.std(arr2))
+print("Correlation coefficient:", np.corrcoef(arr1, arr2)[0, 1])
+
+# Create a 2D array
+matrix = np.random.rand(3, 3)
+print("\\nRandom 3x3 Matrix:")
+print(matrix)
+print("Determinant:", np.linalg.det(matrix))
+print("Inverse:")
+print(np.linalg.inv(matrix))
+""",
+ "SciPy Example": """import numpy as np
+from scipy import optimize
+import matplotlib.pyplot as plt
+
+# Define a function to find the root of
+def f(x):
+ return x**3 - 2*x**2 - 5*x + 6
+
+# Find the roots
+roots = optimize.root_scalar(f, bracket=[-5, 5], method='brentq')
+print(f"Root found: {roots.root}")
+
+# Plot the function
+x = np.linspace(-5, 5, 1000)
+y = f(x)
+
+plt.figure(figsize=(10, 6))
+plt.plot(x, y, 'b-')
+plt.axhline(y=0, color='k', linestyle='-', alpha=0.3)
+plt.axvline(x=roots.root, color='r', linestyle='--', label=f'Root: {roots.root:.2f}')
+plt.grid(True)
+plt.title('Finding roots of a cubic function')
+plt.xlabel('x')
+plt.ylabel('f(x)')
+plt.legend()
+
+# Optimization example
+def g(x):
+ return (x - 2) ** 2 + 1
+
+result = optimize.minimize(g, x0=0)
+print(f"Minimum found at x = {result.x[0]}, with value {result.fun}")
+
+# Plot the optimization
+x = np.linspace(-1, 5, 1000)
+y = g(x)
+
+plt.figure(figsize=(10, 6))
+plt.plot(x, y, 'g-')
+plt.plot(result.x, result.fun, 'ro', label=f'Minimum: ({result.x[0]:.2f}, {result.fun:.2f})')
+plt.grid(True)
+plt.title('Function Optimization')
+plt.xlabel('x')
+plt.ylabel('g(x)')
+plt.legend()
+"""
+ }
+
+ # Select example script
+ selected_example = st.selectbox("Select an example script:", options=list(example_scripts.keys()))
+
+ # Python code editor
+ if selected_example != "Select an example..." and selected_example in example_scripts:
+ st.session_state.python_script = example_scripts[selected_example]
+
+ if ACE_EDITOR_AVAILABLE:
+ python_code = st_ace(
+ value=st.session_state.python_script,
+ language="python",
+ theme="monokai",
+ min_lines=15,
+ key="python_editor"
+ )
+ else:
+ python_code = st.text_area(
+ "Python Code",
+ value=st.session_state.python_script,
+ height=400,
+ key="python_textarea"
+ )
+
+ # Update session state
+ st.session_state.python_script = python_code
+
+ # Check for input() calls
+ input_calls = detect_input_calls(python_code)
+ user_inputs = []
+
+ if input_calls:
+ st.markdown("### Input Values")
+ st.info(f"This script contains {len(input_calls)} input() calls. Please provide values below:")
+
+ for i, input_call in enumerate(input_calls):
+ user_input = st.text_input(
+ f"{input_call['prompt']} (Line {input_call['line']})",
+ key=f"input_{i}"
+ )
+ user_inputs.append(user_input)
+
+ # Options and execution
+ col1, col2 = st.columns([2, 1])
+
+ with col1:
+ timeout_seconds = st.slider("Execution Timeout (seconds)", 5, 120, 30)
+
+ with col2:
+ run_btn = st.button("βΆοΈ Run Script", use_container_width=True)
+
+ if run_btn:
+ with st.spinner("Executing Python script..."):
+ result = run_python_script(python_code, inputs=user_inputs, timeout=timeout_seconds)
+ st.session_state.python_result = result
+
+ # Display results
+ if st.session_state.python_result:
+ display_python_script_results(st.session_state.python_result)
+
+ # Option to insert plots into Manim animation
+ if st.session_state.python_result["plots"]:
+ with st.expander("Add Plots to Manim Animation"):
+ st.markdown("Select a plot to include in your Manim animation:")
+
+ plot_cols = st.columns(min(3, len(st.session_state.python_result["plots"])))
+
+ for i, plot_data in enumerate(st.session_state.python_result["plots"]):
+ # Create a unique temporary file for each plot
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as tmp:
+ tmp.write(plot_data)
+ plot_path = tmp.name
+
+ # Display the plot with selection button
+ with plot_cols[i % len(plot_cols)]:
+ st.image(plot_data, use_column_width=True)
+ if st.button(f"Use Plot {i+1}", key=f"use_plot_{i}"):
+ # Create code to include this plot in Manim
+ plot_code = f"""
+# Import the plot image
+plot_image = ImageMobject(r"{plot_path}")
+plot_image.scale(2) # Adjust size as needed
+self.play(FadeIn(plot_image))
+self.wait(1)
+"""
+ # Insert into editor code
+ if st.session_state.code:
+ new_code = st.session_state.code + "\n" + plot_code
+ st.session_state.pending_temp_code = new_code
+ st.session_state.pending_changes = True
+ st.success(f"Plot {i+1} added to your animation code! Click 'Refresh App' to update the editor.")
+ else:
+ basic_scene = f"""from manim import *
+
+class PlotScene(Scene):
+ def construct(self):
+{plot_code}
+"""
+ st.session_state.pending_temp_code = basic_scene
+ st.session_state.pending_changes = True
+ st.success(f"Created new scene with Plot {i+1}! Click 'Refresh App' to update the editor.")
+
+ # Provide option to save the script
+ if st.button("π Save This Script", key="save_script_btn"):
+ # Generate a unique filename
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
+ script_filename = f"script_{timestamp}.py"
+
+ # Offer download button for the script
+ st.download_button(
+ label="β¬οΈ Download Script",
+ data=python_code,
+ file_name=script_filename,
+ mime="text/plain"
+ )
+
+ # Show advanced examples and tips
+ with st.expander("Python Script Runner Tips"):
+ st.markdown("""
+ ### Python Script Runner Tips
+
+ **What can I run?**
+ - Any Python code that doesn't require direct UI interaction
+ - Libraries like Matplotlib, NumPy, Pandas, SciPy, etc.
+ - Data processing and visualization code
+ - Scripts that ask for user input (now supported!)
+
+ **What can't I run?**
+ - Streamlit, Gradio, Dash, or other web UIs
+ - Long-running operations (timeout will occur)
+ - Code that requires file access outside the temporary environment
+
+ **Working with visualizations:**
+ - All Matplotlib/Seaborn plots will be automatically captured
+ - Pandas DataFrames are detected and displayed as tables
+ - Use `print()` to show text output
+
+ **Handling user input:**
+ - The app detects input() calls and automatically creates text fields
+ - Input values you provide will be passed to the script when it runs
+ - Type conversion (like int(), float()) is preserved
+
+ **Adding to animations:**
+ - Charts and plots can be directly added to your Manim animations
+ - Generated images will be properly scaled for your animation
+ - Perfect for educational content combining data and animations
+ """)
+
+ # Help section
+ with st.sidebar.expander("βΉοΈ Help & Info"):
+ st.markdown("""
+ ### About Manim Animation Studio
+
+ This app allows you to create mathematical animations using Manim,
+ an animation engine for explanatory math videos.
+
+ ### Example Code
+
+ ```python
+ from manim import *
+
+ class SimpleExample(Scene):
+ def construct(self):
+ circle = Circle(color=BLUE)
+ self.play(Create(circle))
+
+ square = Square(color=RED).next_to(circle, RIGHT)
+ self.play(Create(square))
+
+ text = Text("Manim Animation").next_to(VGroup(circle, square), DOWN)
+ self.play(Write(text))
+
+ self.wait(2)
+ ```
+ """)
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file