sunbal7 commited on
Commit
688c6f3
ยท
verified ยท
1 Parent(s): f39c7e1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +467 -94
app.py CHANGED
@@ -1,103 +1,476 @@
1
- import os
2
  import streamlit as st
3
- from dotenv import load_dotenv
4
- from openai import OpenAI
5
- import ast
6
- import subprocess
7
- import tempfile
8
-
9
- load_dotenv()
10
-
11
- class StoryToCodeConverter:
12
- def __init__(self):
13
- api_key = os.getenv("NOVITA_API_KEY")
14
- if not api_key:
15
- raise ValueError("NOVITA_API_KEY not found in environment")
16
- self.client = OpenAI(
17
- api_key=api_key,
18
- base_url="https://api.novita.ai/v3/openai"
19
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
- def generate_code(self, story: str) -> str:
22
- prompt = f"""
23
- Convert this children's story into Python animation code using turtle graphics.
24
- Include comments that explain loops, movement, colorsโ€”easy for kids.
25
-
26
- Story:
27
- \"\"\"{story}\"\"\"
28
-
29
- Wrap the code in triple backticks like ```python ... ```
30
- """
31
- response = self.client.chat.completions.create(
32
- model="meta-llama/llama-3.1-8b-instruct",
33
- messages=[
34
- {"role": "system", "content": "You are a friendly teacher and coder for kids."},
35
- {"role": "user", "content": prompt}
36
- ],
37
- max_tokens=512,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  )
39
- return self._extract_code(response.choices[0].message.content)
40
-
41
- def _extract_code(self, content: str) -> str:
42
- if "```python" in content:
43
- return content.split("```python")[1].split("```")[0]
44
- return content
45
-
46
-
47
- class CodeExecutor:
48
- def execute_safely(self, code: str) -> bool:
49
- try:
50
- ast.parse(code)
51
- with tempfile.NamedTemporaryFile(delete=False, suffix=".py") as tmp:
52
- tmp.write(code.encode("utf-8"))
53
- path = tmp.name
54
- subprocess.Popen(["python", path])
55
- return True
56
- except Exception as e:
57
- st.error(f"Robot Chef burned the cake: {e}")
58
- return False
59
-
60
-
61
- st.set_page_config(page_title="CodeTales", page_icon="โœจ")
62
- with st.sidebar:
63
- st.header("๐Ÿฐ How It Works")
64
  st.markdown("""
65
- 1. ๐Ÿ“ Kids write a story
66
- 2. ๐Ÿ”ฎ Novita AI turns it into Pythonโ€‘turtle code
67
- 3. ๐ŸŽฎ Animation plays
68
- 4. ๐Ÿค– Robot Teacher explains concepts
69
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
 
71
- st.title("โœจ CodeTales: Storytime + Coding Magic")
72
- story = st.text_area("Write your story here:", height=200,
73
- placeholder="A turtle draws a rainbow circle...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
- if st.button("โœจ Bake My Story Cake!"):
76
- if not story.strip():
77
- st.warning("Please enter a story!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  else:
79
- with st.spinner("Baking your story code... ๐Ÿฐ"):
80
- try:
81
- converter = StoryToCodeConverter()
82
- code = converter.generate_code(story)
83
- except Exception as e:
84
- st.error(f"AI oven broke down! {e}")
85
- st.stop()
86
-
87
- st.subheader("๐Ÿ“œ Your Code Recipe")
88
- st.code(code, language="python")
89
-
90
- executor = CodeExecutor()
91
- if executor.execute_safely(code):
92
- st.balloons()
93
- with st.expander("๐Ÿค– Robot Teacher Says:"):
94
- st.markdown("""
95
- - `turtle.forward(100)` โ€“ Moves forward
96
- - `turtle.color("red")` โ€“ Changes color
97
- - `for` loops โ€“ Repeat patterns
98
  """)
99
- st.success("๐ŸŽ‰ Animation ready!")
100
- else:
101
- st.error("Oops, something went wrong.")
102
 
103
- st.caption("Made with โค๏ธ for young coders | Powered by Novita AI + Python Turtle")
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
  import streamlit as st
3
+ import time
4
+ import random
5
+ from transformers import pipeline
6
+ from PIL import Image, ImageDraw, ImageFont
7
+ import numpy as np
8
+ import textwrap
9
+ import os
10
+ import base64
11
+ from io import BytesIO
12
+
13
+ # Set up the page
14
+ st.set_page_config(
15
+ page_title="CodeTales โœจ",
16
+ page_icon="๐Ÿš€",
17
+ layout="wide",
18
+ initial_sidebar_state="expanded"
19
+ )
20
+
21
+ # Custom CSS
22
+ st.markdown("""
23
+ <style>
24
+ @import url('https://fonts.googleapis.com/css2?family=Comic+Neue:wght@700&display=swap');
25
+
26
+ .stApp {
27
+ background: linear-gradient(135deg, #6e8efb, #a777e3);
28
+ }
29
+
30
+ .header {
31
+ font-family: 'Comic Neue', cursive;
32
+ color: white;
33
+ text-align: center;
34
+ font-size: 3.5rem;
35
+ text-shadow: 3px 3px 0 #000;
36
+ padding: 1rem;
37
+ }
38
+
39
+ .subheader {
40
+ font-family: 'Comic Neue', cursive;
41
+ color: #ffeb3b;
42
+ text-align: center;
43
+ font-size: 1.8rem;
44
+ margin-bottom: 2rem;
45
+ }
46
+
47
+ .story-box {
48
+ background-color: rgba(255, 255, 255, 0.9);
49
+ border-radius: 20px;
50
+ padding: 2rem;
51
+ box-shadow: 0 8px 16px rgba(0,0,0,0.2);
52
+ margin-bottom: 2rem;
53
+ }
54
+
55
+ .robot-speech {
56
+ background-color: #4caf50;
57
+ color: white;
58
+ border-radius: 20px;
59
+ padding: 1.5rem;
60
+ font-size: 1.2rem;
61
+ position: relative;
62
+ margin-top: 2rem;
63
+ }
64
+
65
+ .robot-speech:after {
66
+ content: '';
67
+ position: absolute;
68
+ top: -20px;
69
+ left: 50px;
70
+ border: 10px solid transparent;
71
+ border-bottom-color: #4caf50;
72
+ }
73
+
74
+ .generate-btn {
75
+ background: #ff5722 !important;
76
+ color: white !important;
77
+ font-weight: bold !important;
78
+ font-size: 1.2rem !important;
79
+ padding: 0.7rem 2rem !important;
80
+ border-radius: 50px !important;
81
+ border: none !important;
82
+ box-shadow: 0 4px 8px rgba(0,0,0,0.3) !important;
83
+ transition: all 0.3s !important;
84
+ margin-top: 1rem;
85
+ }
86
+
87
+ .generate-btn:hover {
88
+ transform: scale(1.05) !important;
89
+ box-shadow: 0 6px 12px rgba(0,0,0,0.4) !important;
90
+ }
91
+
92
+ .code-block {
93
+ background: #2d2d2d;
94
+ color: #f8f8f2;
95
+ padding: 1rem;
96
+ border-radius: 10px;
97
+ font-family: monospace;
98
+ font-size: 1.1rem;
99
+ margin: 1rem 0;
100
+ overflow-x: auto;
101
+ }
102
+
103
+ .animation-frame {
104
+ border: 3px solid #ffeb3b;
105
+ border-radius: 10px;
106
+ margin: 5px;
107
+ }
108
+ </style>
109
+ """, unsafe_allow_html=True)
110
 
111
+ # Initialize AI models
112
+ @st.cache_resource
113
+ def load_models():
114
+ """Load open-source AI models"""
115
+ try:
116
+ # Text generation model for code explanations
117
+ story_to_code = pipeline("text-generation", model="gpt2", max_length=100)
118
+
119
+ # Sentiment analysis for story understanding
120
+ sentiment_analyzer = pipeline("sentiment-analysis", model="distilbert-base-uncased-finetuned-sst-2-english")
121
+
122
+ # Named entity recognition for identifying objects
123
+ ner_model = pipeline("ner", model="dbmdz/bert-large-cased-finetuned-conll03-english")
124
+
125
+ return story_to_code, sentiment_analyzer, ner_model
126
+ except Exception as e:
127
+ st.error(f"Error loading models: {e}")
128
+ return None, None, None
129
+
130
+ # Image generation functions
131
+ def create_storyboard_image(text, width=400, height=300):
132
+ """Create a storyboard image from text"""
133
+ # Create blank image
134
+ img = Image.new('RGB', (width, height), color=(25, 25, 112)) # Dark blue background
135
+
136
+ # Load a comic-style font (fallback to default if not available)
137
+ try:
138
+ font = ImageFont.truetype("comic.ttf", 16)
139
+ except:
140
+ font = ImageFont.load_default()
141
+
142
+ draw = ImageDraw.Draw(img)
143
+
144
+ # Draw title
145
+ draw.text((10, 10), "Your Story Comes to Life!", fill=(255, 215, 0), font=font)
146
+
147
+ # Draw text box
148
+ draw.rectangle([10, 40, width-10, height-40], fill=(240, 248, 255), outline=(255, 215, 0), width=2)
149
+
150
+ # Wrap text
151
+ wrapped_text = textwrap.fill(text, width=40)
152
+ draw.text((20, 50), wrapped_text, fill=(25, 25, 112), font=font)
153
+
154
+ # Draw decorations
155
+ draw.rectangle([width-50, height-30, width-30, height-10], fill=(220, 20, 60), outline=(255, 215, 0), width=1)
156
+ draw.ellipse([20, height-50, 70, height], fill=(30, 144, 255), outline=(255, 215, 0), width=1)
157
+
158
+ return img
159
+
160
+ def generate_sprite_animation(story, num_frames=4):
161
+ """Generate a sprite-based animation from story"""
162
+ frames = []
163
+ width, height = 300, 200
164
+
165
+ for i in range(num_frames):
166
+ # Create base image
167
+ img = Image.new('RGB', (width, height), color=(0, 0, 30))
168
+ draw = ImageDraw.Draw(img)
169
+
170
+ # Draw stars
171
+ for _ in range(30):
172
+ x = random.randint(0, width)
173
+ y = random.randint(0, height)
174
+ draw.ellipse([x, y, x+2, y+2], fill=(255, 255, 255))
175
+
176
+ # Draw moving elements based on frame
177
+ if "spaceship" in story.lower():
178
+ ship_x = 50 + i * 60
179
+ ship_y = 80
180
+ draw.polygon([(ship_x, ship_y), (ship_x+30, ship_y),
181
+ (ship_x+15, ship_y-20)], fill=(169, 169, 169))
182
+
183
+ if "shoot" in story.lower() and i > 1:
184
+ for j in range(3):
185
+ laser_x = ship_x + 15
186
+ laser_y = ship_y - 20 + j*5
187
+ draw.line([(laser_x, laser_y), (width, laser_y)], fill=(255, 0, 0), width=2)
188
+
189
+ if "alien" in story.lower():
190
+ alien_x = 200
191
+ alien_y = 100 - i*10
192
+ draw.ellipse([alien_x, alien_y, alien_x+20, alien_y+20], fill=(50, 205, 50))
193
+ draw.ellipse([alien_x+5, alien_y+5, alien_x+7, alien_y+7], fill=(0, 0, 0))
194
+ draw.ellipse([alien_x+13, alien_y+5, alien_x+15, alien_y+7], fill=(0, 0, 0))
195
+
196
+ if "dragon" in story.lower():
197
+ dragon_x = 150 + i*20
198
+ dragon_y = 150
199
+ draw.ellipse([dragon_x, dragon_y, dragon_x+40, dragon_y+20], fill=(178, 34, 34))
200
+ draw.line([(dragon_x+40, dragon_y+10), (dragon_x+60, dragon_y)], fill=(178, 34, 34), width=3)
201
+
202
+ if "fire" in story.lower() and i > 0:
203
+ for j in range(5):
204
+ flame_x = dragon_x + 60
205
+ flame_y = dragon_y - j*5
206
+ flame_size = random.randint(5, 15)
207
+ draw.ellipse([flame_x, flame_y, flame_x+flame_size, flame_y+flame_size],
208
+ fill=(255, random.randint(100, 200), 0))
209
+
210
+ frames.append(img)
211
+
212
+ return frames
213
+
214
+ def generate_code_explanation(story, story_to_code):
215
+ """Generate code explanation using open-source model"""
216
+ try:
217
+ # Create a prompt for the model
218
+ prompt = f"Explain how this story would be translated to code: '{story}'. Show a code snippet and explanation."
219
+
220
+ # Generate text
221
+ result = story_to_code(
222
+ prompt,
223
+ max_length=150,
224
+ num_return_sequences=1,
225
+ temperature=0.7,
226
+ top_k=50
227
  )
228
+
229
+ return result[0]['generated_text']
230
+ except:
231
+ # Fallback explanation if model fails
232
+ return f"""See how your story became real code? For example, when you wrote "{story.split()[0]}",
233
+ we used code like: character.move(). That's how we turn words into actions!"""
234
+
235
+ # Header section
236
+ st.markdown('<div class="header">CodeTales โœจ</div>', unsafe_allow_html=True)
237
+ st.markdown('<div class="subheader">Storytime + Coding Magic</div>', unsafe_allow_html=True)
238
+ st.markdown('<div style="text-align:center; color:white; font-size:1.2rem; margin-bottom:2rem;">Turn wild stories into playable games with open-source AI magic!</div>', unsafe_allow_html=True)
239
+
240
+ # How it works section
241
+ with st.expander("โœจ How It Works (Like Baking a Cake) ๐ŸŽ‚"):
 
 
 
 
 
 
 
 
 
 
 
242
  st.markdown("""
243
+ **1. Kids Write a Story (The Recipe)**
244
+ Example: *"The spaceship zooms past aliens and shoots lasers!"*
245
+
246
+ **2. AI is the Magic Oven ๐Ÿ”ฎ**
247
+ We turn words into code using open-source AI models
248
+
249
+ **3. Animation Pops Out (The Cake!) ๐ŸŽฎ**
250
+ See your story come to life with custom animations!
251
+
252
+ **4. Robot Teacher Explains ๐Ÿค–**
253
+ Tavus shows: *"See? 'spaceship.move_right()' makes it fly! That's coding!"*
254
+ """)
255
+
256
+ # Load models
257
+ story_to_code, sentiment_analyzer, ner_model = load_models()
258
+
259
+ # Initialize session state
260
+ if 'animation_generated' not in st.session_state:
261
+ st.session_state.animation_generated = False
262
+ if 'story_text' not in st.session_state:
263
+ st.session_state.story_text = ""
264
+ if 'animation_frames' not in st.session_state:
265
+ st.session_state.animation_frames = []
266
+ if 'code_explanation' not in st.session_state:
267
+ st.session_state.code_explanation = ""
268
+
269
+ # Main content
270
+ col1, col2 = st.columns([1, 1])
271
+
272
+ with col1:
273
+ st.markdown('<div class="story-box">', unsafe_allow_html=True)
274
+ st.markdown("### ๐Ÿ“– Write Your Story Here:")
275
+ story_text = st.text_area(
276
+ "Tell your adventure story...",
277
+ height=200,
278
+ placeholder="Once upon a time, a brave spaceship zoomed through space, shooting lasers at alien spaceships...",
279
+ label_visibility="collapsed",
280
+ value=st.session_state.story_text
281
+ )
282
+
283
+ # Generate button with animation
284
+ if st.button("โœจ Generate Animation!", use_container_width=True, key="generate", type="primary"):
285
+ if story_text.strip():
286
+ st.session_state.story_text = story_text
287
+ st.session_state.animation_generated = True
288
+
289
+ with st.spinner("๐Ÿง™โ€โ™‚๏ธ Cooking your story in the magic oven..."):
290
+ # Generate animation frames
291
+ st.session_state.animation_frames = generate_sprite_animation(story_text)
292
+
293
+ # Generate code explanation
294
+ st.session_state.code_explanation = generate_code_explanation(story_text, story_to_code)
295
+
296
+ # Analyze story sentiment (just for fun)
297
+ if sentiment_analyzer:
298
+ sentiment = sentiment_analyzer(story_text[:512])[0]
299
+ st.session_state.sentiment = f"Your story feels {sentiment['label'].lower()}! (Confidence: {sentiment['score']:.2f})"
300
+ else:
301
+ st.session_state.sentiment = "Your story is full of adventure!"
302
+
303
+ # Identify story characters
304
+ if ner_model and len(story_text) > 10:
305
+ entities = ner_model(story_text[:256])
306
+ characters = list(set([ent['word'] for ent in entities if ent['entity'] in ['B-PER', 'I-PER']]))
307
+ if characters:
308
+ st.session_state.characters = f"Main characters: {', '.join(characters)}"
309
+ else:
310
+ st.session_state.characters = "Exciting characters are ready for action!"
311
+ else:
312
+ st.session_state.characters = "Your heroes are preparing for adventure!"
313
+ else:
314
+ st.warning("Please enter a story first!")
315
+ st.markdown('</div>', unsafe_allow_html=True)
316
 
317
+ with col2:
318
+ st.markdown('<div class="story-box">', unsafe_allow_html=True)
319
+ st.markdown("### ๐ŸŽฎ Your Game Animation")
320
+
321
+ if st.session_state.animation_generated and st.session_state.animation_frames:
322
+ # Display animation frames
323
+ st.markdown("**Your Story Comes to Life!**")
324
+ cols = st.columns(len(st.session_state.animation_frames))
325
+ for i, frame in enumerate(st.session_state.animation_frames):
326
+ with cols[i]:
327
+ st.image(frame, caption=f"Frame {i+1}", use_column_width=True)
328
+
329
+ # Create an animated GIF
330
+ gif_buffer = BytesIO()
331
+ st.session_state.animation_frames[0].save(
332
+ gif_buffer,
333
+ format='GIF',
334
+ save_all=True,
335
+ append_images=st.session_state.animation_frames[1:],
336
+ duration=500,
337
+ loop=0
338
+ )
339
+ gif_data = gif_buffer.getvalue()
340
+ st.download_button(
341
+ label="โฌ‡๏ธ Download Animation",
342
+ data=gif_data,
343
+ file_name="your_story.gif",
344
+ mime="image/gif",
345
+ use_container_width=True
346
+ )
347
+
348
+ # Display story analysis
349
+ st.success("โœจ Animation created!")
350
+ st.info(f"๐ŸŽญ {st.session_state.characters}")
351
+ st.info(f"๐Ÿ˜Š {st.session_state.sentiment}")
352
+
353
+ elif st.session_state.animation_generated:
354
+ st.warning("Couldn't generate animation. Try a different story!")
355
+ st.image("https://img.freepik.com/free-vector/hand-drawn-colorful-space-background_23-2148821798.jpg",
356
+ use_column_width=True)
357
+ elif story_text:
358
+ # Show storyboard preview
359
+ preview_img = create_storyboard_image(story_text)
360
+ st.image(preview_img, caption="Your Story Preview", use_column_width=True)
361
+ st.info("๐Ÿ‘† Click Generate to bring your story to life!")
362
+ else:
363
+ st.image("https://img.freepik.com/free-vector/hand-drawn-colorful-space-background_23-2148821798.jpg",
364
+ use_column_width=True)
365
+ st.info("๐Ÿ‘† Write your story and click Generate to see the magic!")
366
+
367
+ st.markdown('</div>', unsafe_allow_html=True)
368
 
369
+ # Tavus explanation section
370
+ if st.session_state.animation_generated and st.session_state.story_text:
371
+ st.markdown('<div class="robot-speech">', unsafe_allow_html=True)
372
+ st.markdown("### ๐Ÿค– Tavus the Robot Teacher says:")
373
+
374
+ # Extract action words from story
375
+ action_words = ["zoom", "shoot", "fly", "move", "jump", "run", "attack", "laser", "alien", "spaceship",
376
+ "dragon", "fire", "hero", "sword", "castle", "run", "escape", "fight", "win"]
377
+ found_actions = [word for word in action_words if word in st.session_state.story_text.lower()]
378
+
379
+ if found_actions:
380
+ # Create explanation based on found words
381
+ explanations = []
382
+ code_snippets = []
383
+
384
+ for action in found_actions[:3]: # Limit to 3 explanations
385
+ if action == "zoom":
386
+ explanations.append("makes your spaceship move super fast!")
387
+ code_snippets.append("spaceship.accelerate(speed=10)")
388
+ elif action == "shoot":
389
+ explanations.append("creates laser beams to defeat enemies!")
390
+ code_snippets.append("laser = spaceship.fire_weapon()")
391
+ elif action == "fly":
392
+ explanations.append("keeps your character moving through the air!")
393
+ code_snippets.append("character.apply_gravity(False)")
394
+ elif action == "move":
395
+ explanations.append("changes position on the screen!")
396
+ code_snippets.append("player.move(direction='right')")
397
+ elif action == "jump":
398
+ explanations.append("makes your character leap upwards!")
399
+ code_snippets.append("hero.jump(height=100)")
400
+ elif action == "run":
401
+ explanations.append("makes your character move faster!")
402
+ code_snippets.append("player.speed = player.speed * 2")
403
+ elif action == "attack":
404
+ explanations.append("lets your hero fight the bad guys!")
405
+ code_snippets.append("sword.swing(damage=15)")
406
+ elif action == "laser":
407
+ explanations.append("creates powerful energy beams!")
408
+ code_snippets.append("weapon = Laser(color='red', damage=20)")
409
+ elif action == "alien":
410
+ explanations.append("adds enemies to your game!")
411
+ code_snippets.append("enemy = Alien(position=(100, 200))")
412
+ elif action == "spaceship":
413
+ explanations.append("creates your main character!")
414
+ code_snippets.append("player = Spaceship(image='spaceship.png')")
415
+ elif action == "dragon":
416
+ explanations.append("adds a fire-breathing challenge!")
417
+ code_snippets.append("boss = Dragon(health=100, damage=25)")
418
+ elif action == "fire":
419
+ explanations.append("creates dangerous fire effects!")
420
+ code_snippets.append("flame = FireEffect(position, size=30)")
421
+ elif action == "hero":
422
+ explanations.append("creates the main player character!")
423
+ code_snippets.append("player = Hero(name='Your Hero')")
424
+ elif action == "castle":
425
+ explanations.append("adds a majestic building to your world!")
426
+ code_snippets.append("castle = Building(type='castle', position=(300, 150))")
427
+ elif action == "escape":
428
+ explanations.append("creates exciting chase sequences!")
429
+ code_snippets.append("player.start_escape_sequence(speed=15)")
430
+ elif action == "fight":
431
+ explanations.append("initiates battle mechanics!")
432
+ code_snippets.append("initiate_combat(enemy)")
433
+ elif action == "win":
434
+ explanations.append("creates victory conditions!")
435
+ code_snippets.append("if player.score > 100: show_victory_screen()")
436
+
437
+ st.markdown("See how your story became real code? Here's what happened:")
438
+
439
+ for i, (action, explanation, snippet) in enumerate(zip(found_actions, explanations, code_snippets)):
440
+ st.markdown(f"**{i+1}. When you said '{action}'**:")
441
+ st.markdown(f'<div class="code-block">{snippet}</div>', unsafe_allow_html=True)
442
+ st.markdown(f"This code {explanation}")
443
+
444
+ st.markdown("That's the magic of coding - turning words into actions!")
445
  else:
446
+ st.markdown("I see you created an awesome story! Every word you write can become game code. Try adding action words like 'jump', 'run', or 'shoot' next time!")
447
+
448
+ # Show AI-generated explanation
449
+ st.markdown("### ๐Ÿง  AI-Powered Explanation:")
450
+ st.write(st.session_state.code_explanation)
451
+
452
+ st.markdown("</div>", unsafe_allow_html=True)
453
+
454
+ # Benefits section
455
+ st.markdown("""
456
+ ## โค Why Everyone Will Love CodeTales
457
+
458
+ | For Kids ๐Ÿ‘ง๐Ÿ‘ฆ | For Parents & Teachers ๐Ÿ‘ช๐Ÿ‘ฉโ€๐Ÿซ |
459
+ |--------------|----------------------------|
460
+ | โœจ Feels like playing, not learning | ๐Ÿง  Secretly teaches programming concepts |
461
+ | ๐Ÿš€ Brings imagination to life | ๐Ÿ” Develops logical thinking skills |
462
+ | ๐ŸŽฎ Creates personal video games | โž— Reinforces math fundamentals |
463
+ | ๐Ÿ˜„ Makes learning fun and exciting | ๐Ÿงฉ Encourages problem-solving abilities |
464
+ | ๐ŸŒŸ 100% private with no API keys | ๐Ÿ’ฐ Completely free and open-source |
465
  """)
 
 
 
466
 
467
+ # Footer
468
+ st.markdown("---")
469
+ st.markdown("""
470
+ <center>
471
+ <p style="color:white; font-size:1.1rem;">
472
+ โœจ Made with open-source magic by CodeTales Team โœจ<br>
473
+ Transforming stories into games since 2023 | No API Keys Required!
474
+ </p>
475
+ </center>
476
+ """, unsafe_allow_html=True)