Slide_gator / components.py
cryogenic22's picture
Update components.py
3d67919 verified
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)