"""
def prepare_audio_for_manim(audio_file, target_dir):
"""Process audio file and return path for use in Manim"""
try:
# Create audio directory if it doesn't exist
audio_dir = os.path.join(target_dir, "audio")
os.makedirs(audio_dir, exist_ok=True)
# Generate a unique filename
filename = f"audio_{int(time.time())}.mp3"
output_path = os.path.join(audio_dir, filename)
# Save audio file
with open(output_path, "wb") as f:
f.write(audio_file.getvalue())
return output_path
except Exception as e:
logger.error(f"Audio processing error: {str(e)}")
return None
def mp4_to_gif(mp4_path, output_path, fps=15):
"""Convert MP4 to GIF using ffmpeg as a backup when Manim fails"""
try:
# Use ffmpeg for conversion with optimized settings
command = [
"ffmpeg",
"-i", mp4_path,
"-vf", f"fps={fps},scale=640:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse",
"-loop", "0",
output_path
]
# Run the conversion
result = subprocess.run(command, capture_output=True, text=True)
if result.returncode != 0:
logger.error(f"FFmpeg conversion error: {result.stderr}")
return None
return output_path
except Exception as e:
logger.error(f"GIF conversion error: {str(e)}")
return None
def generate_manim_video(python_code, format_type, quality_preset, animation_speed=1.0, audio_path=None, fps=None):
temp_dir = None
progress_placeholder = st.empty()
status_placeholder = st.empty()
log_placeholder = st.empty()
video_data = None # Initialize video data variable
try:
if not python_code or not format_type:
raise ValueError("Missing required parameters")
# Create temporary directory
temp_dir = tempfile.mkdtemp(prefix="manim_render_")
# Extract the scene class name from the code
scene_class = extract_scene_class_name(python_code)
logger.info(f"Detected scene class: {scene_class}")
# If audio is provided, we need to modify the code to include it
if audio_path:
# Check if the code already has a with_sound decorator
if "with_sound" not in python_code:
# Add the necessary import
if "from manim.scene.scene_file_writer import SceneFileWriter" not in python_code:
python_code = "from manim.scene.scene_file_writer import SceneFileWriter\n" + python_code
# Add sound to the scene
scene_def_pattern = f"class {scene_class}\\(.*?\\):"
scene_def_match = re.search(scene_def_pattern, python_code)
if scene_def_match:
scene_def = scene_def_match.group(0)
scene_def_with_sound = f"@with_sound(\"{audio_path}\")\n{scene_def}"
python_code = python_code.replace(scene_def, scene_def_with_sound)
else:
logger.warning("Could not find scene definition to add audio")
# Write the code to a file
scene_file = os.path.join(temp_dir, "scene.py")
with open(scene_file, "w", encoding="utf-8") as f:
f.write(python_code)
# Map quality preset to Manim quality flag
quality_map = {
"480p": "-ql", # Low quality
"720p": "-qm", # Medium quality
"1080p": "-qh", # High quality
"4K": "-qk", # 4K quality
"8K": "-qp" # 8K quality (production quality)
}
quality_flag = quality_map.get(quality_preset, "-qm")
# Handle special formats
if format_type == "png_sequence":
# For PNG sequence, we need additional flags
format_arg = "--format=png"
extra_args = ["--save_pngs"]
elif format_type == "svg":
# For SVG, we need a different format
format_arg = "--format=svg"
extra_args = []
else:
# Standard video formats
format_arg = f"--format={format_type}"
extra_args = []
# Add custom FPS if specified
if fps is not None:
extra_args.append(f"--fps={fps}")
# Show status and create progress bar
status_placeholder.info(f"Rendering {scene_class} with {quality_preset} quality...")
progress_bar = progress_placeholder.progress(0)
# Build command
command = [
"manim",
scene_file,
scene_class,
quality_flag,
format_arg
]
command.extend(extra_args)
logger.info(f"Running command: {' '.join(command)}")
# Execute the command
process = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True
)
# Track output
full_output = []
output_file_path = None
mp4_output_path = None # Track MP4 output for GIF fallback
# Animation tracking variables
total_animations = None
current_animation = 0
total_frames = None
current_frame = 0
while True:
line = process.stdout.readline()
if not line and process.poll() is not None:
break
full_output.append(line)
log_placeholder.code("".join(full_output[-10:]))
# Try to detect total animations
if "Rendering animation number" in line or "Processing animation" in line:
try:
# Extract current animation number
anim_match = re.search(r"(?:Rendering animation number|Processing animation) (\d+) (?:out of|/) (\d+)", line)
if anim_match:
current_animation = int(anim_match.group(1))
total_animations = int(anim_match.group(2))
logger.info(f"Animation progress: {current_animation}/{total_animations}")
# Calculate progress based on animations
animation_progress = current_animation / total_animations
progress_bar.progress(animation_progress)
status_placeholder.info(f"Rendering {scene_class}: Animation {current_animation}/{total_animations} ({int(animation_progress*100)}%)")
except Exception as e:
logger.error(f"Error parsing animation progress: {str(e)}")
# Try to extract total frames information as fallback
elif "Render animations with total frames:" in line and not total_animations:
try:
total_frames = int(line.split("Render animations with total frames:")[1].strip().split()[0])
logger.info(f"Total frames to render: {total_frames}")
except Exception as e:
logger.error(f"Error parsing total frames: {str(e)}")
# Update progress bar based on frame information if animation count not available
elif "Rendering frame" in line and total_frames and not total_animations:
try:
# Extract current frame number
frame_match = re.search(r"Rendering frame (\d+)", line)
if frame_match:
current_frame = int(frame_match.group(1))
# Calculate progress as current frame / total frames
frame_progress = min(0.99, current_frame / total_frames)
progress_bar.progress(frame_progress)
# Update status with frame information
status_placeholder.info(f"Rendering {scene_class}: Frame {current_frame}/{total_frames} ({int(frame_progress*100)}%)")
except Exception as e:
logger.error(f"Error parsing frame progress: {str(e)}")
elif "%" in line and not total_animations and not total_frames:
try:
# Fallback to percentage if available
percent = float(line.split("%")[0].strip().split()[-1])
progress_bar.progress(min(0.99, percent / 100))
except:
pass
# Try to capture the output file path from Manim's output
if "File ready at" in line:
try:
# Combine next few lines to get the full path
path_parts = []
path_parts.append(line.split("File ready at")[-1].strip())
# Read up to 5 more lines to get the complete path
for _ in range(5):
additional_line = process.stdout.readline()
if additional_line:
full_output.append(additional_line)
path_parts.append(additional_line.strip())
if additional_line.strip().endswith(('.mp4', '.gif', '.webm', '.svg')):
break
# Join all parts and clean up
potential_path = ''.join(path_parts).replace("'", "").strip()
# Look for path pattern surrounded by quotes
path_match = re.search(r'([\'"]?)((?:/|[a-zA-Z]:\\).*?\.(?:mp4|gif|webm|svg))(\1)', potential_path)
if path_match:
output_file_path = path_match.group(2)
logger.info(f"Found output path in logs: {output_file_path}")
# Track MP4 file for potential GIF fallback
if output_file_path.endswith('.mp4'):
mp4_output_path = output_file_path
except Exception as e:
logger.error(f"Error parsing output path: {str(e)}")
# Wait for the process to complete
process.wait()
progress_bar.progress(1.0)
# IMPORTANT: Wait a moment for file system to catch up
time.sleep(3)
# Special handling for GIF format - if Manim failed to generate a GIF but we have an MP4
if format_type == "gif" and (not output_file_path or not os.path.exists(output_file_path)) and mp4_output_path and os.path.exists(mp4_output_path):
status_placeholder.info("GIF generation via Manim failed. Trying FFmpeg conversion...")
# Generate a GIF using FFmpeg
gif_output_path = os.path.join(temp_dir, f"{scene_class}_converted.gif")
gif_path = mp4_to_gif(mp4_output_path, gif_output_path, fps=fps if fps else 15)
if gif_path and os.path.exists(gif_path):
output_file_path = gif_path
logger.info(f"Successfully converted MP4 to GIF using FFmpeg: {gif_path}")
# For PNG sequence, we need to collect the PNGs
if format_type == "png_sequence":
# Find the PNG directory
png_dirs = []
search_dirs = [
os.path.join(os.getcwd(), "media", "images", scene_class, "Animations"),
os.path.join(temp_dir, "media", "images", scene_class, "Animations"),
"/tmp/media/images",
]
for search_dir in search_dirs:
if os.path.exists(search_dir):
for root, dirs, _ in os.walk(search_dir):
for d in dirs:
if os.path.exists(os.path.join(root, d)):
png_dirs.append(os.path.join(root, d))
if png_dirs:
# Get the newest directory
newest_dir = max(png_dirs, key=os.path.getctime)
# Create a zip file with all PNGs
png_files = [f for f in os.listdir(newest_dir) if f.endswith('.png')]
if png_files:
zip_path = os.path.join(temp_dir, f"{scene_class}_pngs.zip")
with zipfile.ZipFile(zip_path, 'w') as zipf:
for png in png_files:
png_path = os.path.join(newest_dir, png)
zipf.write(png_path, os.path.basename(png_path))
with open(zip_path, 'rb') as f:
video_data = f.read()
logger.info(f"Created PNG sequence zip: {zip_path}")
else:
logger.error("No PNG files found in directory")
else:
logger.error("No PNG directories found")
elif output_file_path and os.path.exists(output_file_path):
# For other formats, read the output file directly
with open(output_file_path, 'rb') as f:
video_data = f.read()
logger.info(f"Read output file from path: {output_file_path}")
else:
# If we didn't find the output path, search for files
search_paths = [
os.path.join(os.getcwd(), "media", "videos"),
os.path.join(os.getcwd(), "media", "videos", "scene"),
os.path.join(os.getcwd(), "media", "videos", scene_class),
"/tmp/media/videos",
temp_dir,
os.path.join(temp_dir, "media", "videos"),
]
# Add quality-specific paths
for quality in ["480p30", "720p30", "1080p60", "2160p60", "4320p60"]:
search_paths.append(os.path.join(os.getcwd(), "media", "videos", "scene", quality))
search_paths.append(os.path.join(os.getcwd(), "media", "videos", scene_class, quality))
# For SVG format
if format_type == "svg":
search_paths.extend([
os.path.join(os.getcwd(), "media", "designs"),
os.path.join(os.getcwd(), "media", "designs", scene_class),
])
# Find all output files in the search paths
output_files = []
for search_path in search_paths:
if os.path.exists(search_path):
for root, _, files in os.walk(search_path):
for file in files:
if file.endswith(f".{format_type}") and "partial" not in file:
file_path = os.path.join(root, file)
if os.path.exists(file_path):
output_files.append(file_path)
logger.info(f"Found output file: {file_path}")
if output_files:
# Get the newest file
latest_file = max(output_files, key=os.path.getctime)
with open(latest_file, 'rb') as f:
video_data = f.read()
logger.info(f"Read output from file search: {latest_file}")
# If the format is GIF but we got an MP4, try to convert it
if format_type == "gif" and latest_file.endswith('.mp4'):
gif_output_path = os.path.join(temp_dir, f"{scene_class}_converted.gif")
gif_path = mp4_to_gif(latest_file, gif_output_path, fps=fps if fps else 15)
if gif_path and os.path.exists(gif_path):
with open(gif_path, 'rb') as f:
video_data = f.read()
logger.info(f"Successfully converted MP4 to GIF using FFmpeg: {gif_path}")
# If we got output data, return it
if video_data:
file_size_mb = len(video_data) / (1024 * 1024)
# Clear placeholders
progress_placeholder.empty()
status_placeholder.empty()
log_placeholder.empty()
return video_data, f"â Animation generated successfully! ({file_size_mb:.1f} MB)"
else:
output_str = ''.join(full_output)
logger.error(f"No output files found. Full output: {output_str}")
# Check if we have an MP4 but need a GIF (special handling for GIF issues)
if format_type == "gif":
# Try one more aggressive search for any MP4 file
mp4_files = []
for search_path in [os.getcwd(), temp_dir, "/tmp"]:
for root, _, files in os.walk(search_path):
for file in files:
if file.endswith('.mp4') and scene_class.lower() in file.lower():
mp4_path = os.path.join(root, file)
if os.path.exists(mp4_path) and os.path.getsize(mp4_path) > 0:
mp4_files.append(mp4_path)
if mp4_files:
newest_mp4 = max(mp4_files, key=os.path.getctime)
logger.info(f"Found MP4 for GIF conversion: {newest_mp4}")
# Convert to GIF
gif_output_path = os.path.join(temp_dir, f"{scene_class}_converted.gif")
gif_path = mp4_to_gif(newest_mp4, gif_output_path, fps=fps if fps else 15)
if gif_path and os.path.exists(gif_path):
with open(gif_path, 'rb') as f:
video_data = f.read()
# Clear placeholders
progress_placeholder.empty()
status_placeholder.empty()
log_placeholder.empty()
file_size_mb = len(video_data) / (1024 * 1024)
return video_data, f"â Animation converted to GIF successfully! ({file_size_mb:.1f} MB)"
return None, f"â Error: No output files were generated.\n\nMakim output:\n{output_str[:500]}..."
except Exception as e:
logger.error(f"Error: {str(e)}")
import traceback
logger.error(traceback.format_exc())
if progress_placeholder:
progress_placeholder.empty()
if status_placeholder:
status_placeholder.error(f"Rendering Error: {str(e)}")
if log_placeholder:
log_placeholder.empty()
return None, f"â Error: {str(e)}"
finally:
# CRITICAL: Only cleanup after we've captured the output data
if temp_dir and os.path.exists(temp_dir) and video_data is not None:
try:
shutil.rmtree(temp_dir)
logger.info(f"Cleaned up temp dir: {temp_dir}")
except Exception as e:
logger.error(f"Failed to clean temp dir: {str(e)}")
# ENHANCED PYTHON RUNNER FUNCTIONS
def detect_input_calls(code):
"""Detect input() calls in Python code to prepare for handling"""
input_calls = []
lines = code.split('\n')
for i, line in enumerate(lines):
if 'input(' in line and not line.strip().startswith('#'):
# Try to extract the prompt if available
prompt_match = re.search(r'input\([\'"](.+?)[\'"]\)', line)
prompt = prompt_match.group(1) if prompt_match else f"Input for line {i+1}"
input_calls.append({"line": i+1, "prompt": prompt})
return input_calls
def run_python_script_enhanced(code, inputs=None, timeout=60, enable_debug=False, enable_profile=False,
additional_libs=None, project_files=None, realtime_viz=False):
"""Enhanced version of run_python_script with debugging, profiling, etc."""
result = {
"stdout": "",
"stderr": "",
"exception": None,
"plots": [],
"dataframes": [],
"execution_time": 0,
"profile_data": None,
"debug_steps": [],
"realtime_data": []
}
# Create a tempdir for script execution
with tempfile.TemporaryDirectory() as temp_dir:
# Path for saving plots
plot_dir = os.path.join(temp_dir, 'plots')
os.makedirs(plot_dir, exist_ok=True)
# Handle multi-file project if provided
if project_files:
for filename, file_content in project_files.items():
with open(os.path.join(temp_dir, filename), 'w', encoding='utf-8') as f:
f.write(file_content)
# Set the main script path
main_script = os.path.join(temp_dir, "main.py")
else:
# Write the single code file
main_script = os.path.join(temp_dir, "script.py")
with open(main_script, 'w', encoding='utf-8') as f:
f.write(code)
# Add library imports if specified
if additional_libs:
lib_imports = "\n".join([f"import {lib}" for lib in additional_libs if lib != "numpy" and lib != "matplotlib"])
if lib_imports:
with open(main_script, 'r+', encoding='utf-8') as f:
content = f.read()
f.seek(0, 0)
f.write(lib_imports + "\n\n" + content)
# Add debugging setup if enabled
if enable_debug:
debug_setup = """
import pdb
import sys
import traceback
class StringIODebugger:
def __init__(self):
self.steps = []
def add_step(self, frame, event, arg):
if event == 'line':
self.steps.append({
'file': frame.f_code.co_filename,
'line': frame.f_lineno,
'function': frame.f_code.co_name,
'locals': {k: str(v) for k, v in frame.f_locals.items() if not k.startswith('__')}
})
return self
debug_steps = []
def trace_calls(frame, event, arg):
if event != 'call':
return
co = frame.f_code
func_name = co.co_name
if func_name == 'write':
return
line_no = frame.f_lineno
filename = co.co_filename
if 'debugger' in filename or func_name.startswith('__'):
return
debug_steps.append(f"Calling {func_name} in {filename} at line {line_no}")
return trace_calls
sys.settrace(trace_calls)
"""
with open(main_script, 'r+', encoding='utf-8') as f:
content = f.read()
f.seek(0, 0)
f.write(debug_setup + "\n" + content)
# Add profiling if enabled
if enable_profile:
profile_setup = """
import cProfile
import pstats
import io
# Set up profiler
profiler = cProfile.Profile()
profiler.enable()
"""
profile_teardown = """
# Finish profiling
profiler.disable()
s = io.StringIO()
ps = pstats.Stats(profiler, stream=s).sort_stats('cumulative')
ps.print_stats()
with open('profile_results.txt', 'w') as f:
f.write(s.getvalue())
"""
with open(main_script, 'r+', encoding='utf-8') as f:
content = f.read()
f.seek(0, 0)
f.write(profile_setup + "\n" + content + "\n" + profile_teardown)
# Add real-time visualization if enabled
if realtime_viz:
realtime_viz_setup = """
# Setup for real-time visualization
import threading
import json
import time
class RealTimeData:
def __init__(self):
self.data = []
def add_data(self, label, value):
self.data.append({'label': label, 'value': value, 'time': time.time()})
# Write to file for real-time monitoring
with open('realtime_data.json', 'w') as f:
json.dump(self.data, f)
rt_data = RealTimeData()
# Example usage: rt_data.add_data("iteration", i)
"""
with open(main_script, 'r+', encoding='utf-8') as f:
content = f.read()
f.seek(0, 0)
f.write(realtime_viz_setup + "\n" + content)
# Add input handling code
if inputs and len(inputs) > 0:
# Modify the code to use predefined inputs instead of waiting for user input
input_handling = """
# Input values provided by the user
__INPUT_VALUES = {}
__INPUT_INDEX = 0
# Override the built-in input function
def input(prompt=''):
global __INPUT_INDEX
print(prompt, end='')
if __INPUT_INDEX < len(__INPUT_VALUES):
value = __INPUT_VALUES[__INPUT_INDEX]
__INPUT_INDEX += 1
print(value) # Echo the input
return value
else:
print("\\n[WARNING] No more predefined inputs available, using empty string")
return ""
""".format(inputs)
with open(main_script, 'r+', encoding='utf-8') as f:
content = f.read()
f.seek(0, 0)
f.write(input_handling + "\n" + content)
# Add matplotlib and pandas handling
data_handling = """
# Add plot saving code if matplotlib is used
import os
# For matplotlib plots
if 'matplotlib' in globals() or 'matplotlib.pyplot' in globals() or 'plt' in globals():
import matplotlib
matplotlib.use('Agg') # Use non-interactive backend
import matplotlib.pyplot as plt
# Hook to save all figures
original_show = plt.show
def custom_show(*args, **kwargs):
for i, fig in enumerate(map(plt.figure, plt.get_fignums())):
fig.savefig(os.path.join('{}', f'plot_{{i}}.png'))
return original_show(*args, **kwargs)
plt.show = custom_show
# For pandas DataFrames
if 'pandas' in globals() or 'pd' in globals():
import pandas as pd
import json
# Save DataFrames
original_df_repr_html = pd.DataFrame._repr_html_
def custom_df_repr_html(self):
try:
df_info = {{
"name": str(id(self)),
"shape": self.shape,
"columns": list(map(str, self.columns)),
"preview_html": self.head().to_html()
}}
with open(f'df_{{id(self)}}.json', 'w') as f:
json.dump(df_info, f)
except:
pass
return original_df_repr_html(self)
pd.DataFrame._repr_html_ = custom_df_repr_html
""".format(plot_dir.replace('\\', '\\\\'))
with open(main_script, 'r+', encoding='utf-8') as f:
content = f.read()
f.seek(0, 0)
f.write(data_handling + "\n" + content)
# Files for capturing stdout and stderr
stdout_file = os.path.join(temp_dir, 'stdout.txt')
stderr_file = os.path.join(temp_dir, 'stderr.txt')
# Execute with timeout
start_time = time.time()
try:
# Run the script with stdout and stderr redirection
with open(stdout_file, 'w') as stdout_f, open(stderr_file, 'w') as stderr_f:
process = subprocess.Popen(
[sys.executable, main_script],
stdout=stdout_f,
stderr=stderr_f,
cwd=temp_dir
)
# Real-time monitoring for real-time visualization
if realtime_viz:
realtime_data_file = os.path.join(temp_dir, 'realtime_data.json')
while process.poll() is None:
if os.path.exists(realtime_data_file):
try:
with open(realtime_data_file, 'r') as f:
result["realtime_data"] = json.load(f)
except:
pass
time.sleep(0.1)
# Check for timeout
if time.time() - start_time > timeout:
process.kill()
result["stderr"] += f"\nScript execution timed out after {timeout} seconds."
result["exception"] = "TimeoutError"
break
else:
try:
process.wait(timeout=timeout)
except subprocess.TimeoutExpired:
process.kill()
result["stderr"] += f"\nScript execution timed out after {timeout} seconds."
result["exception"] = "TimeoutError"
# Read the output
with open(stdout_file, 'r') as f:
result["stdout"] = f.read()
with open(stderr_file, 'r') as f:
result["stderr"] = f.read()
# Collect plots
if os.path.exists(plot_dir):
plot_files = sorted([f for f in os.listdir(plot_dir) if f.endswith('.png')])
for plot_file in plot_files:
with open(os.path.join(plot_dir, plot_file), 'rb') as f:
result["plots"].append(f.read())
# Collect dataframes
df_files = [f for f in os.listdir(temp_dir) if f.startswith('df_') and f.endswith('.json')]
for df_file in df_files:
with open(os.path.join(temp_dir, df_file), 'r') as f:
result["dataframes"].append(json.load(f))
# Collect profiling data if enabled
if enable_profile and os.path.exists(os.path.join(temp_dir, 'profile_results.txt')):
with open(os.path.join(temp_dir, 'profile_results.txt'), 'r') as f:
result["profile_data"] = f.read()
# Collect debug data if enabled
if enable_debug and 'debug_steps' in globals():
result["debug_steps"] = debug_steps
# Calculate execution time
result["execution_time"] = time.time() - start_time
except Exception as e:
result["exception"] = str(e)
result["stderr"] += f"\nError executing script: {str(e)}"
return result
def display_python_script_results_enhanced(result):
"""Display the enhanced results from the Python script execution"""
if not result:
st.error("No results to display.")
return
# Display execution time
st.info(f"Execution completed in {result['execution_time']:.2f} seconds")
# Display any errors
if result["exception"]:
st.error(f"Exception occurred: {result['exception']}")
if result["stderr"]:
st.error("Errors:")
st.code(result["stderr"], language="bash")
# Display profiling data if available
if result.get("profile_data"):
with st.expander("Profiling Results"):
st.code(result["profile_data"], language="bash")
# Display debugging steps if available
if result.get("debug_steps"):
with st.expander("Debugging Steps"):
for i, step in enumerate(result["debug_steps"]):
st.markdown(f"**Step {i+1}**: {step}")
# Display plots if any
if result["plots"]:
st.markdown("### Plots")
cols = st.columns(min(3, len(result["plots"])))
for i, plot_data in enumerate(result["plots"]):
cols[i % len(cols)].image(plot_data, use_column_width=True)
# Add button to use this plot in Manim
if cols[i % len(cols)].button(f"Use in Manim", key=f"use_plot_{i}"):
# Create a temporary file
with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as tmp:
tmp.write(plot_data)
plot_path = tmp.name
# Generate Manim code
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)
"""
if st.session_state.code:
st.session_state.code += "\n" + plot_code
else:
st.session_state.code = f"""from manim import *
class PlotScene(Scene):
def construct(self):
{plot_code}
"""
st.session_state.temp_code = st.session_state.code
st.success(f"Added plot to your Manim code!")
# Set pending tab switch to editor tab
st.session_state.pending_tab_switch = 0
st.rerun()
# Display dataframes if any
if result["dataframes"]:
st.markdown("### DataFrames")
for df_info in result["dataframes"]:
with st.expander(f"{df_info.get('name', 'DataFrame')} - {df_info['shape'][0]} rows à {df_info['shape'][1]} columns"):
st.markdown(df_info["preview_html"], unsafe_allow_html=True)
# Add button to visualize this dataframe in Manim
if st.button(f"Visualize in Manim", key=f"viz_df_{df_info.get('name', 'df')}"):
# Generate Manim code for dataframe visualization
df_viz_code = f"""
# Create a simple table visualization
columns = {df_info['columns']}
table = Table(
col_labels=[Text(col, font_size=24) for col in columns]
)
# Add data rows (showing first 5 rows)
for i in range(min(5, {df_info['shape'][0]})):
# This is a placeholder - in a real implementation, you'd extract actual data
table.add_row(*[Text(f"Row {{i}}, Col {{j}}", font_size=20) for j in range(len(columns))])
self.play(Create(table))
self.wait(1)
"""
if st.session_state.code:
st.session_state.code += "\n" + df_viz_code
else:
st.session_state.code = f"""from manim import *
class DataFrameScene(Scene):
def construct(self):
{df_viz_code}
"""
st.session_state.temp_code = st.session_state.code
st.success(f"Added DataFrame visualization to your Manim code!")
# Set pending tab switch to editor tab
st.session_state.pending_tab_switch = 0
st.rerun()
# Display standard output
if result["stdout"]:
st.markdown("### Standard Output")
st.code(result["stdout"], language="bash")
# Display real-time data if available
if result.get("realtime_data"):
st.markdown("### Real-time Data")
# Convert to DataFrame for easier visualization
import pandas as pd
rt_df = pd.DataFrame(result["realtime_data"])
# Create a plotly chart
import plotly.express as px
if not rt_df.empty and "time" in rt_df.columns and "value" in rt_df.columns:
fig = px.line(rt_df, x="time", y="value", color="label" if "label" in rt_df.columns else None,
title="Real-time Data Visualization")
st.plotly_chart(fig, use_container_width=True)
# Add button to create Manim animation from this data
if st.button("Create Manim Animation from Data", key="create_manim_from_rt"):
# Extract data points
data_points = []
for _, row in rt_df.iterrows():
if "value" in row:
data_points.append(float(row["value"]))
# Generate Manim code
rt_viz_code = f"""
# Visualize real-time data
data = {data_points}
axes = Axes(
x_range=[0, {len(data_points)}, 1],
y_range=[{min(data_points) if data_points else 0}, {max(data_points) if data_points else 10}, {(max(data_points)-min(data_points))/10 if data_points and max(data_points) > min(data_points) else 1}],
axis_config={{"color": BLUE}}
)
points = [axes.coords_to_point(i, v) for i, v in enumerate(data)]
graph = VMobject(color=RED)
graph.set_points_as_corners(points)
self.play(Create(axes))
self.play(Create(graph), run_time=2)
self.wait(1)
"""
if st.session_state.code:
st.session_state.code += "\n" + rt_viz_code
else:
st.session_state.code = f"""from manim import *
class DataVisualizationScene(Scene):
def construct(self):
{rt_viz_code}
"""
st.session_state.temp_code = st.session_state.code
st.success(f"Added real-time data visualization to your Manim code!")
# Set pending tab switch to editor tab
st.session_state.pending_tab_switch = 0
st.rerun()
# C/C++ RUNNER FUNCTIONS
def compile_cpp_code_enhanced(code, settings, project_files=None, enable_debug=False, breakpoints=None, watch_vars=None):
"""Enhanced function to compile C++ code with advanced options."""
try:
# Create a temporary directory for compilation
temp_dir = tempfile.mkdtemp(prefix="cpp_runner_")
# Write the project files
if project_files:
for filename, content in project_files.items():
file_path = os.path.join(temp_dir, filename)
with open(file_path, "w") as f:
f.write(content)
# Set main file for single file mode
cpp_file = os.path.join(temp_dir, "main.cpp")
else:
# Write the single code file
cpp_file = os.path.join(temp_dir, "main.cpp")
with open(cpp_file, "w") as f:
f.write(code)
# Output executable path
exe_file = os.path.join(temp_dir, "program.exe" if platform.system() == "Windows" else "program")
# Build the compilation command
compiler = settings.get("compiler", "g++")
std_version = settings.get("std", "c++17")
optimization = settings.get("optimization", "-O2")
compile_cmd = [
compiler,
"-std=" + std_version,
optimization
]
# Add debug flag if debugging is enabled
if enable_debug:
compile_cmd.append("-g")
# Auto-detect include paths if not specified
include_paths = settings.get("include_paths", [])
if not include_paths:
# Common include directories
common_include_dirs = [
"/usr/include",
"/usr/local/include",
"/opt/local/include",
"/opt/homebrew/include"
]
# Add detected paths for specified libraries
for lib in settings.get("libraries", []):
if lib == "Eigen":
for dir in common_include_dirs:
if os.path.exists(os.path.join(dir, "Eigen")):
include_paths.append(dir)
elif os.path.exists(os.path.join(dir, "eigen3")):
include_paths.append(dir)
elif lib == "OpenCV":
try:
# Get OpenCV include paths using pkg-config
result = subprocess.run(
["pkg-config", "--cflags", "opencv4"],
capture_output=True,
text=True,
check=False
)
if result.returncode == 0:
# Extract include paths from pkg-config output
for flag in result.stdout.strip().split():
if flag.startswith("-I"):
include_paths.append(flag[2:])
except Exception:
pass
# Add preprocessor definitions
for definition in settings.get("definitions", []):
if "=" in definition:
name, value = definition.split("=", 1)
compile_cmd.append(f"-D{name}={value}")
else:
compile_cmd.append(f"-D{definition}")
# Add include paths
for path in include_paths:
compile_cmd.append(f"-I{path}")
# Add library paths
for path in settings.get("library_paths", []):
compile_cmd.append(f"-L{path}")
# Add files to compile
if project_files:
source_files = [os.path.join(temp_dir, f) for f in project_files.keys() if f.endswith((".cpp", ".c", ".cc"))]
compile_cmd.extend(source_files)
else:
compile_cmd.append(cpp_file)
# Output file
compile_cmd.extend(["-o", exe_file])
# Add libraries
for lib in settings.get("libraries", []):
if lib == "Eigen":
# Eigen is header-only, nothing to link
pass
elif lib == "OpenCV":
# Add OpenCV libraries
try:
# Get OpenCV libraries using pkg-config
pkg_config = subprocess.run(
["pkg-config", "--libs", "opencv4"],
capture_output=True,
text=True,
check=False
)
if pkg_config.returncode == 0:
compile_cmd.extend(pkg_config.stdout.strip().split())
else:
# Try opencv instead of opencv4
pkg_config = subprocess.run(
["pkg-config", "--libs", "opencv"],
capture_output=True,
text=True,
check=False
)
if pkg_config.returncode == 0:
compile_cmd.extend(pkg_config.stdout.strip().split())
else:
# Fallback to common OpenCV libraries
compile_cmd.extend(["-lopencv_core", "-lopencv_imgproc", "-lopencv_highgui"])
except:
# Fallback to common OpenCV libraries
compile_cmd.extend(["-lopencv_core", "-lopencv_imgproc", "-lopencv_highgui"])
elif lib == "Boost":
# Add common Boost libraries
compile_cmd.extend(["-lboost_system", "-lboost_filesystem"])
elif lib == "FFTW":
compile_cmd.append("-lfftw3")
elif lib == "SDL2":
compile_cmd.append("-lSDL2")
elif lib == "SFML":
compile_cmd.extend(["-lsfml-graphics", "-lsfml-window", "-lsfml-system"])
elif lib == "OpenGL":
compile_cmd.extend(["-lGL", "-lGLU", "-lglut"])
# Add additional libraries
for lib in settings.get("additional_libs", []):
compile_cmd.append(f"-l{lib}")
# Add advanced flags
if settings.get("advanced_flags"):
compile_cmd.extend(settings["advanced_flags"].split())
# Run the compilation process
logger.info(f"Compiling with command: {' '.join(compile_cmd)}")
result = subprocess.run(
compile_cmd,
capture_output=True,
text=True,
check=False,
cwd=temp_dir
)
if result.returncode != 0:
return None, result.stderr, temp_dir
return exe_file, None, temp_dir
except Exception as e:
return None, str(e), None
def run_cpp_executable_enhanced(exe_path, temp_dir, inputs=None, timeout=30, enable_debug=False, breakpoints=None, watch_vars=None):
"""Enhanced function to run C++ executable with debugging support."""
result = {
"stdout": "",
"stderr": "",
"execution_time": 0,
"images": [],
"exception": None,
"debug_output": None,
"memory_usage": None
}
try:
# Prepare input data if provided
input_data = "\n".join(inputs) if inputs else None
# Start timing
start_time = time.time()
if enable_debug and breakpoints:
# Run with GDB for debugging
gdb_commands = ["set pagination off"]
# Add breakpoints
for bp in breakpoints:
gdb_commands.append(f"break {bp}")
# Add watchpoints for variables
if watch_vars:
for var in watch_vars:
gdb_commands.append(f"watch {var}")
# Run the program
gdb_commands.append("run")
# Continue to end
gdb_commands.append("continue")
# Quit GDB
gdb_commands.append("quit")
# Create GDB command file
gdb_cmd_file = os.path.join(temp_dir, "gdb_commands.txt")
with open(gdb_cmd_file, "w") as f:
f.write("\n".join(gdb_commands))
# Run with GDB
process = subprocess.run(
["gdb", "-x", gdb_cmd_file, "-batch", exe_path],
input=input_data,
text=True,
capture_output=True,
timeout=timeout,
cwd=temp_dir
)
# Capture outputs
result["stdout"] = process.stdout
result["stderr"] = process.stderr
result["debug_output"] = process.stdout
else:
# Run normally
process = subprocess.run(
[exe_path],
input=input_data,
text=True,
capture_output=True,
timeout=timeout,
cwd=temp_dir
)
# Capture outputs
result["stdout"] = process.stdout
result["stderr"] = process.stderr
# Calculate execution time
result["execution_time"] = time.time() - start_time
# Look for generated images in the executable directory
for ext in [".png", ".jpg", ".jpeg", ".bmp", ".ppm"]:
image_files = [f for f in os.listdir(temp_dir) if f.endswith(ext)]
for img_file in image_files:
try:
img_path = os.path.join(temp_dir, img_file)
# For PPM files, convert to PNG for easier display
if img_file.endswith(".ppm"):
# Create output path
png_path = os.path.join(temp_dir, img_file.replace(".ppm", ".png"))
# Convert using PIL
from PIL import Image
Image.open(img_path).save(png_path)
img_path = png_path
img_file = img_file.replace(".ppm", ".png")
with open(img_path, "rb") as f:
result["images"].append({
"name": img_file,
"data": f.read()
})
except Exception as e:
logger.error(f"Error processing image {img_file}: {str(e)}")
# Estimate memory usage
try:
if platform.system() != "Windows":
# Use ps command to get memory usage
ps_output = subprocess.run(
["ps", "-p", str(process.pid), "-o", "rss="],
capture_output=True,
text=True,
check=False
)
if ps_output.returncode == 0:
mem_kb = int(ps_output.stdout.strip())
result["memory_usage"] = mem_kb / 1024 # Convert to MB
except:
pass
return result
except subprocess.TimeoutExpired:
result["stderr"] += f"\nProgram execution timed out after {timeout} seconds."
result["exception"] = "TimeoutError"
return result
except Exception as e:
result["stderr"] += f"\nError executing program: {str(e)}"
result["exception"] = str(e)
return result
def parse_animation_steps(python_code):
"""Parse Manim code to extract animation steps for timeline editor"""
animation_steps = []
# Look for self.play calls in the code
play_calls = re.findall(r'self\.play\((.*?)\)', python_code, re.DOTALL)
wait_calls = re.findall(r'self\.wait\((.*?)\)', python_code, re.DOTALL)
# Extract animation objects from play calls
for i, play_call in enumerate(play_calls):
# Parse the arguments to self.play()
animations = [arg.strip() for arg in play_call.split(',')]
# Get wait time after this animation if available
wait_time = 1.0 # Default wait time
if i < len(wait_calls):
wait_match = re.search(r'(\d+\.?\d*)', wait_calls[i])
if wait_match:
wait_time = float(wait_match.group(1))
# Add to animation steps
animation_steps.append({
"id": i+1,
"type": "play",
"animations": animations,
"duration": wait_time,
"start_time": sum([step.get("duration", 1.0) for step in animation_steps]),
"code": f"self.play({play_call})"
})
return animation_steps
def generate_code_from_timeline(animation_steps, original_code):
"""Generate Manim code from the timeline data"""
# Extract the class definition and setup
class_match = re.search(r'(class\s+\w+\s*\([^)]*\)\s*:.*?def\s+construct\s*\(\s*self\s*\)\s*:)', original_code, re.DOTALL)
if not class_match:
return original_code # Can't find proper structure to modify
setup_code = class_match.group(1)
# Build the new construct method
new_code = [setup_code]
indent = " " # Standard Manim indentation
# Add each animation step in order
for step in sorted(animation_steps, key=lambda x: x["id"]):
new_code.append(f"{indent}{step['code']}")
if "duration" in step and step["duration"] > 0:
new_code.append(f"{indent}self.wait({step['duration']})")
# Add any code that might come after animations
end_match = re.search(r'(#\s*End\s+of\s+animations.*?$)', original_code, re.DOTALL)
if end_match:
new_code.append(end_match.group(1))
# Combine the code parts with proper indentation
return "\n".join(new_code)
def create_timeline_editor(code):
"""Create an interactive timeline editor for animation sequences"""
st.markdown("### đī¸ Animation Timeline Editor")
if not code:
st.warning("Add animation code first to use the timeline editor.")
return code
# Parse animation steps from the code
animation_steps = parse_animation_steps(code)
if not animation_steps:
st.warning("No animation steps detected in your code.")
return code
# Convert to DataFrame for easier manipulation
df = pd.DataFrame(animation_steps)
# Create an interactive Gantt chart with plotly
st.markdown("#### Animation Timeline")
st.markdown("Drag timeline elements to reorder or resize to change duration")
# Create the Gantt chart
fig = px.timeline(
df,
x_start="start_time",
x_end=df["start_time"] + df["duration"],
y="id",
color="type",
hover_name="animations",
labels={"id": "Step", "start_time": "Time (seconds)"}
)
# Make it interactive
fig.update_layout(
height=400,
xaxis=dict(
title="Time (seconds)",
rangeslider_visible=True
)
)
# Add buttons and interactivity
timeline_chart = st.plotly_chart(fig, use_container_width=True)
# Control panel
st.markdown("#### Timeline Controls")
controls_col1, controls_col2, controls_col3 = st.columns(3)
with controls_col1:
selected_step = st.selectbox(
"Select Step to Edit:",
options=list(range(1, len(animation_steps) + 1)),
format_func=lambda x: f"Step {x}"
)
with controls_col2:
new_duration = st.number_input(
"Duration (seconds):",
min_value=0.1,
max_value=10.0,
value=float(df[df["id"] == selected_step]["duration"].values[0]),
step=0.1
)
with controls_col3:
step_action = st.selectbox(
"Action:",
options=["Update Duration", "Move Up", "Move Down", "Delete Step"]
)
apply_btn = st.button("Apply Change", key="apply_timeline_change")
# Handle timeline modifications
if apply_btn:
modified = False
if step_action == "Update Duration":
# Update the duration of the selected step
idx = df[df["id"] == selected_step].index[0]
df.at[idx, "duration"] = new_duration
modified = True
elif step_action == "Move Up" and selected_step > 1:
# Swap with the step above
idx1 = df[df["id"] == selected_step].index[0]
idx2 = df[df["id"] == selected_step - 1].index[0]
# Swap IDs to maintain order
df.at[idx1, "id"], df.at[idx2, "id"] = selected_step - 1, selected_step
modified = True
elif step_action == "Move Down" and selected_step < len(animation_steps):
# Swap with the step below
idx1 = df[df["id"] == selected_step].index[0]
idx2 = df[df["id"] == selected_step + 1].index[0]
# Swap IDs to maintain order
df.at[idx1, "id"], df.at[idx2, "id"] = selected_step + 1, selected_step
modified = True
elif step_action == "Delete Step":
# Remove the selected step
df = df[df["id"] != selected_step]
# Reindex remaining steps
new_ids = list(range(1, len(df) + 1))
df["id"] = new_ids
modified = True
if modified:
# Recalculate start times
df = df.sort_values("id")
cumulative_time = 0
for idx, row in df.iterrows():
df.at[idx, "start_time"] = cumulative_time
cumulative_time += row["duration"]
# Regenerate animation code
animation_steps = df.to_dict('records')
new_code = generate_code_from_timeline(animation_steps, code)
st.success("Timeline updated! Code has been regenerated.")
return new_code
# Visual keyframe editor
st.markdown("#### Visual Keyframe Editor")
st.markdown("Add keyframes for smooth property transitions")
keyframe_obj = st.selectbox(
"Select object to animate:",
options=[f"Object {i+1}" for i in range(5)] # Placeholder for actual objects
)
keyframe_prop = st.selectbox(
"Select property:",
options=["position", "scale", "rotation", "opacity", "color"]
)
# Keyframe timeline visualization
keyframe_times = [0, 1, 2, 3, 4] # Placeholder
keyframe_values = [0, 0.5, 0.8, 0.2, 1.0] # Placeholder
keyframe_df = pd.DataFrame({
"time": keyframe_times,
"value": keyframe_values
})
keyframe_fig = px.line(
keyframe_df,
x="time",
y="value",
markers=True,
title=f"{keyframe_prop.capitalize()} Keyframes"
)
keyframe_fig.update_layout(
xaxis_title="Time (seconds)",
yaxis_title="Value",
height=250
)
st.plotly_chart(keyframe_fig, use_container_width=True)
keyframe_col1, keyframe_col2, keyframe_col3 = st.columns(3)
with keyframe_col1:
keyframe_time = st.number_input("Time (s)", min_value=0.0, max_value=10.0, value=0.0, step=0.1)
with keyframe_col2:
keyframe_value = st.number_input("Value", min_value=0.0, max_value=1.0, value=0.0, step=0.1)
with keyframe_col3:
add_keyframe = st.button("Add Keyframe")
# Return the original code or modified code
return code
def export_to_educational_format(video_data, format_type, animation_title, explanation_text, temp_dir):
"""Export animation to various educational formats"""
try:
if format_type == "powerpoint":
# Make sure python-pptx is installed
try:
import pptx
from pptx.util import Inches
except ImportError:
logger.error("python-pptx not installed")
subprocess.run([sys.executable, "-m", "pip", "install", "python-pptx"], check=True)
import pptx
from pptx.util import Inches
# Create PowerPoint presentation
prs = pptx.Presentation()
# Title slide
title_slide = prs.slides.add_slide(prs.slide_layouts[0])
title_slide.shapes.title.text = animation_title
title_slide.placeholders[1].text = "Created with Manim Animation Studio"
# Video slide
video_slide = prs.slides.add_slide(prs.slide_layouts[5])
video_slide.shapes.title.text = "Animation"
# Save video to temp file
video_path = os.path.join(temp_dir, "animation.mp4")
with open(video_path, "wb") as f:
f.write(video_data)
# Add video to slide
try:
left = Inches(1)
top = Inches(1.5)
width = Inches(8)
height = Inches(4.5)
video_slide.shapes.add_movie(video_path, left, top, width, height)
except Exception as e:
logger.error(f"Error adding video to PowerPoint: {str(e)}")
# Fallback to adding a picture with link
img_path = os.path.join(temp_dir, "thumbnail.png")
# Generate thumbnail with ffmpeg
subprocess.run([
"ffmpeg", "-i", video_path, "-ss", "00:00:01.000",
"-vframes", "1", img_path
], check=True)
if os.path.exists(img_path):
pic = video_slide.shapes.add_picture(img_path, left, top, width, height)
video_slide.shapes.add_textbox(left, top + height + Inches(0.5), width, Inches(0.5)).text_frame.text = "Click to play video (exported separately)"
# Explanation slide
if explanation_text:
text_slide = prs.slides.add_slide(prs.slide_layouts[1])
text_slide.shapes.title.text = "Explanation"
text_slide.placeholders[1].text = explanation_text
# Save presentation
output_path = os.path.join(temp_dir, f"{animation_title.replace(' ', '_')}.pptx")
prs.save(output_path)
# Read the file to return it
with open(output_path, "rb") as f:
return f.read(), "powerpoint"
elif format_type == "html":
# Create interactive HTML animation
html_template = """
{title}
{title}
Explanation
{explanation_html}
"""
# Convert video data to base64
video_base64 = base64.b64encode(video_data).decode('utf-8')
# Convert markdown explanation to HTML
explanation_html = markdown.markdown(explanation_text) if explanation_text else "
No explanation provided.
"
# Format the HTML template
html_content = html_template.format(
title=animation_title,
video_base64=video_base64,
explanation_html=explanation_html
)
# Save to file
output_path = os.path.join(temp_dir, f"{animation_title.replace(' ', '_')}.html")
with open(output_path, "w", encoding="utf-8") as f:
f.write(html_content)
# Read the file to return it
with open(output_path, "rb") as f:
return f.read(), "html"
elif format_type == "sequence":
# Generate animation sequence with explanatory text
# Make sure FPDF is installed
try:
from fpdf import FPDF
except ImportError:
logger.error("fpdf not installed")
subprocess.run([sys.executable, "-m", "pip", "install", "fpdf"], check=True)
from fpdf import FPDF
# Save video temporarily
temp_video_path = os.path.join(temp_dir, "temp_video.mp4")
with open(temp_video_path, "wb") as f:
f.write(video_data)
# Create frames directory
frames_dir = os.path.join(temp_dir, "frames")
os.makedirs(frames_dir, exist_ok=True)
# Extract frames using ffmpeg (assuming it's installed)
frame_count = 5 # Number of key frames to extract
try:
subprocess.run([
"ffmpeg",
"-i", temp_video_path,
"-vf", f"select=eq(n\\,0)+eq(n\\,{frame_count//4})+eq(n\\,{frame_count//2})+eq(n\\,{frame_count*3//4})+eq(n\\,{frame_count-1})",
"-vsync", "0",
os.path.join(frames_dir, "frame_%03d.png")
], check=True)
except Exception as e:
logger.error(f"Error extracting frames: {str(e)}")
# Try a simpler approach
subprocess.run([
"ffmpeg",
"-i", temp_video_path,
"-r", "1", # 1 frame per second
os.path.join(frames_dir, "frame_%03d.png")
], check=True)
# Parse explanation text into segments (assuming sections divided by ##)
explanation_segments = explanation_text.split("##") if explanation_text else ["No explanation provided."]
# Create a PDF with frames and explanations
pdf = FPDF()
pdf.set_auto_page_break(auto=True, margin=15)
# Title page
pdf.add_page()
pdf.set_font("Arial", "B", 20)
pdf.cell(190, 10, animation_title, ln=True, align="C")
pdf.ln(10)
pdf.set_font("Arial", "", 12)
pdf.cell(190, 10, "Animation Sequence with Explanations", ln=True, align="C")
# Add each frame with explanation
frame_files = sorted([f for f in os.listdir(frames_dir) if f.endswith('.png')])
for i, frame_file in enumerate(frame_files):
pdf.add_page()
# Add frame image
frame_path = os.path.join(frames_dir, frame_file)
pdf.image(frame_path, x=10, y=10, w=190)
# Add explanation text
pdf.ln(140) # Move below the image
pdf.set_font("Arial", "B", 12)
pdf.cell(190, 10, f"Step {i+1}", ln=True)
pdf.set_font("Arial", "", 10)
# Use the corresponding explanation segment if available
explanation = explanation_segments[min(i, len(explanation_segments)-1)]
pdf.multi_cell(190, 5, explanation.strip())
# Save PDF
output_path = os.path.join(temp_dir, f"{animation_title.replace(' ', '_')}_sequence.pdf")
pdf.output(output_path)
# Read the file to return it
with open(output_path, "rb") as f:
return f.read(), "pdf"
return None, None
except Exception as e:
logger.error(f"Educational export error: {str(e)}")
import traceback
logger.error(traceback.format_exc())
return None, None
def main():
# Initialize session state variables if they don't exist
if 'init' not in st.session_state:
st.session_state.init = True
st.session_state.video_data = None
st.session_state.status = None
st.session_state.ai_models = None
st.session_state.generated_code = ""
st.session_state.code = ""
st.session_state.temp_code = ""
st.session_state.editor_key = str(uuid.uuid4())
st.session_state.packages_checked = False # Track if packages were already checked
st.session_state.audio_path = None
st.session_state.image_paths = []
st.session_state.custom_library_result = ""
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"
st.session_state.python_result = None
st.session_state.active_tab = 0 # Track currently active tab
st.session_state.settings = {
"quality": "720p",
"format_type": "mp4",
"animation_speed": "Normal",
"fps": 30 # Default FPS
}
st.session_state.password_entered = False # Track password authentication
st.session_state.custom_model = "gpt-4o" # Default model
st.session_state.first_load_complete = False # Prevent refreshes on first load
st.session_state.pending_tab_switch = None # Track pending tab switches
# C++ runner state
st.session_state.cpp_code = """#include
#include
#include
int main() {
std::cout << "Hello, Manim Animation Studio!" << std::endl;
// Create a vector of numbers
std::vector numbers = {5, 2, 8, 1, 9, 3, 7, 4, 6};
// Sort the vector
std::sort(numbers.begin(), numbers.end());
// Print the sorted numbers
std::cout << "Sorted numbers: ";
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}"""
st.session_state.cpp_result = None
st.session_state.cpp_project_files = {"main.cpp": st.session_state.cpp_code}
st.session_state.cpp_settings = {
"compiler": "g++",
"std": "c++17",
"optimization": "-O2",
"include_paths": [],
"library_paths": [],
"libraries": []
}
# Page configuration with improved layout
st.set_page_config(
page_title="Manim Animation Studio",
page_icon="đŦ",
layout="wide",
initial_sidebar_state="expanded"
)
# Custom CSS for improved UI
st.markdown("""
""", unsafe_allow_html=True)
# Header
st.markdown("""
đŦ Manim Animation Studio
Create mathematical animations with Manim
""", unsafe_allow_html=True)
# Check for packages ONLY ONCE per session
if not st.session_state.packages_checked:
if ensure_packages():
st.session_state.packages_checked = True
else:
st.error("Failed to install required packages. Please try again.")
st.stop()
# Create main tabs
tab_names = ["⨠Editor", "đ¤ AI Assistant", "đ¨ Assets", "đī¸ Timeline", "đ Educational Export", "đ Python Runner", "đ§ C/C++ Runner"]
tabs = st.tabs(tab_names)
# Sidebar for rendering settings and custom libraries
with st.sidebar:
# Rendering settings section
st.markdown("## âī¸ Rendering Settings")
col1, col2 = st.columns(2)
with col1:
quality = st.selectbox(
"đ¯ Quality",
options=list(QUALITY_PRESETS.keys()),
index=list(QUALITY_PRESETS.keys()).index(st.session_state.settings["quality"]),
key="quality_select"
)
with col2:
format_type_display = st.selectbox(
"đĻ Format",
options=list(EXPORT_FORMATS.keys()),
index=list(EXPORT_FORMATS.values()).index(st.session_state.settings["format_type"])
if st.session_state.settings["format_type"] in EXPORT_FORMATS.values() else 0,
key="format_select_display"
)
# Convert display name to actual format value
format_type = EXPORT_FORMATS[format_type_display]
# Add FPS control
fps = st.selectbox(
"đī¸ FPS",
options=FPS_OPTIONS,
index=FPS_OPTIONS.index(st.session_state.settings["fps"]) if st.session_state.settings["fps"] in FPS_OPTIONS else 2, # Default to 30 FPS (index 2)
key="fps_select"
)
animation_speed = st.selectbox(
"⥠Speed",
options=list(ANIMATION_SPEEDS.keys()),
index=list(ANIMATION_SPEEDS.keys()).index(st.session_state.settings["animation_speed"]),
key="speed_select"
)
# Apply the settings without requiring a button
st.session_state.settings = {
"quality": quality,
"format_type": format_type,
"animation_speed": animation_speed,
"fps": fps
}
# Custom libraries section
st.markdown("## đ Custom Libraries")
st.markdown("Enter additional Python packages needed for your animations (comma-separated):")
custom_libraries = st.text_area(
"Libraries to install",
placeholder="e.g., scipy, networkx, matplotlib",
key="custom_libraries"
)
if st.button("Install Libraries", key="install_libraries_btn"):
success, result = install_custom_packages(custom_libraries)
st.session_state.custom_library_result = result
if success:
st.success("Installation complete!")
else:
st.error("Installation failed for some packages.")
if st.session_state.custom_library_result:
with st.expander("Installation Results"):
st.code(st.session_state.custom_library_result)
# System Package Management section
with st.sidebar.expander("đ ī¸ System Package Management"):
st.markdown("## System Dependencies")
st.markdown("Manage system packages and libraries")
# Auto-detect C/C++ libraries
if st.button("Detect Installed Libraries", key="detect_system_libs"):
with st.spinner("Detecting installed libraries..."):
libraries = detect_cpp_libraries()
# Display results
st.markdown("### Detected Libraries")
for lib, installed in libraries.items():
if installed:
st.success(f"â {lib}: Installed")
else:
st.warning(f"â ī¸ {lib}: Not detected")
# Install C/C++ libraries
st.markdown("### Install C/C++ Libraries")
cpp_libs_to_install = st.multiselect(
"Select libraries to install",
options=["Eigen", "Boost", "OpenCV", "FFTW", "SDL2", "SFML", "OpenGL"],
default=[]
)
if st.button("Install Selected Libraries", key="install_cpp_libs"):
success, result = install_cpp_libraries(cpp_libs_to_install)
if success:
st.success("Libraries installed successfully!")
else:
st.error("Failed to install some libraries")
st.code(result)
# System package installation
st.markdown("### Install System Packages")
system_packages = st.text_area(
"Enter system packages to install (comma separated)",
placeholder="e.g., ffmpeg, git, cmake"
)
if st.button("Install System Packages", key="install_system_packages"):
if not system_packages.strip():
st.warning("No packages specified")
else:
packages = [pkg.strip() for pkg in system_packages.split(',') if pkg.strip()]
# Detect package manager
package_manager = None
install_cmd = []
if platform.system() == "Linux":
which_apt = subprocess.run(["which", "apt-get"], capture_output=True, text=True)
which_dnf = subprocess.run(["which", "dnf"], capture_output=True, text=True)
which_yum = subprocess.run(["which", "yum"], capture_output=True, text=True)
which_pacman = subprocess.run(["which", "pacman"], capture_output=True, text=True)
if which_apt.returncode == 0:
package_manager = "apt-get"
install_cmd = ["apt-get", "install", "-y"]
elif which_dnf.returncode == 0:
package_manager = "dnf"
install_cmd = ["dnf", "install", "-y"]
elif which_yum.returncode == 0:
package_manager = "yum"
install_cmd = ["yum", "install", "-y"]
elif which_pacman.returncode == 0:
package_manager = "pacman"
install_cmd = ["pacman", "-S", "--noconfirm"]
elif platform.system() == "Darwin":
which_brew = subprocess.run(["which", "brew"], capture_output=True, text=True)
if which_brew.returncode == 0:
package_manager = "brew"
install_cmd = ["brew", "install"]
if not package_manager:
st.error(f"Could not detect package manager for {platform.system()}. Please install packages manually.")
else:
# Ask for sudo password if needed
sudo_password = None
if is_sudo_available() and platform.system() != "Darwin": # macOS Homebrew doesn't need sudo
sudo_password = st.text_input("Enter sudo password for system package installation:", type="password")
# Update package lists if needed
if package_manager in ["apt-get", "apt"]:
with st.spinner("Updating package lists..."):
try:
if is_sudo_available() and sudo_password:
result = run_with_sudo(["apt-get", "update"], sudo_password)
elif is_sudo_available():
result = run_with_sudo(["apt-get", "update"])
else:
result = subprocess.run(["apt-get", "update"], capture_output=True, text=True)
if result.returncode != 0:
st.warning(f"Failed to update package lists: {result.stderr}")
except Exception as e:
st.warning(f"Error updating package lists: {str(e)}")
# Install packages
results = []
success = True
progress_bar = st.sidebar.progress(0)
status_text = st.sidebar.empty()
for i, package in enumerate(packages):
try:
progress = (i / len(packages))
progress_bar.progress(progress)
status_text.text(f"Installing {package}...")
cmd = install_cmd + [package]
if is_sudo_available() and platform.system() != "Darwin": # macOS Homebrew doesn't need sudo
if sudo_password:
result = run_with_sudo(cmd, sudo_password)
else:
result = run_with_sudo(cmd)
else:
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
error_msg = f"Failed to install {package}: {result.stderr}"
results.append(error_msg)
success = False
else:
results.append(f"Successfully installed {package}")
except Exception as e:
error_msg = f"Error installing {package}: {str(e)}"
results.append(error_msg)
success = False
progress_bar.progress(1.0)
time.sleep(0.5)
progress_bar.empty()
status_text.empty()
if success:
st.success("All packages installed successfully!")
else:
st.error("Failed to install some packages")
st.code("\n".join(results))
# C/C++ Library Options
with st.sidebar.expander("C/C++ Library Options"):
st.markdown("### Advanced C/C++ Settings")
cpp_libs = st.multiselect(
"Include Libraries",
options=["Eigen", "Boost", "OpenCV", "FFTW", "Matplotlib-cpp"],
default=st.session_state.cpp_settings.get("libraries", [])
)
st.session_state.cpp_settings["libraries"] = cpp_libs
custom_include = st.text_input("Custom Include Path:")
custom_lib = st.text_input("Custom Library Path:")
if custom_include and custom_include not in st.session_state.cpp_settings.get("include_paths", []):
if "include_paths" not in st.session_state.cpp_settings:
st.session_state.cpp_settings["include_paths"] = []
st.session_state.cpp_settings["include_paths"].append(custom_include)
if custom_lib and custom_lib not in st.session_state.cpp_settings.get("library_paths", []):
if "library_paths" not in st.session_state.cpp_settings:
st.session_state.cpp_settings["library_paths"] = []
st.session_state.cpp_settings["library_paths"].append(custom_lib)
if st.button("Update Library Settings"):
st.success("Library settings updated!")
# EDITOR TAB
with tabs[0]:
col1, col2 = st.columns([3, 2])
with col1:
st.markdown("### đ Animation Editor")
# Toggle between upload and type
editor_mode = st.radio(
"Choose how to input your code:",
["Type Code", "Upload File"],
key="editor_mode"
)
if editor_mode == "Upload File":
uploaded_file = st.file_uploader("Upload Manim Python File", type=["py"], key="code_uploader")
if uploaded_file:
code_content = uploaded_file.getvalue().decode("utf-8")
if code_content.strip(): # Only update if file has content
st.session_state.code = code_content
st.session_state.temp_code = code_content
# Code editor
if ACE_EDITOR_AVAILABLE:
current_code = st.session_state.code if hasattr(st.session_state, 'code') and st.session_state.code else ""
st.session_state.temp_code = st_ace(
value=current_code,
language="python",
theme="monokai",
min_lines=20,
key=f"ace_editor_{st.session_state.editor_key}"
)
else:
current_code = st.session_state.code if hasattr(st.session_state, 'code') and st.session_state.code else ""
st.session_state.temp_code = st.text_area(
"Manim Python Code",
value=current_code,
height=400,
key=f"code_textarea_{st.session_state.editor_key}"
)
# Update code in session state if it changed
if st.session_state.temp_code != st.session_state.code:
st.session_state.code = st.session_state.temp_code
# Generate button (use a form to prevent page reloads)
generate_btn = st.button("đ Generate Animation", use_container_width=True, key="generate_btn")
if generate_btn:
if not st.session_state.code:
st.error("Please enter some code before generating animation")
else:
# Extract scene class name
scene_class = extract_scene_class_name(st.session_state.code)
# If no valid scene class found, add a basic one
if scene_class == "MyScene" and "class MyScene" not in st.session_state.code:
default_scene = """
class MyScene(Scene):
def construct(self):
text = Text("Default Scene")
self.play(Write(text))
self.wait(2)
"""
st.session_state.code += default_scene
st.session_state.temp_code = st.session_state.code
st.warning("No scene class found. Added a default scene.")
with st.spinner("Generating animation..."):
video_data, status = generate_manim_video(
st.session_state.code,
st.session_state.settings["format_type"],
st.session_state.settings["quality"],
ANIMATION_SPEEDS[st.session_state.settings["animation_speed"]],
st.session_state.audio_path,
st.session_state.settings["fps"]
)
st.session_state.video_data = video_data
st.session_state.status = status
with col2:
st.markdown("### đĨī¸ Preview & Output")
# Preview container
if st.session_state.code:
with st.container():
st.markdown("
", 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 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")
# Check password before allowing access
if check_password():
# Debug section
with st.expander("đ§ Debug Connection"):
st.markdown("Test the AI model connection directly")
if st.button("Test API Connection", key="test_api_btn"):
with st.spinner("Testing API connection..."):
try:
# Get token from secrets
token = get_secret("github_token_api")
if not token:
st.error("GitHub token not found in secrets")
st.stop()
# Get model details
model_name = st.session_state.custom_model
config = MODEL_CONFIGS.get(model_name, MODEL_CONFIGS["default"])
category = config.get("category", "Other")
if category == "OpenAI":
# Use OpenAI client for GitHub AI models
try:
from openai import OpenAI
except ImportError:
st.error("OpenAI package not installed. Please run 'pip install openai'")
st.stop()
# Create OpenAI client with GitHub AI endpoint
client = OpenAI(
base_url="https://models.github.ai/inference",
api_key=token,
)
# For GitHub AI models, ensure the model_name includes the publisher
# If it doesn't have a publisher prefix, add "openai/"
if "/" not in model_name:
full_model_name = f"openai/{model_name}"
st.info(f"Using full model name: {full_model_name}")
else:
full_model_name = model_name
# Prepare parameters based on model configuration
params = {
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello, this is a connection test."}
],
"model": full_model_name
}
# Add appropriate token parameter
token_param = config["param_name"]
params[token_param] = config[token_param]
# Make API call
response = client.chat.completions.create(**params)
# Check if response is valid
if response and response.choices and len(response.choices) > 0:
test_response = response.choices[0].message.content
st.success(f"â Connection successful! Response: {test_response[:50]}...")
# Save working connection to session state
st.session_state.ai_models = {
"openai_client": client,
"model_name": full_model_name, # Store the full model name
"endpoint": "https://models.github.ai/inference",
"last_loaded": datetime.now().isoformat(),
"category": category
}
else:
st.error("â API returned an empty response")
elif category == "Azure" or category in ["DeepSeek", "Meta", "Microsoft", "Mistral", "Other"]:
# Use Azure client for Azure API models
try:
from azure.ai.inference import ChatCompletionsClient
from azure.ai.inference.models import SystemMessage, UserMessage
from azure.core.credentials import AzureKeyCredential
except ImportError:
st.error("Azure AI packages not installed. Please run 'pip install azure-ai-inference azure-core'")
st.stop()
# Define endpoint
endpoint = "https://models.inference.ai.azure.com"
# Prepare API parameters
messages = [UserMessage("Hello, this is a connection test.")]
api_params, config = prepare_api_params(messages, model_name)
# Create client with appropriate API version
api_version = config.get("api_version")
if api_version:
client = ChatCompletionsClient(
endpoint=endpoint,
credential=AzureKeyCredential(token),
api_version=api_version
)
else:
client = ChatCompletionsClient(
endpoint=endpoint,
credential=AzureKeyCredential(token),
)
# Test with the prepared parameters
response = client.complete(**api_params)
# Check if response is valid
if response and response.choices and len(response.choices) > 0:
test_response = response.choices[0].message.content
st.success(f"â Connection successful! Response: {test_response[:50]}...")
# Save working connection to session state
st.session_state.ai_models = {
"client": client,
"model_name": model_name,
"endpoint": endpoint,
"last_loaded": datetime.now().isoformat(),
"category": category,
"api_version": api_version
}
else:
st.error("â API returned an empty response")
else:
st.error(f"Unsupported model category: {category}")
except ImportError as ie:
st.error(f"Module import error: {str(ie)}")
st.info("Try installing required packages: openai, azure-ai-inference and azure-core")
except Exception as e:
st.error(f"â API test failed: {str(e)}")
import traceback
st.code(traceback.format_exc())
# Model selection with enhanced UI
st.markdown("### đ¤ Model Selection")
st.markdown("Select an AI model for generating animation code:")
# Group models by category for better organization
model_categories = {}
for model_name in MODEL_CONFIGS:
if model_name != "default":
category = MODEL_CONFIGS[model_name].get("category", "Other")
if category not in model_categories:
model_categories[category] = []
model_categories[category].append(model_name)
# Create tabbed interface for model categories
category_tabs = st.tabs(sorted(model_categories.keys()))
for i, category in enumerate(sorted(model_categories.keys())):
with category_tabs[i]:
for model_name in sorted(model_categories[category]):
config = MODEL_CONFIGS[model_name]
is_selected = model_name == st.session_state.custom_model
warning = config.get("warning")
# Create styled card for each model
warning_html = f'
â ī¸ {warning}
' if warning else ""
st.markdown(f"""
{model_name}
Max Tokens: {config.get(config['param_name'], 'Unknown')}
Category: {config['category']}
API Version: {config['api_version'] if config['api_version'] else 'Default'}
{warning_html}
""", unsafe_allow_html=True)
# Button to select this model
button_label = "Selected â" if is_selected else "Select Model"
if st.button(button_label, key=f"model_{model_name}", disabled=is_selected):
st.session_state.custom_model = model_name
if st.session_state.ai_models and 'model_name' in st.session_state.ai_models:
st.session_state.ai_models['model_name'] = model_name
st.rerun()
# Display current model selection
st.info(f"đ¤ **Currently using: {st.session_state.custom_model}**")
# Add a refresh button to update model connection
if st.button("đ Refresh Model Connection", key="refresh_model_connection"):
if st.session_state.ai_models and 'client' in st.session_state.ai_models:
try:
# Test connection with minimal prompt
from azure.ai.inference.models import UserMessage
model_name = st.session_state.custom_model
# Prepare parameters
messages = [UserMessage("Hello")]
api_params, config = prepare_api_params(messages, model_name)
# Check if we need a new client with specific API version
if config["api_version"] and config["api_version"] != st.session_state.ai_models.get("api_version"):
# Create version-specific client if needed
token = get_secret("github_token_api")
from azure.ai.inference import ChatCompletionsClient
from azure.core.credentials import AzureKeyCredential
client = ChatCompletionsClient(
endpoint=st.session_state.ai_models["endpoint"],
credential=AzureKeyCredential(token),
api_version=config["api_version"]
)
response = client.complete(**api_params)
# Update session state with the new client
st.session_state.ai_models["client"] = client
st.session_state.ai_models["api_version"] = config["api_version"]
else:
response = st.session_state.ai_models["client"].complete(**api_params)
st.success(f"â Connection to {model_name} successful!")
st.session_state.ai_models["model_name"] = model_name
except Exception as e:
st.error(f"â Connection error: {str(e)}")
st.info("Please try the Debug Connection section to re-initialize the API connection.")
# AI code generation
if st.session_state.ai_models and "client" in st.session_state.ai_models:
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..."):
try:
# Get the client and model name
client = st.session_state.ai_models["client"]
model_name = st.session_state.ai_models["model_name"]
# Create the prompt
prompt = f"""Write a complete Manim animation scene based on this code or idea:
{code_input}
The code should be a complete, working Manim animation that includes:
- Proper Scene class definition
- Constructor with animations
- Proper use of self.play() for animations
- Proper wait times between animations
Here's the complete Manim code:
"""
# Prepare API parameters
from azure.ai.inference.models import UserMessage
messages = [UserMessage(prompt)]
api_params, config = prepare_api_params(messages, model_name)
# Make the API call with proper parameters
response = client.complete(**api_params)
# Process the response
if response and response.choices and len(response.choices) > 0:
completed_code = response.choices[0].message.content
# Extract code from markdown if present
if "```python" in completed_code:
completed_code = completed_code.split("```python")[1].split("```")[0]
elif "```" in completed_code:
completed_code = completed_code.split("```")[1].split("```")[0]
# Add Scene class if missing
if "Scene" not in completed_code:
completed_code = f"""from manim import *
class MyScene(Scene):
def construct(self):
{completed_code}"""
# Store the generated code
st.session_state.generated_code = completed_code
else:
st.error("Failed to generate code. API returned an empty response.")
except Exception as e:
st.error(f"Error generating code: {str(e)}")
import traceback
st.code(traceback.format_exc())
else:
st.warning("Please enter a description or prompt first")
# AI generated code display and actions
if "generated_code" in st.session_state and 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.code = st.session_state.generated_code
st.session_state.temp_code = st.session_state.generated_code
# Set pending tab switch to editor tab
st.session_state.pending_tab_switch = 0
st.rerun()
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"],
fps=st.session_state.settings["fps"]
)
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 not initialized. Please use the Debug Connection section to test API connectivity.")
else:
st.info("Please enter the correct password to access AI features")
# ASSETS TAB
with tabs[2]:
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:")
# Allow multiple image uploads
uploaded_images = st.file_uploader(
"Upload Images",
type=["jpg", "png", "jpeg", "svg"],
accept_multiple_files=True,
key="image_uploader_tab"
)
if uploaded_images:
# Create a unique image directory if it doesn't exist
image_dir = os.path.join(os.getcwd(), "manim_assets", "images")
os.makedirs(image_dir, exist_ok=True)
# Process each uploaded image
for uploaded_image in uploaded_images:
# Generate a unique filename and save the image
file_extension = uploaded_image.name.split(".")[-1]
unique_filename = f"image_{int(time.time())}_{uuid.uuid4().hex[:8]}.{file_extension}"
image_path = os.path.join(image_dir, unique_filename)
with open(image_path, "wb") as f:
f.write(uploaded_image.getvalue())
# Store the path in session state
if "image_paths" not in st.session_state:
st.session_state.image_paths = []
# Check if this image was already added
image_already_added = False
for img in st.session_state.image_paths:
if img["name"] == uploaded_image.name:
image_already_added = True
break
if not image_already_added:
st.session_state.image_paths.append({
"name": uploaded_image.name,
"path": image_path
})
# Display uploaded images in a grid
st.markdown("##### Uploaded Images:")
image_cols = st.columns(3)
for i, img_info in enumerate(st.session_state.image_paths[-len(uploaded_images):]):
with image_cols[i % 3]:
try:
img = Image.open(img_info["path"])
st.image(img, caption=img_info["name"], width=150)
# Show code snippet for this specific image
if st.button(f"Use {img_info['name']}", key=f"use_img_{i}"):
image_code = f"""
# Load and display image
image = ImageMobject(r"{img_info['path']}")
image.scale(2) # Adjust size as needed
self.play(FadeIn(image))
self.wait(1)
"""
if not st.session_state.code:
base_code = """from manim import *
class ImageScene(Scene):
def construct(self):
"""
st.session_state.code = base_code + "\n " + image_code.replace("\n", "\n ")
else:
st.session_state.code += "\n" + image_code
st.session_state.temp_code = st.session_state.code
st.success(f"Added {img_info['name']} to your code!")
# Set pending tab switch to editor tab
st.session_state.pending_tab_switch = 0
st.rerun()
except Exception as e:
st.error(f"Error loading image {img_info['name']}: {e}")
# Display previously uploaded images
if st.session_state.image_paths:
with st.expander("Previously Uploaded Images"):
# Group images by 3 in each row
for i in range(0, len(st.session_state.image_paths), 3):
prev_cols = st.columns(3)
for j in range(3):
if i+j < len(st.session_state.image_paths):
img_info = st.session_state.image_paths[i+j]
with prev_cols[j]:
try:
img = Image.open(img_info["path"])
st.image(img, caption=img_info["name"], width=100)
st.markdown(f"
", 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[3]:
# 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.code = updated_code
st.session_state.temp_code = updated_code
# EDUCATIONAL EXPORT TAB
with tabs[4]:
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[5]:
st.markdown("### đ Python Script Runner")
st.markdown("Execute Python scripts and visualize the results directly.")
# New UI elements for advanced features
with st.expander("đ§ Advanced Python Features"):
py_feature_col1, py_feature_col2 = st.columns(2)
with py_feature_col1:
enable_debugging = st.checkbox("Enable Debugging", value=False, key="py_debug_enable")
enable_profiling = st.checkbox("Enable Profiling", value=False, key="py_profile_enable")
with py_feature_col2:
py_libs = st.multiselect(
"Additional Libraries",
options=["numpy", "scipy", "pandas", "matplotlib", "seaborn", "plotly", "scikit-learn", "tensorflow", "pytorch", "sympy"],
default=["numpy", "matplotlib"],
key="py_additional_libs"
)
# Multi-file project support
with st.expander("đ Multi-file Project"):
st.markdown("Add multiple Python files to your project")
# File manager
if "py_project_files" not in st.session_state:
st.session_state.py_project_files = {"main.py": st.session_state.python_script}
# File selector
current_file = st.selectbox(
"Select File",
options=list(st.session_state.py_project_files.keys()),
key="py_current_file"
)
# New file creation
new_file_col1, new_file_col2 = st.columns([3, 1])
with new_file_col1:
new_filename = st.text_input("New File Name", value="", key="py_new_filename")
with new_file_col2:
if st.button("Add File", key="py_add_file_btn"):
if new_filename and new_filename not in st.session_state.py_project_files:
if not new_filename.endswith(".py"):
new_filename += ".py"
st.session_state.py_project_files[new_filename] = "# New Python file\n\n"
st.session_state.py_current_file = new_filename
st.experimental_rerun()
# Update the current file content in session state
if current_file in st.session_state.py_project_files:
st.session_state.py_project_files[current_file] = st.session_state.python_script
# Update main script if we're editing the main file
if current_file == "main.py":
st.session_state.python_script = st.session_state.python_script
# Real-time visualization toggle
real_time_viz = st.checkbox("Enable Real-time Visualization", value=False, key="py_realtime_viz")
# 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')
"""
}
# 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:
python_code = example_scripts[selected_example]
else:
python_code = st.session_state.python_script
if ACE_EDITOR_AVAILABLE:
python_code = st_ace(
value=python_code,
language="python",
theme="monokai",
min_lines=15,
key=f"python_editor_{st.session_state.editor_key}"
)
else:
python_code = st.text_area(
"Python Code",
value=python_code,
height=400,
key=f"python_textarea_{st.session_state.editor_key}"
)
# Store script in session state (without clearing existing code)
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, 3600, 30)
with col2:
run_btn = st.button("âļī¸ Run Script", use_container_width=True)
if run_btn:
with st.spinner("Executing Python script..."):
# Use the enhanced function
result = run_python_script_enhanced(
python_code,
inputs=user_inputs,
timeout=timeout_seconds,
enable_debug=enable_debugging,
enable_profile=enable_profiling,
additional_libs=py_libs,
project_files=st.session_state.py_project_files if "py_project_files" in st.session_state else None,
realtime_viz=real_time_viz
)
st.session_state.python_result = result
# Display results
if st.session_state.python_result:
display_python_script_results_enhanced(st.session_state.python_result)
# 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
""")
# C/C++ RUNNER TAB
with tabs[6]: # Assuming this is the 7th tab (index 6)
st.markdown("### đ§ C/C++ Runner")
st.markdown("Write, compile, and run C/C++ code with advanced features.")
# Create a tabbed interface for different C++ features
cpp_tabs = st.tabs(["Code Editor", "Project Files", "Libraries", "Build Settings", "Debugger"])
with cpp_tabs[0]: # Code Editor tab
# Compiler options
cpp_col1, cpp_col2, cpp_col3 = st.columns(3)
with cpp_col1:
compiler = st.selectbox(
"Compiler",
options=["g++", "clang++", "gcc", "msvc"],
index=["g++", "clang++", "gcc", "msvc"].index(st.session_state.cpp_settings["compiler"]),
key="cpp_compiler"
)
st.session_state.cpp_settings["compiler"] = compiler
with cpp_col2:
std_version = st.selectbox(
"Standard",
options=["c++11", "c++14", "c++17", "c++20"],
index=["c++11", "c++14", "c++17", "c++20"].index(st.session_state.cpp_settings["std"]),
key="cpp_std"
)
st.session_state.cpp_settings["std"] = std_version
with cpp_col3:
optimization = st.selectbox(
"Optimization",
options=["-O0", "-O1", "-O2", "-O3"],
index=["-O0", "-O1", "-O2", "-O3"].index(st.session_state.cpp_settings["optimization"]),
key="cpp_opt"
)
st.session_state.cpp_settings["optimization"] = optimization
# Example code templates
cpp_examples = {
"Select an example...": "",
"Hello World": """#include
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}""",
"Calculate Prime Numbers": """#include
#include
#include
bool isPrime(int n) {
if (n <= 1) return false;
if (n <= 3) return true;
if (n % 2 == 0 || n % 3 == 0) return false;
for (int i = 5; i * i <= n; i += 6) {
if (n % i == 0 || n % (i + 2) == 0)
return false;
}
return true;
}
int main() {
int limit = 10000;
std::vector primes;
auto start = std::chrono::high_resolution_clock::now();
for (int i = 2; i <= limit; i++) {
if (isPrime(i)) {
primes.push_back(i);
}
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast(end - start);
std::cout << "Found " << primes.size() << " prime numbers up to " << limit << std::endl;
std::cout << "First 10 primes: ";
for (int i = 0; i < std::min(10, (int)primes.size()); i++) {
std::cout << primes[i] << " ";
}
std::cout << std::endl;
std::cout << "Computation time: " << duration.count() << " ms" << std::endl;
return 0;
}""",
"Image Generation (PPM)": """#include
#include
#include
// Generate a simple gradient image in PPM format
int main() {
const int width = 800;
const int height = 600;
// Create a PPM file (P3 format - ASCII)
std::ofstream image("output.ppm");
image << "P3\\n" << width << " " << height << "\\n255\\n";
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// Create a gradient based on position
int r = static_cast(255.0 * x / width);
int g = static_cast(255.0 * y / height);
int b = static_cast(255.0 * (x + y) / (width + height));
// Write RGB values
image << r << " " << g << " " << b << "\\n";
}
}
image.close();
std::cout << "Generated gradient image: output.ppm" << std::endl;
return 0;
}""",
"Data Processing with Vectors": """#include
#include
#include
#include
#include
#include
int main() {
const int data_size = 1000;
// Generate random data
std::vector data(data_size);
std::random_device rd;
std::mt19937 gen(rd());
std::normal_distribution dist(100.0, 15.0);
std::cout << "Generating " << data_size << " random values..." << std::endl;
for (auto& value : data) {
value = dist(gen);
}
// Calculate statistics
double sum = std::accumulate(data.begin(), data.end(), 0.0);
double mean = sum / data.size();
std::vector deviations(data_size);
std::transform(data.begin(), data.end(), deviations.begin(),
[mean](double x) { return x - mean; });
double sq_sum = std::inner_product(deviations.begin(), deviations.end(),
deviations.begin(), 0.0);
double stddev = std::sqrt(sq_sum / data.size());
// Sort data
std::sort(data.begin(), data.end());
double median = data.size() % 2 == 0 ?
(data[data.size()/2 - 1] + data[data.size()/2]) / 2 :
data[data.size()/2];
// Output results
std::cout << std::fixed << std::setprecision(2);
std::cout << "Data analysis results:" << std::endl;
std::cout << "Mean: " << mean << std::endl;
std::cout << "Median: " << median << std::endl;
std::cout << "StdDev: " << stddev << std::endl;
std::cout << "Min: " << data.front() << std::endl;
std::cout << "Max: " << data.back() << std::endl;
return 0;
}""",
"Interactive User Input": """#include
#include
#include
int main() {
std::string name;
int age;
// Get user input
std::cout << "Enter your name: ";
std::getline(std::cin, name);
std::cout << "Enter your age: ";
std::cin >> age;
std::cin.ignore(); // Clear the newline from the buffer
std::cout << "Hello, " << name << "! ";
std::cout << "In 10 years, you will be " << age + 10 << " years old." << std::endl;
// Get multiple numbers
int num_count;
std::cout << "How many numbers would you like to enter? ";
std::cin >> num_count;
std::vector numbers;
double total = 0.0;
for (int i = 0; i < num_count; i++) {
double num;
std::cout << "Enter number " << (i+1) << ": ";
std::cin >> num;
numbers.push_back(num);
total += num;
}
if (!numbers.empty()) {
double average = total / numbers.size();
std::cout << "The average of your numbers is: " << average << std::endl;
}
return 0;
}""",
"Eigen Matrix Operations": """#include
#include
using Eigen::MatrixXd;
using Eigen::VectorXd;
int main() {
// Create a 3x3 matrix
MatrixXd A(3, 3);
A << 1, 2, 3,
4, 5, 6,
7, 8, 9;
// Create a 3D vector
VectorXd b(3);
b << 1, 2, 3;
// Perform operations
std::cout << "Matrix A:\\n" << A << std::endl;
std::cout << "Vector b:\\n" << b << std::endl;
std::cout << "A * b:\\n" << A * b << std::endl;
std::cout << "A transpose:\\n" << A.transpose() << std::endl;
// Solve a linear system Ax = b
VectorXd x = A.colPivHouseholderQr().solve(b);
std::cout << "Solution to Ax = b:\\n" << x << std::endl;
// Compute eigenvalues and eigenvectors
Eigen::EigenSolver solver(A);
std::cout << "Eigenvalues:\\n" << solver.eigenvalues() << std::endl;
std::cout << "Eigenvectors:\\n" << solver.eigenvectors() << std::endl;
return 0;
}""",
"OpenCV Image Processing": """#include
#include
int main() {
// Load an image (this will create a blank image if no file is found)
cv::Mat image = cv::Mat::zeros(500, 500, CV_8UC3);
// Draw a circle
cv::circle(image, cv::Point(250, 250), 100, cv::Scalar(0, 0, 255), 5);
// Draw a rectangle
cv::rectangle(image, cv::Point(150, 150), cv::Point(350, 350), cv::Scalar(0, 255, 0), 3);
// Add text
cv::putText(image, "OpenCV Example", cv::Point(100, 50), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255, 255, 255), 2);
// Save the image
cv::imwrite("opencv_output.png", image);
std::cout << "Image created and saved as 'opencv_output.png'" << std::endl;
return 0;
}"""
}
# Example selection
selected_cpp_example = st.selectbox("Example code:", options=list(cpp_examples.keys()))
# Set initial code from example or session state
if selected_cpp_example != "Select an example..." and cpp_examples[selected_cpp_example] != "":
initial_code = cpp_examples[selected_cpp_example]
else:
if "cpp_current_file" in st.session_state and st.session_state.cpp_current_file in st.session_state.cpp_project_files:
initial_code = st.session_state.cpp_project_files[st.session_state.cpp_current_file]
else:
initial_code = st.session_state.cpp_code
# Code editor for C++
if ACE_EDITOR_AVAILABLE:
cpp_code = st_ace(
value=initial_code,
language="c_cpp",
theme="monokai",
min_lines=15,
key=f"cpp_editor_{st.session_state.editor_key}"
)
else:
cpp_code = st.text_area(
"C/C++ Code",
value=initial_code,
height=400,
key=f"cpp_textarea_{st.session_state.editor_key}"
)
# Save the code to session state
st.session_state.cpp_code = cpp_code
# Update project files
if "cpp_current_file" in st.session_state and st.session_state.cpp_current_file in st.session_state.cpp_project_files:
st.session_state.cpp_project_files[st.session_state.cpp_current_file] = cpp_code
# Check for standard input in the code
has_cin = "std::cin" in cpp_code or "cin" in cpp_code
# Input values section if needed
cpp_inputs = []
if has_cin:
with st.expander("Input Values"):
st.info("This program uses standard input. Please provide input values below:")
num_inputs = st.number_input("Number of input lines:", min_value=1, max_value=10, value=1)
for i in range(int(num_inputs)):
cpp_input = st.text_input(f"Input line {i+1}:", key=f"cpp_input_{i}")
cpp_inputs.append(cpp_input)
with cpp_tabs[1]: # Project Files tab
st.markdown("### Project Files")
st.markdown("Manage multiple source files for your C/C++ project")
# File selector
cpp_current_file = st.selectbox(
"Current File",
options=list(st.session_state.cpp_project_files.keys()),
index=list(st.session_state.cpp_project_files.keys()).index(st.session_state.cpp_current_file) if "cpp_current_file" in st.session_state else 0,
key="cpp_file_selector"
)
# Update the current file in session state
st.session_state.cpp_current_file = cpp_current_file
# Create new file form
new_file_col1, new_file_col2 = st.columns([3, 1])
with new_file_col1:
new_cpp_filename = st.text_input("New File Name", placeholder="e.g., utils.h, helper.cpp", key="new_cpp_file")
with new_file_col2:
if st.button("Add File", key="add_cpp_file"):
if new_cpp_filename and new_cpp_filename not in st.session_state.cpp_project_files:
# Add file extension if missing
if not new_cpp_filename.endswith((".cpp", ".h", ".hpp", ".c", ".cc")):
new_cpp_filename += ".cpp"
# Create a template based on file type
if new_cpp_filename.endswith((".h", ".hpp")):
template = f"""#ifndef {new_cpp_filename.split('.')[0].upper()}_H
#define {new_cpp_filename.split('.')[0].upper()}_H
// Your header content here
#endif // {new_cpp_filename.split('.')[0].upper()}_H
"""
else:
template = f"""#include
// Your implementation here
"""
st.session_state.cpp_project_files[new_cpp_filename] = template
st.session_state.cpp_current_file = new_cpp_filename
st.experimental_rerun()
# File actions
file_action_col1, file_action_col2 = st.columns(2)
with file_action_col1:
if st.button("Delete Current File", key="delete_cpp_file"):
if cpp_current_file != "main.cpp" and cpp_current_file in st.session_state.cpp_project_files:
del st.session_state.cpp_project_files[cpp_current_file]
st.session_state.cpp_current_file = "main.cpp"
st.experimental_rerun()
else:
st.error("Cannot delete main.cpp")
with file_action_col2:
if st.button("Download Project Files", key="download_cpp_project"):
# Create a zip file with all project files
with tempfile.NamedTemporaryFile(delete=False, suffix=".zip") as tmp:
with zipfile.ZipFile(tmp.name, 'w') as zipf:
for filename, content in st.session_state.cpp_project_files.items():
# Add file to zip
zipf.writestr(filename, content)
# Download the zip file
with open(tmp.name, "rb") as f:
zip_data = f.read()
st.download_button(
label="Download ZIP",
data=zip_data,
file_name="cpp_project.zip",
mime="application/zip"
)
# Project structure visualization
st.markdown("### Project Structure")
# Group files by type
headers = []
sources = []
others = []
for filename in st.session_state.cpp_project_files:
if filename.endswith((".h", ".hpp")):
headers.append(filename)
elif filename.endswith((".cpp", ".c", ".cc")):
sources.append(filename)
else:
others.append(filename)
# Display structure
st.markdown("#### Header Files")
if headers:
for header in sorted(headers):
st.markdown(f"- `{header}`")
else:
st.markdown("No header files")
st.markdown("#### Source Files")
if sources:
for source in sorted(sources):
st.markdown(f"- `{source}`")
else:
st.markdown("No source files")
if others:
st.markdown("#### Other Files")
for other in sorted(others):
st.markdown(f"- `{other}`")
with cpp_tabs[2]: # Libraries tab
st.markdown("### Library Manager")
st.markdown("Configure libraries and dependencies for your C/C++ project")
# Common library selection
common_libs = st.multiselect(
"Common Libraries",
options=["Eigen", "Boost", "OpenCV", "FFTW", "SDL2", "SFML", "OpenGL", "stb_image", "nlohmann_json", "fmt"],
default=st.session_state.cpp_settings.get("libraries", []),
key="cpp_common_libs"
)
# Update libraries in settings
st.session_state.cpp_settings["libraries"] = common_libs
# Include paths
st.markdown("#### Include Paths")
include_paths = st.text_area(
"Include Directories (one per line)",
value="\n".join(st.session_state.cpp_settings.get("include_paths", [])),
height=100,
key="cpp_include_paths"
)
# Update include paths in settings
st.session_state.cpp_settings["include_paths"] = [path for path in include_paths.split("\n") if path.strip()]
# Library paths
st.markdown("#### Library Paths")
library_paths = st.text_area(
"Library Directories (one per line)",
value="\n".join(st.session_state.cpp_settings.get("library_paths", [])),
height=100,
key="cpp_library_paths"
)
# Update library paths in settings
st.session_state.cpp_settings["library_paths"] = [path for path in library_paths.split("\n") if path.strip()]
# Additional libraries
st.markdown("#### Additional Libraries")
additional_libs = st.text_area(
"Additional Libraries (one per line, without -l prefix)",
value="\n".join(st.session_state.cpp_settings.get("additional_libs", [])),
height=100,
key="cpp_additional_libs"
)
# Update additional libraries in settings
st.session_state.cpp_settings["additional_libs"] = [lib for lib in additional_libs.split("\n") if lib.strip()]
# Library detection
if st.button("Detect Installed Libraries", key="detect_libs"):
with st.spinner("Detecting libraries..."):
# This is a placeholder - in a real implementation, you'd scan the system
detected_libs = []
# Check for Eigen
try:
result = subprocess.run(
["find", "/usr/include", "-name", "Eigen"],
capture_output=True,
text=True,
timeout=5
)
if "Eigen" in result.stdout:
detected_libs.append("Eigen")
except:
pass
# Check for Boost
try:
result = subprocess.run(
["find", "/usr/include", "-name", "boost"],
capture_output=True,
text=True,
timeout=5
)
if "boost" in result.stdout:
detected_libs.append("Boost")
except:
pass
# Check for OpenCV
try:
result = subprocess.run(
["pkg-config", "--exists", "opencv4"],
capture_output=True,
timeout=5
)
if result.returncode == 0:
detected_libs.append("OpenCV")
except:
pass
# Display detected libraries
if detected_libs:
st.success(f"Detected libraries: {', '.join(detected_libs)}")
# Add to selected libraries if not already present
for lib in detected_libs:
if lib not in st.session_state.cpp_settings["libraries"]:
st.session_state.cpp_settings["libraries"].append(lib)
else:
st.warning("No common libraries detected")
with cpp_tabs[3]: # Build Settings tab
st.markdown("### Build Configuration")
# Build type
build_type = st.radio(
"Build Type",
options=["Debug", "Release", "RelWithDebInfo"],
index=1, # Default to Release
key="cpp_build_type"
)
# Update build type in settings
st.session_state.cpp_settings["build_type"] = build_type
# Advanced compiler flags
st.markdown("#### Advanced Compiler Flags")
advanced_flags = st.text_area(
"Additional Compiler Flags",
value=st.session_state.cpp_settings.get("advanced_flags", ""),
height=100,
key="cpp_advanced_flags"
)
# Update advanced flags in settings
st.session_state.cpp_settings["advanced_flags"] = advanced_flags
# Preprocessor definitions
st.markdown("#### Preprocessor Definitions")
definitions = st.text_area(
"Preprocessor Definitions (one per line)",
value="\n".join(st.session_state.cpp_settings.get("definitions", [])),
height=100,
placeholder="Example:\nDEBUG\nVERSION=1.0\nUSE_FEATURE_X",
key="cpp_definitions"
)
# Update definitions in settings
st.session_state.cpp_settings["definitions"] = [d for d in definitions.split("\n") if d.strip()]
# Generate CMakeLists.txt
if st.button("Generate CMakeLists.txt", key="gen_cmake"):
# Create CMakeLists.txt content
cmake_content = f"""cmake_minimum_required(VERSION 3.10)
project(ManimCppProject)
set(CMAKE_CXX_STANDARD {st.session_state.cpp_settings["std"].replace("c++", "")})
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Build type
set(CMAKE_BUILD_TYPE {build_type})
# Preprocessor definitions
"""
# Add definitions
for definition in st.session_state.cpp_settings.get("definitions", []):
if "=" in definition:
name, value = definition.split("=", 1)
cmake_content += f'add_definitions(-D{name}="{value}")\n'
else:
cmake_content += f"add_definitions(-D{definition})\n"
# Add include paths
if st.session_state.cpp_settings.get("include_paths", []):
cmake_content += "\n# Include directories\n"
for path in st.session_state.cpp_settings["include_paths"]:
cmake_content += f"include_directories({path})\n"
# Add library paths
if st.session_state.cpp_settings.get("library_paths", []):
cmake_content += "\n# Library directories\n"
for path in st.session_state.cpp_settings["library_paths"]:
cmake_content += f"link_directories({path})\n"
# Add common libraries
if "Eigen" in st.session_state.cpp_settings.get("libraries", []):
cmake_content += "\n# Eigen\n"
cmake_content += "find_package(Eigen3 REQUIRED)\n"
cmake_content += "include_directories(${EIGEN3_INCLUDE_DIR})\n"
if "OpenCV" in st.session_state.cpp_settings.get("libraries", []):
cmake_content += "\n# OpenCV\n"
cmake_content += "find_package(OpenCV REQUIRED)\n"
cmake_content += "include_directories(${OpenCV_INCLUDE_DIRS})\n"
if "Boost" in st.session_state.cpp_settings.get("libraries", []):
cmake_content += "\n# Boost\n"
cmake_content += "find_package(Boost REQUIRED)\n"
cmake_content += "include_directories(${Boost_INCLUDE_DIRS})\n"
# Add source files
cmake_content += "\n# Source files\n"
source_files = [f for f in st.session_state.cpp_project_files.keys() if f.endswith((".cpp", ".c", ".cc"))]
cmake_content += "add_executable(main\n"
for src in source_files:
cmake_content += f" {src}\n"
cmake_content += ")\n"
# Add libraries to link
cmake_content += "\n# Link libraries\n"
cmake_content += "target_link_libraries(main\n"
if "OpenCV" in st.session_state.cpp_settings.get("libraries", []):
cmake_content += " ${OpenCV_LIBS}\n"
if "Boost" in st.session_state.cpp_settings.get("libraries", []):
cmake_content += " ${Boost_LIBRARIES}\n"
# Additional libraries
for lib in st.session_state.cpp_settings.get("additional_libs", []):
cmake_content += f" {lib}\n"
cmake_content += ")\n"
# Save CMakeLists.txt to project files
st.session_state.cpp_project_files["CMakeLists.txt"] = cmake_content
# Show the generated file
st.success("CMakeLists.txt generated!")
st.code(cmake_content, language="cmake")
with cpp_tabs[4]: # Debugger tab
st.markdown("### C++ Debugger")
st.markdown("Debug your C++ code with breakpoints and variable inspection")
# Enable debugging
enable_cpp_debug = st.checkbox("Enable Debugging", value=False, key="cpp_debug_enable")
if enable_cpp_debug:
# Breakpoints
st.markdown("#### Breakpoints")
st.markdown("Enter line numbers for breakpoints (one per line)")
breakpoints = st.text_area(
"Breakpoints",
placeholder="Example:\n10\n15\n20",
height=100,
key="cpp_breakpoints"
)
breakpoint_lines = []
for line in breakpoints.split("\n"):
try:
line_num = int(line.strip())
if line_num > 0:
breakpoint_lines.append(line_num)
except:
pass
# Watch variables
st.markdown("#### Watch Variables")
st.markdown("Enter variable names to watch (one per line)")
watch_vars = st.text_area(
"Watch Variables",
placeholder="Example:\ni\nsum\nresult",
height=100,
key="cpp_watch_vars"
)
watch_variables = [var.strip() for var in watch_vars.split("\n") if var.strip()]
# Compilation and execution options
st.markdown("### Run Configuration")
run_options_col1, run_options_col2 = st.columns(2)
with run_options_col1:
cpp_timeout = st.slider("Execution Timeout (seconds)", 1, 60, 10)
with run_options_col2:
compile_btn = st.button("đ ī¸ Compile and Run", use_container_width=True)
# Compile and run the C++ code
if compile_btn:
with st.spinner("Compiling C++ code..."):
cpp_code_to_compile = st.session_state.cpp_code
if "cpp_project_files" in st.session_state and st.session_state.cpp_project_files:
# Use project files
executable_path, compile_error, temp_dir = compile_cpp_code_enhanced(
cpp_code_to_compile,
st.session_state.cpp_settings,
project_files=st.session_state.cpp_project_files,
enable_debug=enable_cpp_debug if "enable_cpp_debug" in locals() else False,
breakpoints=breakpoint_lines if "breakpoint_lines" in locals() else None,
watch_vars=watch_variables if "watch_variables" in locals() else None
)
else:
# Use single file
executable_path, compile_error, temp_dir = compile_cpp_code_enhanced(
cpp_code_to_compile,
st.session_state.cpp_settings,
enable_debug=enable_cpp_debug if "enable_cpp_debug" in locals() else False,
breakpoints=breakpoint_lines if "breakpoint_lines" in locals() else None,
watch_vars=watch_variables if "watch_variables" in locals() else None
)
if compile_error:
st.error("Compilation Error:")
st.code(compile_error, language="bash")
else:
st.success("Compilation successful!")
with st.spinner("Running program..."):
result = run_cpp_executable_enhanced(
executable_path,
temp_dir,
inputs=cpp_inputs if "cpp_inputs" in locals() else None,
timeout=cpp_timeout,
enable_debug=enable_cpp_debug if "enable_cpp_debug" in locals() else False,
breakpoints=breakpoint_lines if "breakpoint_lines" in locals() else None,
watch_vars=watch_variables if "watch_variables" in locals() else None
)
st.session_state.cpp_result = result
# Display results
if "cpp_result" in st.session_state and st.session_state.cpp_result:
result = st.session_state.cpp_result
st.markdown("### Results")
# Execution information
info_cols = st.columns(3)
with info_cols[0]:
st.info(f"Execution Time: {result['execution_time']:.3f} seconds")
with info_cols[1]:
if result.get("memory_usage"):
st.info(f"Memory Usage: {result['memory_usage']:.2f} MB")
with info_cols[2]:
if result["exception"]:
st.error(f"Exception: {result['exception']}")
# Show debug output if available
if result.get("debug_output"):
with st.expander("Debug Output", expanded=True):
st.code(result["debug_output"], language="bash")
# Result tabs
result_tabs = st.tabs(["Output", "Images", "Manim Integration"])
with result_tabs[0]: # Output tab
# Show stdout if any
if result["stdout"]:
st.markdown("#### Standard Output")
st.code(result["stdout"], language="bash")
# Show stderr if any
if result["stderr"]:
st.markdown("#### Standard Error")
st.code(result["stderr"], language="bash")
with result_tabs[1]: # Images tab
# Show images if any
if result["images"]:
st.markdown("#### Generated Images")
img_cols = st.columns(min(3, len(result["images"])))
for i, img in enumerate(result["images"]):
with img_cols[i % len(img_cols)]:
st.image(img["data"], caption=img["name"])
else:
st.info("No images were generated by the program.")
with result_tabs[2]: # Manim Integration tab
st.markdown("#### Integrate C++ Results with Manim")
# Create options for integration
integration_type = st.radio(
"Integration Type",
options=["Data Visualization", "Image Import", "Animation Sequence"],
key="cpp_integration_type"
)
if integration_type == "Data Visualization":
# Extract numerical data from stdout if possible
lines = result["stdout"].strip().split("\n")
data_options = []
for i, line in enumerate(lines):
# Check if line contains numbers
numbers = []
try:
# Try to extract numbers from the line
numbers = [float(x) for x in line.split() if x.replace(".", "").isdigit()]
if numbers:
data_options.append(f"Line {i+1}: {line[:30]}{'...' if len(line) > 30 else ''}")
except:
pass
if data_options:
selected_data_line = st.selectbox(
"Select Data to Visualize",
options=["Select a line..."] + data_options,
key="cpp_data_line"
)
if selected_data_line != "Select a line...":
line_idx = int(selected_data_line.split(":")[0].replace("Line ", "")) - 1
line = lines[line_idx]
# Extract numbers
try:
numbers = [float(x) for x in line.split() if x.replace(".", "").isdigit()]
# Preview the data
st.markdown(f"**Extracted Data:** {numbers}")
# Create visualization code
if st.button("Create Manim Visualization", key="cpp_create_viz"):
viz_code = f"""
# Visualize data from C++ output
values = {numbers}
axes = Axes(
x_range=[0, {len(numbers)}, 1],
y_range=[{min(numbers) if numbers else 0}, {max(numbers) if numbers else 10}, {(max(numbers)-min(numbers))/10 if numbers and max(numbers) > min(numbers) else 1}],
axis_config={{"color": BLUE}}
)
points = [axes.coords_to_point(i, v) for i, v in enumerate(values)]
dots = VGroup(*[Dot(point, color=RED) for point in points])
graph = VMobject(color=YELLOW)
graph.set_points_as_corners(points)
self.play(Create(axes))
self.play(Create(dots), run_time=2)
self.play(Create(graph), run_time=2)
self.wait(1)
"""
if st.session_state.code:
st.session_state.code += "\n" + viz_code
else:
st.session_state.code = f"""from manim import *
class CppDataVisualizationScene(Scene):
def construct(self):
{viz_code}
"""
st.session_state.temp_code = st.session_state.code
st.success("Added C++ data visualization to your Manim code!")
# Set pending tab switch to editor tab
st.session_state.pending_tab_switch = 0
st.rerun()
except Exception as e:
st.error(f"Error extracting numbers: {str(e)}")
else:
st.warning("No numeric data detected in the output.")
elif integration_type == "Image Import":
# Handle image import
if result["images"]:
st.markdown("#### Select Images to Import")
for i, img in enumerate(result["images"]):
st.markdown(f"**{img['name']}**")
st.image(img["data"], width=300)
if st.button(f"Use in Manim", key=f"use_cpp_img_{i}"):
# Save image to a temporary file
with tempfile.NamedTemporaryFile(delete=False, suffix=f"_{img['name']}") as tmp:
tmp.write(img["data"])
img_path = tmp.name
# Generate Manim code
image_code = f"""
# Load and display image generated from C++
cpp_image = ImageMobject(r"{img_path}")
cpp_image.scale(2) # Adjust size as needed
self.play(FadeIn(cpp_image))
self.wait(1)
"""
if st.session_state.code:
st.session_state.code += "\n" + image_code
else:
st.session_state.code = f"""from manim import *
class CppImageScene(Scene):
def construct(self):
{image_code}
"""
st.session_state.temp_code = st.session_state.code
st.success(f"Added C++ generated image to your Manim code!")
st.session_state.pending_tab_switch = 0 # Switch to editor tab
st.rerun()
else:
st.warning("No images were generated by the C++ program.")
elif integration_type == "Animation Sequence":
st.markdown("#### Create Animation Sequence")
st.info("This will create a Manim animation that visualizes the execution of your C++ program.")
# Animation type options
animation_style = st.selectbox(
"Animation Style",
options=["Algorithm Visualization", "Data Flow", "Memory Model"],
key="cpp_anim_style"
)
if st.button("Generate Animation Sequence", key="cpp_gen_anim_seq"):
# Create different animations based on selected style
if animation_style == "Algorithm Visualization":
# Example code for algorithm visualization
algo_code = f"""
# C++ Algorithm Visualization
title = Text("C++ Algorithm Visualization")
self.play(Write(title))
self.play(title.animate.to_edge(UP))
self.wait(0.5)
# Create an array representation
values = [5, 2, 8, 1, 9, 3, 7, 4, 6] # Example values
squares = VGroup(*[Square(side_length=0.7, fill_opacity=0.8, fill_color=BLUE) for _ in values])
squares.arrange(RIGHT, buff=0.1)
labels = VGroup(*[Text(str(v), font_size=24) for v in values])
for label, square in zip(labels, squares):
label.move_to(square.get_center())
array = VGroup(squares, labels)
array_label = Text("Array", font_size=20).next_to(array, UP)
self.play(FadeIn(array), Write(array_label))
self.wait(1)
# Simulate sorting algorithm
for i in range(len(values)-1):
# Highlight current element
self.play(squares[i].animate.set_fill(RED))
for j in range(i+1, len(values)):
# Highlight comparison element
self.play(squares[j].animate.set_fill(YELLOW))
# Simulate comparison
if values[i] > values[j]:
# Swap animation
self.play(
labels[i].animate.move_to(squares[j].get_center()),
labels[j].animate.move_to(squares[i].get_center())
)
# Update values and labels
labels[i], labels[j] = labels[j], labels[i]
values[i], values[j] = values[j], values[i]
# Reset comparison element
self.play(squares[j].animate.set_fill(BLUE))
# Mark current element as processed
self.play(squares[i].animate.set_fill(GREEN))
# Mark the last element as processed
self.play(squares[-1].animate.set_fill(GREEN))
# Show sorted array
sorted_label = Text("Sorted Array", font_size=20).next_to(array, DOWN)
self.play(Write(sorted_label))
self.wait(2)
"""
if st.session_state.code:
st.session_state.code += "\n" + algo_code
else:
st.session_state.code = f"""from manim import *
class CppAlgorithmScene(Scene):
def construct(self):
{algo_code}
"""
st.session_state.temp_code = st.session_state.code
st.success("Added C++ algorithm visualization to your Manim code!")
st.session_state.pending_tab_switch = 0 # Switch to editor tab
st.rerun()
elif animation_style == "Data Flow":
# Example code for data flow visualization
data_flow_code = f"""
# C++ Data Flow Visualization
title = Text("C++ Data Flow")
self.play(Write(title))
self.play(title.animate.to_edge(UP))
self.wait(0.5)
# Create nodes for data flow
input_node = Circle(radius=0.5, fill_opacity=0.8, fill_color=BLUE)
process_node = Square(side_length=1, fill_opacity=0.8, fill_color=GREEN)
output_node = Circle(radius=0.5, fill_opacity=0.8, fill_color=RED)
# Position nodes
input_node.move_to(LEFT*4)
process_node.move_to(ORIGIN)
output_node.move_to(RIGHT*4)
# Add labels
input_label = Text("Input", font_size=20).next_to(input_node, DOWN)
process_label = Text("Process", font_size=20).next_to(process_node, DOWN)
output_label = Text("Output", font_size=20).next_to(output_node, DOWN)
# Create arrows
arrow1 = Arrow(input_node.get_right(), process_node.get_left(), buff=0.2)
arrow2 = Arrow(process_node.get_right(), output_node.get_left(), buff=0.2)
# Display nodes and arrows
self.play(FadeIn(input_node), Write(input_label))
self.wait(0.5)
self.play(FadeIn(process_node), Write(process_label))
self.wait(0.5)
self.play(FadeIn(output_node), Write(output_label))
self.wait(0.5)
self.play(Create(arrow1), Create(arrow2))
self.wait(1)
# Simulate data flow
data = Text("Data", font_size=16).move_to(input_node.get_center())
self.play(FadeIn(data))
self.wait(0.5)
# Move data along the flow
self.play(data.animate.move_to(arrow1.get_center()))
self.wait(0.5)
self.play(data.animate.move_to(process_node.get_center()))
self.wait(0.5)
transformed_data = Text("Processed", font_size=16, color=YELLOW)
transformed_data.move_to(process_node.get_center())
self.play(Transform(data, transformed_data))
self.wait(0.5)
self.play(data.animate.move_to(arrow2.get_center()))
self.wait(0.5)
self.play(data.animate.move_to(output_node.get_center()))
self.wait(1)
result_text = Text("Final Result", font_size=24).to_edge(DOWN)
self.play(Write(result_text))
self.wait(2)
"""
if st.session_state.code:
st.session_state.code += "\n" + data_flow_code
else:
st.session_state.code = f"""from manim import *
class CppDataFlowScene(Scene):
def construct(self):
{data_flow_code}
"""
st.session_state.temp_code = st.session_state.code
st.success("Added C++ data flow visualization to your Manim code!")
st.session_state.pending_tab_switch = 0 # Switch to editor tab
st.rerun()
elif animation_style == "Memory Model":
# Example code for memory model visualization
memory_code = f"""
# C++ Memory Model Visualization
title = Text("C++ Memory Model")
self.play(Write(title))
self.play(title.animate.to_edge(UP))
self.wait(0.5)
# Create memory blocks
stack_rect = Rectangle(height=3, width=4, fill_opacity=0.2, fill_color=BLUE)
stack_rect.move_to(LEFT*3.5)
stack_label = Text("Stack", font_size=20).next_to(stack_rect, UP)
heap_rect = Rectangle(height=3, width=4, fill_opacity=0.2, fill_color=RED)
heap_rect.move_to(RIGHT*3.5)
heap_label = Text("Heap", font_size=20).next_to(heap_rect, UP)
# Display memory areas
self.play(
Create(stack_rect), Write(stack_label),
Create(heap_rect), Write(heap_label)
)
self.wait(1)
# Create variables on the stack
int_var = Rectangle(height=0.5, width=1.5, fill_opacity=0.8, fill_color=BLUE_C)
int_var.move_to(stack_rect.get_center() + UP*1)
int_label = Text("int x = 5", font_size=16).next_to(int_var, RIGHT)
pointer_var = Rectangle(height=0.5, width=1.5, fill_opacity=0.8, fill_color=BLUE_D)
pointer_var.move_to(stack_rect.get_center())
pointer_label = Text("int* ptr", font_size=16).next_to(pointer_var, RIGHT)
# Display stack variables
self.play(FadeIn(int_var), Write(int_label))
self.wait(0.5)
self.play(FadeIn(pointer_var), Write(pointer_label))
self.wait(1)
# Create heap allocation
heap_alloc = Rectangle(height=0.8, width=2, fill_opacity=0.8, fill_color=RED_C)
heap_alloc.move_to(heap_rect.get_center() + UP*0.5)
heap_label = Text("new int[4]", font_size=16).next_to(heap_alloc, LEFT)
# Display heap allocation
self.play(FadeIn(heap_alloc), Write(heap_label))
self.wait(1)
# Create arrow from pointer to heap
arrow = Arrow(pointer_var.get_right(), heap_alloc.get_left(), buff=0.2, color=YELLOW)
self.play(Create(arrow))
self.wait(0.5)
# Simulate pointer assignment
assign_text = Text("ptr = new int[4]", font_size=24).to_edge(DOWN)
self.play(Write(assign_text))
self.wait(1)
# Simulate memory deallocation
delete_text = Text("delete[] ptr", font_size=24).to_edge(DOWN)
self.play(Transform(assign_text, delete_text))
self.play(FadeOut(arrow), FadeOut(heap_alloc), FadeOut(heap_label))
self.wait(1)
# Simulate end of scope
end_scope = Text("End of scope", font_size=24).to_edge(DOWN)
self.play(Transform(assign_text, end_scope))
self.play(FadeOut(int_var), FadeOut(int_label), FadeOut(pointer_var), FadeOut(pointer_label))
self.wait(2)
"""
if st.session_state.code:
st.session_state.code += "\n" + memory_code
else:
st.session_state.code = f"""from manim import *
class CppMemoryModelScene(Scene):
def construct(self):
{memory_code}
"""
st.session_state.temp_code = st.session_state.code
st.success("Added C++ memory model visualization to your Manim code!")
st.session_state.pending_tab_switch = 0 # Switch to editor tab
st.rerun()
# C++ Information and tips
with st.expander("C/C++ Runner Information"):
st.markdown("""
### C/C++ Runner Tips
**Compilation Options:**
- Choose the appropriate compiler based on your platform
- Select the C++ standard version for your code
- Optimization levels affect performance and debugging
**Library Support:**
- Common libraries like Eigen, OpenCV, and Boost are supported
- Add custom include paths and library paths as needed
- Use the library detection feature to find installed libraries
**Input/Output:**
- Standard input/output (cin/cout) is fully supported
- File I/O works within the execution directory
- For interactive programs, provide input values in advance
**Debugging:**
- Set breakpoints at specific line numbers
- Watch variables to track their values
- Debug with GDB for detailed analysis
**Project Management:**
- Create multi-file projects with headers and source files
- Generate CMakeLists.txt for complex projects
- Download project files as a ZIP archive
**Images and Visualization:**
- Generate images in PPM, PNG, JPG formats
- Use OpenCV for more advanced image processing
- All generated images can be used in Manim animations
**Manim Integration:**
- Create algorithm visualizations from C++ code
- Import C++ generated images into Manim scenes
- Visualize data structures and memory models
**Performance:**
- Use release mode for best performance
- Profile your code to identify bottlenecks
- C++ is ideal for computationally intensive tasks
""")
# 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)
```
""")
# Handle tab switching with session state to prevent refresh loop
if st.session_state.pending_tab_switch is not None:
st.session_state.active_tab = st.session_state.pending_tab_switch
st.session_state.pending_tab_switch = None
# Set tabs active state
for i, tab in enumerate(tabs):
if i == st.session_state.active_tab:
tab.active = True
# Mark first load as complete to prevent unnecessary refreshes
if not st.session_state.first_load_complete:
st.session_state.first_load_complete = True
if __name__ == "__main__":
main()