import os import tempfile import traceback import gradio as gr from slide_generator import generate_presentation from pptx_builder import build_pptx STYLES = ["Professional", "Creative", "Academic", "Startup"] CSS = """ * { box-sizing: border-box; } body, .gradio-container { background: #f0f7f4 !important; font-family: 'Inter', system-ui, sans-serif !important; } footer { display: none !important; } .header-block { background: linear-gradient(135deg, #1b6ca8 0%, #19a88a 100%); border-radius: 16px; padding: 32px 36px 28px; margin-bottom: 24px; } button.primary { background: linear-gradient(135deg, #1b6ca8, #19a88a) !important; color: #fff !important; border: none !important; border-radius: 12px !important; font-size: 17px !important; font-weight: 700 !important; padding: 16px 0 !important; width: 100% !important; cursor: pointer !important; box-shadow: 0 4px 16px rgba(25,168,138,0.3) !important; } button.primary:hover { opacity: .87 !important; } textarea, input[type="text"] { background: #f5fbf9 !important; border: 1.5px solid #b2ddd1 !important; border-radius: 10px !important; color: #1a3a3a !important; font-size: 14px !important; } input[type="range"] { accent-color: #19a88a !important; } .status-ok { background: #e6f7f2; border: 1px solid #a8dfd0; border-radius: 10px; padding: 12px 18px; font-size: 14px; color: #1a5a4a; margin-bottom: 8px; } .status-wait { background: #f0f7ff; border: 1px solid #b2cfe8; border-radius: 10px; padding: 12px 18px; font-size: 14px; color: #1a3a6a; margin-bottom: 8px; } .preview-md, .preview-md p, .preview-md li, .preview-md h1, .preview-md h2, .preview-md h3 { color: #0d1b2a !important; } .preview-md { background: #f5fbf9 !important; border: 1px solid #c8e8df !important; border-radius: 12px !important; padding: 16px 20px !important; min-height: 220px !important; max-height: 420px !important; overflow-y: auto !important; font-size: 14px !important; line-height: 1.75 !important; } .preview-md h1 { color: #1b6ca8 !important; font-size: 18px !important; } .preview-md h3 { color: #19a88a !important; font-size: 14px !important; margin: 10px 0 4px !important; } .preview-md blockquote { border-left: 3px solid #19a88a; padding-left: 10px; } .preview-md em { color: #5a8a8a !important; font-size: 12px !important; } """ def format_preview(data): lines = [f"# {data.get('title','')}", ""] if data.get("subtitle"): lines += [f"*{data['subtitle']}*", ""] lines.append("---") for slide in data.get("slides", []): num = slide.get("slide_number", "") title = slide.get("title", "") kw = slide.get("image_keyword", "") if slide.get("type") == "title": lines.append(f"\n### 🎯 Slide {num} — {title}") if slide.get("subtitle"): lines.append(f"> {slide['subtitle']}") else: lines.append(f"\n### 📄 Slide {num} — {title}") if kw: lines.append(f"*📸 Image: {kw}*") for b in slide.get("bullets", []): lines.append(f"- {b}") if slide.get("speaker_notes"): lines.append(f"\n*🗒 {slide['speaker_notes']}*") return "\n".join(lines) def generate_and_download(topic, audience, style, num_slides, key_points, progress=gr.Progress()): if not topic.strip(): raise gr.Error("Please enter a topic.") if not audience.strip(): raise gr.Error("Please enter the target audience.") try: progress(0.1, desc="AI is writing your slides…") data = generate_presentation( topic=topic.strip(), style=style, num_slides=int(num_slides), audience=audience.strip(), key_points=key_points.strip(), ) progress(0.65, desc="Fetching images & building PPTX…") pptx_bytes = build_pptx(data, style) tmp_dir = tempfile.mkdtemp() safe = topic[:30].replace(" ", "_").replace("/", "-") out_path = os.path.join(tmp_dir, f"{safe}.pptx") with open(out_path, "wb") as f: f.write(pptx_bytes) progress(1.0, desc="Done!") n = len(data.get("slides", [])) status = f"
✅ {n} slides with images — download below!
" return format_preview(data), out_path, status except gr.Error: raise except Exception: raise gr.Error(traceback.format_exc()) with gr.Blocks(title="SlideAI", css=CSS) as demo: gr.HTML("""

SlideAI

Turn any topic into a polished, image-rich, download-ready presentation.

""") with gr.Row(): with gr.Column(scale=1, min_width=300): topic = gr.Textbox(label="Topic *", placeholder="e.g. Climate Change…", lines=2) audience = gr.Textbox(label="Target Audience *", placeholder="e.g. Students…", lines=1) with gr.Row(): style = gr.Dropdown(choices=STYLES, value="Professional", label="Style", scale=1) num_slides = gr.Slider(minimum=5, maximum=15, step=1, value=8, label="Slides", scale=1) key_points = gr.Textbox(label="Key Points (optional)", placeholder="Specific facts or ideas to include…", lines=2) btn = gr.Button("✨ Generate Presentation", variant="primary", size="lg") with gr.Column(scale=2, min_width=380): status = gr.HTML("
Fill in the form and hit Generate.
") preview = gr.Markdown(elem_classes=["preview-md"]) download = gr.File(label="📥 Download your PPTX", interactive=False) btn.click(fn=generate_and_download, inputs=[topic, audience, style, num_slides, key_points], outputs=[preview, download, status]) if __name__ == "__main__": demo.launch()