WikiTales / app.py
rishipal's picture
Upload 4 files
dca465c verified
# app.py
import streamlit as st
from streamlit.components.v1 import html
from story_engine.story_builder import build_story
st.markdown("""
<meta name="viewport" content="width=device-width, initial-scale=1">
""", unsafe_allow_html=True)
st.set_page_config(page_title="WikiTales", layout="centered")
st.title("πŸ“– WikiTales – Interactive Historical Storybooks")
with st.container():
st.markdown("#### πŸ” Enter a historical figure or event:")
topic = st.text_input(
label="Search Topic",
placeholder="e.g., French Revolution",
key="search_box",
label_visibility="collapsed"
)
narrator = st.selectbox(
"Choose Narrator Voice",
options=["πŸ“š Neutral Historian", "πŸ“° Wartime Journalist", "πŸ‘©β€πŸŒΎ Eyewitness", "πŸ§™ Fictional Guide", "πŸ€– AI Assistant"],
index=0
)
# -------------------------
# Carousel Renderer Function
# -------------------------
def render_image_carousel(images):
if not images:
st.warning("No images found.")
return
# Build slides
slides = ""
for idx, img in enumerate(images):
slides += f"""
<div class="slide" id="slide-{idx}">
<img src="{img['url']}" alt="{img['title']}" />
<div class="caption">{img['title']}</div>
</div>"""
html_code = f"""
<style>
.carousel {{ position: relative; width: 100%; max-width: 700px; margin: auto; }}
.slide {{ display: none; text-align: center; }}
.slide img {{ max-width: 100%; max-height: 350px; border-radius: 8px; }}
.caption {{ color: #fff; background: rgba(0,0,0,0.5); padding: 0.5em; position: absolute; bottom: 8px; width: 100%; }}
.prev, .next {{
cursor: pointer;
position: absolute;
top: 50%;
width: auto;
margin-top: -22px;
padding: 16px;
color: white;
font-weight: bold;
font-size: 18px;
border-radius: 0 3px 3px 0;
user-select: none;
}}
.next {{ right: 0; border-radius: 3px 0 0 3px; }}
</style>
<div class="carousel">
{slides}
<a class="prev" onclick="changeSlide(-1)">&#10094;</a>
<a class="next" onclick="changeSlide(1)">&#10095;</a>
</div>
<script>
let currentSlide = 0;
const slides = document.getElementsByClassName('slide');
function showSlide(idx) {{
if (idx >= slides.length) currentSlide = 0;
else if (idx < 0) currentSlide = slides.length - 1;
else currentSlide = idx;
for (let i = 0; i < slides.length; i++) {{
slides[i].style.display = 'none';
}}
slides[currentSlide].style.display = 'block';
}}
function changeSlide(n) {{
showSlide(currentSlide + n);
}}
showSlide(0);
</script>
"""
html(html_code, height=420)
# -------------------------
# Main Search + Display
# -------------------------
if topic:
with st.spinner(f"Generating story for '{topic}'..."):
story_data = build_story(topic, narrator)
story = story_data["story"]
images = story_data["images"]
wikibooks = story_data.get("wikibooks", []) # βœ… added
if story:
render_image_carousel(images)
st.markdown(f"#### πŸŽ™οΈ Narrated by: {narrator}")
st.markdown("## 🧠 Generated Story")
with st.container():
st.markdown(story, unsafe_allow_html=True)
# βœ… SIDEBAR: Wikibooks Deep Dive
if wikibooks:
with st.sidebar:
st.markdown("## πŸ“š Learn More on Wikibooks")
for module in wikibooks:
st.markdown(f"- [{module['title']}]({module['link']})")
else:
st.error("Sorry, we couldn't build a story. Try another topic.")