Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -15,6 +15,7 @@ st.set_page_config(page_title="PPTX Smart Enhancer", layout="wide")
|
|
| 15 |
# Configuration
|
| 16 |
GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
|
| 17 |
MODEL_NAME = "gemma2-9b-it"
|
|
|
|
| 18 |
# Style Options
|
| 19 |
FONT_CHOICES = {
|
| 20 |
"Arial": "Arial",
|
|
@@ -41,19 +42,21 @@ COLOR_PALETTES = {
|
|
| 41 |
}
|
| 42 |
|
| 43 |
def get_custom_colors():
|
| 44 |
-
"""Get custom colors from user input"""
|
| 45 |
-
col1, col2, col3 = st.columns(
|
| 46 |
with col1:
|
| 47 |
primary = st.color_picker("Primary Color", "#2B579A")
|
| 48 |
with col2:
|
| 49 |
secondary = st.color_picker("Secondary Color", "#5B9BD5")
|
| 50 |
with col3:
|
| 51 |
text = st.color_picker("Text Color", "#000000")
|
|
|
|
|
|
|
| 52 |
return {
|
| 53 |
"primary": primary,
|
| 54 |
"secondary": secondary,
|
| 55 |
"text": text,
|
| 56 |
-
"background":
|
| 57 |
}
|
| 58 |
|
| 59 |
def get_ai_response(prompt: str) -> str:
|
|
@@ -87,10 +90,10 @@ def extract_slide_text(slide) -> str:
|
|
| 87 |
def split_formatted_runs(text):
|
| 88 |
"""Split text into formatted runs removing markdown."""
|
| 89 |
formatted_segments = []
|
| 90 |
-
# Split bold first
|
| 91 |
bold_parts = re.split(r'(\*\*)', text)
|
| 92 |
bold = False
|
| 93 |
current_text = []
|
|
|
|
| 94 |
for part in bold_parts:
|
| 95 |
if part == '**':
|
| 96 |
if current_text:
|
|
@@ -98,25 +101,19 @@ def split_formatted_runs(text):
|
|
| 98 |
current_text = []
|
| 99 |
bold = not bold
|
| 100 |
else:
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
formatted_segments.append({'text': ''.join(current_text), 'bold': False, 'italic': italic})
|
| 115 |
-
current_text = []
|
| 116 |
-
else:
|
| 117 |
-
current_text.append(part)
|
| 118 |
-
if current_text:
|
| 119 |
-
formatted_segments.append({'text': ''.join(current_text), 'bold': bold, 'italic': False})
|
| 120 |
return formatted_segments
|
| 121 |
|
| 122 |
def apply_formatting(text_frame, content, design_settings):
|
|
@@ -125,10 +122,8 @@ def apply_formatting(text_frame, content, design_settings):
|
|
| 125 |
paragraphs = [p for p in content.split('\n') if p.strip()]
|
| 126 |
|
| 127 |
for para in paragraphs:
|
| 128 |
-
# Remove any markdown headers completely
|
| 129 |
clean_para = re.sub(r'^#+\s*', '', para).strip()
|
| 130 |
|
| 131 |
-
# Handle section headers (original markdown headers become bold titles)
|
| 132 |
if re.match(r'^(What is|Understanding|What\'s|Drive Theory|Incentive Theory)', clean_para, re.IGNORECASE):
|
| 133 |
p = text_frame.add_paragraph()
|
| 134 |
p.text = clean_para
|
|
@@ -137,7 +132,6 @@ def apply_formatting(text_frame, content, design_settings):
|
|
| 137 |
p.space_after = Pt(12)
|
| 138 |
continue
|
| 139 |
|
| 140 |
-
# Handle bullet points
|
| 141 |
if re.match(r'^[\•\-\*] ', clean_para):
|
| 142 |
p = text_frame.add_paragraph()
|
| 143 |
p.level = 0
|
|
@@ -151,7 +145,6 @@ def apply_formatting(text_frame, content, design_settings):
|
|
| 151 |
p.font.size = Pt(design_settings["body_size"])
|
| 152 |
continue
|
| 153 |
|
| 154 |
-
# Regular paragraph with clean formatting
|
| 155 |
p = text_frame.add_paragraph()
|
| 156 |
segments = split_formatted_runs(clean_para)
|
| 157 |
for seg in segments:
|
|
@@ -161,18 +154,16 @@ def apply_formatting(text_frame, content, design_settings):
|
|
| 161 |
run.font.italic = seg['italic']
|
| 162 |
p.font.size = Pt(design_settings["body_size"])
|
| 163 |
p.space_after = Pt(6)
|
| 164 |
-
|
| 165 |
def enhance_slide(slide, user_prompt, design_settings):
|
| 166 |
"""Enhance slide content with proper formatting."""
|
| 167 |
original_text = extract_slide_text(slide)
|
| 168 |
if not original_text.strip():
|
| 169 |
return
|
| 170 |
|
| 171 |
-
# Get enhanced content
|
| 172 |
prompt = f"Improve this slide content:\n{original_text}\n\nInstructions: {user_prompt}"
|
| 173 |
enhanced_content = get_ai_response(prompt)
|
| 174 |
|
| 175 |
-
# Clear existing content (keep title if exists)
|
| 176 |
title_shape = getattr(slide.shapes, 'title', None)
|
| 177 |
shapes_to_remove = [s for s in slide.shapes if s != title_shape]
|
| 178 |
|
|
@@ -180,7 +171,6 @@ def enhance_slide(slide, user_prompt, design_settings):
|
|
| 180 |
sp = shape._element
|
| 181 |
sp.getparent().remove(sp)
|
| 182 |
|
| 183 |
-
# Add formatted content
|
| 184 |
left = Inches(1) if title_shape else Inches(0.5)
|
| 185 |
top = Inches(1.5) if title_shape else Inches(0.5)
|
| 186 |
textbox = slide.shapes.add_textbox(left, top, Inches(8), Inches(5))
|
|
@@ -192,7 +182,7 @@ def enhance_slide(slide, user_prompt, design_settings):
|
|
| 192 |
|
| 193 |
def apply_design(slide, design_settings):
|
| 194 |
"""Apply visual design to slide."""
|
| 195 |
-
colors =
|
| 196 |
|
| 197 |
for shape in slide.shapes:
|
| 198 |
if hasattr(shape, "text_frame"):
|
|
@@ -201,7 +191,6 @@ def apply_design(slide, design_settings):
|
|
| 201 |
run.font.name = design_settings["font"]
|
| 202 |
run.font.color.rgb = RGBColor.from_string(colors["text"][1:])
|
| 203 |
|
| 204 |
-
# Title styling
|
| 205 |
if shape == getattr(slide.shapes, 'title', None):
|
| 206 |
run.font.color.rgb = RGBColor.from_string(colors["primary"][1:])
|
| 207 |
run.font.size = Pt(design_settings["title_size"])
|
|
@@ -209,7 +198,7 @@ def apply_design(slide, design_settings):
|
|
| 209 |
|
| 210 |
if design_settings["set_background"]:
|
| 211 |
slide.background.fill.solid()
|
| 212 |
-
slide.background.fill.fore_color.rgb = RGBColor.from_string(colors["
|
| 213 |
|
| 214 |
def process_presentation(uploaded_file, user_prompt, design_settings):
|
| 215 |
"""Process and enhance the entire presentation."""
|
|
@@ -220,7 +209,7 @@ def process_presentation(uploaded_file, user_prompt, design_settings):
|
|
| 220 |
prs = Presentation(tmp_path)
|
| 221 |
|
| 222 |
with st.spinner("Enhancing slides..."):
|
| 223 |
-
for
|
| 224 |
enhance_slide(slide, user_prompt, design_settings)
|
| 225 |
|
| 226 |
os.unlink(tmp_path)
|
|
@@ -229,17 +218,13 @@ def process_presentation(uploaded_file, user_prompt, design_settings):
|
|
| 229 |
def main():
|
| 230 |
st.title("Professional PPTX Enhancer")
|
| 231 |
|
| 232 |
-
# File upload
|
| 233 |
uploaded_file = st.file_uploader("Upload PowerPoint", type=["pptx"])
|
| 234 |
-
|
| 235 |
-
# Content settings
|
| 236 |
user_prompt = st.text_area(
|
| 237 |
"Enhancement Instructions",
|
| 238 |
placeholder="Example: 'Make headings clear and add bullet points for key items'",
|
| 239 |
height=100
|
| 240 |
)
|
| 241 |
|
| 242 |
-
# Enhanced Design settings
|
| 243 |
with st.expander("🎨 Design Options", expanded=True):
|
| 244 |
tab1, tab2 = st.tabs(["Preset Themes", "Custom Colors"])
|
| 245 |
|
|
@@ -259,17 +244,10 @@ def main():
|
|
| 259 |
custom_colors = get_custom_colors()
|
| 260 |
use_custom = st.checkbox("Use Custom Colors")
|
| 261 |
|
| 262 |
-
# Prepare design settings
|
| 263 |
-
if use_custom:
|
| 264 |
-
colors = custom_colors
|
| 265 |
-
color_palette = "Custom"
|
| 266 |
-
else:
|
| 267 |
-
colors = COLOR_PALETTES[color_palette]
|
| 268 |
-
|
| 269 |
design_settings = {
|
| 270 |
"font": FONT_CHOICES[font],
|
| 271 |
-
"colors":
|
| 272 |
-
"color_palette": color_palette,
|
| 273 |
"title_size": title_size,
|
| 274 |
"heading_size": heading_size,
|
| 275 |
"body_size": body_size,
|
|
@@ -278,9 +256,12 @@ def main():
|
|
| 278 |
|
| 279 |
if st.button("Enhance Presentation") and uploaded_file and user_prompt:
|
| 280 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 281 |
enhanced_prs = process_presentation(uploaded_file, user_prompt, design_settings)
|
| 282 |
|
| 283 |
-
# Create download
|
| 284 |
buffer = io.BytesIO()
|
| 285 |
enhanced_prs.save(buffer)
|
| 286 |
buffer.seek(0)
|
|
|
|
| 15 |
# Configuration
|
| 16 |
GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
|
| 17 |
MODEL_NAME = "gemma2-9b-it"
|
| 18 |
+
|
| 19 |
# Style Options
|
| 20 |
FONT_CHOICES = {
|
| 21 |
"Arial": "Arial",
|
|
|
|
| 42 |
}
|
| 43 |
|
| 44 |
def get_custom_colors():
|
| 45 |
+
"""Get custom colors from user input with background option"""
|
| 46 |
+
col1, col2, col3, col4 = st.columns(4)
|
| 47 |
with col1:
|
| 48 |
primary = st.color_picker("Primary Color", "#2B579A")
|
| 49 |
with col2:
|
| 50 |
secondary = st.color_picker("Secondary Color", "#5B9BD5")
|
| 51 |
with col3:
|
| 52 |
text = st.color_picker("Text Color", "#000000")
|
| 53 |
+
with col4:
|
| 54 |
+
background = st.color_picker("Background Color", "#FFFFFF")
|
| 55 |
return {
|
| 56 |
"primary": primary,
|
| 57 |
"secondary": secondary,
|
| 58 |
"text": text,
|
| 59 |
+
"background": background
|
| 60 |
}
|
| 61 |
|
| 62 |
def get_ai_response(prompt: str) -> str:
|
|
|
|
| 90 |
def split_formatted_runs(text):
|
| 91 |
"""Split text into formatted runs removing markdown."""
|
| 92 |
formatted_segments = []
|
|
|
|
| 93 |
bold_parts = re.split(r'(\*\*)', text)
|
| 94 |
bold = False
|
| 95 |
current_text = []
|
| 96 |
+
|
| 97 |
for part in bold_parts:
|
| 98 |
if part == '**':
|
| 99 |
if current_text:
|
|
|
|
| 101 |
current_text = []
|
| 102 |
bold = not bold
|
| 103 |
else:
|
| 104 |
+
italic_parts = re.split(r'(\*)', part)
|
| 105 |
+
italic = False
|
| 106 |
+
for italic_part in italic_parts:
|
| 107 |
+
if italic_part == '*':
|
| 108 |
+
if current_text:
|
| 109 |
+
formatted_segments.append({'text': ''.join(current_text), 'bold': bold, 'italic': italic})
|
| 110 |
+
current_text = []
|
| 111 |
+
italic = not italic
|
| 112 |
+
else:
|
| 113 |
+
current_text.append(italic_part)
|
| 114 |
+
if current_text:
|
| 115 |
+
formatted_segments.append({'text': ''.join(current_text), 'bold': bold, 'italic': italic})
|
| 116 |
+
current_text = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
return formatted_segments
|
| 118 |
|
| 119 |
def apply_formatting(text_frame, content, design_settings):
|
|
|
|
| 122 |
paragraphs = [p for p in content.split('\n') if p.strip()]
|
| 123 |
|
| 124 |
for para in paragraphs:
|
|
|
|
| 125 |
clean_para = re.sub(r'^#+\s*', '', para).strip()
|
| 126 |
|
|
|
|
| 127 |
if re.match(r'^(What is|Understanding|What\'s|Drive Theory|Incentive Theory)', clean_para, re.IGNORECASE):
|
| 128 |
p = text_frame.add_paragraph()
|
| 129 |
p.text = clean_para
|
|
|
|
| 132 |
p.space_after = Pt(12)
|
| 133 |
continue
|
| 134 |
|
|
|
|
| 135 |
if re.match(r'^[\•\-\*] ', clean_para):
|
| 136 |
p = text_frame.add_paragraph()
|
| 137 |
p.level = 0
|
|
|
|
| 145 |
p.font.size = Pt(design_settings["body_size"])
|
| 146 |
continue
|
| 147 |
|
|
|
|
| 148 |
p = text_frame.add_paragraph()
|
| 149 |
segments = split_formatted_runs(clean_para)
|
| 150 |
for seg in segments:
|
|
|
|
| 154 |
run.font.italic = seg['italic']
|
| 155 |
p.font.size = Pt(design_settings["body_size"])
|
| 156 |
p.space_after = Pt(6)
|
| 157 |
+
|
| 158 |
def enhance_slide(slide, user_prompt, design_settings):
|
| 159 |
"""Enhance slide content with proper formatting."""
|
| 160 |
original_text = extract_slide_text(slide)
|
| 161 |
if not original_text.strip():
|
| 162 |
return
|
| 163 |
|
|
|
|
| 164 |
prompt = f"Improve this slide content:\n{original_text}\n\nInstructions: {user_prompt}"
|
| 165 |
enhanced_content = get_ai_response(prompt)
|
| 166 |
|
|
|
|
| 167 |
title_shape = getattr(slide.shapes, 'title', None)
|
| 168 |
shapes_to_remove = [s for s in slide.shapes if s != title_shape]
|
| 169 |
|
|
|
|
| 171 |
sp = shape._element
|
| 172 |
sp.getparent().remove(sp)
|
| 173 |
|
|
|
|
| 174 |
left = Inches(1) if title_shape else Inches(0.5)
|
| 175 |
top = Inches(1.5) if title_shape else Inches(0.5)
|
| 176 |
textbox = slide.shapes.add_textbox(left, top, Inches(8), Inches(5))
|
|
|
|
| 182 |
|
| 183 |
def apply_design(slide, design_settings):
|
| 184 |
"""Apply visual design to slide."""
|
| 185 |
+
colors = design_settings["colors"]
|
| 186 |
|
| 187 |
for shape in slide.shapes:
|
| 188 |
if hasattr(shape, "text_frame"):
|
|
|
|
| 191 |
run.font.name = design_settings["font"]
|
| 192 |
run.font.color.rgb = RGBColor.from_string(colors["text"][1:])
|
| 193 |
|
|
|
|
| 194 |
if shape == getattr(slide.shapes, 'title', None):
|
| 195 |
run.font.color.rgb = RGBColor.from_string(colors["primary"][1:])
|
| 196 |
run.font.size = Pt(design_settings["title_size"])
|
|
|
|
| 198 |
|
| 199 |
if design_settings["set_background"]:
|
| 200 |
slide.background.fill.solid()
|
| 201 |
+
slide.background.fill.fore_color.rgb = RGBColor.from_string(colors["background"][1:])
|
| 202 |
|
| 203 |
def process_presentation(uploaded_file, user_prompt, design_settings):
|
| 204 |
"""Process and enhance the entire presentation."""
|
|
|
|
| 209 |
prs = Presentation(tmp_path)
|
| 210 |
|
| 211 |
with st.spinner("Enhancing slides..."):
|
| 212 |
+
for slide in prs.slides:
|
| 213 |
enhance_slide(slide, user_prompt, design_settings)
|
| 214 |
|
| 215 |
os.unlink(tmp_path)
|
|
|
|
| 218 |
def main():
|
| 219 |
st.title("Professional PPTX Enhancer")
|
| 220 |
|
|
|
|
| 221 |
uploaded_file = st.file_uploader("Upload PowerPoint", type=["pptx"])
|
|
|
|
|
|
|
| 222 |
user_prompt = st.text_area(
|
| 223 |
"Enhancement Instructions",
|
| 224 |
placeholder="Example: 'Make headings clear and add bullet points for key items'",
|
| 225 |
height=100
|
| 226 |
)
|
| 227 |
|
|
|
|
| 228 |
with st.expander("🎨 Design Options", expanded=True):
|
| 229 |
tab1, tab2 = st.tabs(["Preset Themes", "Custom Colors"])
|
| 230 |
|
|
|
|
| 244 |
custom_colors = get_custom_colors()
|
| 245 |
use_custom = st.checkbox("Use Custom Colors")
|
| 246 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 247 |
design_settings = {
|
| 248 |
"font": FONT_CHOICES[font],
|
| 249 |
+
"colors": custom_colors if use_custom else COLOR_PALETTES[color_palette],
|
| 250 |
+
"color_palette": "Custom" if use_custom else color_palette,
|
| 251 |
"title_size": title_size,
|
| 252 |
"heading_size": heading_size,
|
| 253 |
"body_size": body_size,
|
|
|
|
| 256 |
|
| 257 |
if st.button("Enhance Presentation") and uploaded_file and user_prompt:
|
| 258 |
try:
|
| 259 |
+
if not GROQ_API_KEY:
|
| 260 |
+
st.error("GROQ API key not found! Please set it in Space settings.")
|
| 261 |
+
st.stop()
|
| 262 |
+
|
| 263 |
enhanced_prs = process_presentation(uploaded_file, user_prompt, design_settings)
|
| 264 |
|
|
|
|
| 265 |
buffer = io.BytesIO()
|
| 266 |
enhanced_prs.save(buffer)
|
| 267 |
buffer.seek(0)
|