import streamlit as st import base64 from io import BytesIO import json import time import os from utils import create_slide_preview, TEMPLATES, generate_slide_content from enhanced_pptx import enhanced_create_ppt from slide_editor_enhancements import render_enhanced_slide_editor from multi_llm_provider import get_ai_manager from visual_elements import search_stock_images, download_image, analyze_slide_for_visuals # Check for API keys api_key = os.getenv("ANTHROPIC_API_KEY") openai_key = os.getenv("OPENAI_API_KEY") deepseek_key = os.getenv("DEEPSEEK_API_KEY") perplexity_key = os.getenv("PERPLEXITY_API_KEY") pexels_key = os.getenv("PEXELS_API_KEY") def nav_button(label, stage, icon=None, primary=False): """Create a navigation button with improved styling""" button_style = "primary" if primary else "secondary" icon_text = f"{icon} " if icon else "" if st.button(f"{icon_text}{label}", key=f"nav_{stage}", type=button_style, use_container_width=True): st.session_state.current_stage = stage st.rerun() def display_navigation_bar(): """Display navigation bar at the top of the page""" stages = [ {"name": "ideation", "label": "Ideation", "icon": "💡"}, {"name": "storyboard", "label": "Storyboard", "icon": "📋"}, {"name": "template", "label": "Template", "icon": "🎨"}, {"name": "slides", "label": "Edit Slides", "icon": "🖼️"}, {"name": "export", "label": "Export", "icon": "📤"} ] cols = st.columns(len(stages)) for i, stage in enumerate(stages): with cols[i]: is_current = st.session_state.current_stage == stage["name"] # Create a clickable button with custom CSS if st.button( f"{stage['icon']} {stage['label']}", key=f"nav_top_{stage['name']}", disabled=is_current, use_container_width=True, type="primary" if is_current else "secondary" ): # If we've already completed previous stages, allow direct navigation if stage["name"] in ["ideation"] or ( stage["name"] == "storyboard" and st.session_state.storyboard ) or ( stage["name"] == "template" and st.session_state.storyboard ) or ( stage["name"] == "slides" and st.session_state.slides_content ) or ( stage["name"] == "export" and st.session_state.slides_content ): st.session_state.current_stage = stage["name"] st.rerun() else: st.warning(f"Please complete the previous stages before going to {stage['label']}") # Progress bar stages_order = ["ideation", "storyboard", "template", "slides", "export"] current_idx = stages_order.index(st.session_state.current_stage) progress = current_idx / (len(stages_order) - 1) st.progress(progress) def render_ai_settings(): """Render AI settings sidebar section""" st.sidebar.write("## 🧠 AI Settings") # Get AI manager ai_manager = get_ai_manager() available_models = ai_manager.get_available_models() if not available_models: st.sidebar.warning("No AI providers configured. Add API keys in secrets.") default_model = "claude-3-sonnet-20250219" else: default_model = list(available_models.keys())[0] if available_models else "claude-3-sonnet-20250219" # Select default model for the whole presentation selected_model = st.sidebar.selectbox( "Default AI Model", options=list(available_models.keys()), format_func=lambda x: available_models.get(x, x), index=0 if default_model in available_models else 0 ) st.session_state.default_model = selected_model # Temperature setting temperature = st.sidebar.slider( "AI Creativity", min_value=0.0, max_value=1.0, value=0.7, step=0.1, help="Higher values make output more creative but less predictable" ) st.session_state.ai_temperature = temperature # Web search integration enable_search = st.sidebar.checkbox( "Enable Web Search", value=st.session_state.get("enable_web_search", False), help="Use Perplexity to search for up-to-date information", disabled=not perplexity_key ) st.session_state.enable_web_search = enable_search if enable_search and not perplexity_key: st.sidebar.warning("Perplexity API key required for web search") # Stock image settings st.sidebar.write("## 🖼️ Image Settings") if not pexels_key: st.sidebar.warning("Pexels API key required for better stock images") # Test different AI providers if st.sidebar.button("Test AI Providers"): with st.sidebar: st.write("Testing AI providers...") for model in list(available_models.keys())[:2]: # Test first two models try: with st.spinner(f"Testing {model}..."): response = ai_manager.generate_text( "Write a one-sentence test response.", model=model, max_tokens=50 ) st.success(f"{model}: {response}") except Exception as e: st.error(f"{model}: Error - {str(e)}") def render_ideation_stage(): """Render the ideation stage UI""" display_navigation_bar() # Add AI settings sidebar render_ai_settings() st.header("💡 Step 1: Presentation Ideation") st.write("Let's start by defining the purpose and scope of your presentation.") col1, col2 = st.columns(2) with col1: st.session_state.presentation_title = st.text_input( "Presentation Title", value=st.session_state.presentation_title ) st.session_state.presentation_purpose = st.text_area( "What's the purpose of this presentation?", value=st.session_state.presentation_purpose, help="E.g., Inform, persuade, pitch a product, update stakeholders, etc." ) with col2: st.session_state.target_audience = st.text_area( "Who is your target audience?", value=st.session_state.target_audience, help="E.g., Executives, customers, technical team, general public, etc." ) # Add example suggestions st.write("📚 **Example presentations:**") examples = [ "Quarterly Business Review", "Product Launch Presentation", "Team Project Update", "Investor Pitch Deck" ] # Create two columns for examples ex_col1, ex_col2 = st.columns(2) for i, example in enumerate(examples): with ex_col1 if i % 2 == 0 else ex_col2: if st.button(f"📄 {example}", key=f"example_{example}"): st.session_state.presentation_title = example # Set appropriate purpose and audience based on example if "Quarterly" in example: st.session_state.presentation_purpose = "Review business performance for the past quarter and outline goals for the next quarter" st.session_state.target_audience = "Executives and department heads" elif "Product" in example: st.session_state.presentation_purpose = "Introduce a new product to customers and highlight its key features and benefits" st.session_state.target_audience = "Potential customers and sales team" elif "Project" in example: st.session_state.presentation_purpose = "Update team members on project progress, achievements, and next steps" st.session_state.target_audience = "Project stakeholders and team members" elif "Investor" in example: st.session_state.presentation_purpose = "Pitch our business to potential investors to secure funding" st.session_state.target_audience = "Venture capitalists and angel investors" st.rerun() # AI model selection for storyboard generation st.write("### 🧠 AI Engine Selection") # Get AI manager ai_manager = get_ai_manager() available_models = ai_manager.get_available_models() cols = st.columns([2, 1]) with cols[0]: selected_model = st.selectbox( "AI Model for Storyboard Generation", options=list(available_models.keys()), format_func=lambda x: available_models.get(x, x), index=0, key="storyboard_model" ) with cols[1]: web_research = st.checkbox( "Include Web Research", value=st.session_state.get("enable_web_search", False), disabled=not perplexity_key, help="Search the web for the latest information related to your presentation topic" ) # Generate button with loading indicator st.markdown("---") if st.button("🐊 Generate Storyboard", type="primary", use_container_width=True): if not st.session_state.presentation_title or not st.session_state.presentation_purpose: st.warning("Please provide at least a title and purpose to proceed.") else: with st.spinner("🧠 SlideGator.AI is chomping on your presentation ideas..."): from utils import generate_storyboard # If web research is enabled, perform a search first if web_research and perplexity_key: with st.spinner("🔍 Researching the latest information..."): search_query = f"latest information about {st.session_state.presentation_title}" search_results = ai_manager.web_search(search_query) if search_results: # Create a research summary st.success(f"Found {len(search_results)} relevant sources of information!") # Store in session state st.session_state.web_research = search_results else: st.warning("No research results found. Proceeding without research.") # Generate storyboard with the selected model storyboard = generate_storyboard( st.session_state.presentation_title, st.session_state.presentation_purpose, st.session_state.target_audience, model=selected_model ) if storyboard: st.session_state.storyboard = storyboard st.session_state.current_stage = "storyboard" st.success("Storyboard created successfully!") st.rerun() def render_storyboard_stage(): """Render the storyboard review stage UI""" display_navigation_bar() st.header("📋 Step 2: Review and Edit Storyboard") st.write(f"Storyboard for: **{st.session_state.presentation_title}**") st.write("Expand each slide to edit its content or add notes.") # Display storyboard for review with edit options edited_storyboard = [] # Add a "regenerate" button and AI options col1, col2, col3 = st.columns([2, 1, 1]) with col1: # Get AI manager ai_manager = get_ai_manager() available_models = ai_manager.get_available_models() selected_model = st.selectbox( "AI Model", options=list(available_models.keys()), format_func=lambda x: available_models.get(x, x), index=0, key="storyboard_regen_model" ) with col2: if st.button("🔄 Regenerate All", help="Create a new storyboard with current title/purpose"): from utils import generate_storyboard with st.spinner("🐊 SlideGator.AI is rethinking your presentation structure..."): storyboard = generate_storyboard( st.session_state.presentation_title, st.session_state.presentation_purpose, st.session_state.target_audience, model=selected_model ) if storyboard: st.session_state.storyboard = storyboard st.success("Storyboard regenerated!") st.rerun() with col3: if st.button("🔍 Analyze Flow", help="Analyze the narrative flow of your presentation"): with st.spinner("🧠 Analyzing presentation flow..."): # Convert storyboard to text for analysis storyboard_text = "" for i, slide in enumerate(st.session_state.storyboard): storyboard_text += f"Slide {i+1}: {slide.get('title', 'Untitled')}\n" storyboard_text += f"Purpose: {slide.get('purpose', '')}\n" key_points = slide.get('key_points', []) if isinstance(key_points, list): for point in key_points: storyboard_text += f"- {point}\n" else: storyboard_text += f"- {key_points}\n" storyboard_text += "\n" # Analyze the flow prompt = f""" You are an expert presentation designer. Please analyze the flow and structure of this presentation storyboard: {storyboard_text} Provide feedback on: 1. The narrative flow and logical progression 2. Any gaps in the content 3. Balance of information across slides 4. Suggestions for improvement Be constructive and specific. """ try: analysis = ai_manager.generate_text( prompt=prompt, model=selected_model, system_prompt="You are an expert presentation flow analyst focusing on business presentations.", temperature=0.3, max_tokens=1000 ) # Display analysis st.info("### Presentation Flow Analysis") st.write(analysis) except Exception as e: st.error(f"Error analyzing presentation flow: {str(e)}") # Add slide button with options col1, col2 = st.columns([3, 1]) with col1: slide_position = st.radio( "Add new slide:", ["At End", "After Current", "Before Current"], horizontal=True ) with col2: if st.button("➕ Add New Slide", use_container_width=True): # Create a new slide new_slide = { 'title': 'New Slide', 'purpose': 'Additional content', 'key_points': ['Add your content here'], 'visual_elements': ['Suggested visuals will appear here'] } # Insert at the selected position if slide_position == "At End": st.session_state.storyboard.append(new_slide) elif slide_position == "After Current": # Get current slide index from session state or default to last current_idx = st.session_state.get("current_storyboard_slide", len(st.session_state.storyboard) - 1) st.session_state.storyboard.insert(current_idx + 1, new_slide) else: # Before Current current_idx = st.session_state.get("current_storyboard_slide", 0) st.session_state.storyboard.insert(max(0, current_idx), new_slide) st.rerun() # Add slide templates section with st.expander("📑 Slide Templates"): st.write("Add pre-designed slide templates to your presentation:") template_cols = st.columns(3) slide_templates = [ {"name": "Section Divider", "icon": "🏷️", "title": "Section Title", "purpose": "Introduce a new section", "key_points": ["A clean slide to mark a new section of your presentation"]}, {"name": "Comparison", "icon": "⚖️", "title": "Comparison", "purpose": "Compare two options or approaches", "key_points": ["Option A benefits", "Option A challenges", "Option B benefits", "Option B challenges"], "visual_elements": ["Two-column layout", "Comparison table or chart"]}, {"name": "Quote", "icon": "💬", "title": "Key Quote", "purpose": "Highlight an important quote", "key_points": ["The quote goes here", "- Attribution"], "visual_elements": ["Quotation marks graphic", "Simple background"]}, {"name": "Data Visualization", "icon": "📊", "title": "Key Metrics", "purpose": "Present important data", "key_points": ["Metric 1 and its significance", "Metric 2 and its significance", "Trends and patterns"], "visual_elements": ["Chart or graph", "Data visualization"]}, {"name": "Call to Action", "icon": "🎯", "title": "Next Steps", "purpose": "Define clear action items", "key_points": ["Specific action item 1", "Specific action item 2", "Timeline and ownership"], "visual_elements": ["Arrow or path graphic", "Timeline visualization"]}, {"name": "Team Slide", "icon": "👥", "title": "Our Team", "purpose": "Introduce key team members", "key_points": ["Team member 1 with role", "Team member 2 with role", "Team member 3 with role"], "visual_elements": ["Team photos or icons", "Organizational chart"]} ] for i, template in enumerate(slide_templates): with template_cols[i % 3]: if st.button(f"{template['icon']} {template['name']}", key=f"template_{template['name']}"): # Create a new slide based on the template new_slide = { 'title': template['title'], 'purpose': template['purpose'], 'key_points': template['key_points'], 'visual_elements': template.get('visual_elements', []) } # Add to storyboard st.session_state.storyboard.append(new_slide) st.success(f"Added {template['name']} slide!") st.rerun() # Display storyboard slides for i, slide in enumerate(st.session_state.storyboard): # Set current slide in session state when expander is opened is_expanded = i == 0 or st.session_state.get("current_storyboard_slide") == i with st.expander(f"Slide {i+1}: {slide.get('title', 'Untitled')}", expanded=is_expanded): if is_expanded: st.session_state.current_storyboard_slide = i # Main slide content cols = st.columns([3, 1]) with cols[0]: slide_title = st.text_input(f"Title ###{i}", value=slide.get('title', 'Untitled')) with cols[1]: # Slide reordering and deletion cols2 = st.columns([1, 1, 1]) with cols2[0]: if i > 0 and st.button("⬆️", key=f"up_{i}"): st.session_state.storyboard[i], st.session_state.storyboard[i-1] = st.session_state.storyboard[i-1], st.session_state.storyboard[i] st.session_state.current_storyboard_slide = i - 1 st.rerun() with cols2[1]: if i < len(st.session_state.storyboard) - 1 and st.button("⬇️", key=f"down_{i}"): st.session_state.storyboard[i], st.session_state.storyboard[i+1] = st.session_state.storyboard[i+1], st.session_state.storyboard[i] st.session_state.current_storyboard_slide = i + 1 st.rerun() with cols2[2]: if st.button("🗑️", key=f"delete_{i}"): if len(st.session_state.storyboard) > 1: # Prevent deleting the last slide st.session_state.storyboard.pop(i) st.session_state.current_storyboard_slide = min(i, len(st.session_state.storyboard) - 1) st.rerun() else: st.error("Cannot delete the last slide") slide_purpose = st.text_area(f"Purpose ###{i}", value=slide.get('purpose', '')) # Handle key points (could be string or list) if isinstance(slide.get('key_points', ""), list): key_points_text = "\n".join(slide['key_points']) else: key_points_text = slide.get('key_points', "") key_points = st.text_area(f"Key Points (one per line) ###{i}", value=key_points_text) # Handle visual elements (could be string or list) if isinstance(slide.get('visual_elements', ""), list): visual_elements_text = "\n".join(slide['visual_elements']) else: visual_elements_text = slide.get('visual_elements', "") visual_elements = st.text_area(f"Visual Elements (one per line) ###{i}", value=visual_elements_text) # AI enhancement options with st.expander("🧠 AI Enhancement"): ai_cols = st.columns(2) with ai_cols[0]: # Get AI manager ai_manager = get_ai_manager() available_models = ai_manager.get_available_models() enhancement_model = st.selectbox( "AI Model", options=list(available_models.keys()), format_func=lambda x: available_models.get(x, x), index=0, key=f"enhance_model_{i}" ) with ai_cols[1]: enhancement_type = st.selectbox( "Enhancement Type", ["Content", "Title", "Visual Suggestions", "All"], key=f"enhance_type_{i}" ) if st.button("✨ Enhance with AI", key=f"enhance_slide_{i}"): with st.spinner("🧠 Enhancing slide content..."): # Update slide with current edits first updated_slide = { 'title': slide_title, 'purpose': slide_purpose, 'key_points': key_points.split("\n") if "\n" in key_points else [key_points] if key_points else [], 'visual_elements': visual_elements.split("\n") if "\n" in visual_elements else [visual_elements] if visual_elements else [] } try: # Create appropriate prompt based on enhancement type if enhancement_type == "Content" or enhancement_type == "All": content_prompt = f""" You are an expert presentation content creator. Enhance the content for this slide: Title: {updated_slide['title']} Purpose: {updated_slide['purpose']} Current content: {updated_slide['key_points']} Make the content more impactful, clear, and aligned with the slide's purpose. Return only the enhanced content points as a JSON array of strings like this: ["Enhanced point 1", "Enhanced point 2", "Enhanced point 3"] """ content_response = ai_manager.generate_text( prompt=content_prompt, model=enhancement_model, system_prompt="You are an expert presentation content creator. Return only JSON array of enhanced bullet points.", temperature=0.7, max_tokens=500 ) # Extract JSON array from response try: json_start = content_response.find("[") json_end = content_response.rfind("]") + 1 if json_start >= 0 and json_end > 0: json_str = content_response[json_start:json_end] enhanced_content = json.loads(json_str) updated_slide['key_points'] = enhanced_content except Exception as e: st.error(f"Error parsing content enhancement: {str(e)}") if enhancement_type == "Title" or enhancement_type == "All": title_prompt = f""" You are an expert presentation title creator. Current slide title: {updated_slide['title']} Slide purpose: {updated_slide['purpose']} Slide content: {updated_slide['key_points']} Create a more engaging, clear, and impactful title for this slide. Return only the new title with no additional text or explanation. """ title_response = ai_manager.generate_text( prompt=title_prompt, model=enhancement_model, system_prompt="You are an expert presentation title creator. Return only the enhanced title.", temperature=0.7, max_tokens=50 ) updated_slide['title'] = title_response.strip() if enhancement_type == "Visual Suggestions" or enhancement_type == "All": visual_prompt = f""" You are an expert presentation visual designer. Slide title: {updated_slide['title']} Slide purpose: {updated_slide['purpose']} Slide content: {updated_slide['key_points']} Suggest specific visual elements that would enhance this slide. Consider charts, diagrams, images, icons, and layout suggestions. Return only a JSON array of specific recommendations like: ["Bar chart comparing quarterly revenue", "Icon representing customer satisfaction", "Split-screen layout with image on left"] """ visual_response = ai_manager.generate_text( prompt=visual_prompt, model=enhancement_model, system_prompt="You are an expert presentation visual designer. Return only a JSON array of visual element recommendations.", temperature=0.7, max_tokens=300 ) # Extract JSON array from response try: json_start = visual_response.find("[") json_end = visual_response.rfind("]") + 1 if json_start >= 0 and json_end > 0: json_str = visual_response[json_start:json_end] enhanced_visuals = json.loads(json_str) updated_slide['visual_elements'] = enhanced_visuals except Exception as e: st.error(f"Error parsing visual enhancement: {str(e)}") # Update the storyboard with enhanced slide st.session_state.storyboard[i] = updated_slide st.success("Slide enhanced with AI!") st.rerun() except Exception as e: st.error(f"Error enhancing slide: {str(e)}") # Web search enhancement option if perplexity_key: with st.expander("🔍 Enhance with Web Search"): search_query = st.text_input( "Search Query", value=f"latest information about {slide_title}", key=f"search_query_{i}" ) if st.button("🔍 Search & Enhance", key=f"search_enhance_{i}"): # Update slide with current edits first updated_slide = { 'title': slide_title, 'purpose': slide_purpose, 'key_points': key_points.split("\n") if "\n" in key_points else [key_points] if key_points else [], 'visual_elements': visual_elements.split("\n") if "\n" in visual_elements else [visual_elements] if visual_elements else [] } # Enhance with web search enhanced_slide = ai_manager.enhance_with_web_search(updated_slide, search_query) # Update the storyboard st.session_state.storyboard[i] = enhanced_slide st.success("Slide enhanced with web search results!") st.rerun() # Update storyboard with edits edited_slide = { 'title': slide_title, 'purpose': slide_purpose, 'key_points': key_points.split("\n") if "\n" in key_points else [key_points] if key_points else [], 'visual_elements': visual_elements.split("\n") if "\n" in visual_elements else [visual_elements] if visual_elements else [] } edited_storyboard.append(edited_slide) # Update the storyboard in session state st.session_state.storyboard = edited_storyboard st.markdown("---") col1, col2 = st.columns(2) with col1: nav_button("Back to Ideation", "ideation", icon="⬅️") with col2: nav_button("Continue to Template Selection", "template", icon="➡️", primary=True)