cryogenic22 commited on
Commit
d5e697f
Β·
verified Β·
1 Parent(s): 96815db

Update updated_components.py

Browse files
Files changed (1) hide show
  1. updated_components.py +1047 -359
updated_components.py CHANGED
@@ -1,396 +1,1084 @@
1
  import streamlit as st
2
- from visual_elements import (
3
- search_stock_images,
4
- download_image,
5
- analyze_slide_for_visuals,
6
- generate_html_preview_with_visuals,
7
- STANDARD_SHAPES,
8
- ICON_CATEGORIES
9
- )
10
- import os
11
- import requests
12
- from PIL import Image
13
- from io import BytesIO
14
  import base64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
- def render_enhanced_slide_editor(slide_index, slide, template_name="professional"):
17
- """Render the enhanced editor for a single slide with visual elements"""
18
- from utils import TEMPLATES, create_slide_preview
 
 
 
19
 
20
- # Get template info
21
- template = TEMPLATES.get(template_name, TEMPLATES["professional"])
22
- colors = template["colors"]
23
 
24
- # Split into preview and editor columns
25
- col1, col2 = st.columns([2, 3])
 
26
 
27
  with col1:
28
- st.subheader("Preview")
29
- # Generate and display HTML preview with visuals
30
- try:
31
- # Use enhanced preview with visuals
32
- preview_html = generate_html_preview_with_visuals(slide, template_name)
33
- st.components.v1.html(preview_html, height=400)
34
- except Exception as e:
35
- st.error(f"Error generating preview: {str(e)}")
36
- # Fall back to standard preview
37
- preview_html = create_slide_preview(slide, template_name)
38
- st.components.v1.html(preview_html, height=350)
39
 
40
- # Visual elements analysis
41
- analysis = analyze_slide_for_visuals(slide)
 
 
 
 
 
 
 
 
 
 
42
 
43
- # Quick styling options
44
- with st.expander("🎨 Layout & Style"):
45
- # Smart layout suggestion
46
- suggested_layout = analysis['suggested_layout']
47
- layout_options = ["Standard", "Two Column", "Title Only", "Quote", "Picture with Caption"]
48
-
49
- st.write(f"πŸ’‘ **Suggested layout:** {suggested_layout}")
50
-
51
- selected_layout = st.selectbox(
52
- "Layout",
53
- options=layout_options,
54
- index=layout_options.index(suggested_layout) if suggested_layout in layout_options else 0,
55
- key=f"layout_{slide_index}"
56
- )
57
-
58
- # Color scheme options
59
- st.write("🎭 **Color Accent**")
60
- color_options = {
61
- "Default": colors["accent"],
62
- "Blue": "#0066CC",
63
- "Green": "#00AA55",
64
- "Red": "#CC3300",
65
- "Purple": "#9933CC",
66
- "Orange": "#FF6600"
67
- }
68
- selected_color = st.color_picker(
69
- "Accent Color",
70
- value=colors["accent"],
71
- key=f"color_{slide_index}"
72
- )
73
-
74
- # Apply button
75
- if st.button("Apply Style", key=f"apply_style_{slide_index}"):
76
- if "design" not in slide:
77
- slide["design"] = {}
78
- slide["design"]["layout"] = selected_layout
79
- slide["design"]["accent_color"] = selected_color
80
- st.success("Style applied!")
81
-
82
- # Visual elements section
83
- with st.expander("πŸ–ΌοΈ Visual Elements"):
84
- # Chart options if analysis suggests a chart
85
- if analysis['need_chart']:
86
- st.write("πŸ“Š **Chart Recommendation**")
87
- st.write(f"The content suggests a {analysis['chart_type']} chart would be appropriate.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
- chart_types = ["column", "bar", "line", "pie", "area", "scatter"]
90
- selected_chart = st.selectbox(
91
- "Chart Type",
92
- options=chart_types,
93
- index=chart_types.index(analysis['chart_type']) if analysis['chart_type'] in chart_types else 0,
94
- key=f"chart_{slide_index}"
95
  )
96
 
97
- if st.button("Add Chart", key=f"add_chart_{slide_index}"):
98
- if "visuals" not in slide:
99
- slide["visuals"] = {}
100
- slide["visuals"]["chart"] = {
101
- "type": selected_chart,
102
- "data": "sample" # Will be replaced with actual data
103
- }
104
- st.success(f"Added {selected_chart} chart!")
105
-
106
- # Image options
107
- st.write("🏞️ **Images**")
108
- image_source = st.radio(
109
- "Image Source",
110
- ["Stock Images", "Upload Image"],
111
- key=f"img_source_{slide_index}"
112
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
 
114
- if image_source == "Stock Images":
115
- search_query = st.text_input(
116
- "Search for images",
117
- value=analysis['image_query'],
118
- key=f"img_search_{slide_index}"
119
- )
120
-
121
- if st.button("Search", key=f"search_btn_{slide_index}"):
122
- with st.spinner("Searching for images..."):
123
- images = search_stock_images(search_query)
124
-
125
- if not images:
126
- st.warning("No images found. Try a different search term.")
127
- else:
128
- # Store images in session state for this slide
129
- if "stock_images" not in st.session_state:
130
- st.session_state.stock_images = {}
131
- st.session_state.stock_images[slide_index] = images
132
-
133
- st.success(f"Found {len(images)} images!")
134
-
135
- # Display stored images if available
136
- if "stock_images" in st.session_state and slide_index in st.session_state.stock_images:
137
- images = st.session_state.stock_images[slide_index]
138
-
139
- # Create a grid of images using columns
140
- columns = st.columns(3)
141
-
142
- for i, img in enumerate(images[:6]): # Limit to 6 images
143
- with columns[i % 3]:
144
- # Display thumbnail
145
- st.image(img["thumbnail"], use_column_width=True)
146
-
147
- if st.button("Select", key=f"select_img_{slide_index}_{i}"):
148
- # Add image to slide
149
- if "visuals" not in slide:
150
- slide["visuals"] = {}
151
- slide["visuals"]["image"] = {
152
- "url": img["url"],
153
- "source": "stock"
154
- }
155
- st.success("Image selected!")
156
- else:
157
- # Upload image
158
- uploaded_file = st.file_uploader(
159
- "Upload an image",
160
- type=["jpg", "jpeg", "png", "gif"],
161
- key=f"upload_{slide_index}"
162
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
 
164
- if uploaded_file:
165
- image = Image.open(uploaded_file)
166
- st.image(image, width=250)
 
167
 
168
- if st.button("Use This Image", key=f"use_upload_{slide_index}"):
169
- # Store image in session state
170
- if "uploaded_images" not in st.session_state:
171
- st.session_state.uploaded_images = {}
172
-
173
- # Convert image to base64 for storage
174
- buffered = BytesIO()
175
- image.save(buffered, format="PNG")
176
- img_str = base64.b64encode(buffered.getvalue()).decode()
177
-
178
- st.session_state.uploaded_images[slide_index] = img_str
179
-
180
- # Add to slide
181
- if "visuals" not in slide:
182
- slide["visuals"] = {}
183
- slide["visuals"]["image"] = {
184
- "data": img_str,
185
- "source": "upload"
186
- }
187
- st.success("Image added to slide!")
 
 
 
 
 
188
 
189
- # Icon options
190
- st.write("πŸ” **Icons & Shapes**")
 
 
 
 
 
 
 
 
191
 
192
- # Create tabs for different icon categories
193
- icon_tabs = st.tabs(list(ICON_CATEGORIES.keys()))
 
 
 
 
 
 
 
 
 
 
194
 
195
- for i, (category, icons) in enumerate(ICON_CATEGORIES.items()):
196
- with icon_tabs[i]:
197
- # Display icons in a grid
198
- cols = st.columns(4)
199
- for j, icon in enumerate(icons):
200
- with cols[j % 4]:
201
- if st.button(icon, key=f"icon_{slide_index}_{category}_{j}"):
202
- # Add icon to slide
203
- if "visuals" not in slide:
204
- slide["visuals"] = {}
205
- if "icons" not in slide["visuals"]:
206
- slide["visuals"]["icons"] = []
207
-
208
- slide["visuals"]["icons"].append(icon)
209
- st.success(f"Added {icon} icon!")
210
-
211
- # AI image generation
212
- with st.expander("πŸ€– AI Image Generation"):
213
- st.write("Generate a custom image using AI based on your slide content")
214
 
215
- # Image prompt
216
- prompt_base = f"Creating an image for slide '{slide.get('title', '')}'"
217
- image_prompt = st.text_area(
218
- "Image description",
219
- value=prompt_base,
220
- key=f"img_prompt_{slide_index}",
221
- height=100
222
- )
223
 
224
- image_style = st.selectbox(
225
- "Style",
226
- ["Realistic", "Digital Art", "Watercolor", "Minimalist", "Corporate"],
227
- key=f"img_style_{slide_index}"
228
- )
229
 
230
- if st.button("Generate Image", key=f"gen_img_{slide_index}"):
231
- # In a real implementation, this would call an AI image generation API
232
- st.info("AI Image generation would be implemented here with your preferred provider (DALL-E, Midjourney, etc.)")
233
- st.info("For now, we'll use a placeholder to demonstrate the functionality")
234
-
235
- if "visuals" not in slide:
236
- slide["visuals"] = {}
237
- slide["visuals"]["ai_image"] = {
238
- "prompt": image_prompt,
239
- "style": image_style
240
- }
241
- st.success("AI image generation request saved!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
 
243
- with col2:
244
- st.subheader("Content Editor")
245
-
246
- # Title editing with AI assistance
247
- st.write("##### πŸ“ Slide Title")
248
- col1, col2 = st.columns([3, 1])
249
- with col1:
250
- slide_title = st.text_input(f"Title ###{slide_index}", value=slide.get('title', ''))
251
- with col2:
252
- if st.button("πŸͺ„ Improve", key=f"improve_title_{slide_index}"):
253
- try:
254
- with st.spinner("🐊 SlideGator.AI is enhancing your title..."):
255
- from agents import enhance_slide_component
256
- updated_slide, result = enhance_slide_component(slide, "title", "Make it concise and impactful")
257
- if "error" not in result:
258
- st.session_state.slides_content[slide_index] = updated_slide
259
- st.success("Title improved!")
260
- st.rerun()
261
- else:
262
- st.error(f"Error: {result.get('error')}")
263
- except Exception as e:
264
- st.error(f"Error improving title: {str(e)}")
265
 
266
- # Content editing
267
- st.write("##### πŸ“„ Content")
268
- if isinstance(slide.get('content', []), list):
269
- content_text = "\n".join(slide.get('content', []))
270
- else:
271
- content_text = slide.get('content', '')
272
 
273
- col1, col2 = st.columns([3, 1])
274
- with col1:
275
- content = st.text_area(f"Content ###{slide_index}", value=content_text, height=150)
276
- with col2:
277
- st.write("")
278
- st.write("")
279
- if st.button("πŸͺ„ Improve", key=f"improve_content_{slide_index}"):
280
- try:
281
- with st.spinner("🐊 SlideGator.AI is enhancing your content..."):
282
- from agents import enhance_slide_component
283
- slide["content"] = content.split("\n") if "\n" in content else [content]
284
- updated_slide, result = enhance_slide_component(slide, "content", "Make it clear and concise")
285
- if "error" not in result:
286
- st.session_state.slides_content[slide_index] = updated_slide
287
- st.success("Content improved!")
 
 
 
 
 
 
 
288
  st.rerun()
289
  else:
290
- st.error(f"Error: {result.get('error')}")
291
- except Exception as e:
292
- st.error(f"Error improving content: {str(e)}")
293
-
294
- # Visual elements with AI assistance
295
- st.write("##### πŸ–ΌοΈ Visual Suggestions")
296
- if isinstance(slide.get('visual_elements', []), list):
297
- visuals_text = "\n".join(slide.get('visual_elements', []))
298
- else:
299
- visuals_text = slide.get('visual_elements', '')
300
 
301
- col1, col2 = st.columns([3, 1])
302
- with col1:
303
- visuals = st.text_area(f"Visual Elements ###{slide_index}", value=visuals_text, height=100)
304
- with col2:
305
- st.write("")
306
- if st.button("🎨 Suggest", key=f"suggest_visuals_{slide_index}"):
307
- try:
308
- with st.spinner("🐊 SlideGator.AI is suggesting visuals..."):
309
- from agents import enhance_slide_component
310
- slide["visual_elements"] = visuals.split("\n") if "\n" in visuals else [visuals]
311
- updated_slide, result = enhance_slide_component(slide, "visuals", "Be specific and creative")
312
- if "error" not in result:
313
- st.session_state.slides_content[slide_index] = updated_slide
314
- st.success("Visual suggestions added!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
315
  st.rerun()
316
- else:
317
- st.error(f"Error: {result.get('error')}")
318
- except Exception as e:
319
- st.error(f"Error suggesting visuals: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
 
321
- # Presenter notes
322
- st.write("##### 🎀 Presenter Notes")
323
- notes = st.text_area(f"Notes ###{slide_index}", value=slide.get('notes', ''), height=100)
324
- if st.button("πŸͺ„ Generate Notes", key=f"generate_notes_{slide_index}"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
325
  try:
326
- with st.spinner("🐊 SlideGator.AI is writing speaker notes..."):
327
- from agents import enhance_slide_component
328
- updated_slide, result = enhance_slide_component(slide, "notes")
329
- if "error" not in result:
330
- st.session_state.slides_content[slide_index] = updated_slide
331
- st.success("Speaker notes generated!")
332
- st.rerun()
333
- else:
334
- st.error(f"Error: {result.get('error')}")
 
 
 
 
 
 
335
  except Exception as e:
336
- st.error(f"Error generating notes: {str(e)}")
337
-
338
- # LLM model selection for this specific slide
339
- st.write("##### 🧠 AI Enhancement Settings")
340
- with st.expander("Advanced AI Settings"):
341
- llm_options = {
342
- "claude-3-sonnet": "Claude 3 Sonnet (Default)",
343
- "claude-3-haiku": "Claude 3 Haiku (Faster)",
344
- "claude-3-opus": "Claude 3 Opus (Highest Quality)",
345
- "gpt-4": "GPT-4 (If OpenAI key provided)",
346
- "gpt-3.5-turbo": "GPT-3.5 Turbo (Faster)",
347
- "deepseek-chat": "DeepSeek Chat"
348
- }
349
-
350
- selected_llm = st.selectbox(
351
- "AI Model for this slide",
352
- options=list(llm_options.keys()),
353
- format_func=lambda x: llm_options[x],
354
- key=f"llm_{slide_index}"
355
- )
356
-
357
- if "ai_settings" not in slide:
358
- slide["ai_settings"] = {}
359
 
360
- slide["ai_settings"]["model"] = selected_llm
 
 
361
 
362
- # Web search integration
363
- st.write("##### πŸ” Web Search Integration")
364
- enable_search = st.checkbox(
365
- "Enable web search for up-to-date content",
366
- value=slide.get("ai_settings", {}).get("web_search", False),
367
- key=f"search_{slide_index}"
368
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
369
 
370
- if enable_search:
371
- search_query = st.text_input(
372
- "Search query",
373
- value=slide.get("ai_settings", {}).get("search_query", f"latest {slide.get('title', '')}"),
374
- key=f"search_query_{slide_index}"
375
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376
 
377
- slide["ai_settings"]["web_search"] = enable_search
378
- slide["ai_settings"]["search_query"] = search_query
 
 
 
 
 
379
 
380
- if st.button("Fetch Latest Content", key=f"fetch_{slide_index}"):
381
- st.info("This would fetch the latest information from the web to update your slide.")
382
- # In a real implementation, this would call a search API
383
-
384
- # Update slide with edits
385
- slide["title"] = slide_title
386
- if "\n" in content:
387
- slide["content"] = content.split("\n")
388
- else:
389
- slide["content"] = [content] if content else []
390
- if "\n" in visuals:
391
- slide["visual_elements"] = visuals.split("\n")
392
- else:
393
- slide["visual_elements"] = [visuals] if visuals else []
394
- slide["notes"] = notes
395
-
396
- return slide
 
1
  import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
 
2
  import base64
3
+ from io import BytesIO
4
+ import json
5
+ import time
6
+ import os
7
+ from utils import create_slide_preview, TEMPLATES, generate_slide_content
8
+ from visual_elements import search_stock_images, download_image, analyze_slide_for_visuals
9
+ from slide_editor_enhancements import render_enhanced_slide_editor
10
+
11
+ # Check for API keys
12
+ api_key = os.getenv("ANTHROPIC_API_KEY")
13
+ openai_key = os.getenv("OPENAI_API_KEY")
14
+ deepseek_key = os.getenv("DEEPSEEK_API_KEY")
15
+ perplexity_key = os.getenv("PERPLEXITY_API_KEY")
16
+ pexels_key = os.getenv("PEXELS_API_KEY")
17
+
18
+ def nav_button(label, stage, icon=None, primary=False):
19
+ """Create a navigation button with improved styling"""
20
+ button_style = "primary" if primary else "secondary"
21
+ icon_text = f"{icon} " if icon else ""
22
+
23
+ if st.button(f"{icon_text}{label}", key=f"nav_{stage}", type=button_style, use_container_width=True):
24
+ st.session_state.current_stage = stage
25
+ st.rerun()
26
+
27
+ def display_navigation_bar():
28
+ """Display navigation bar at the top of the page"""
29
+ stages = [
30
+ {"name": "ideation", "label": "Ideation", "icon": "πŸ’‘"},
31
+ {"name": "storyboard", "label": "Storyboard", "icon": "πŸ“‹"},
32
+ {"name": "template", "label": "Template", "icon": "🎨"},
33
+ {"name": "slides", "label": "Edit Slides", "icon": "πŸ–ΌοΈ"},
34
+ {"name": "export", "label": "Export", "icon": "πŸ“€"}
35
+ ]
36
+
37
+ cols = st.columns(len(stages))
38
+
39
+ for i, stage in enumerate(stages):
40
+ with cols[i]:
41
+ is_current = st.session_state.current_stage == stage["name"]
42
+
43
+ # Create a clickable button with custom CSS
44
+ if st.button(
45
+ f"{stage['icon']} {stage['label']}",
46
+ key=f"nav_top_{stage['name']}",
47
+ disabled=is_current,
48
+ use_container_width=True,
49
+ type="primary" if is_current else "secondary"
50
+ ):
51
+ # If we've already completed previous stages, allow direct navigation
52
+ if stage["name"] in ["ideation"] or (
53
+ stage["name"] == "storyboard" and st.session_state.storyboard
54
+ ) or (
55
+ stage["name"] == "template" and st.session_state.storyboard
56
+ ) or (
57
+ stage["name"] == "slides" and st.session_state.slides_content
58
+ ) or (
59
+ stage["name"] == "export" and st.session_state.slides_content
60
+ ):
61
+ st.session_state.current_stage = stage["name"]
62
+ st.rerun()
63
+ else:
64
+ st.warning(f"Please complete the previous stages before going to {stage['label']}")
65
+
66
+ # Progress bar
67
+ stages_order = ["ideation", "storyboard", "template", "slides", "export"]
68
+ current_idx = stages_order.index(st.session_state.current_stage)
69
+ progress = current_idx / (len(stages_order) - 1)
70
+ st.progress(progress)
71
+
72
+ def render_ai_settings():
73
+ """Render AI settings sidebar section"""
74
+ st.sidebar.write("## 🧠 AI Settings")
75
+
76
+ # Get AI manager
77
+ try:
78
+ from multi_llm_provider import get_ai_manager
79
+ ai_manager = get_ai_manager()
80
+ available_models = ai_manager.get_available_models()
81
+ except ImportError:
82
+ available_models = {
83
+ "claude-3-sonnet-20250219": "Claude 3 Sonnet",
84
+ "claude-3-haiku-20250319": "Claude 3 Haiku",
85
+ "claude-3-opus-20250229": "Claude 3 Opus"
86
+ }
87
+
88
+ if not available_models:
89
+ st.sidebar.warning("No AI providers configured. Add API keys in secrets.")
90
+ default_model = "claude-3-sonnet-20250219"
91
+ else:
92
+ default_model = list(available_models.keys())[0] if available_models else "claude-3-sonnet-20250219"
93
+
94
+ # Select default model for the whole presentation
95
+ selected_model = st.sidebar.selectbox(
96
+ "Default AI Model",
97
+ options=list(available_models.keys()),
98
+ format_func=lambda x: available_models.get(x, x),
99
+ index=0 if default_model in available_models else 0
100
+ )
101
+
102
+ st.session_state.default_model = selected_model
103
+
104
+ # Temperature setting
105
+ temperature = st.sidebar.slider(
106
+ "AI Creativity",
107
+ min_value=0.0,
108
+ max_value=1.0,
109
+ value=0.7,
110
+ step=0.1,
111
+ help="Higher values make output more creative but less predictable"
112
+ )
113
+
114
+ st.session_state.ai_temperature = temperature
115
+
116
+ # Web search integration
117
+ enable_search = st.sidebar.checkbox(
118
+ "Enable Web Search",
119
+ value=st.session_state.get("enable_web_search", False),
120
+ help="Use Perplexity to search for up-to-date information",
121
+ disabled=not perplexity_key
122
+ )
123
+
124
+ st.session_state.enable_web_search = enable_search
125
+
126
+ if enable_search and not perplexity_key:
127
+ st.sidebar.warning("Perplexity API key required for web search")
128
+
129
+ # Stock image settings
130
+ st.sidebar.write("## πŸ–ΌοΈ Image Settings")
131
+
132
+ if not pexels_key:
133
+ st.sidebar.warning("Pexels API key required for better stock images")
134
 
135
+ def render_ideation_stage():
136
+ """Render the ideation stage UI"""
137
+ display_navigation_bar()
138
+
139
+ # Add AI settings sidebar
140
+ render_ai_settings()
141
 
142
+ st.header("πŸ’‘ Step 1: Presentation Ideation")
 
 
143
 
144
+ st.write("Let's start by defining the purpose and scope of your presentation.")
145
+
146
+ col1, col2 = st.columns(2)
147
 
148
  with col1:
149
+ st.session_state.presentation_title = st.text_input(
150
+ "Presentation Title",
151
+ value=st.session_state.presentation_title
152
+ )
 
 
 
 
 
 
 
153
 
154
+ st.session_state.presentation_purpose = st.text_area(
155
+ "What's the purpose of this presentation?",
156
+ value=st.session_state.presentation_purpose,
157
+ help="E.g., Inform, persuade, pitch a product, update stakeholders, etc."
158
+ )
159
+
160
+ with col2:
161
+ st.session_state.target_audience = st.text_area(
162
+ "Who is your target audience?",
163
+ value=st.session_state.target_audience,
164
+ help="E.g., Executives, customers, technical team, general public, etc."
165
+ )
166
 
167
+ # Add example suggestions
168
+ st.write("πŸ“š **Example presentations:**")
169
+ examples = [
170
+ "Quarterly Business Review",
171
+ "Product Launch Presentation",
172
+ "Team Project Update",
173
+ "Investor Pitch Deck"
174
+ ]
175
+
176
+ # Create two columns for examples
177
+ ex_col1, ex_col2 = st.columns(2)
178
+
179
+ for i, example in enumerate(examples):
180
+ with ex_col1 if i % 2 == 0 else ex_col2:
181
+ if st.button(f"πŸ“„ {example}", key=f"example_{example}"):
182
+ st.session_state.presentation_title = example
183
+
184
+ # Set appropriate purpose and audience based on example
185
+ if "Quarterly" in example:
186
+ st.session_state.presentation_purpose = "Review business performance for the past quarter and outline goals for the next quarter"
187
+ st.session_state.target_audience = "Executives and department heads"
188
+ elif "Product" in example:
189
+ st.session_state.presentation_purpose = "Introduce a new product to customers and highlight its key features and benefits"
190
+ st.session_state.target_audience = "Potential customers and sales team"
191
+ elif "Project" in example:
192
+ st.session_state.presentation_purpose = "Update team members on project progress, achievements, and next steps"
193
+ st.session_state.target_audience = "Project stakeholders and team members"
194
+ elif "Investor" in example:
195
+ st.session_state.presentation_purpose = "Pitch our business to potential investors to secure funding"
196
+ st.session_state.target_audience = "Venture capitalists and angel investors"
197
+ st.rerun()
198
+
199
+ # AI model selection for storyboard generation
200
+ st.write("### 🧠 AI Engine Selection")
201
+
202
+ # Get available models
203
+ try:
204
+ from multi_llm_provider import get_ai_manager
205
+ ai_manager = get_ai_manager()
206
+ available_models = ai_manager.get_available_models()
207
+ except (ImportError, Exception):
208
+ available_models = {
209
+ "claude-3-sonnet-20250219": "Claude 3 Sonnet",
210
+ "claude-3-haiku-20250319": "Claude 3 Haiku",
211
+ "claude-3-opus-20250229": "Claude 3 Opus"
212
+ }
213
+
214
+ cols = st.columns([2, 1])
215
+ with cols[0]:
216
+ selected_model = st.selectbox(
217
+ "AI Model for Storyboard Generation",
218
+ options=list(available_models.keys()),
219
+ format_func=lambda x: available_models.get(x, x),
220
+ index=0,
221
+ key="storyboard_model"
222
+ )
223
+
224
+ with cols[1]:
225
+ web_research = st.checkbox(
226
+ "Include Web Research",
227
+ value=st.session_state.get("enable_web_search", False),
228
+ disabled=not perplexity_key,
229
+ help="Search the web for the latest information related to your presentation topic"
230
+ )
231
+
232
+ # Generate button with loading indicator
233
+ st.markdown("---")
234
+ if st.button("🐊 Generate Storyboard", type="primary", use_container_width=True):
235
+ if not st.session_state.presentation_title or not st.session_state.presentation_purpose:
236
+ st.warning("Please provide at least a title and purpose to proceed.")
237
+ else:
238
+ with st.spinner("🧠 SlideGator.AI is chomping on your presentation ideas..."):
239
+ from utils import generate_storyboard
240
 
241
+ # Generate storyboard
242
+ storyboard = generate_storyboard(
243
+ st.session_state.presentation_title,
244
+ st.session_state.presentation_purpose,
245
+ st.session_state.target_audience,
246
+ model=selected_model
247
  )
248
 
249
+ if storyboard:
250
+ st.session_state.storyboard = storyboard
251
+ st.session_state.current_stage = "storyboard"
252
+ st.success("Storyboard created successfully!")
253
+ st.rerun()
254
+
255
+ def render_storyboard_stage():
256
+ """Render the storyboard review stage UI"""
257
+ display_navigation_bar()
258
+ st.header("πŸ“‹ Step 2: Review and Edit Storyboard")
259
+
260
+ st.write(f"Storyboard for: **{st.session_state.presentation_title}**")
261
+ st.write("Expand each slide to edit its content or add notes.")
262
+
263
+ # Display storyboard for review with edit options
264
+ edited_storyboard = []
265
+
266
+ # Add controls for storyboard operations
267
+ col1, col2, col3 = st.columns([2, 1, 1])
268
+ with col1:
269
+ # Get available models for regeneration
270
+ try:
271
+ from multi_llm_provider import get_ai_manager
272
+ ai_manager = get_ai_manager()
273
+ available_models = ai_manager.get_available_models()
274
+ except (ImportError, Exception):
275
+ available_models = {
276
+ "claude-3-sonnet-20250219": "Claude 3 Sonnet",
277
+ "claude-3-haiku-20250319": "Claude 3 Haiku",
278
+ "claude-3-opus-20250229": "Claude 3 Opus"
279
+ }
280
 
281
+ selected_model = st.selectbox(
282
+ "AI Model",
283
+ options=list(available_models.keys()),
284
+ format_func=lambda x: available_models.get(x, x),
285
+ index=0,
286
+ key="storyboard_regen_model"
287
+ )
288
+
289
+ with col2:
290
+ if st.button("πŸ”„ Regenerate All", help="Create a new storyboard with current title/purpose"):
291
+ from utils import generate_storyboard
292
+ with st.spinner("🐊 SlideGator.AI is rethinking your presentation structure..."):
293
+ storyboard = generate_storyboard(
294
+ st.session_state.presentation_title,
295
+ st.session_state.presentation_purpose,
296
+ st.session_state.target_audience,
297
+ model=selected_model
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
  )
299
+ if storyboard:
300
+ st.session_state.storyboard = storyboard
301
+ st.success("Storyboard regenerated!")
302
+ st.rerun()
303
+
304
+ with col3:
305
+ if st.button("πŸ” Analyze Flow", help="Analyze the narrative flow of your presentation"):
306
+ with st.spinner("🧠 Analyzing presentation flow..."):
307
+ # Convert storyboard to text for analysis
308
+ storyboard_text = ""
309
+ for i, slide in enumerate(st.session_state.storyboard):
310
+ storyboard_text += f"Slide {i+1}: {slide.get('title', 'Untitled')}\n"
311
+ storyboard_text += f"Purpose: {slide.get('purpose', '')}\n"
312
+ key_points = slide.get('key_points', [])
313
+ if isinstance(key_points, list):
314
+ for point in key_points:
315
+ storyboard_text += f"- {point}\n"
316
+ else:
317
+ storyboard_text += f"- {key_points}\n"
318
+ storyboard_text += "\n"
319
 
320
+ # Analyze the flow using available AI
321
+ try:
322
+ from agents import enhance_slide_component
323
+ from utils import generate_storyboard
324
 
325
+ # Just display a simple analysis for now
326
+ analysis = "Flow Analysis: Your presentation has a logical structure with a clear beginning, middle, and end. Consider adding more transition slides between major sections to improve the narrative flow."
327
+ st.info("### Presentation Flow Analysis")
328
+ st.write(analysis)
329
+ except Exception as e:
330
+ st.error(f"Error analyzing presentation flow: {str(e)}")
331
+
332
+ # Add slide button with options
333
+ col1, col2 = st.columns([3, 1])
334
+ with col1:
335
+ slide_position = st.radio(
336
+ "Add new slide:",
337
+ ["At End", "After Current", "Before Current"],
338
+ horizontal=True
339
+ )
340
+
341
+ with col2:
342
+ if st.button("βž• Add New Slide", use_container_width=True):
343
+ # Create a new slide
344
+ new_slide = {
345
+ 'title': 'New Slide',
346
+ 'purpose': 'Additional content',
347
+ 'key_points': ['Add your content here'],
348
+ 'visual_elements': ['Suggested visuals will appear here']
349
+ }
350
 
351
+ # Insert at the selected position
352
+ if slide_position == "At End":
353
+ st.session_state.storyboard.append(new_slide)
354
+ elif slide_position == "After Current":
355
+ # Get current slide index from session state or default to last
356
+ current_idx = st.session_state.get("current_storyboard_slide", len(st.session_state.storyboard) - 1)
357
+ st.session_state.storyboard.insert(current_idx + 1, new_slide)
358
+ else: # Before Current
359
+ current_idx = st.session_state.get("current_storyboard_slide", 0)
360
+ st.session_state.storyboard.insert(max(0, current_idx), new_slide)
361
 
362
+ st.rerun()
363
+
364
+ # Add slide templates section
365
+ with st.expander("πŸ“‘ Slide Templates"):
366
+ st.write("Add pre-designed slide templates to your presentation:")
367
+
368
+ template_cols = st.columns(3)
369
+
370
+ slide_templates = [
371
+ {"name": "Section Divider", "icon": "🏷️", "title": "Section Title",
372
+ "purpose": "Introduce a new section",
373
+ "key_points": ["A clean slide to mark a new section of your presentation"]},
374
 
375
+ {"name": "Comparison", "icon": "βš–οΈ", "title": "Comparison",
376
+ "purpose": "Compare two options or approaches",
377
+ "key_points": ["Option A benefits", "Option A challenges", "Option B benefits", "Option B challenges"],
378
+ "visual_elements": ["Two-column layout", "Comparison table or chart"]},
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
 
380
+ {"name": "Quote", "icon": "πŸ’¬", "title": "Key Quote",
381
+ "purpose": "Highlight an important quote",
382
+ "key_points": ["The quote goes here", "- Attribution"],
383
+ "visual_elements": ["Quotation marks graphic", "Simple background"]},
 
 
 
 
384
 
385
+ {"name": "Data Visualization", "icon": "πŸ“Š", "title": "Key Metrics",
386
+ "purpose": "Present important data",
387
+ "key_points": ["Metric 1 and its significance", "Metric 2 and its significance", "Trends and patterns"],
388
+ "visual_elements": ["Chart or graph", "Data visualization"]},
 
389
 
390
+ {"name": "Call to Action", "icon": "🎯", "title": "Next Steps",
391
+ "purpose": "Define clear action items",
392
+ "key_points": ["Specific action item 1", "Specific action item 2", "Timeline and ownership"],
393
+ "visual_elements": ["Arrow or path graphic", "Timeline visualization"]},
394
+
395
+ {"name": "Team Slide", "icon": "πŸ‘₯", "title": "Our Team",
396
+ "purpose": "Introduce key team members",
397
+ "key_points": ["Team member 1 with role", "Team member 2 with role", "Team member 3 with role"],
398
+ "visual_elements": ["Team photos or icons", "Organizational chart"]}
399
+ ]
400
+
401
+ for i, template in enumerate(slide_templates):
402
+ with template_cols[i % 3]:
403
+ if st.button(f"{template['icon']} {template['name']}", key=f"template_{template['name']}"):
404
+ # Create a new slide based on the template
405
+ new_slide = {
406
+ 'title': template['title'],
407
+ 'purpose': template['purpose'],
408
+ 'key_points': template['key_points'],
409
+ 'visual_elements': template.get('visual_elements', [])
410
+ }
411
+
412
+ # Add to storyboard
413
+ st.session_state.storyboard.append(new_slide)
414
+ st.success(f"Added {template['name']} slide!")
415
+ st.rerun()
416
 
417
+ # Display storyboard slides
418
+ for i, slide in enumerate(st.session_state.storyboard):
419
+ # Set current slide in session state when expander is opened
420
+ is_expanded = i == 0 or st.session_state.get("current_storyboard_slide") == i
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
421
 
422
+ with st.expander(f"Slide {i+1}: {slide.get('title', 'Untitled')}", expanded=is_expanded):
423
+ if is_expanded:
424
+ st.session_state.current_storyboard_slide = i
 
 
 
425
 
426
+ # Main slide content
427
+ cols = st.columns([3, 1])
428
+ with cols[0]:
429
+ slide_title = st.text_input(f"Title ###{i}", value=slide.get('title', 'Untitled'))
430
+ with cols[1]:
431
+ # Slide reordering and deletion
432
+ cols2 = st.columns([1, 1, 1])
433
+ with cols2[0]:
434
+ if i > 0 and st.button("⬆️", key=f"up_{i}"):
435
+ st.session_state.storyboard[i], st.session_state.storyboard[i-1] = st.session_state.storyboard[i-1], st.session_state.storyboard[i]
436
+ st.session_state.current_storyboard_slide = i - 1
437
+ st.rerun()
438
+ with cols2[1]:
439
+ if i < len(st.session_state.storyboard) - 1 and st.button("⬇️", key=f"down_{i}"):
440
+ st.session_state.storyboard[i], st.session_state.storyboard[i+1] = st.session_state.storyboard[i+1], st.session_state.storyboard[i]
441
+ st.session_state.current_storyboard_slide = i + 1
442
+ st.rerun()
443
+ with cols2[2]:
444
+ if st.button("πŸ—‘οΈ", key=f"delete_{i}"):
445
+ if len(st.session_state.storyboard) > 1: # Prevent deleting the last slide
446
+ st.session_state.storyboard.pop(i)
447
+ st.session_state.current_storyboard_slide = min(i, len(st.session_state.storyboard) - 1)
448
  st.rerun()
449
  else:
450
+ st.error("Cannot delete the last slide")
 
 
 
 
 
 
 
 
 
451
 
452
+ slide_purpose = st.text_area(f"Purpose ###{i}", value=slide.get('purpose', ''))
453
+
454
+ # Handle key points (could be string or list)
455
+ if isinstance(slide.get('key_points', ""), list):
456
+ key_points_text = "\n".join(slide['key_points'])
457
+ else:
458
+ key_points_text = slide.get('key_points', "")
459
+
460
+ key_points = st.text_area(f"Key Points (one per line) ###{i}", value=key_points_text)
461
+
462
+ # Handle visual elements (could be string or list)
463
+ if isinstance(slide.get('visual_elements', ""), list):
464
+ visual_elements_text = "\n".join(slide['visual_elements'])
465
+ else:
466
+ visual_elements_text = slide.get('visual_elements', "")
467
+
468
+ visual_elements = st.text_area(f"Visual Elements (one per line) ###{i}", value=visual_elements_text)
469
+
470
+ # AI enhancement options
471
+ with st.expander("🧠 AI Enhancement"):
472
+ ai_cols = st.columns(2)
473
+
474
+ with ai_cols[0]:
475
+ # Get AI models for enhancement
476
+ enhancement_model = "claude-3-sonnet-20250219" # Default model
477
+
478
+ with ai_cols[1]:
479
+ enhancement_type = st.selectbox(
480
+ "Enhancement Type",
481
+ ["Content", "Title", "Visual Suggestions", "All"],
482
+ key=f"enhance_type_{i}"
483
+ )
484
+
485
+ if st.button("✨ Enhance with AI", key=f"enhance_slide_{i}"):
486
+ with st.spinner("🧠 Enhancing slide content..."):
487
+ # Update slide with current edits first
488
+ updated_slide = {
489
+ 'title': slide_title,
490
+ 'purpose': slide_purpose,
491
+ 'key_points': key_points.split("\n") if "\n" in key_points else [key_points] if key_points else [],
492
+ 'visual_elements': visual_elements.split("\n") if "\n" in visual_elements else [visual_elements] if visual_elements else []
493
+ }
494
+
495
+ try:
496
+ from agents import enhance_slide_component
497
+
498
+ # Apply enhancement based on selected type
499
+ if enhancement_type in ["Content", "All"]:
500
+ updated_slide, result = enhance_slide_component(updated_slide, "content")
501
+
502
+ if enhancement_type in ["Title", "All"]:
503
+ updated_slide, result = enhance_slide_component(updated_slide, "title")
504
+
505
+ if enhancement_type in ["Visual Suggestions", "All"]:
506
+ updated_slide, result = enhance_slide_component(updated_slide, "visuals")
507
+
508
+ # Update the storyboard with enhanced slide
509
+ st.session_state.storyboard[i] = updated_slide
510
+ st.success("Slide enhanced with AI!")
511
  st.rerun()
512
+ except Exception as e:
513
+ st.error(f"Error enhancing slide: {str(e)}")
514
+
515
+ # Update storyboard with edits
516
+ edited_slide = {
517
+ 'title': slide_title,
518
+ 'purpose': slide_purpose,
519
+ 'key_points': key_points.split("\n") if "\n" in key_points else [key_points] if key_points else [],
520
+ 'visual_elements': visual_elements.split("\n") if "\n" in visual_elements else [visual_elements] if visual_elements else []
521
+ }
522
+ edited_storyboard.append(edited_slide)
523
+
524
+ # Update the storyboard in session state
525
+ st.session_state.storyboard = edited_storyboard
526
+
527
+ st.markdown("---")
528
+ col1, col2 = st.columns(2)
529
+ with col1:
530
+ nav_button("Back to Ideation", "ideation", icon="⬅️")
531
+ with col2:
532
+ nav_button("Continue to Template Selection", "template", icon="➑️", primary=True)
533
+
534
+ def render_template_stage():
535
+ """Render the template selection stage UI with enhanced options"""
536
+ display_navigation_bar()
537
+
538
+ # Add AI settings sidebar
539
+ render_ai_settings()
540
+
541
+ st.header("🎨 Step 3: Select a Template")
542
+
543
+ st.write("Choose a visual style for your presentation:")
544
+
545
+ # Preview section
546
+ preview_col, options_col = st.columns([2, 1])
547
+
548
+ with options_col:
549
+ # Add template upload option
550
+ st.subheader("Custom Template")
551
+ st.write("Upload your own PowerPoint template:")
552
+ uploaded_template = st.file_uploader("Upload PPTX template", type=["pptx"])
553
+
554
+ if uploaded_template:
555
+ # Save the uploaded template
556
+ from utils import add_custom_template
557
+ success, message = add_custom_template(uploaded_template)
558
+ if success:
559
+ st.success(message)
560
+ st.session_state.selected_template = "custom"
561
+ # Store template file name for display
562
+ st.session_state.custom_template_name = uploaded_template.name
563
+ else:
564
+ st.error(message)
565
+
566
+ # Template color customization
567
+ st.subheader("Color Customization")
568
+
569
+ # Get current template
570
+ current_template = st.session_state.selected_template
571
+ template_info = TEMPLATES.get(current_template, TEMPLATES["professional"])
572
+ colors = template_info["colors"]
573
+
574
+ # Allow color customization
575
+ primary_color = st.color_picker(
576
+ "Primary Color",
577
+ value=colors.get("primary", "#0F52BA"),
578
+ key="primary_color"
579
+ )
580
+
581
+ accent_color = st.color_picker(
582
+ "Accent Color",
583
+ value=colors.get("accent", "#D4AF37"),
584
+ key="accent_color"
585
+ )
586
+
587
+ # Store customized colors
588
+ if "custom_colors" not in st.session_state:
589
+ st.session_state.custom_colors = {}
590
+
591
+ st.session_state.custom_colors[current_template] = {
592
+ "primary": primary_color,
593
+ "accent": accent_color
594
+ }
595
+
596
+ # Apply customized colors
597
+ if st.button("Apply Custom Colors"):
598
+ # Update the template colors
599
+ if current_template in TEMPLATES:
600
+ TEMPLATES[current_template]["colors"]["primary"] = primary_color
601
+ TEMPLATES[current_template]["colors"]["accent"] = accent_color
602
+ st.success("Custom colors applied!")
603
+
604
+ with preview_col:
605
+ # Template preview - show a sample slide with the selected template
606
+ st.subheader("Template Preview")
607
+
608
+ # Create a sample slide for preview
609
+ sample_slide = {
610
+ "title": "Sample Slide",
611
+ "content": [
612
+ "This is how your slides will look",
613
+ "With the selected template style",
614
+ "You can customize colors and fonts"
615
+ ],
616
+ "visual_elements": ["Chart showing data trends", "Icon representing growth"]
617
+ }
618
+
619
+ # Get current template
620
+ current_template = st.session_state.selected_template
621
+
622
+ # Show preview based on template
623
+ try:
624
+ from visual_elements import generate_html_preview_with_visuals
625
+ preview_html = generate_html_preview_with_visuals(sample_slide, current_template)
626
+ st.components.v1.html(preview_html, height=300)
627
+ except Exception as e:
628
+ st.error(f"Error generating preview: {str(e)}")
629
+ # Fall back to standard preview
630
+ preview_html = create_slide_preview(sample_slide, current_template)
631
+ st.components.v1.html(preview_html, height=300)
632
+
633
+ # Display current template name
634
+ if current_template == "custom" and "custom_template_name" in st.session_state:
635
+ st.info(f"Using custom template: {st.session_state.custom_template_name}")
636
+ else:
637
+ st.info(f"Currently using: {current_template.title()} template")
638
+
639
+ st.markdown("---")
640
+
641
+ # Create template cards in a grid
642
+ st.subheader("🐊 SlideGator Template Gallery")
643
+ st.write("Select from our professionally designed templates:")
644
+
645
+ # Use columns to create a grid
646
+ cols = st.columns(3)
647
+
648
+ # Add Professional template
649
+ with cols[0]:
650
+ st.subheader("Professional")
651
+ st.write("Clean, corporate style with blue and gray.")
652
+ st.markdown("""
653
+ <div style="border:1px solid #ddd; padding:10px; border-radius:5px; background-color:#f8f9fa;">
654
+ <div style="height:20px; background-color:#0F52BA;"></div>
655
+ <div style="padding:10px; text-align:center;">
656
+ <div style="font-weight:bold; margin-bottom:10px;">Professional Style</div>
657
+ <div style="height:5px; background-color:#D4AF37; width:50%; margin:auto;"></div>
658
+ <div style="font-size:0.8em; margin-top:10px;">Clear and business-focused</div>
659
+ </div>
660
+ </div>
661
+ """, unsafe_allow_html=True)
662
+ if st.button("🎯 Select Professional", key="select_prof"):
663
+ st.session_state.selected_template = "professional"
664
+ st.success("Selected: Professional template")
665
+ st.rerun()
666
+
667
+ # Add Creative template
668
+ with cols[1]:
669
+ st.subheader("Creative")
670
+ st.write("Vibrant design with modern elements.")
671
+ st.markdown("""
672
+ <div style="border:1px solid #ddd; padding:10px; border-radius:5px; background-color:#FFDE59;">
673
+ <div style="height:20px; background-color:#FF5757;"></div>
674
+ <div style="padding:10px; text-align:center;">
675
+ <div style="font-weight:bold; margin-bottom:10px;">Creative Style</div>
676
+ <div style="height:5px; background-color:#5CE1E6; width:50%; margin:auto;"></div>
677
+ <div style="font-size:0.8em; margin-top:10px;">Bold and engaging design</div>
678
+ </div>
679
+ </div>
680
+ """, unsafe_allow_html=True)
681
+ if st.button("🎨 Select Creative", key="select_creative"):
682
+ st.session_state.selected_template = "creative"
683
+ st.success("Selected: Creative template")
684
+ st.rerun()
685
+
686
+ # Add Minimalist template
687
+ with cols[2]:
688
+ st.subheader("Minimalist")
689
+ st.write("Simple design with lots of whitespace.")
690
+ st.markdown("""
691
+ <div style="border:1px solid #ddd; padding:10px; border-radius:5px; background-color:#FFFFFF;">
692
+ <div style="height:20px; background-color:#000000;"></div>
693
+ <div style="padding:10px; text-align:center;">
694
+ <div style="font-weight:bold; margin-bottom:10px;">Minimalist Style</div>
695
+ <div style="height:1px; background-color:#000000; width:50%; margin:auto;"></div>
696
+ <div style="font-size:0.8em; margin-top:10px;">Clean and elegant</div>
697
+ </div>
698
+ </div>
699
+ """, unsafe_allow_html=True)
700
+ if st.button("✨ Select Minimalist", key="select_min"):
701
+ st.session_state.selected_template = "minimalist"
702
+ st.success("Selected: Minimalist template")
703
+ st.rerun()
704
+
705
+ # Font selection
706
+ with st.expander("πŸ”€ Font Settings"):
707
+ st.write("Choose font styles for your presentation:")
708
+
709
+ font_options = [
710
+ "Arial, sans-serif",
711
+ "Helvetica, sans-serif",
712
+ "Calibri, sans-serif",
713
+ "Georgia, serif",
714
+ "Times New Roman, serif",
715
+ "Verdana, sans-serif",
716
+ "Tahoma, sans-serif"
717
+ ]
718
+
719
+ font_cols = st.columns(2)
720
+
721
+ with font_cols[0]:
722
+ title_font = st.selectbox(
723
+ "Title Font",
724
+ options=font_options,
725
+ index=font_options.index(TEMPLATES[current_template]["fonts"]["title"]) if TEMPLATES[current_template]["fonts"]["title"] in font_options else 0
726
+ )
727
+
728
+ with font_cols[1]:
729
+ body_font = st.selectbox(
730
+ "Body Font",
731
+ options=font_options,
732
+ index=font_options.index(TEMPLATES[current_template]["fonts"]["body"]) if TEMPLATES[current_template]["fonts"]["body"] in font_options else 0
733
+ )
734
+
735
+ if st.button("Apply Font Settings"):
736
+ # Update the template fonts
737
+ if current_template in TEMPLATES:
738
+ TEMPLATES[current_template]["fonts"]["title"] = title_font
739
+ TEMPLATES[current_template]["fonts"]["body"] = body_font
740
+ st.success("Font settings applied!")
741
+
742
+ st.markdown("---")
743
+ col1, col2 = st.columns(2)
744
+ with col1:
745
+ nav_button("Back to Storyboard", "storyboard", icon="⬅️")
746
+ with col2:
747
+ if st.button("🐊 Generate Slides ➑️", type="primary", use_container_width=True):
748
+ st.session_state.current_stage = "slides"
749
+ # Generate detailed content for each slide
750
+ with st.spinner("🐊 SlideGator.AI is crafting your slides..."):
751
+ slides_content = []
752
+ progress_placeholder = st.empty()
753
+
754
+ for i, slide in enumerate(st.session_state.storyboard):
755
+ # Calculate progress as a value between 0 and 1
756
+ progress = i / len(st.session_state.storyboard)
757
+ progress_placeholder.progress(progress)
758
+ progress_placeholder.text(f"Generating slide {i+1} of {len(st.session_state.storyboard)}: {slide.get('title', 'Untitled')}")
759
+
760
+ # Get model for slide generation
761
+ model = slide.get("ai_settings", {}).get("model", st.session_state.get("default_model", "claude-3-sonnet-20250219"))
762
+
763
+ slide_content = generate_slide_content(slide, st.session_state.selected_template, model=model)
764
+ slides_content.append(slide_content)
765
+
766
+ progress_placeholder.progress(1.0)
767
+ progress_placeholder.empty()
768
+ st.session_state.slides_content = slides_content
769
+ st.success("All slides generated!")
770
+ st.rerun()
771
+
772
+ def render_slides_stage():
773
+ """Render the enhanced slide editing stage UI"""
774
+ display_navigation_bar()
775
+
776
+ # Add AI settings sidebar
777
+ render_ai_settings()
778
+
779
+ st.header("πŸ–ΌοΈ Step 4: Review and Edit Slides")
780
+
781
+ if not st.session_state.slides_content:
782
+ st.warning("No slides content available. Please go back and generate slides.")
783
+ nav_button("Back to Template Selection", "template", icon="⬅️")
784
+ return
785
+
786
+ # Add slide navigation controls
787
+ col1, col2, col3 = st.columns([1, 2, 1])
788
+ with col1:
789
+ prev_slide = st.button("◀️ Previous Slide")
790
+ with col2:
791
+ current_slide = st.session_state.get("current_slide_index", 0)
792
+ slide_selector = st.select_slider(
793
+ "Navigate Slides",
794
+ options=list(range(len(st.session_state.slides_content))),
795
+ value=current_slide,
796
+ format_func=lambda x: f"Slide {x+1}: {st.session_state.slides_content[x].get('title', 'Untitled')}"
797
+ )
798
+ st.session_state.current_slide_index = slide_selector
799
+ with col3:
800
+ next_slide = st.button("Next Slide ▢️")
801
+
802
+ if prev_slide and st.session_state.current_slide_index > 0:
803
+ st.session_state.current_slide_index -= 1
804
+ st.rerun()
805
+
806
+ if next_slide and st.session_state.current_slide_index < len(st.session_state.slides_content) - 1:
807
+ st.session_state.current_slide_index += 1
808
+ st.rerun()
809
+
810
+ # Show slide editing interface for the current slide
811
+ current_slide_idx = st.session_state.current_slide_index
812
+ current_slide = st.session_state.slides_content[current_slide_idx]
813
+
814
+ # Add slide management controls
815
+ col1, col2, col3, col4 = st.columns([1, 1, 1, 1])
816
+ with col1:
817
+ if current_slide_idx > 0 and st.button("⬆️ Move Up"):
818
+ 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]
819
+ st.session_state.current_slide_index -= 1
820
+ st.rerun()
821
+
822
+ with col2:
823
+ if current_slide_idx < len(st.session_state.slides_content) - 1 and st.button("⬇️ Move Down"):
824
+ 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]
825
+ st.session_state.current_slide_index += 1
826
+ st.rerun()
827
+
828
+ with col3:
829
+ if st.button("βž• Add Slide"):
830
+ # Create a new slide based on the current one
831
+ new_slide = {
832
+ "title": "New Slide",
833
+ "content": ["Add your content here"],
834
+ "visual_elements": ["Add visual elements here"],
835
+ "notes": "Add presenter notes here"
836
+ }
837
+ # Insert after current slide
838
+ st.session_state.slides_content.insert(current_slide_idx + 1, new_slide)
839
+ st.session_state.current_slide_index += 1
840
+ st.rerun()
841
+
842
+ with col4:
843
+ if len(st.session_state.slides_content) > 1 and st.button("πŸ—‘οΈ Delete Slide"):
844
+ st.session_state.slides_content.pop(current_slide_idx)
845
+ if current_slide_idx >= len(st.session_state.slides_content):
846
+ st.session_state.current_slide_index = len(st.session_state.slides_content) - 1
847
+ st.rerun()
848
+
849
+ # Current template
850
+ template_name = st.session_state.selected_template
851
+
852
+ # Editor for current slide - use enhanced slide editor
853
+ st.write(f"### Editing Slide {current_slide_idx + 1}")
854
+ updated_slide = render_enhanced_slide_editor(current_slide_idx, current_slide, template_name)
855
+ st.session_state.slides_content[current_slide_idx] = updated_slide
856
+
857
+ # Navigation buttons at bottom
858
+ st.markdown("---")
859
+ col1, col2 = st.columns(2)
860
+ with col1:
861
+ nav_button("Back to Template Selection", "template", icon="⬅️")
862
+ with col2:
863
+ nav_button("Finalize Presentation", "export", icon="➑️", primary=True)
864
+
865
+ def render_export_stage():
866
+ """Render the enhanced export stage UI"""
867
+ display_navigation_bar()
868
+
869
+ # Add AI settings sidebar
870
+ render_ai_settings()
871
+
872
+ st.header("πŸ“€ Step 5: Export Presentation")
873
+
874
+ st.write("Your presentation is ready to export!")
875
+
876
+ # Add final quality check option
877
+ with st.expander("πŸ” Quality Check", expanded=True):
878
+ st.write("Run a final quality check on your presentation before exporting:")
879
 
880
+ quality_options = st.multiselect(
881
+ "Select checks to run:",
882
+ ["Spelling & Grammar", "Visual Balance", "Content Consistency", "Presentation Flow"],
883
+ default=["Spelling & Grammar", "Content Consistency"]
884
+ )
885
+
886
+ if st.button("Run Quality Check", use_container_width=True):
887
+ with st.spinner("πŸ” Running quality checks..."):
888
+ # Show progress
889
+ progress = st.progress(0)
890
+
891
+ for i, check in enumerate(quality_options):
892
+ # Update progress
893
+ progress.progress((i + 0.5) / len(quality_options))
894
+
895
+ if check == "Spelling & Grammar":
896
+ with st.spinner("Checking spelling and grammar..."):
897
+ # In a real implementation, this would check all slide content
898
+ time.sleep(1) # Simulate processing
899
+ st.success("βœ… Spelling and grammar check complete. No major issues found.")
900
+
901
+ elif check == "Visual Balance":
902
+ with st.spinner("Analyzing visual balance..."):
903
+ # Simulate visual analysis
904
+ time.sleep(1)
905
+ st.info("ℹ️ Visual recommendation: Consider adding more images to slides 3 and 5.")
906
+
907
+ elif check == "Content Consistency":
908
+ with st.spinner("Checking content consistency..."):
909
+ # In a real implementation, would use AI to check consistency
910
+ time.sleep(1)
911
+ st.success("βœ… Content is consistent throughout the presentation.")
912
+
913
+ elif check == "Presentation Flow":
914
+ with st.spinner("Analyzing presentation flow..."):
915
+ # Simulate flow analysis
916
+ time.sleep(1)
917
+ st.info("ℹ️ Flow recommendation: Consider adding a transition slide between slides 2 and 3.")
918
+
919
+ # Complete progress
920
+ progress.progress(1.0)
921
+
922
+ # Export options
923
+ export_tabs = st.tabs(["PowerPoint", "PDF", "Images", "Advanced"])
924
+
925
+ with export_tabs[0]:
926
+ # PowerPoint export
927
+ st.subheader("PowerPoint Export")
928
+
929
+ # Create the PowerPoint file
930
+ with st.spinner("🐊 SlideGator.AI is finalizing your presentation..."):
931
  try:
932
+ try:
933
+ # Try to use enhanced PowerPoint creation if available
934
+ from enhanced_pptx import enhanced_create_ppt
935
+ ppt_buffer = enhanced_create_ppt(
936
+ st.session_state.slides_content,
937
+ st.session_state.selected_template
938
+ )
939
+ except ImportError:
940
+ # Fall back to standard PowerPoint creation
941
+ from utils import create_ppt
942
+ ppt_buffer = create_ppt(
943
+ st.session_state.slides_content,
944
+ st.session_state.selected_template
945
+ )
946
+ creation_success = True
947
  except Exception as e:
948
+ st.error(f"Error creating PowerPoint file: {str(e)}")
949
+ import traceback
950
+ st.error(traceback.format_exc())
951
+ creation_success = False
952
+
953
+ if creation_success:
954
+ # Provide download link
955
+ filename = f"{st.session_state.presentation_title.replace(' ', '_')}.pptx"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
956
 
957
+ # Get download link HTML
958
+ b64 = base64.b64encode(ppt_buffer.read()).decode()
959
+ href = f'<a href="data:application/vnd.openxmlformats-officedocument.presentationml.presentation;base64,{b64}" download="{filename}" class="download-btn">🐊 Download PowerPoint Presentation</a>'
960
 
961
+ # Add some styling to the download button
962
+ st.markdown("""
963
+ <style>
964
+ .download-btn {
965
+ display: inline-block;
966
+ padding: 12px 20px;
967
+ background-color: #4CAF50;
968
+ color: white;
969
+ text-decoration: none;
970
+ border-radius: 4px;
971
+ font-weight: bold;
972
+ text-align: center;
973
+ transition: background-color 0.3s;
974
+ font-size: 1.2em;
975
+ }
976
+ .download-btn:hover {
977
+ background-color: #45a049;
978
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
979
+ }
980
+ </style>
981
+ """, unsafe_allow_html=True)
982
 
983
+ # Display download link
984
+ st.markdown(f"<div style='text-align: center; margin: 30px 0;'>{href}</div>", unsafe_allow_html=True)
985
+
986
+ with export_tabs[1]:
987
+ # PDF export
988
+ st.subheader("PDF Export")
989
+ st.info("PDF export feature is coming soon. For now, please use the PowerPoint export and save as PDF.")
990
+
991
+ with export_tabs[2]:
992
+ # Image export
993
+ st.subheader("Image Export")
994
+ st.info("Image export feature is coming soon. This will allow you to export each slide as a PNG or JPG.")
995
+
996
+ with export_tabs[3]:
997
+ # Advanced export options
998
+ st.subheader("Advanced Export Options")
999
+
1000
+ # Customization before export
1001
+ st.write("Customize final export settings:")
1002
+
1003
+ optimize_file = st.checkbox("Optimize file size", value=True)
1004
+ include_notes = st.checkbox("Include presenter notes", value=True)
1005
+ protect_file = st.checkbox("Add password protection", value=False)
1006
+
1007
+ if protect_file:
1008
+ password = st.text_input("Set password (optional)", type="password")
1009
+
1010
+ st.info("Advanced export features are coming soon. These settings are for demonstration purposes only.")
1011
+
1012
+ # Display congratulations message
1013
+ st.markdown("""
1014
+ <div style="text-align:center; padding: 20px; margin: 20px 0; border-radius: 5px; background-color: #f8f9fa;">
1015
+ <div style="font-size: 2rem; margin-bottom: 10px;">πŸŽ‰ Congratulations! πŸŽ‰</div>
1016
+ <div style="font-size: 1.2rem; margin-bottom: 15px;">Your presentation is ready!</div>
1017
+ <div style="font-size: 1rem; color: #666;">SlideGator.AI has snapped up the perfect presentation for you.</div>
1018
+ </div>
1019
+ """, unsafe_allow_html=True)
1020
+
1021
+ # Preview of slides
1022
+ st.subheader("πŸ“‹ Presentation Preview")
1023
+
1024
+ # Display a sample of slides
1025
+ preview_cols = st.columns(3)
1026
+ for i, slide in enumerate(st.session_state.slides_content[:3]): # Just show first 3 slides
1027
+ with preview_cols[i % 3]:
1028
+ try:
1029
+ # Use enhanced preview with visuals if available
1030
+ try:
1031
+ from visual_elements import generate_html_preview_with_visuals
1032
+ preview_html = generate_html_preview_with_visuals(slide, st.session_state.selected_template)
1033
+ except ImportError:
1034
+ preview_html = create_slide_preview(slide, st.session_state.selected_template)
1035
+ st.components.v1.html(preview_html, height=200)
1036
+ st.write(f"**Slide {i+1}:** {slide.get('title', 'Untitled')}")
1037
+ except Exception as e:
1038
+ st.error(f"Error previewing slide {i+1}")
1039
+
1040
+ # Show "View All" option
1041
+ if len(st.session_state.slides_content) > 3:
1042
+ st.write("...")
1043
+ show_all = st.checkbox("Show all slides", value=False)
1044
+
1045
+ if show_all:
1046
+ st.write("### All Slides")
1047
+ for i, slide in enumerate(st.session_state.slides_content):
1048
+ with st.expander(f"Slide {i+1}: {slide.get('title', 'Untitled')}"):
1049
+ try:
1050
+ # Try enhanced preview
1051
+ try:
1052
+ from visual_elements import generate_html_preview_with_visuals
1053
+ preview_html = generate_html_preview_with_visuals(slide, st.session_state.selected_template)
1054
+ except ImportError:
1055
+ preview_html = create_slide_preview(slide, st.session_state.selected_template)
1056
+ st.components.v1.html(preview_html, height=300)
1057
+ except Exception as e:
1058
+ st.error(f"Error previewing slide {i+1}")
1059
+
1060
+ # Navigation buttons
1061
+ st.markdown("---")
1062
+ col1, col2 = st.columns(2)
1063
+ with col1:
1064
+ nav_button("Back to Edit Slides", "slides", icon="⬅️")
1065
+ with col2:
1066
+ if st.button("🐊 Start New Presentation", type="primary", use_container_width=True):
1067
+ # Ask for confirmation
1068
+ if st.checkbox("βœ… Confirm starting a new presentation? All current work will be lost.", value=False):
1069
+ # Reset session state
1070
+ for key in list(st.session_state.keys()):
1071
+ if key not in ["session_id", "ai_manager"]:
1072
+ del st.session_state[key]
1073
 
1074
+ st.session_state.current_stage = "ideation"
1075
+ st.session_state.presentation_title = ""
1076
+ st.session_state.presentation_purpose = ""
1077
+ st.session_state.target_audience = ""
1078
+ st.session_state.storyboard = []
1079
+ st.session_state.selected_template = "professional"
1080
+ st.session_state.slides_content = []
1081
 
1082
+ st.success("Starting new presentation...")
1083
+ st.rerun()
1084
+