Spaces:
Running
Running
File size: 6,135 Bytes
46af8e9 6b4926b 46af8e9 6b4926b 46af8e9 6b4926b 46af8e9 6b4926b 46af8e9 6b4926b 46af8e9 6b4926b 46af8e9 6b4926b 46af8e9 6b4926b 46af8e9 6b4926b 46af8e9 6b4926b 46af8e9 6b4926b 46af8e9 6b4926b 46af8e9 6b4926b 46af8e9 6b4926b 46af8e9 6b4926b 46af8e9 6b4926b 46af8e9 6b4926b 46af8e9 | 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 | 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"<div class='status-ok'>✅ <strong>{n} slides</strong> with images — download below!</div>"
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("""
<div class="header-block">
<h1 style="margin:0;font-size:28px;font-weight:800;color:#fff;">SlideAI</h1>
<p style="margin:6px 0 0;font-size:14px;color:rgba(255,255,255,.88);">
Turn any topic into a polished, image-rich, download-ready presentation.
</p>
</div>""")
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("<div class='status-wait'>Fill in the form and hit <strong>Generate</strong>.</div>")
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()
|