Spaces:
Runtime error
Runtime error
File size: 40,791 Bytes
7166956 d5e697f 7166956 d5e697f 7166956 d5e697f 7166956 d5e697f 7166956 d5e697f 7166956 d5e697f 7166956 d5e697f 7166956 d5e697f 7166956 d5e697f 1b888d4 d5e697f 1b888d4 d5e697f 7166956 d5e697f 7166956 d5e697f 7166956 d5e697f 7166956 d5e697f 7166956 d5e697f 7166956 d5e697f 7166956 d5e697f 7166956 d5e697f 7166956 d5e697f 7166956 d5e697f 7166956 d5e697f 7166956 d5e697f 7166956 d5e697f 7166956 d5e697f |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 |
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 visual_elements import search_stock_images, download_image, analyze_slide_for_visuals
from slide_editor_enhancements import render_enhanced_slide_editor
# 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
try:
from multi_llm_provider import get_ai_manager
ai_manager = get_ai_manager()
available_models = ai_manager.get_available_models()
except ImportError:
available_models = {
"claude-3-sonnet-20250219": "Claude 3 Sonnet",
"claude-3-haiku-20250319": "Claude 3 Haiku",
"claude-3-opus-20250229": "Claude 3 Opus"
}
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")
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 available models
try:
from multi_llm_provider import get_ai_manager
ai_manager = get_ai_manager()
available_models = ai_manager.get_available_models()
except (ImportError, Exception):
available_models = {
"claude-3-sonnet-20250219": "Claude 3 Sonnet",
"claude-3-haiku-20250319": "Claude 3 Haiku",
"claude-3-opus-20250229": "Claude 3 Opus"
}
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
# Generate storyboard
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 (simplified version without AI features)"""
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 = []
# Simple regenerate button
if st.button("π Regenerate All", help="Create a new storyboard with current title/purpose"):
st.info("Storyboard regeneration feature coming soon")
# 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()
# 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)
# 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)
def render_template_stage():
"""Render the template selection stage UI with enhanced options"""
display_navigation_bar()
# Add AI settings sidebar
render_ai_settings()
st.header("π¨ Step 3: Select a Template")
st.write("Choose a visual style for your presentation:")
# Preview section
preview_col, options_col = st.columns([2, 1])
with options_col:
# Add template upload option
st.subheader("Custom Template")
st.write("Upload your own PowerPoint template:")
uploaded_template = st.file_uploader("Upload PPTX template", type=["pptx"])
if uploaded_template:
# Save the uploaded template
from utils import add_custom_template
success, message = add_custom_template(uploaded_template)
if success:
st.success(message)
st.session_state.selected_template = "custom"
# Store template file name for display
st.session_state.custom_template_name = uploaded_template.name
else:
st.error(message)
# Template color customization
st.subheader("Color Customization")
# Get current template
current_template = st.session_state.selected_template
template_info = TEMPLATES.get(current_template, TEMPLATES["professional"])
colors = template_info["colors"]
# Allow color customization
primary_color = st.color_picker(
"Primary Color",
value=colors.get("primary", "#0F52BA"),
key="primary_color"
)
accent_color = st.color_picker(
"Accent Color",
value=colors.get("accent", "#D4AF37"),
key="accent_color"
)
# Store customized colors
if "custom_colors" not in st.session_state:
st.session_state.custom_colors = {}
st.session_state.custom_colors[current_template] = {
"primary": primary_color,
"accent": accent_color
}
# Apply customized colors
if st.button("Apply Custom Colors"):
# Update the template colors
if current_template in TEMPLATES:
TEMPLATES[current_template]["colors"]["primary"] = primary_color
TEMPLATES[current_template]["colors"]["accent"] = accent_color
st.success("Custom colors applied!")
with preview_col:
# Template preview - show a sample slide with the selected template
st.subheader("Template Preview")
# Create a sample slide for preview
sample_slide = {
"title": "Sample Slide",
"content": [
"This is how your slides will look",
"With the selected template style",
"You can customize colors and fonts"
],
"visual_elements": ["Chart showing data trends", "Icon representing growth"]
}
# Get current template
current_template = st.session_state.selected_template
# Show preview based on template
try:
from visual_elements import generate_html_preview_with_visuals
preview_html = generate_html_preview_with_visuals(sample_slide, current_template)
st.components.v1.html(preview_html, height=300)
except Exception as e:
st.error(f"Error generating preview: {str(e)}")
# Fall back to standard preview
preview_html = create_slide_preview(sample_slide, current_template)
st.components.v1.html(preview_html, height=300)
# Display current template name
if current_template == "custom" and "custom_template_name" in st.session_state:
st.info(f"Using custom template: {st.session_state.custom_template_name}")
else:
st.info(f"Currently using: {current_template.title()} template")
st.markdown("---")
# Create template cards in a grid
st.subheader("π SlideGator Template Gallery")
st.write("Select from our professionally designed templates:")
# Use columns to create a grid
cols = st.columns(3)
# Add Professional template
with cols[0]:
st.subheader("Professional")
st.write("Clean, corporate style with blue and gray.")
st.markdown("""
<div style="border:1px solid #ddd; padding:10px; border-radius:5px; background-color:#f8f9fa;">
<div style="height:20px; background-color:#0F52BA;"></div>
<div style="padding:10px; text-align:center;">
<div style="font-weight:bold; margin-bottom:10px;">Professional Style</div>
<div style="height:5px; background-color:#D4AF37; width:50%; margin:auto;"></div>
<div style="font-size:0.8em; margin-top:10px;">Clear and business-focused</div>
</div>
</div>
""", unsafe_allow_html=True)
if st.button("π― Select Professional", key="select_prof"):
st.session_state.selected_template = "professional"
st.success("Selected: Professional template")
st.rerun()
# Add Creative template
with cols[1]:
st.subheader("Creative")
st.write("Vibrant design with modern elements.")
st.markdown("""
<div style="border:1px solid #ddd; padding:10px; border-radius:5px; background-color:#FFDE59;">
<div style="height:20px; background-color:#FF5757;"></div>
<div style="padding:10px; text-align:center;">
<div style="font-weight:bold; margin-bottom:10px;">Creative Style</div>
<div style="height:5px; background-color:#5CE1E6; width:50%; margin:auto;"></div>
<div style="font-size:0.8em; margin-top:10px;">Bold and engaging design</div>
</div>
</div>
""", unsafe_allow_html=True)
if st.button("π¨ Select Creative", key="select_creative"):
st.session_state.selected_template = "creative"
st.success("Selected: Creative template")
st.rerun()
# Add Minimalist template
with cols[2]:
st.subheader("Minimalist")
st.write("Simple design with lots of whitespace.")
st.markdown("""
<div style="border:1px solid #ddd; padding:10px; border-radius:5px; background-color:#FFFFFF;">
<div style="height:20px; background-color:#000000;"></div>
<div style="padding:10px; text-align:center;">
<div style="font-weight:bold; margin-bottom:10px;">Minimalist Style</div>
<div style="height:1px; background-color:#000000; width:50%; margin:auto;"></div>
<div style="font-size:0.8em; margin-top:10px;">Clean and elegant</div>
</div>
</div>
""", unsafe_allow_html=True)
if st.button("β¨ Select Minimalist", key="select_min"):
st.session_state.selected_template = "minimalist"
st.success("Selected: Minimalist template")
st.rerun()
# Font selection
with st.expander("π€ Font Settings"):
st.write("Choose font styles for your presentation:")
font_options = [
"Arial, sans-serif",
"Helvetica, sans-serif",
"Calibri, sans-serif",
"Georgia, serif",
"Times New Roman, serif",
"Verdana, sans-serif",
"Tahoma, sans-serif"
]
font_cols = st.columns(2)
with font_cols[0]:
title_font = st.selectbox(
"Title Font",
options=font_options,
index=font_options.index(TEMPLATES[current_template]["fonts"]["title"]) if TEMPLATES[current_template]["fonts"]["title"] in font_options else 0
)
with font_cols[1]:
body_font = st.selectbox(
"Body Font",
options=font_options,
index=font_options.index(TEMPLATES[current_template]["fonts"]["body"]) if TEMPLATES[current_template]["fonts"]["body"] in font_options else 0
)
if st.button("Apply Font Settings"):
# Update the template fonts
if current_template in TEMPLATES:
TEMPLATES[current_template]["fonts"]["title"] = title_font
TEMPLATES[current_template]["fonts"]["body"] = body_font
st.success("Font settings applied!")
st.markdown("---")
col1, col2 = st.columns(2)
with col1:
nav_button("Back to Storyboard", "storyboard", icon="β¬
οΈ")
with col2:
if st.button("π Generate Slides β‘οΈ", type="primary", use_container_width=True):
st.session_state.current_stage = "slides"
# Generate detailed content for each slide
with st.spinner("π SlideGator.AI is crafting your slides..."):
slides_content = []
progress_placeholder = st.empty()
for i, slide in enumerate(st.session_state.storyboard):
# Calculate progress as a value between 0 and 1
progress = i / len(st.session_state.storyboard)
progress_placeholder.progress(progress)
progress_placeholder.text(f"Generating slide {i+1} of {len(st.session_state.storyboard)}: {slide.get('title', 'Untitled')}")
# Get model for slide generation
model = slide.get("ai_settings", {}).get("model", st.session_state.get("default_model", "claude-3-sonnet-20250219"))
slide_content = generate_slide_content(slide, st.session_state.selected_template, model=model)
slides_content.append(slide_content)
progress_placeholder.progress(1.0)
progress_placeholder.empty()
st.session_state.slides_content = slides_content
st.success("All slides generated!")
st.rerun()
def render_slides_stage():
"""Render the enhanced slide editing stage UI"""
display_navigation_bar()
# Add AI settings sidebar
render_ai_settings()
st.header("πΌοΈ Step 4: Review and Edit Slides")
if not st.session_state.slides_content:
st.warning("No slides content available. Please go back and generate slides.")
nav_button("Back to Template Selection", "template", icon="β¬
οΈ")
return
# Add slide navigation controls
col1, col2, col3 = st.columns([1, 2, 1])
with col1:
prev_slide = st.button("βοΈ Previous Slide")
with col2:
current_slide = st.session_state.get("current_slide_index", 0)
slide_selector = st.select_slider(
"Navigate Slides",
options=list(range(len(st.session_state.slides_content))),
value=current_slide,
format_func=lambda x: f"Slide {x+1}: {st.session_state.slides_content[x].get('title', 'Untitled')}"
)
st.session_state.current_slide_index = slide_selector
with col3:
next_slide = st.button("Next Slide βΆοΈ")
if prev_slide and st.session_state.current_slide_index > 0:
st.session_state.current_slide_index -= 1
st.rerun()
if next_slide and st.session_state.current_slide_index < len(st.session_state.slides_content) - 1:
st.session_state.current_slide_index += 1
st.rerun()
# Show slide editing interface for the current slide
current_slide_idx = st.session_state.current_slide_index
current_slide = st.session_state.slides_content[current_slide_idx]
# Add slide management controls
col1, col2, col3, col4 = st.columns([1, 1, 1, 1])
with col1:
if current_slide_idx > 0 and st.button("β¬οΈ Move Up"):
st.session_state.slides_content[current_slide_idx], st.session_state.slides_content[current_slide_idx-1] = st.session_state.slides_content[current_slide_idx-1], st.session_state.slides_content[current_slide_idx]
st.session_state.current_slide_index -= 1
st.rerun()
with col2:
if current_slide_idx < len(st.session_state.slides_content) - 1 and st.button("β¬οΈ Move Down"):
st.session_state.slides_content[current_slide_idx], st.session_state.slides_content[current_slide_idx+1] = st.session_state.slides_content[current_slide_idx+1], st.session_state.slides_content[current_slide_idx]
st.session_state.current_slide_index += 1
st.rerun()
with col3:
if st.button("β Add Slide"):
# Create a new slide based on the current one
new_slide = {
"title": "New Slide",
"content": ["Add your content here"],
"visual_elements": ["Add visual elements here"],
"notes": "Add presenter notes here"
}
# Insert after current slide
st.session_state.slides_content.insert(current_slide_idx + 1, new_slide)
st.session_state.current_slide_index += 1
st.rerun()
with col4:
if len(st.session_state.slides_content) > 1 and st.button("ποΈ Delete Slide"):
st.session_state.slides_content.pop(current_slide_idx)
if current_slide_idx >= len(st.session_state.slides_content):
st.session_state.current_slide_index = len(st.session_state.slides_content) - 1
st.rerun()
# Current template
template_name = st.session_state.selected_template
# Editor for current slide - use enhanced slide editor
st.write(f"### Editing Slide {current_slide_idx + 1}")
updated_slide = render_enhanced_slide_editor(current_slide_idx, current_slide, template_name)
st.session_state.slides_content[current_slide_idx] = updated_slide
# Navigation buttons at bottom
st.markdown("---")
col1, col2 = st.columns(2)
with col1:
nav_button("Back to Template Selection", "template", icon="β¬
οΈ")
with col2:
nav_button("Finalize Presentation", "export", icon="β‘οΈ", primary=True)
def render_export_stage():
"""Render the enhanced export stage UI"""
display_navigation_bar()
# Add AI settings sidebar
render_ai_settings()
st.header("π€ Step 5: Export Presentation")
st.write("Your presentation is ready to export!")
# Add final quality check option
with st.expander("π Quality Check", expanded=True):
st.write("Run a final quality check on your presentation before exporting:")
quality_options = st.multiselect(
"Select checks to run:",
["Spelling & Grammar", "Visual Balance", "Content Consistency", "Presentation Flow"],
default=["Spelling & Grammar", "Content Consistency"]
)
if st.button("Run Quality Check", use_container_width=True):
with st.spinner("π Running quality checks..."):
# Show progress
progress = st.progress(0)
for i, check in enumerate(quality_options):
# Update progress
progress.progress((i + 0.5) / len(quality_options))
if check == "Spelling & Grammar":
with st.spinner("Checking spelling and grammar..."):
# In a real implementation, this would check all slide content
time.sleep(1) # Simulate processing
st.success("β
Spelling and grammar check complete. No major issues found.")
elif check == "Visual Balance":
with st.spinner("Analyzing visual balance..."):
# Simulate visual analysis
time.sleep(1)
st.info("βΉοΈ Visual recommendation: Consider adding more images to slides 3 and 5.")
elif check == "Content Consistency":
with st.spinner("Checking content consistency..."):
# In a real implementation, would use AI to check consistency
time.sleep(1)
st.success("β
Content is consistent throughout the presentation.")
elif check == "Presentation Flow":
with st.spinner("Analyzing presentation flow..."):
# Simulate flow analysis
time.sleep(1)
st.info("βΉοΈ Flow recommendation: Consider adding a transition slide between slides 2 and 3.")
# Complete progress
progress.progress(1.0)
# Export options
export_tabs = st.tabs(["PowerPoint", "PDF", "Images", "Advanced"])
with export_tabs[0]:
# PowerPoint export
st.subheader("PowerPoint Export")
# Create the PowerPoint file
with st.spinner("π SlideGator.AI is finalizing your presentation..."):
try:
try:
# Try to use enhanced PowerPoint creation if available
from enhanced_pptx import enhanced_create_ppt
ppt_buffer = enhanced_create_ppt(
st.session_state.slides_content,
st.session_state.selected_template
)
except ImportError:
# Fall back to standard PowerPoint creation
from utils import create_ppt
ppt_buffer = create_ppt(
st.session_state.slides_content,
st.session_state.selected_template
)
creation_success = True
except Exception as e:
st.error(f"Error creating PowerPoint file: {str(e)}")
import traceback
st.error(traceback.format_exc())
creation_success = False
if creation_success:
# Provide download link
filename = f"{st.session_state.presentation_title.replace(' ', '_')}.pptx"
# Get download link HTML
b64 = base64.b64encode(ppt_buffer.read()).decode()
href = f'<a href="data:application/vnd.openxmlformats-officedocument.presentationml.presentation;base64,{b64}" download="{filename}" class="download-btn">π Download PowerPoint Presentation</a>'
# Add some styling to the download button
st.markdown("""
<style>
.download-btn {
display: inline-block;
padding: 12px 20px;
background-color: #4CAF50;
color: white;
text-decoration: none;
border-radius: 4px;
font-weight: bold;
text-align: center;
transition: background-color 0.3s;
font-size: 1.2em;
}
.download-btn:hover {
background-color: #45a049;
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
</style>
""", unsafe_allow_html=True)
# Display download link
st.markdown(f"<div style='text-align: center; margin: 30px 0;'>{href}</div>", unsafe_allow_html=True)
with export_tabs[1]:
# PDF export
st.subheader("PDF Export")
st.info("PDF export feature is coming soon. For now, please use the PowerPoint export and save as PDF.")
with export_tabs[2]:
# Image export
st.subheader("Image Export")
st.info("Image export feature is coming soon. This will allow you to export each slide as a PNG or JPG.")
with export_tabs[3]:
# Advanced export options
st.subheader("Advanced Export Options")
# Customization before export
st.write("Customize final export settings:")
optimize_file = st.checkbox("Optimize file size", value=True)
include_notes = st.checkbox("Include presenter notes", value=True)
protect_file = st.checkbox("Add password protection", value=False)
if protect_file:
password = st.text_input("Set password (optional)", type="password")
st.info("Advanced export features are coming soon. These settings are for demonstration purposes only.")
# Display congratulations message
st.markdown("""
<div style="text-align:center; padding: 20px; margin: 20px 0; border-radius: 5px; background-color: #f8f9fa;">
<div style="font-size: 2rem; margin-bottom: 10px;">π Congratulations! π</div>
<div style="font-size: 1.2rem; margin-bottom: 15px;">Your presentation is ready!</div>
<div style="font-size: 1rem; color: #666;">SlideGator.AI has snapped up the perfect presentation for you.</div>
</div>
""", unsafe_allow_html=True)
# Preview of slides
st.subheader("π Presentation Preview")
# Display a sample of slides
preview_cols = st.columns(3)
for i, slide in enumerate(st.session_state.slides_content[:3]): # Just show first 3 slides
with preview_cols[i % 3]:
try:
# Use enhanced preview with visuals if available
try:
from visual_elements import generate_html_preview_with_visuals
preview_html = generate_html_preview_with_visuals(slide, st.session_state.selected_template)
except ImportError:
preview_html = create_slide_preview(slide, st.session_state.selected_template)
st.components.v1.html(preview_html, height=200)
st.write(f"**Slide {i+1}:** {slide.get('title', 'Untitled')}")
except Exception as e:
st.error(f"Error previewing slide {i+1}")
# Show "View All" option
if len(st.session_state.slides_content) > 3:
st.write("...")
show_all = st.checkbox("Show all slides", value=False)
if show_all:
st.write("### All Slides")
for i, slide in enumerate(st.session_state.slides_content):
with st.expander(f"Slide {i+1}: {slide.get('title', 'Untitled')}"):
try:
# Try enhanced preview
try:
from visual_elements import generate_html_preview_with_visuals
preview_html = generate_html_preview_with_visuals(slide, st.session_state.selected_template)
except ImportError:
preview_html = create_slide_preview(slide, st.session_state.selected_template)
st.components.v1.html(preview_html, height=300)
except Exception as e:
st.error(f"Error previewing slide {i+1}")
# Navigation buttons
st.markdown("---")
col1, col2 = st.columns(2)
with col1:
nav_button("Back to Edit Slides", "slides", icon="β¬
οΈ")
with col2:
if st.button("π Start New Presentation", type="primary", use_container_width=True):
# Ask for confirmation
if st.checkbox("β
Confirm starting a new presentation? All current work will be lost.", value=False):
# Reset session state
for key in list(st.session_state.keys()):
if key not in ["session_id", "ai_manager"]:
del st.session_state[key]
st.session_state.current_stage = "ideation"
st.session_state.presentation_title = ""
st.session_state.presentation_purpose = ""
st.session_state.target_audience = ""
st.session_state.storyboard = []
st.session_state.selected_template = "professional"
st.session_state.slides_content = []
st.success("Starting new presentation...")
st.rerun()
|