Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,10 +1,4 @@
|
|
1 |
-
# app.py - Final Version with
|
2 |
-
# Fix for huggingface_hub compatibility
|
3 |
-
import huggingface_hub
|
4 |
-
if not hasattr(huggingface_hub, 'cached_download'):
|
5 |
-
from huggingface_hub import hf_hub_download
|
6 |
-
huggingface_hub.cached_download = hf_hub_download
|
7 |
-
|
8 |
import streamlit as st
|
9 |
import os
|
10 |
import time
|
@@ -21,15 +15,6 @@ import matplotlib.pyplot as plt
|
|
21 |
import requests
|
22 |
from io import BytesIO
|
23 |
import json
|
24 |
-
import torch
|
25 |
-
|
26 |
-
# Use compatible import for diffusers
|
27 |
-
try:
|
28 |
-
from diffusers import StableDiffusionPipeline
|
29 |
-
except ImportError:
|
30 |
-
from diffusers import DiffusionPipeline as StableDiffusionPipeline
|
31 |
-
|
32 |
-
from transformers import pipeline
|
33 |
|
34 |
# Configure Streamlit page
|
35 |
st.set_page_config(
|
@@ -39,44 +24,45 @@ st.set_page_config(
|
|
39 |
initial_sidebar_state="expanded"
|
40 |
)
|
41 |
|
42 |
-
# Custom CSS
|
43 |
st.markdown("""
|
44 |
<style>
|
45 |
-
@import url('https://fonts.googleapis.com/css2?family=Comic+Neue:wght@700&display=swap');
|
46 |
|
47 |
:root {
|
48 |
-
--primary: #
|
49 |
-
--secondary: #
|
50 |
-
--accent: #
|
51 |
-
--dark: #
|
52 |
-
--light: #
|
|
|
53 |
}
|
54 |
|
55 |
body {
|
56 |
-
background: linear-gradient(135deg, var(--light) 0%,
|
57 |
-
font-family: '
|
58 |
}
|
59 |
|
60 |
.stApp {
|
61 |
-
background: url('https://www.transparenttextures.com/patterns/
|
62 |
}
|
63 |
|
64 |
.story-box {
|
65 |
background-color: white;
|
66 |
border-radius: 20px;
|
67 |
padding: 25px;
|
68 |
-
box-shadow: 0 8px 16px rgba(
|
69 |
border: 3px solid var(--accent);
|
70 |
margin-bottom: 25px;
|
71 |
}
|
72 |
|
73 |
.header {
|
74 |
color: var(--dark);
|
75 |
-
text-shadow:
|
76 |
}
|
77 |
|
78 |
.concept-card {
|
79 |
-
background: linear-gradient(145deg, #ffffff, #
|
80 |
border-radius: 15px;
|
81 |
padding: 15px;
|
82 |
margin: 10px 0;
|
@@ -88,22 +74,24 @@ st.markdown("""
|
|
88 |
background: linear-gradient(45deg, var(--primary), var(--secondary));
|
89 |
color: white;
|
90 |
border-radius: 12px;
|
91 |
-
padding:
|
92 |
font-weight: bold;
|
93 |
font-size: 18px;
|
94 |
border: none;
|
95 |
transition: all 0.3s;
|
|
|
96 |
}
|
97 |
|
98 |
.stButton>button:hover {
|
99 |
-
transform:
|
100 |
-
box-shadow: 0 6px 12px rgba(
|
101 |
}
|
102 |
|
103 |
.stTextInput>div>div>input {
|
104 |
border-radius: 12px;
|
105 |
padding: 12px;
|
106 |
border: 2px solid var(--accent);
|
|
|
107 |
}
|
108 |
|
109 |
.tabs {
|
@@ -114,17 +102,23 @@ st.markdown("""
|
|
114 |
}
|
115 |
|
116 |
.tab {
|
117 |
-
padding:
|
118 |
-
background-color: var(--
|
119 |
-
border-radius:
|
120 |
cursor: pointer;
|
121 |
font-weight: bold;
|
122 |
white-space: nowrap;
|
|
|
|
|
|
|
|
|
|
|
|
|
123 |
}
|
124 |
|
125 |
.tab.active {
|
126 |
-
background-color: var(--
|
127 |
-
|
128 |
}
|
129 |
|
130 |
@media (max-width: 768px) {
|
@@ -134,10 +128,10 @@ st.markdown("""
|
|
134 |
}
|
135 |
|
136 |
.animation-container {
|
137 |
-
background
|
138 |
border-radius: 15px;
|
139 |
padding: 20px;
|
140 |
-
box-shadow: 0 8px 32px rgba(
|
141 |
margin-bottom: 25px;
|
142 |
position: relative;
|
143 |
overflow: hidden;
|
@@ -161,14 +155,15 @@ st.markdown("""
|
|
161 |
width: 100%;
|
162 |
margin: 20px 0;
|
163 |
border-radius: 20px;
|
164 |
-
background: #
|
165 |
padding: 15px;
|
166 |
}
|
167 |
|
168 |
.animation-gif {
|
169 |
max-width: 100%;
|
170 |
border-radius: 10px;
|
171 |
-
box-shadow: 0 8px 16px rgba(0,0,0,0.
|
|
|
172 |
}
|
173 |
|
174 |
.ai-animation {
|
@@ -180,7 +175,7 @@ st.markdown("""
|
|
180 |
}
|
181 |
|
182 |
.explanation-box {
|
183 |
-
background: linear-gradient(135deg, #
|
184 |
border-radius: 15px;
|
185 |
padding: 20px;
|
186 |
margin: 20px 0;
|
@@ -193,6 +188,24 @@ st.markdown("""
|
|
193 |
display: flex;
|
194 |
align-items: center;
|
195 |
gap: 10px;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
196 |
}
|
197 |
</style>
|
198 |
""", unsafe_allow_html=True)
|
@@ -204,7 +217,7 @@ CONCEPTS = {
|
|
204 |
"emoji": "🔄",
|
205 |
"description": "Loops repeat actions multiple times",
|
206 |
"example": "for i in range(5):\n print('Hello!')",
|
207 |
-
"color": "#
|
208 |
"explanation": "A loop is like a magic spell that makes something happen again and again. In programming, we use loops when we want to repeat an action multiple times without writing the same code over and over."
|
209 |
},
|
210 |
"conditional": {
|
@@ -212,7 +225,7 @@ CONCEPTS = {
|
|
212 |
"emoji": "❓",
|
213 |
"description": "Conditionals make decisions in code",
|
214 |
"example": "if sunny:\n go_outside()\nelse:\n stay_inside()",
|
215 |
-
"color": "#
|
216 |
"explanation": "Conditionals are like crossroads in a story where you choose which path to take. In programming, we use 'if' statements to make decisions based on certain conditions, just like choosing what to wear based on the weather."
|
217 |
},
|
218 |
"function": {
|
@@ -220,7 +233,7 @@ CONCEPTS = {
|
|
220 |
"emoji": "✨",
|
221 |
"description": "Functions are reusable blocks of code",
|
222 |
"example": "def greet(name):\n print(f'Hello {name}!')",
|
223 |
-
"color": "#
|
224 |
"explanation": "Functions are like magic spells you can create and reuse whenever you need them. They help us organize code into reusable blocks so we don't have to write the same thing multiple times."
|
225 |
},
|
226 |
"variable": {
|
@@ -228,7 +241,7 @@ CONCEPTS = {
|
|
228 |
"emoji": "📦",
|
229 |
"description": "Variables store information",
|
230 |
"example": "score = 10\nplayer = 'Alex'",
|
231 |
-
"color": "#
|
232 |
"explanation": "Variables are like labeled boxes where you can store information. They help us remember values and use them later in our code, just like remembering a character's name in a story."
|
233 |
},
|
234 |
"list": {
|
@@ -236,7 +249,7 @@ CONCEPTS = {
|
|
236 |
"emoji": "📝",
|
237 |
"description": "Lists store collections of items",
|
238 |
"example": "fruits = ['apple', 'banana', 'orange']",
|
239 |
-
"color": "#
|
240 |
"explanation": "Lists are like magical scrolls that can hold multiple items. In programming, we use lists to store collections of related things, like a wizard's inventory of potions."
|
241 |
}
|
242 |
}
|
@@ -245,35 +258,40 @@ CONCEPTS = {
|
|
245 |
ANIMATION_EXAMPLES = {
|
246 |
"loop": "https://media.giphy.com/media/3o7abKhOpu0NwenH3O/giphy.gif",
|
247 |
"conditional": "https://media.giphy.com/media/l0HlG8vJXW0X5yX4s/giphy.gif",
|
248 |
-
"function": "https://media.giphy.com/media/3o7TKsQ8UQ4l4LhGz6/giphy.gif"
|
|
|
|
|
249 |
}
|
250 |
|
251 |
-
#
|
252 |
-
|
253 |
-
|
254 |
-
""
|
255 |
-
|
256 |
-
|
|
|
|
|
|
|
|
|
|
|
257 |
try:
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
models['text_to_image'] = models['text_to_image'].to(device)
|
266 |
-
except Exception as e:
|
267 |
-
st.error(f"Could not load text-to-image model: {str(e)}")
|
268 |
-
models['text_to_image'] = None
|
269 |
-
|
270 |
try:
|
271 |
-
#
|
272 |
-
|
|
|
|
|
|
|
|
|
|
|
273 |
except:
|
274 |
-
|
275 |
-
|
276 |
-
return models
|
277 |
|
278 |
def analyze_story(story):
|
279 |
"""Analyze story and identify programming concepts"""
|
@@ -281,23 +299,23 @@ def analyze_story(story):
|
|
281 |
detected_concepts = []
|
282 |
|
283 |
# Check for loops
|
284 |
-
if any(word in story_lower for word in ["times", "repeat", "again", "multiple"]):
|
285 |
detected_concepts.append("loop")
|
286 |
|
287 |
# Check for conditionals
|
288 |
-
if any(word in story_lower for word in ["if", "when", "unless", "whether"]):
|
289 |
detected_concepts.append("conditional")
|
290 |
|
291 |
# Check for functions
|
292 |
-
if any(word in story_lower for word in ["make", "create", "do", "perform", "cast"]):
|
293 |
detected_concepts.append("function")
|
294 |
|
295 |
# Check for variables
|
296 |
-
if any(word in story_lower for word in ["is", "has", "set to", "value"]):
|
297 |
detected_concepts.append("variable")
|
298 |
|
299 |
# Check for lists
|
300 |
-
if any(word in story_lower for word in ["and", "many", "several", "collection", "items"]):
|
301 |
detected_concepts.append("list")
|
302 |
|
303 |
return list(set(detected_concepts))
|
@@ -309,55 +327,9 @@ def extract_count_from_story(story):
|
|
309 |
return min(int(word), 10)
|
310 |
return 3 # Default value
|
311 |
|
312 |
-
def
|
313 |
-
"""
|
314 |
-
try:
|
315 |
-
# Create a prompt based on the story and concept
|
316 |
-
style = "cartoon style, bright colors, children's book illustration"
|
317 |
-
prompt = f"{story}. {CONCEPTS[concept]['name']} concept. {style}"
|
318 |
-
|
319 |
-
# Generate image with optimized settings
|
320 |
-
image = models['text_to_image'](
|
321 |
-
prompt,
|
322 |
-
num_inference_steps=15, # Faster generation
|
323 |
-
guidance_scale=7.5,
|
324 |
-
height=256, # Smaller image for faster generation
|
325 |
-
width=256
|
326 |
-
).images[0]
|
327 |
-
|
328 |
-
# Convert to bytes
|
329 |
-
buf = io.BytesIO()
|
330 |
-
image.save(buf, format='PNG')
|
331 |
-
buf.seek(0)
|
332 |
-
|
333 |
-
return buf
|
334 |
-
|
335 |
-
except Exception as e:
|
336 |
-
st.error(f"AI scene generation error: {str(e)}")
|
337 |
-
return None
|
338 |
-
|
339 |
-
def create_animation_preview(story, concept):
|
340 |
-
"""Create an animation preview for the concept"""
|
341 |
-
try:
|
342 |
-
# Return a sample GIF for the concept
|
343 |
-
return ANIMATION_EXAMPLES.get(concept, "https://media.giphy.com/media/3o7abKhOpu0NwenH3O/giphy.gif")
|
344 |
-
except:
|
345 |
-
return "https://media.giphy.com/media/3o7abKhOpu0NwenH3O/giphy.gif"
|
346 |
-
|
347 |
-
def text_to_speech_custom(text, models, filename="story_audio.wav"):
|
348 |
-
"""Convert text to speech using AI model"""
|
349 |
try:
|
350 |
-
if models['text_to_speech']:
|
351 |
-
# Generate audio
|
352 |
-
audio = models['text_to_speech'](text)
|
353 |
-
|
354 |
-
# Save to file
|
355 |
-
with open(filename, "wb") as f:
|
356 |
-
f.write(audio["audio"])
|
357 |
-
|
358 |
-
return filename
|
359 |
-
|
360 |
-
# Fallback to gTTS
|
361 |
tts = gTTS(text=text, lang='en')
|
362 |
tts.save(filename)
|
363 |
return filename
|
@@ -372,7 +344,7 @@ def autoplay_audio(file_path):
|
|
372 |
b64 = base64.b64encode(data).decode()
|
373 |
md = f"""
|
374 |
<audio autoplay>
|
375 |
-
<source src="data:audio/
|
376 |
</audio>
|
377 |
"""
|
378 |
st.markdown(md, unsafe_allow_html=True)
|
@@ -380,20 +352,28 @@ def autoplay_audio(file_path):
|
|
380 |
def generate_animation_code(story, concept):
|
381 |
"""Generate Python animation code based on story"""
|
382 |
try:
|
|
|
|
|
|
|
383 |
# Sample code based on concept
|
384 |
if concept == "loop":
|
385 |
code = f"""
|
386 |
-
# Loop Animation
|
387 |
import time
|
388 |
|
389 |
def animate_story():
|
390 |
-
actions = {
|
|
|
|
|
|
|
391 |
|
392 |
for i in range(actions):
|
393 |
-
print(f"Action {{i+1}}:
|
394 |
# Animation code would go here
|
395 |
time.sleep(0.5)
|
396 |
|
|
|
|
|
397 |
animate_story()
|
398 |
"""
|
399 |
description = "Looping through actions multiple times"
|
@@ -401,35 +381,93 @@ animate_story()
|
|
401 |
|
402 |
elif concept == "conditional":
|
403 |
code = f"""
|
404 |
-
# Conditional Animation
|
405 |
import random
|
406 |
|
|
|
|
|
|
|
407 |
condition = random.choice([True, False])
|
|
|
408 |
|
409 |
if condition:
|
410 |
-
print("
|
411 |
# True branch animation
|
412 |
else:
|
413 |
-
print("
|
414 |
# False branch animation
|
|
|
|
|
415 |
"""
|
416 |
description = "Making decisions based on conditions"
|
417 |
visual_cue = "Branching paths based on conditions"
|
418 |
|
419 |
-
|
420 |
code = f"""
|
421 |
-
# Function Animation
|
|
|
|
|
|
|
|
|
|
|
422 |
def perform_action():
|
423 |
-
print("Performing action
|
424 |
# Action animation code
|
425 |
|
426 |
# Call the function multiple times
|
427 |
-
for _ in range({
|
428 |
perform_action()
|
|
|
|
|
|
|
429 |
"""
|
430 |
description = "Reusing code with functions"
|
431 |
visual_cue = "Calling a reusable function"
|
432 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
433 |
return code, description, visual_cue
|
434 |
|
435 |
except Exception as e:
|
@@ -464,7 +502,7 @@ def create_interactive_animation(story, concept):
|
|
464 |
size="size",
|
465 |
size_max=45,
|
466 |
color="color",
|
467 |
-
color_discrete_sequence=px.colors.qualitative.
|
468 |
range_x=[0, 10],
|
469 |
range_y=[0, 10],
|
470 |
title=f"Interactive Animation: {story[:40]}{'...' if len(story) > 40 else ''}"
|
@@ -473,16 +511,17 @@ def create_interactive_animation(story, concept):
|
|
473 |
# Customize layout
|
474 |
fig.update_layout(
|
475 |
showlegend=False,
|
476 |
-
plot_bgcolor='rgba(240,
|
477 |
-
paper_bgcolor='rgba(240,
|
478 |
width=800,
|
479 |
height=600,
|
480 |
xaxis=dict(showgrid=False, zeroline=False, visible=False),
|
481 |
yaxis=dict(showgrid=False, zeroline=False, visible=False),
|
|
|
482 |
)
|
483 |
|
484 |
fig.update_traces(
|
485 |
-
textfont_size=
|
486 |
textposition='middle center',
|
487 |
marker=dict(sizemode='diameter')
|
488 |
)
|
@@ -500,24 +539,29 @@ def generate_concept_explanation(story, concept, code_description, visual_cue):
|
|
500 |
explanation = f"""
|
501 |
Let me explain what's happening in your story and how it relates to programming!
|
502 |
|
503 |
-
Your story
|
504 |
|
505 |
-
|
506 |
|
507 |
{concept_info.get('explanation', 'This is a fundamental programming concept.')}
|
508 |
|
509 |
-
In our animation
|
510 |
- {visual_cue}
|
511 |
- This represents: {code_description}
|
512 |
|
513 |
-
How it works in code
|
514 |
We use {concept_info.get('name', 'this concept')} like this:
|
|
|
515 |
{concept_info.get('example', 'Example code will appear here')}
|
|
|
516 |
|
517 |
-
|
|
|
518 |
- Creating games with repeating actions (loops)
|
519 |
- Making decisions in apps (conditionals)
|
520 |
- Building reusable components (functions)
|
|
|
|
|
521 |
|
522 |
Try to spot this concept in other stories you create!
|
523 |
"""
|
@@ -534,8 +578,6 @@ def main():
|
|
534 |
st.session_state.story = ""
|
535 |
if 'concepts' not in st.session_state:
|
536 |
st.session_state.concepts = []
|
537 |
-
if 'story_scene' not in st.session_state:
|
538 |
-
st.session_state.story_scene = None
|
539 |
if 'interactive_animation' not in st.session_state:
|
540 |
st.session_state.interactive_animation = None
|
541 |
if 'animation_code' not in st.session_state:
|
@@ -557,22 +599,21 @@ def main():
|
|
557 |
tabs = st.empty()
|
558 |
tab_cols = st.columns(5)
|
559 |
with tab_cols[0]:
|
560 |
-
if st.button("📖 Create Story"):
|
561 |
st.session_state.active_tab = "story"
|
562 |
with tab_cols[1]:
|
563 |
-
if st.button("🎬 Animation"):
|
564 |
st.session_state.active_tab = "animation"
|
565 |
with tab_cols[2]:
|
566 |
-
if st.button("🔍 Concepts"):
|
567 |
st.session_state.active_tab = "concepts"
|
568 |
with tab_cols[3]:
|
569 |
-
if st.button("💻 Code"):
|
570 |
st.session_state.active_tab = "code"
|
571 |
with tab_cols[4]:
|
572 |
-
if st.button("🔄 Reset"):
|
573 |
st.session_state.story = ""
|
574 |
st.session_state.concepts = []
|
575 |
-
st.session_state.story_scene = None
|
576 |
st.session_state.interactive_animation = None
|
577 |
st.session_state.animation_code = ""
|
578 |
st.session_state.code_description = ""
|
@@ -596,7 +637,7 @@ def main():
|
|
596 |
key="story_input"
|
597 |
)
|
598 |
|
599 |
-
if st.button("Create Animation!", use_container_width=True):
|
600 |
if len(story) < 10:
|
601 |
st.error("Your story needs to be at least 10 characters long!")
|
602 |
else:
|
@@ -607,15 +648,6 @@ def main():
|
|
607 |
# Get the main concept
|
608 |
main_concept = st.session_state.concepts[0] if st.session_state.concepts else "variable"
|
609 |
|
610 |
-
# Load models only when needed
|
611 |
-
with st.spinner("🚀 Loading AI models..."):
|
612 |
-
ai_models = load_models()
|
613 |
-
|
614 |
-
with st.spinner("🎨 Generating AI story scene (this may take 5-10 seconds)..."):
|
615 |
-
st.session_state.story_scene = create_story_scene(
|
616 |
-
story, main_concept, ai_models
|
617 |
-
)
|
618 |
-
|
619 |
with st.spinner("🚀 Creating interactive animation..."):
|
620 |
st.session_state.interactive_animation = create_interactive_animation(
|
621 |
story, main_concept
|
@@ -629,7 +661,7 @@ def main():
|
|
629 |
with st.spinner("🔊 Creating audio narration..."):
|
630 |
audio_text = f"Your story: {story}. This demonstrates the programming concept: {st.session_state.code_description}"
|
631 |
st.session_state.audio_file = text_to_speech_custom(
|
632 |
-
audio_text
|
633 |
)
|
634 |
|
635 |
with st.spinner("🧠 Generating concept explanation..."):
|
@@ -640,6 +672,13 @@ def main():
|
|
640 |
st.session_state.visual_cue
|
641 |
)
|
642 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
643 |
st.session_state.active_tab = "animation"
|
644 |
st.rerun()
|
645 |
|
@@ -649,19 +688,19 @@ def main():
|
|
649 |
with col1:
|
650 |
st.caption("Loop Example")
|
651 |
st.code('"A dragon breathes fire 5 times at the castle"', language="text")
|
652 |
-
st.image(create_animation_preview("
|
653 |
use_container_width=True,
|
654 |
caption="Loop Animation Preview")
|
655 |
with col2:
|
656 |
st.caption("Conditional Example")
|
657 |
st.code('"If it rains, the cat stays inside, else it goes out"', language="text")
|
658 |
-
st.image(create_animation_preview("
|
659 |
use_container_width=True,
|
660 |
caption="Conditional Animation Preview")
|
661 |
with col3:
|
662 |
st.caption("Function Example")
|
663 |
st.code('"A wizard casts a spell to make flowers grow"', language="text")
|
664 |
-
st.image(create_animation_preview("
|
665 |
use_container_width=True,
|
666 |
caption="Function Animation Preview")
|
667 |
|
@@ -674,16 +713,6 @@ def main():
|
|
674 |
st.session_state.active_tab = "story"
|
675 |
st.rerun()
|
676 |
|
677 |
-
# Display AI-generated scene
|
678 |
-
st.subheader("🖼️ AI-Generated Story Scene")
|
679 |
-
if st.session_state.story_scene:
|
680 |
-
st.image(st.session_state.story_scene, use_container_width=True)
|
681 |
-
else:
|
682 |
-
st.warning("Scene couldn't be generated. Showing example instead.")
|
683 |
-
concept = st.session_state.concepts[0] if st.session_state.concepts else "loop"
|
684 |
-
st.image(create_animation_preview("", concept),
|
685 |
-
use_container_width=True)
|
686 |
-
|
687 |
# Display interactive animation
|
688 |
st.subheader("🎮 Interactive Animation")
|
689 |
if st.session_state.interactive_animation:
|
@@ -691,14 +720,14 @@ def main():
|
|
691 |
else:
|
692 |
st.info("Interactive animation preview. The real animation would run on your computer!")
|
693 |
concept = st.session_state.concepts[0] if st.session_state.concepts else "loop"
|
694 |
-
st.image(create_animation_preview(
|
695 |
use_container_width=True)
|
696 |
|
697 |
# Animation concept preview
|
698 |
st.subheader("📽️ Animation Concept Preview")
|
699 |
if st.session_state.concepts:
|
700 |
concept = st.session_state.concepts[0]
|
701 |
-
st.image(create_animation_preview(
|
702 |
caption=f"{CONCEPTS[concept]['name']} Animation Example",
|
703 |
use_container_width=True)
|
704 |
else:
|
@@ -708,7 +737,7 @@ def main():
|
|
708 |
st.subheader("🔊 Story Narration")
|
709 |
if st.session_state.audio_file:
|
710 |
audio_bytes = open(st.session_state.audio_file, 'rb').read()
|
711 |
-
st.audio(audio_bytes, format='audio/
|
712 |
|
713 |
if st.button("▶️ Play Story Automatically", use_container_width=True):
|
714 |
autoplay_audio(st.session_state.audio_file)
|
@@ -727,23 +756,13 @@ def main():
|
|
727 |
<span style="font-size:36px;">{details.get('emoji', '✨')}</span>
|
728 |
<h3>Understanding {details.get('name', 'Programming')} Concept</h3>
|
729 |
</div>
|
730 |
-
|
731 |
</div>
|
732 |
""", unsafe_allow_html=True)
|
733 |
|
734 |
-
# Generate explanation audio if not already generated
|
735 |
-
if not st.session_state.explanation_audio:
|
736 |
-
with st.spinner("🔊 Creating concept explanation audio..."):
|
737 |
-
ai_models = load_models()
|
738 |
-
st.session_state.explanation_audio = text_to_speech_custom(
|
739 |
-
st.session_state.concept_explanation,
|
740 |
-
ai_models,
|
741 |
-
"concept_explanation.wav"
|
742 |
-
)
|
743 |
-
|
744 |
if st.session_state.explanation_audio:
|
745 |
explanation_bytes = open(st.session_state.explanation_audio, 'rb').read()
|
746 |
-
st.audio(explanation_bytes, format='audio/
|
747 |
|
748 |
if st.button("▶️ Play Concept Explanation", use_container_width=True):
|
749 |
autoplay_audio(st.session_state.explanation_audio)
|
@@ -773,21 +792,21 @@ def main():
|
|
773 |
<h3 style="color:{details['color']};">{details['name']}</h3>
|
774 |
</div>
|
775 |
<p>{details['description']}</p>
|
776 |
-
<pre style="background:#
|
777 |
<p><strong>Explanation:</strong> {details.get('explanation', 'Learn how this concept works!')}</p>
|
778 |
</div>
|
779 |
""", unsafe_allow_html=True)
|
780 |
|
781 |
# Show animation preview for the concept
|
782 |
-
st.image(create_animation_preview(
|
783 |
caption=f"{details['name']} Animation Example",
|
784 |
use_container_width=True)
|
785 |
|
786 |
# Play explanation if available
|
787 |
if st.session_state.explanation_audio:
|
788 |
-
st.subheader("🔊 Concept Explanation")
|
789 |
explanation_bytes = open(st.session_state.explanation_audio, 'rb').read()
|
790 |
-
st.audio(explanation_bytes, format='audio/
|
791 |
|
792 |
if st.button("▶️ Play Full Explanation", use_container_width=True):
|
793 |
autoplay_audio(st.session_state.explanation_audio)
|
@@ -808,7 +827,7 @@ def main():
|
|
808 |
|
809 |
# Download button
|
810 |
st.download_button(
|
811 |
-
label="Download Animation Code",
|
812 |
data=st.session_state.animation_code,
|
813 |
file_name="story_animation.py",
|
814 |
mime="text/python",
|
@@ -828,7 +847,7 @@ def main():
|
|
828 |
# Show what the animation would look like
|
829 |
st.subheader("🎬 What Your Animation Would Look Like")
|
830 |
concept = st.session_state.concepts[0] if st.session_state.concepts else "loop"
|
831 |
-
st.image(create_animation_preview(
|
832 |
caption="This is similar to what your animation would look like",
|
833 |
use_container_width=True)
|
834 |
|
@@ -844,21 +863,21 @@ def main():
|
|
844 |
<span style="font-size:36px;">{details.get('emoji', '✨')}</span>
|
845 |
<h3>Understanding the Code</h3>
|
846 |
</div>
|
847 |
-
|
848 |
</div>
|
849 |
""", unsafe_allow_html=True)
|
850 |
|
851 |
# Play explanation audio
|
852 |
if st.session_state.explanation_audio:
|
853 |
explanation_bytes = open(st.session_state.explanation_audio, 'rb').read()
|
854 |
-
st.audio(explanation_bytes, format='audio/
|
855 |
|
856 |
if st.button("▶️ Play Code Explanation", use_container_width=True):
|
857 |
autoplay_audio(st.session_state.explanation_audio)
|
858 |
else:
|
859 |
st.warning("No code generated yet!")
|
860 |
|
861 |
-
if st.button("Create Another Story!", use_container_width=True):
|
862 |
st.session_state.active_tab = "story"
|
863 |
st.session_state.story = ""
|
864 |
st.rerun()
|
|
|
1 |
+
# app.py - Final Version with Enhanced Performance & Light Purple Theme
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
import streamlit as st
|
3 |
import os
|
4 |
import time
|
|
|
15 |
import requests
|
16 |
from io import BytesIO
|
17 |
import json
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
|
19 |
# Configure Streamlit page
|
20 |
st.set_page_config(
|
|
|
24 |
initial_sidebar_state="expanded"
|
25 |
)
|
26 |
|
27 |
+
# Custom CSS with Light Purple Theme
|
28 |
st.markdown("""
|
29 |
<style>
|
30 |
+
@import url('https://fonts.googleapis.com/css2?family=Comic+Neue:wght@700&family=Poppins:wght@400;600&display=swap');
|
31 |
|
32 |
:root {
|
33 |
+
--primary: #8A2BE2; /* Violet */
|
34 |
+
--secondary: #9370DB; /* Medium Purple */
|
35 |
+
--accent: #FFD700; /* Gold */
|
36 |
+
--dark: #4B0082; /* Indigo */
|
37 |
+
--light: #E6E6FA; /* Lavender */
|
38 |
+
--light2: #F5F0FF; /* Light Lavender */
|
39 |
}
|
40 |
|
41 |
body {
|
42 |
+
background: linear-gradient(135deg, var(--light) 0%, var(--light2) 100%);
|
43 |
+
font-family: 'Poppins', sans-serif;
|
44 |
}
|
45 |
|
46 |
.stApp {
|
47 |
+
background: url('https://www.transparenttextures.com/patterns/soft-circle-scales.png');
|
48 |
}
|
49 |
|
50 |
.story-box {
|
51 |
background-color: white;
|
52 |
border-radius: 20px;
|
53 |
padding: 25px;
|
54 |
+
box-shadow: 0 8px 16px rgba(74, 0, 128, 0.15);
|
55 |
border: 3px solid var(--accent);
|
56 |
margin-bottom: 25px;
|
57 |
}
|
58 |
|
59 |
.header {
|
60 |
color: var(--dark);
|
61 |
+
text-shadow: 1px 1px 2px rgba(0,0,0,0.1);
|
62 |
}
|
63 |
|
64 |
.concept-card {
|
65 |
+
background: linear-gradient(145deg, #ffffff, #f8f4ff);
|
66 |
border-radius: 15px;
|
67 |
padding: 15px;
|
68 |
margin: 10px 0;
|
|
|
74 |
background: linear-gradient(45deg, var(--primary), var(--secondary));
|
75 |
color: white;
|
76 |
border-radius: 12px;
|
77 |
+
padding: 12px 28px;
|
78 |
font-weight: bold;
|
79 |
font-size: 18px;
|
80 |
border: none;
|
81 |
transition: all 0.3s;
|
82 |
+
font-family: 'Comic Neue', cursive;
|
83 |
}
|
84 |
|
85 |
.stButton>button:hover {
|
86 |
+
transform: translateY(-3px);
|
87 |
+
box-shadow: 0 6px 12px rgba(138, 43, 226, 0.3);
|
88 |
}
|
89 |
|
90 |
.stTextInput>div>div>input {
|
91 |
border-radius: 12px;
|
92 |
padding: 12px;
|
93 |
border: 2px solid var(--accent);
|
94 |
+
font-family: 'Poppins', sans-serif;
|
95 |
}
|
96 |
|
97 |
.tabs {
|
|
|
102 |
}
|
103 |
|
104 |
.tab {
|
105 |
+
padding: 12px 24px;
|
106 |
+
background-color: var(--secondary);
|
107 |
+
border-radius: 12px;
|
108 |
cursor: pointer;
|
109 |
font-weight: bold;
|
110 |
white-space: nowrap;
|
111 |
+
color: white;
|
112 |
+
transition: all 0.3s;
|
113 |
+
}
|
114 |
+
|
115 |
+
.tab:hover {
|
116 |
+
background-color: var(--primary);
|
117 |
}
|
118 |
|
119 |
.tab.active {
|
120 |
+
background-color: var(--primary);
|
121 |
+
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
122 |
}
|
123 |
|
124 |
@media (max-width: 768px) {
|
|
|
128 |
}
|
129 |
|
130 |
.animation-container {
|
131 |
+
background: linear-gradient(135deg, #d8bfd8, #e6e6fa);
|
132 |
border-radius: 15px;
|
133 |
padding: 20px;
|
134 |
+
box-shadow: 0 8px 32px rgba(75, 0, 130, 0.2);
|
135 |
margin-bottom: 25px;
|
136 |
position: relative;
|
137 |
overflow: hidden;
|
|
|
155 |
width: 100%;
|
156 |
margin: 20px 0;
|
157 |
border-radius: 20px;
|
158 |
+
background: #f0e6ff;
|
159 |
padding: 15px;
|
160 |
}
|
161 |
|
162 |
.animation-gif {
|
163 |
max-width: 100%;
|
164 |
border-radius: 10px;
|
165 |
+
box-shadow: 0 8px 16px rgba(0,0,0,0.15);
|
166 |
+
border: 2px solid white;
|
167 |
}
|
168 |
|
169 |
.ai-animation {
|
|
|
175 |
}
|
176 |
|
177 |
.explanation-box {
|
178 |
+
background: linear-gradient(135deg, #f0e6ff, #e6d8ff);
|
179 |
border-radius: 15px;
|
180 |
padding: 20px;
|
181 |
margin: 20px 0;
|
|
|
188 |
display: flex;
|
189 |
align-items: center;
|
190 |
gap: 10px;
|
191 |
+
margin-bottom: 15px;
|
192 |
+
}
|
193 |
+
|
194 |
+
.magic-pulse {
|
195 |
+
animation: pulse 2s infinite;
|
196 |
+
}
|
197 |
+
|
198 |
+
@keyframes pulse {
|
199 |
+
0% { transform: scale(1); }
|
200 |
+
50% { transform: scale(1.05); }
|
201 |
+
100% { transform: scale(1); }
|
202 |
+
}
|
203 |
+
|
204 |
+
.concept-highlight {
|
205 |
+
background: linear-gradient(120deg, #e0d6ff, #d8c8ff);
|
206 |
+
padding: 3px 8px;
|
207 |
+
border-radius: 6px;
|
208 |
+
font-weight: bold;
|
209 |
}
|
210 |
</style>
|
211 |
""", unsafe_allow_html=True)
|
|
|
217 |
"emoji": "🔄",
|
218 |
"description": "Loops repeat actions multiple times",
|
219 |
"example": "for i in range(5):\n print('Hello!')",
|
220 |
+
"color": "#8A2BE2",
|
221 |
"explanation": "A loop is like a magic spell that makes something happen again and again. In programming, we use loops when we want to repeat an action multiple times without writing the same code over and over."
|
222 |
},
|
223 |
"conditional": {
|
|
|
225 |
"emoji": "❓",
|
226 |
"description": "Conditionals make decisions in code",
|
227 |
"example": "if sunny:\n go_outside()\nelse:\n stay_inside()",
|
228 |
+
"color": "#9370DB",
|
229 |
"explanation": "Conditionals are like crossroads in a story where you choose which path to take. In programming, we use 'if' statements to make decisions based on certain conditions, just like choosing what to wear based on the weather."
|
230 |
},
|
231 |
"function": {
|
|
|
233 |
"emoji": "✨",
|
234 |
"description": "Functions are reusable blocks of code",
|
235 |
"example": "def greet(name):\n print(f'Hello {name}!')",
|
236 |
+
"color": "#DA70D6",
|
237 |
"explanation": "Functions are like magic spells you can create and reuse whenever you need them. They help us organize code into reusable blocks so we don't have to write the same thing multiple times."
|
238 |
},
|
239 |
"variable": {
|
|
|
241 |
"emoji": "📦",
|
242 |
"description": "Variables store information",
|
243 |
"example": "score = 10\nplayer = 'Alex'",
|
244 |
+
"color": "#BA55D3",
|
245 |
"explanation": "Variables are like labeled boxes where you can store information. They help us remember values and use them later in our code, just like remembering a character's name in a story."
|
246 |
},
|
247 |
"list": {
|
|
|
249 |
"emoji": "📝",
|
250 |
"description": "Lists store collections of items",
|
251 |
"example": "fruits = ['apple', 'banana', 'orange']",
|
252 |
+
"color": "#9932CC",
|
253 |
"explanation": "Lists are like magical scrolls that can hold multiple items. In programming, we use lists to store collections of related things, like a wizard's inventory of potions."
|
254 |
}
|
255 |
}
|
|
|
258 |
ANIMATION_EXAMPLES = {
|
259 |
"loop": "https://media.giphy.com/media/3o7abKhOpu0NwenH3O/giphy.gif",
|
260 |
"conditional": "https://media.giphy.com/media/l0HlG8vJXW0X5yX4s/giphy.gif",
|
261 |
+
"function": "https://media.giphy.com/media/3o7TKsQ8UQ4l4LhGz6/giphy.gif",
|
262 |
+
"variable": "https://media.giphy.com/media/3o7TKsQ8UQ4l4LhGz6/giphy.gif",
|
263 |
+
"list": "https://media.giphy.com/media/3o7abKhOpu0NwenH3O/giphy.gif"
|
264 |
}
|
265 |
|
266 |
+
# Fallback images if URLs fail
|
267 |
+
FALLBACK_IMAGES = {
|
268 |
+
"loop": "https://i.ibb.co/4sY7d0j/loop.gif",
|
269 |
+
"conditional": "https://i.ibb.co/8Xg7zY6/conditional.gif",
|
270 |
+
"function": "https://i.ibb.co/7yVnZ8C/function.gif",
|
271 |
+
"variable": "https://i.ibb.co/7yVnZ8C/function.gif",
|
272 |
+
"list": "https://i.ibb.co/4sY7d0j/loop.gif"
|
273 |
+
}
|
274 |
+
|
275 |
+
def check_url(url):
|
276 |
+
"""Check if a URL is valid and accessible"""
|
277 |
try:
|
278 |
+
response = requests.head(url, timeout=5)
|
279 |
+
return response.status_code == 200
|
280 |
+
except:
|
281 |
+
return False
|
282 |
+
|
283 |
+
def create_animation_preview(concept):
|
284 |
+
"""Create an animation preview for the concept with fallback"""
|
|
|
|
|
|
|
|
|
|
|
285 |
try:
|
286 |
+
# Check if the URL is valid
|
287 |
+
url = ANIMATION_EXAMPLES.get(concept, "")
|
288 |
+
if url and check_url(url):
|
289 |
+
return url
|
290 |
+
|
291 |
+
# Use fallback if primary URL fails
|
292 |
+
return FALLBACK_IMAGES.get(concept, FALLBACK_IMAGES["loop"])
|
293 |
except:
|
294 |
+
return FALLBACK_IMAGES["loop"]
|
|
|
|
|
295 |
|
296 |
def analyze_story(story):
|
297 |
"""Analyze story and identify programming concepts"""
|
|
|
299 |
detected_concepts = []
|
300 |
|
301 |
# Check for loops
|
302 |
+
if any(word in story_lower for word in ["times", "repeat", "again", "multiple", "each"]):
|
303 |
detected_concepts.append("loop")
|
304 |
|
305 |
# Check for conditionals
|
306 |
+
if any(word in story_lower for word in ["if", "when", "unless", "whether", "decide"]):
|
307 |
detected_concepts.append("conditional")
|
308 |
|
309 |
# Check for functions
|
310 |
+
if any(word in story_lower for word in ["make", "create", "do", "perform", "cast", "spell"]):
|
311 |
detected_concepts.append("function")
|
312 |
|
313 |
# Check for variables
|
314 |
+
if any(word in story_lower for word in ["is", "has", "set to", "value", "named", "called"]):
|
315 |
detected_concepts.append("variable")
|
316 |
|
317 |
# Check for lists
|
318 |
+
if any(word in story_lower for word in ["and", "many", "several", "collection", "items", "group"]):
|
319 |
detected_concepts.append("list")
|
320 |
|
321 |
return list(set(detected_concepts))
|
|
|
327 |
return min(int(word), 10)
|
328 |
return 3 # Default value
|
329 |
|
330 |
+
def text_to_speech_custom(text, filename="story_audio.mp3"):
|
331 |
+
"""Convert text to speech using gTTS (faster and reliable)"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
332 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
333 |
tts = gTTS(text=text, lang='en')
|
334 |
tts.save(filename)
|
335 |
return filename
|
|
|
344 |
b64 = base64.b64encode(data).decode()
|
345 |
md = f"""
|
346 |
<audio autoplay>
|
347 |
+
<source src="data:audio/mp3;base64,{b64}" type="audio/mp3">
|
348 |
</audio>
|
349 |
"""
|
350 |
st.markdown(md, unsafe_allow_html=True)
|
|
|
352 |
def generate_animation_code(story, concept):
|
353 |
"""Generate Python animation code based on story"""
|
354 |
try:
|
355 |
+
count = extract_count_from_story(story)
|
356 |
+
short_story = story[:20] + '...' if len(story) > 20 else story
|
357 |
+
|
358 |
# Sample code based on concept
|
359 |
if concept == "loop":
|
360 |
code = f"""
|
361 |
+
# Loop Animation: Repeating Actions
|
362 |
import time
|
363 |
|
364 |
def animate_story():
|
365 |
+
actions = {count}
|
366 |
+
|
367 |
+
print("Story: {short_story}")
|
368 |
+
print("Concept: Looping {count} times")
|
369 |
|
370 |
for i in range(actions):
|
371 |
+
print(f"Action {{i+1}}: Performing story action")
|
372 |
# Animation code would go here
|
373 |
time.sleep(0.5)
|
374 |
|
375 |
+
print("Story animation complete!")
|
376 |
+
|
377 |
animate_story()
|
378 |
"""
|
379 |
description = "Looping through actions multiple times"
|
|
|
381 |
|
382 |
elif concept == "conditional":
|
383 |
code = f"""
|
384 |
+
# Conditional Animation: Making Decisions
|
385 |
import random
|
386 |
|
387 |
+
print("Story: {short_story}")
|
388 |
+
print("Concept: Making decisions based on conditions")
|
389 |
+
|
390 |
condition = random.choice([True, False])
|
391 |
+
print(f"Condition is {{condition}}")
|
392 |
|
393 |
if condition:
|
394 |
+
print("Path 1: Following the true branch")
|
395 |
# True branch animation
|
396 |
else:
|
397 |
+
print("Path 2: Following the false branch")
|
398 |
# False branch animation
|
399 |
+
|
400 |
+
print("Story animation complete!")
|
401 |
"""
|
402 |
description = "Making decisions based on conditions"
|
403 |
visual_cue = "Branching paths based on conditions"
|
404 |
|
405 |
+
elif concept == "function":
|
406 |
code = f"""
|
407 |
+
# Function Animation: Reusable Actions
|
408 |
+
import time
|
409 |
+
|
410 |
+
print("Story: {short_story}")
|
411 |
+
print("Concept: Creating reusable functions")
|
412 |
+
|
413 |
def perform_action():
|
414 |
+
print("Performing story action")
|
415 |
# Action animation code
|
416 |
|
417 |
# Call the function multiple times
|
418 |
+
for _ in range({count}):
|
419 |
perform_action()
|
420 |
+
time.sleep(0.3)
|
421 |
+
|
422 |
+
print("Story animation complete!")
|
423 |
"""
|
424 |
description = "Reusing code with functions"
|
425 |
visual_cue = "Calling a reusable function"
|
426 |
|
427 |
+
elif concept == "variable":
|
428 |
+
code = f"""
|
429 |
+
# Variable Animation: Storing Information
|
430 |
+
import time
|
431 |
+
|
432 |
+
print("Story: {short_story}")
|
433 |
+
print("Concept: Using variables to store information")
|
434 |
+
|
435 |
+
character = "Hero"
|
436 |
+
score = 10
|
437 |
+
inventory = ["sword", "shield", "potion"]
|
438 |
+
|
439 |
+
print(f"Welcome {{character}}! You have {{score}} points.")
|
440 |
+
print(f"Your inventory: {{inventory}}")
|
441 |
+
|
442 |
+
for item in inventory:
|
443 |
+
print(f"Using {{item}}...")
|
444 |
+
time.sleep(0.5)
|
445 |
+
|
446 |
+
print("Story animation complete!")
|
447 |
+
"""
|
448 |
+
description = "Storing information in variables"
|
449 |
+
visual_cue = "Using stored values"
|
450 |
+
|
451 |
+
else: # list
|
452 |
+
code = f"""
|
453 |
+
# List Animation: Managing Collections
|
454 |
+
import time
|
455 |
+
|
456 |
+
print("Story: {short_story}")
|
457 |
+
print("Concept: Working with lists of items")
|
458 |
+
|
459 |
+
items = ["item1", "item2", "item3", "item4", "item5"][:{count}]
|
460 |
+
print(f"Collection has {{len(items)}} items")
|
461 |
+
|
462 |
+
for i, item in enumerate(items):
|
463 |
+
print(f"Processing item {{i+1}}: {{item}}")
|
464 |
+
time.sleep(0.4)
|
465 |
+
|
466 |
+
print("Story animation complete!")
|
467 |
+
"""
|
468 |
+
description = "Managing collections of items"
|
469 |
+
visual_cue = "Processing multiple items"
|
470 |
+
|
471 |
return code, description, visual_cue
|
472 |
|
473 |
except Exception as e:
|
|
|
502 |
size="size",
|
503 |
size_max=45,
|
504 |
color="color",
|
505 |
+
color_discrete_sequence=px.colors.qualitative.Pastel,
|
506 |
range_x=[0, 10],
|
507 |
range_y=[0, 10],
|
508 |
title=f"Interactive Animation: {story[:40]}{'...' if len(story) > 40 else ''}"
|
|
|
511 |
# Customize layout
|
512 |
fig.update_layout(
|
513 |
showlegend=False,
|
514 |
+
plot_bgcolor='rgba(245, 240, 255, 0.8)',
|
515 |
+
paper_bgcolor='rgba(245, 240, 255, 0.8)',
|
516 |
width=800,
|
517 |
height=600,
|
518 |
xaxis=dict(showgrid=False, zeroline=False, visible=False),
|
519 |
yaxis=dict(showgrid=False, zeroline=False, visible=False),
|
520 |
+
font_family="Poppins"
|
521 |
)
|
522 |
|
523 |
fig.update_traces(
|
524 |
+
textfont_size=18,
|
525 |
textposition='middle center',
|
526 |
marker=dict(sizemode='diameter')
|
527 |
)
|
|
|
539 |
explanation = f"""
|
540 |
Let me explain what's happening in your story and how it relates to programming!
|
541 |
|
542 |
+
<div class='concept-highlight'>Your story</div>: "{story[:100]}..."
|
543 |
|
544 |
+
<div class='concept-highlight'>Programming concept</div>: {concept_info.get('name', 'Programming')}
|
545 |
|
546 |
{concept_info.get('explanation', 'This is a fundamental programming concept.')}
|
547 |
|
548 |
+
<div class='concept-highlight'>In our animation</div>:
|
549 |
- {visual_cue}
|
550 |
- This represents: {code_description}
|
551 |
|
552 |
+
<div class='concept-highlight'>How it works in code</div>:
|
553 |
We use {concept_info.get('name', 'this concept')} like this:
|
554 |
+
```
|
555 |
{concept_info.get('example', 'Example code will appear here')}
|
556 |
+
```
|
557 |
|
558 |
+
<div class='concept-highlight'>Real-world use</div>:
|
559 |
+
You might use this concept when:
|
560 |
- Creating games with repeating actions (loops)
|
561 |
- Making decisions in apps (conditionals)
|
562 |
- Building reusable components (functions)
|
563 |
+
- Storing player information (variables)
|
564 |
+
- Managing collections of data (lists)
|
565 |
|
566 |
Try to spot this concept in other stories you create!
|
567 |
"""
|
|
|
578 |
st.session_state.story = ""
|
579 |
if 'concepts' not in st.session_state:
|
580 |
st.session_state.concepts = []
|
|
|
|
|
581 |
if 'interactive_animation' not in st.session_state:
|
582 |
st.session_state.interactive_animation = None
|
583 |
if 'animation_code' not in st.session_state:
|
|
|
599 |
tabs = st.empty()
|
600 |
tab_cols = st.columns(5)
|
601 |
with tab_cols[0]:
|
602 |
+
if st.button("📖 Create Story", key="tab_story"):
|
603 |
st.session_state.active_tab = "story"
|
604 |
with tab_cols[1]:
|
605 |
+
if st.button("🎬 Animation", key="tab_animation"):
|
606 |
st.session_state.active_tab = "animation"
|
607 |
with tab_cols[2]:
|
608 |
+
if st.button("🔍 Concepts", key="tab_concepts"):
|
609 |
st.session_state.active_tab = "concepts"
|
610 |
with tab_cols[3]:
|
611 |
+
if st.button("💻 Code", key="tab_code"):
|
612 |
st.session_state.active_tab = "code"
|
613 |
with tab_cols[4]:
|
614 |
+
if st.button("🔄 Reset", key="tab_reset"):
|
615 |
st.session_state.story = ""
|
616 |
st.session_state.concepts = []
|
|
|
617 |
st.session_state.interactive_animation = None
|
618 |
st.session_state.animation_code = ""
|
619 |
st.session_state.code_description = ""
|
|
|
637 |
key="story_input"
|
638 |
)
|
639 |
|
640 |
+
if st.button("✨ Create Animation!", use_container_width=True, key="create_animation"):
|
641 |
if len(story) < 10:
|
642 |
st.error("Your story needs to be at least 10 characters long!")
|
643 |
else:
|
|
|
648 |
# Get the main concept
|
649 |
main_concept = st.session_state.concepts[0] if st.session_state.concepts else "variable"
|
650 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
651 |
with st.spinner("🚀 Creating interactive animation..."):
|
652 |
st.session_state.interactive_animation = create_interactive_animation(
|
653 |
story, main_concept
|
|
|
661 |
with st.spinner("🔊 Creating audio narration..."):
|
662 |
audio_text = f"Your story: {story}. This demonstrates the programming concept: {st.session_state.code_description}"
|
663 |
st.session_state.audio_file = text_to_speech_custom(
|
664 |
+
audio_text
|
665 |
)
|
666 |
|
667 |
with st.spinner("🧠 Generating concept explanation..."):
|
|
|
672 |
st.session_state.visual_cue
|
673 |
)
|
674 |
|
675 |
+
with st.spinner("🔊 Creating explanation audio..."):
|
676 |
+
explanation_text = st.session_state.concept_explanation.replace('<div class="concept-highlight">', '').replace('</div>', '')
|
677 |
+
st.session_state.explanation_audio = text_to_speech_custom(
|
678 |
+
explanation_text,
|
679 |
+
"concept_explanation.mp3"
|
680 |
+
)
|
681 |
+
|
682 |
st.session_state.active_tab = "animation"
|
683 |
st.rerun()
|
684 |
|
|
|
688 |
with col1:
|
689 |
st.caption("Loop Example")
|
690 |
st.code('"A dragon breathes fire 5 times at the castle"', language="text")
|
691 |
+
st.image(create_animation_preview("loop"),
|
692 |
use_container_width=True,
|
693 |
caption="Loop Animation Preview")
|
694 |
with col2:
|
695 |
st.caption("Conditional Example")
|
696 |
st.code('"If it rains, the cat stays inside, else it goes out"', language="text")
|
697 |
+
st.image(create_animation_preview("conditional"),
|
698 |
use_container_width=True,
|
699 |
caption="Conditional Animation Preview")
|
700 |
with col3:
|
701 |
st.caption("Function Example")
|
702 |
st.code('"A wizard casts a spell to make flowers grow"', language="text")
|
703 |
+
st.image(create_animation_preview("function"),
|
704 |
use_container_width=True,
|
705 |
caption="Function Animation Preview")
|
706 |
|
|
|
713 |
st.session_state.active_tab = "story"
|
714 |
st.rerun()
|
715 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
716 |
# Display interactive animation
|
717 |
st.subheader("🎮 Interactive Animation")
|
718 |
if st.session_state.interactive_animation:
|
|
|
720 |
else:
|
721 |
st.info("Interactive animation preview. The real animation would run on your computer!")
|
722 |
concept = st.session_state.concepts[0] if st.session_state.concepts else "loop"
|
723 |
+
st.image(create_animation_preview(concept),
|
724 |
use_container_width=True)
|
725 |
|
726 |
# Animation concept preview
|
727 |
st.subheader("📽️ Animation Concept Preview")
|
728 |
if st.session_state.concepts:
|
729 |
concept = st.session_state.concepts[0]
|
730 |
+
st.image(create_animation_preview(concept),
|
731 |
caption=f"{CONCEPTS[concept]['name']} Animation Example",
|
732 |
use_container_width=True)
|
733 |
else:
|
|
|
737 |
st.subheader("🔊 Story Narration")
|
738 |
if st.session_state.audio_file:
|
739 |
audio_bytes = open(st.session_state.audio_file, 'rb').read()
|
740 |
+
st.audio(audio_bytes, format='audio/mp3')
|
741 |
|
742 |
if st.button("▶️ Play Story Automatically", use_container_width=True):
|
743 |
autoplay_audio(st.session_state.audio_file)
|
|
|
756 |
<span style="font-size:36px;">{details.get('emoji', '✨')}</span>
|
757 |
<h3>Understanding {details.get('name', 'Programming')} Concept</h3>
|
758 |
</div>
|
759 |
+
{st.session_state.concept_explanation}
|
760 |
</div>
|
761 |
""", unsafe_allow_html=True)
|
762 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
763 |
if st.session_state.explanation_audio:
|
764 |
explanation_bytes = open(st.session_state.explanation_audio, 'rb').read()
|
765 |
+
st.audio(explanation_bytes, format='audio/mp3')
|
766 |
|
767 |
if st.button("▶️ Play Concept Explanation", use_container_width=True):
|
768 |
autoplay_audio(st.session_state.explanation_audio)
|
|
|
792 |
<h3 style="color:{details['color']};">{details['name']}</h3>
|
793 |
</div>
|
794 |
<p>{details['description']}</p>
|
795 |
+
<pre style="background:#f8f4ff; padding:10px; border-radius:8px;">{details['example']}</pre>
|
796 |
<p><strong>Explanation:</strong> {details.get('explanation', 'Learn how this concept works!')}</p>
|
797 |
</div>
|
798 |
""", unsafe_allow_html=True)
|
799 |
|
800 |
# Show animation preview for the concept
|
801 |
+
st.image(create_animation_preview(concept),
|
802 |
caption=f"{details['name']} Animation Example",
|
803 |
use_container_width=True)
|
804 |
|
805 |
# Play explanation if available
|
806 |
if st.session_state.explanation_audio:
|
807 |
+
st.subheader("🔊 Full Concept Explanation")
|
808 |
explanation_bytes = open(st.session_state.explanation_audio, 'rb').read()
|
809 |
+
st.audio(explanation_bytes, format='audio/mp3')
|
810 |
|
811 |
if st.button("▶️ Play Full Explanation", use_container_width=True):
|
812 |
autoplay_audio(st.session_state.explanation_audio)
|
|
|
827 |
|
828 |
# Download button
|
829 |
st.download_button(
|
830 |
+
label="📥 Download Animation Code",
|
831 |
data=st.session_state.animation_code,
|
832 |
file_name="story_animation.py",
|
833 |
mime="text/python",
|
|
|
847 |
# Show what the animation would look like
|
848 |
st.subheader("🎬 What Your Animation Would Look Like")
|
849 |
concept = st.session_state.concepts[0] if st.session_state.concepts else "loop"
|
850 |
+
st.image(create_animation_preview(concept),
|
851 |
caption="This is similar to what your animation would look like",
|
852 |
use_container_width=True)
|
853 |
|
|
|
863 |
<span style="font-size:36px;">{details.get('emoji', '✨')}</span>
|
864 |
<h3>Understanding the Code</h3>
|
865 |
</div>
|
866 |
+
{st.session_state.concept_explanation}
|
867 |
</div>
|
868 |
""", unsafe_allow_html=True)
|
869 |
|
870 |
# Play explanation audio
|
871 |
if st.session_state.explanation_audio:
|
872 |
explanation_bytes = open(st.session_state.explanation_audio, 'rb').read()
|
873 |
+
st.audio(explanation_bytes, format='audio/mp3')
|
874 |
|
875 |
if st.button("▶️ Play Code Explanation", use_container_width=True):
|
876 |
autoplay_audio(st.session_state.explanation_audio)
|
877 |
else:
|
878 |
st.warning("No code generated yet!")
|
879 |
|
880 |
+
if st.button("✨ Create Another Story!", use_container_width=True):
|
881 |
st.session_state.active_tab = "story"
|
882 |
st.session_state.story = ""
|
883 |
st.rerun()
|