agharsallah commited on
Commit
d31d45c
·
1 Parent(s): 30528d9

Update styles Totally

Browse files
.gitattributes CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ *.jpg filter=lfs diff=lfs merge=lfs -text
37
+ *.png filter=lfs diff=lfs merge=lfs -text
app.py CHANGED
@@ -1,15 +1,20 @@
 
 
 
 
 
1
  import gradio as gr
2
  import logging
3
  from dotenv import load_dotenv
4
  from controllers.app_controller import (
5
  process_story_generation,
6
  clear_fields,
7
- process_chapters,
8
  handle_chapter_processing,
9
  generate_audio_with_status,
10
  generate_melody_from_story_with_status,
11
  )
12
  from config import constants
 
13
 
14
  # Configure logging
15
  logging.basicConfig(
@@ -20,165 +25,626 @@ logger = logging.getLogger(__name__)
20
  # Load environment variables from .env file if present (local development)
21
  load_dotenv()
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
- # Define the Gradio interface
25
- with gr.Blocks() as demo:
26
- gr.Markdown("# Dynamic Story Generator")
27
  tabs = gr.Tabs()
28
  with tabs:
29
  with gr.Tab("Story Generator") as story_tab:
30
- with gr.Row():
31
- story_type = gr.Dropdown(
32
- constants.STORY_TYPES, label="Story Type", value="Fantasy"
33
- )
34
- tone = gr.Dropdown(
35
- constants.TONE_TYPES, label="Tone", value="Enthusiastic"
36
- )
37
- model_selector = gr.Dropdown(
38
- constants.MODEL_OPTIONS,
39
- label="AI Model",
40
- value=constants.DEFAULT_MODEL,
41
- )
42
 
43
- with gr.Row():
44
- kid_age = gr.Number(
45
- minimum=constants.KID_AGE_MIN,
46
- maximum=constants.KID_AGE_MAX,
47
- label="Kid Age",
48
- value=constants.DEFAULT_AGE,
49
- )
50
- kid_language = gr.Textbox(
51
- label="Kid Language", value=constants.DEFAULT_LANGUAGE
52
- )
53
- kid_interests = gr.Textbox(
54
- label="Kid Interests", placeholder="Robots, Space, Dinosaurs..."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  )
56
 
57
- subject = gr.Textbox(
58
- placeholder="MCP servers, Quantum Computing...", label="Subject"
59
- )
60
- reading_time = gr.Slider(
61
- minimum=constants.READING_TIME_MIN,
62
- maximum=constants.READING_TIME_MAX,
63
- value=constants.DEFAULT_READING_TIME,
64
- step=1,
65
- label="Reading Time (minutes)",
66
- )
67
- pdf_upload = gr.File(file_types=[".pdf"], label="PDF Upload (Optional)")
68
-
69
- with gr.Row():
70
- generate_button = gr.Button("Generate Story", variant="primary")
71
- clear_button = gr.Button("Clear")
72
-
73
- story_title = gr.Textbox(label="Story Title", interactive=False, lines=1)
74
- story_text = gr.Textbox(
75
- label="Generated Story", interactive=False, lines=10
76
- )
77
-
78
- with gr.Row():
79
- process_chapters_button = gr.Button(
80
- "Process into Chapters with Image Prompts",
81
- variant="primary",
82
  interactive=False,
 
 
83
  )
84
 
 
 
 
 
 
 
 
 
85
  with gr.Tab("Chapters & Image Prompts") as chapters_tab:
86
  # State to store chapter data
87
  chapters_state = gr.State(value=None)
88
 
 
 
 
 
 
 
 
 
 
 
 
89
  @gr.render(inputs=[chapters_state])
90
  def render_chapters(chapters_data):
91
  if chapters_data is None:
92
- gr.Markdown(
93
- "Generate a story and process it into chapters to see the content here."
94
- )
 
 
 
 
95
  return
96
- # TODO Add a loading indicator while processing chapters
97
 
98
  # Check if chapters_data is a string indicating an error
99
  if isinstance(chapters_data, str) and chapters_data.startswith(
100
  "Error:"
101
  ):
102
- gr.Markdown(chapters_data)
 
 
 
 
 
 
103
  return
104
 
105
- # Display story title
106
- gr.Markdown(f"## {chapters_data.get('title', 'Story Chapters')}")
 
 
 
 
 
 
 
107
 
108
- # Display each chapter with buttons
 
 
 
 
 
 
 
 
109
  for i, chapter in enumerate(chapters_data.get("chapters", []), 1):
110
  with gr.Accordion(
111
  label=f"Chapter {i}: {chapter.get('title', 'Untitled')}",
112
  open=False,
 
113
  ):
114
- gr.Markdown(chapter.get("content", ""))
115
- gr.Markdown(
116
- f"**Image Prompt:** *{chapter.get('image_prompt', '')}*"
117
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
 
119
- # Display image if available
120
- image_b64 = chapter.get("image_b64")
121
-
122
- # Add chapter-specific buttons
123
- with gr.Row():
124
- if image_b64:
125
- gr.Markdown(
126
- f'<img src="data:image/png;base64,{chapter["image_b64"]}"/>'
127
- )
128
- else:
129
- gr.Markdown("*Image generating...*")
130
-
131
- with gr.Column():
132
- chapter_content_state = gr.State(
133
- value=chapter.get("content", "")
134
- )
135
- audio_statuss = gr.Markdown("", visible=False)
136
-
137
- read_aloud_btn = gr.Button(
138
- f"Read Chapter {i} Aloud", size="sm"
139
- )
140
- read_aloud_audio = gr.Audio(
141
- label="Generated Audio",
142
- interactive=False,
143
- autoplay=True,
144
- show_download_button=True,
145
- show_share_button=False,
146
- waveform_options=gr.WaveformOptions(
147
- waveform_color="#01C6FF",
148
- waveform_progress_color="#0066B4",
149
- skip_length=2,
150
- show_recording_waveform=True,
151
- ),
152
- )
153
 
154
  read_aloud_btn.click(
155
  generate_audio_with_status,
156
  inputs=[chapter_content_state],
157
  outputs=[read_aloud_audio, audio_statuss],
158
  )
159
- gr.Markdown(
160
- "### Generate a melody that matches the mood and theme of your story"
161
- )
162
- with gr.Row():
163
- with gr.Column():
164
- generate_melody_button = gr.Button(
165
- "Generate Story Melody", variant="primary"
166
- )
167
- melody_status = gr.Markdown("", visible=False)
168
- with gr.Column():
169
- melody_output = gr.Audio(
170
- label="Generated Story Melody",
171
- interactive=False,
172
- autoplay=True,
173
- show_download_button=True,
174
- show_share_button=False,
175
- waveform_options=gr.WaveformOptions(
176
- waveform_color="#9c5fff",
177
- waveform_progress_color="#6a2fff",
178
- skip_length=2,
179
- show_recording_waveform=True,
180
- ),
181
- )
 
 
 
 
 
 
 
 
 
 
 
182
  # Add melody generation from story functionality
183
  generate_melody_button.click(
184
  generate_melody_from_story_with_status,
@@ -188,7 +654,7 @@ with gr.Blocks() as demo:
188
 
189
  error_text = gr.Markdown(visible=False)
190
 
191
- # Connect UI components to actions
192
  generate_button.click(
193
  process_story_generation,
194
  inputs=[
@@ -205,21 +671,23 @@ with gr.Blocks() as demo:
205
  outputs=[story_title, story_text, process_chapters_button],
206
  )
207
 
 
208
  clear_button.click(
209
  clear_fields,
210
  inputs=[],
211
  outputs=[subject, story_title, story_text, process_chapters_button],
212
  )
213
 
 
214
  process_chapters_button.click(
215
  handle_chapter_processing,
216
  inputs=[story_text, story_title],
217
  outputs=[chapters_state],
218
  )
219
 
220
- # Launch the Gradio app
221
  try:
222
- logger.info("Starting Dynamic Story Generator app")
223
  demo.launch(mcp_server=True)
224
  except Exception as e:
225
  logger.error(f"Failed to start app: {e}", exc_info=True)
 
1
+ """
2
+ Magic Story Creator - An interactive AI-powered story generator for kids
3
+ with a colorful, child-friendly UI
4
+ """
5
+
6
  import gradio as gr
7
  import logging
8
  from dotenv import load_dotenv
9
  from controllers.app_controller import (
10
  process_story_generation,
11
  clear_fields,
 
12
  handle_chapter_processing,
13
  generate_audio_with_status,
14
  generate_melody_from_story_with_status,
15
  )
16
  from config import constants
17
+ from pathlib import Path
18
 
19
  # Configure logging
20
  logging.basicConfig(
 
25
  # Load environment variables from .env file if present (local development)
26
  load_dotenv()
27
 
28
+ gr.set_static_paths(paths=[Path.cwd().absolute() / "assets/images"])
29
+
30
+ # Define the Gradio interface with custom CSS for kid-friendly UI
31
+ css = """
32
+ :root {
33
+ --main-bg: #f8fcff;
34
+ --primary-color: #fba639;
35
+ --accent-color: #00af64;
36
+ --text-color: #000;
37
+ --border-radius: 15px;
38
+ --shadow: 0 4px 8px rgba(0,0,0,0.1);
39
+
40
+ --bg-surface: #ffffff;
41
+ --bg-accent-yellow: #FFF8E1;
42
+ --bg-accent-blue: #E3F2FD;
43
+ --bg-accent-pink: #FCE4EC;
44
+
45
+ --text-main: #212121;
46
+ --text-heading: #C62828;
47
+ --text-subheading: #1565C0;
48
+ --text-accent: #00897B;
49
+ --text-warning: #FF8F00;
50
+ --text-on-accent: #ffffff;
51
+
52
+ --accent-green: #43A047;
53
+ --accent-pink: #EC407A;
54
+ --accent-pink-light: #ff96b9;
55
+ --accent-yellow: #FFD600;
56
+ --accent-blue: #1976D2;
57
+ --accent-purple: #8E24AA;
58
+ --interactive-hover: #1565C0;
59
+
60
+ --shadow-color: #B0BEC5;
61
+ --hover-color: #F06292;
62
+ }
63
+
64
+ .gradio-container {
65
+ margin: 10 auto;
66
+ font-family: 'Comic Sans MS', cursive, sans-serif;
67
+ background-image: url("/gradio_api/file=assets/images/bg.jpg");
68
+ background-repeat: no-repeat;
69
+ background-size: cover;
70
+ background-attachment: fixed;
71
+ }
72
+
73
+ .main-header {
74
+ color: var(--text-heading);
75
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
76
+ font-size: 2.8em;
77
+ margin-bottom: 20px;
78
+ text-align: center;
79
+ }
80
+
81
+ .sub-title {
82
+ color: var(--accent-purple);
83
+ text-align: center;
84
+ font-size: 1.5em;
85
+ }
86
+
87
+ .sub-header {
88
+ color: var(--text-subheading);
89
+ }
90
+
91
+ .step-header {
92
+ font-size: 1.5em;
93
+ font-weight: bold;
94
+ text-align: center;
95
+ color: var(--text-subheading);
96
+ }
97
+
98
+ .image-next-header {
99
+ width: 80px;
100
+ height: 80px;
101
+ display: block;
102
+ }
103
+
104
+ .image-header-wrapper {
105
+ display: flex;
106
+ align-items: center;
107
+ justify-content: center;
108
+ text-align: center;
109
+ width: 100%;
110
+ }
111
+
112
+ .text {
113
+ color: var(--text-main);
114
+ }
115
+
116
+ .box {
117
+ background-color: var(--bg-surface);
118
+ border: 2px solid var(--text-main);
119
+ border-radius: var(--border-radius);
120
+ padding: 15px;
121
+ box-shadow: var(--shadow);
122
+ margin-bottom: 20px;
123
+ flex: 1;
124
+ min-width: 200px;
125
+ p, strong {
126
+ color: var(--text-main);
127
+ }
128
+ }
129
+
130
+ .box-header{
131
+ color: var(--accent-blue);
132
+ }
133
+
134
+ .gr-accordion {
135
+ border-radius: var(--border-radius);
136
+ overflow: hidden;
137
+ box-shadow: var(--shadow);
138
+ margin-bottom: 15px;
139
+ background-color: var(--bg-accent-yellow);
140
+ button {
141
+ font-weight: bold;
142
+ color: var(--text-main) ;
143
+ }
144
+ }
145
+
146
+ .tab-container {
147
+ button:not([aria-selected="true"]) {
148
+ background-color: transparent;
149
+ color: var(--text-main);
150
+ }
151
+ button:hover {
152
+ background-color: var(--bg-accent-blue);
153
+ }
154
+ }
155
+
156
+ #language-input, #age-slider, #subject-input, #reading-time-slider {
157
+ min-height: 100px;
158
+ }
159
+
160
+ #story-tone-buttons > span, #story-type-buttons > span,
161
+ #age-slider label span, #language-input label span,
162
+ #interests-input label span,
163
+ #subject-input label span, #reading-time-slider label span,
164
+ #model-selector div span {
165
+ display: block;
166
+ font-weight: bold;
167
+ color: var(--accent-pink-light);
168
+ font-size: 1.1em;
169
+ margin-bottom: 16px;
170
+ }
171
+
172
+ .radio-label {
173
+ display: block;
174
+ font-weight: bold;
175
+ color: var(--accent-pink);
176
+ font-size: 1.1em;
177
+ }
178
+ .radio-options {
179
+ display: flex;
180
+ flex-wrap: wrap;
181
+ gap: 10px;
182
+ justify-content: center;
183
+ }
184
+ .radio-option {
185
+ width: 100px;
186
+ height: 120px;
187
+ border: 3px solid #d4e6ff;
188
+ border-radius: 15px;
189
+ display: flex;
190
+ flex-direction: column;
191
+ align-items: center;
192
+ justify-content: center;
193
+ cursor: pointer;
194
+ padding: 10px;
195
+ background-color: white;
196
+ transition: all 0.3s ease;
197
+ box-shadow: 0 4px 8px rgba(0,0,0,0.1);
198
+ }
199
+ .radio-option:hover {
200
+ transform: translateY(-5px);
201
+ box-shadow: 0 8px 15px rgba(0,0,0,0.1);
202
+ }
203
+ .radio-option.selected {
204
+ border-color: #5d9df5;
205
+ background-color: #f0f7ff;
206
+ box-shadow: 0 0 0 3px rgba(93, 157, 245, 0.3);
207
+ }
208
+ .radio-option img {
209
+ width: 50px;
210
+ height: 50px;
211
+ margin-bottom: 10px;
212
+ transition: all 0.3s ease;
213
+ }
214
+ .radio-option.selected img {
215
+ transform: scale(1.1);
216
+ }
217
+ .radio-option span {
218
+ font-weight: bold;
219
+ color: #4a6baf;
220
+ text-align: center;
221
+ }
222
+ .hidden-radio {
223
+ display: none !important;
224
+ }
225
+
226
+ /* Animations for page transitions */
227
+ @keyframes fadeIn {
228
+ from { opacity: 0; transform: translateY(20px); }
229
+ to { opacity: 1; transform: translateY(0); }
230
+ }
231
+ .story-container {
232
+ animation: fadeIn 0.7s ease-out;
233
+ }
234
+ .chapter-accordion {
235
+ animation: fadeIn 0.5s ease-out;
236
+ animation-fill-mode: both;
237
+ }
238
+ .chapter-accordion:nth-child(1) { animation-delay: 0.1s; }
239
+ .chapter-accordion:nth-child(2) { animation-delay: 0.2s; }
240
+ .chapter-accordion:nth-child(3) { animation-delay: 0.3s; }
241
+ .chapter-accordion:nth-child(4) { animation-delay: 0.4s; }
242
+ .chapter-accordion:nth-child(5) { animation-delay: 0.5s; }
243
+
244
+ """
245
+
246
+ with gr.Blocks(
247
+ css=css,
248
+ theme=gr.themes.Ocean(),
249
+ ) as demo:
250
+ gr.HTML(
251
+ '<div class="image-header-wrapper">'
252
+ '<h1 class="main-header">Magic Story Creator </h1>'
253
+ '<img src="/gradio_api/file=assets/images/wand.png" class="image-next-header" alt="Magic Wand">'
254
+ "</div>"
255
+ )
256
+
257
+ with gr.Accordion(
258
+ "How to Create Your Magical Story", elem_classes="gr-accordion", open=False
259
+ ):
260
+ gr.HTML("""
261
+ <div>
262
+ <div style="display: flex; align-items: center; margin-bottom: 15px;">
263
+ <div style="margin-right: 15px;">
264
+ <img src="/gradio_api/file=assets/images/wand.png" style="width: 80px; height: 80px;" alt="Magic Wand">
265
+ </div>
266
+ <div>
267
+ <h3 class="sub-header">Welcome to Magic Story Creator!</h3>
268
+ <p class="text">This magical tool helps you create personalized stories for children. Follow these simple steps:</p>
269
+ </div>
270
+ </div>
271
+
272
+ <div style="display: flex; flex-wrap: wrap; gap: 20px;">
273
+ <div class ="box">
274
+ <h4 class = "box-header">Step 1: Choose Your Adventure</h4>
275
+ <p>Select a story type and tone that your child will enjoy.</p>
276
+ <p><strong>Example:</strong> Fantasy with Enthusiastic tone</p>
277
+ </div>
278
+ <div class ="box">
279
+ <h4 class = "box-header">Step 2: Child's Details</h4>
280
+ <p>Tell us about the child who will be enjoying the story.</p>
281
+ <p><strong>Example:</strong> Age 6, English language, interests in dinosaurs and space</p>
282
+ </div>
283
+
284
+ <div class ="box">
285
+ <h4 class = "box-header">Step 3: Story Details</h4>
286
+ <p>Add a subject for your story and how long you'd like it to be.</p>
287
+ <p><strong>Example:</strong> "Quantum Computing" for 3 minutes of reading time</p>
288
+ </div>
289
+
290
+ <div class ="box">
291
+ <h4 class = "box-header">Step 4: Create Magic!</h4>
292
+ <p>Click "Create My Magical Story" and watch the magic happen! Once your story appears, click "Create Illustrated Chapters" to add pictures.</p>
293
+ </div>
294
+ </div>
295
+ </div>
296
+ """)
297
 
 
 
 
298
  tabs = gr.Tabs()
299
  with tabs:
300
  with gr.Tab("Story Generator") as story_tab:
301
+ gr.HTML("""
302
+ <div class="image-header-wrapper">
303
+ <img src="/gradio_api/file=assets/images/wizard.png" class="image-next-header" alt="Magic Wand">
304
+ <div class="sub-title" style="margin-right: 15px;">
305
+ Let's create a magical story just for you!
306
+ </div>
307
+ </div>
308
+ """)
 
 
 
 
309
 
310
+ with gr.Column(elem_classes="gr-form"):
311
+ gr.HTML("""
312
+ <div style="text-align: center; margin-bottom: 15px;">
313
+ <span class="step-header">Step 1: Choose your adventure!</span>
314
+ </div>
315
+ """)
316
+
317
+ with gr.Row():
318
+ # Story Type
319
+ with gr.Column(scale=1):
320
+ # Create custom radio buttons for story types
321
+
322
+ story_type = gr.Radio(
323
+ choices=constants.STORY_TYPES,
324
+ value=constants.DEFAULT_STORY_TYPE,
325
+ interactive=True,
326
+ label="Story Type",
327
+ elem_id="story-type-buttons",
328
+ )
329
+
330
+ # Tone
331
+ with gr.Column(scale=1):
332
+ tone = gr.Radio(
333
+ choices=constants.TONE_TYPES,
334
+ label="Story Tone",
335
+ value=constants.DEFAULT_TONE_TYPE,
336
+ interactive=True,
337
+ elem_id="story-tone-buttons",
338
+ )
339
+
340
+ with gr.Column(elem_classes="gr-form"):
341
+ gr.HTML("""
342
+ <div style="text-align: center; margin-bottom: 15px;">
343
+ <span class="step-header">Step 2: Tell us about the reader!</span>
344
+ </div>
345
+ """)
346
+
347
+ with gr.Row():
348
+ # Kid Age
349
+ with gr.Column(scale=1):
350
+ kid_age = gr.Slider(
351
+ minimum=constants.KID_AGE_MIN,
352
+ maximum=constants.KID_AGE_MAX,
353
+ value=constants.DEFAULT_KID_AGE,
354
+ step=1,
355
+ label="Kid Age",
356
+ elem_id="age-slider",
357
+ )
358
+
359
+ # Kid Language
360
+ with gr.Column(scale=1):
361
+ kid_language = gr.Textbox(
362
+ label="Kid Language",
363
+ value=constants.DEFAULT_LANGUAGE,
364
+ elem_id="language-input",
365
+ )
366
+
367
+ with gr.Row():
368
+ # Kid Interests with icon
369
+ with gr.Column():
370
+ kid_interests = gr.Textbox(
371
+ label="Kid Interests",
372
+ placeholder="Robots, Space, Dinosaurs...",
373
+ elem_id="interests-input",
374
+ )
375
+
376
+ with gr.Column(elem_classes="gr-form"):
377
+ gr.HTML("""
378
+ <div style="text-align: center; margin-bottom: 15px;">
379
+ <span class="step-header">Step 3: Story details</span>
380
+ </div>
381
+ """)
382
+
383
+ with gr.Row():
384
+ # Subject
385
+ with gr.Column(scale=1):
386
+ subject = gr.Textbox(
387
+ placeholder="MCP servers, Quantum Computing...",
388
+ label="Subject (What is the story about?)",
389
+ elem_id="subject-input",
390
+ )
391
+
392
+ # Reading time
393
+ with gr.Column(scale=1):
394
+ reading_time = gr.Slider(
395
+ minimum=constants.READING_TIME_MIN,
396
+ maximum=constants.READING_TIME_MAX,
397
+ value=constants.DEFAULT_READING_TIME,
398
+ step=1,
399
+ label="Reading Time (minutes)",
400
+ elem_id="reading-time-slider",
401
+ )
402
+
403
+ with gr.Row():
404
+ # PDF Upload with icon
405
+ with gr.Column(scale=1):
406
+ pdf_upload = gr.File(
407
+ file_types=[".pdf"],
408
+ label="PDF Upload (Optional)",
409
+ elem_id="pdf-upload",
410
+ )
411
+
412
+ # AI Model with icon (hidden behind a collapsible for simplicity)
413
+ with gr.Column(scale=1):
414
+ with gr.Accordion("Advanced: Choose AI Model", open=False):
415
+ model_selector = gr.Dropdown(
416
+ constants.MODEL_OPTIONS,
417
+ label="AI Model",
418
+ value=constants.DEFAULT_MODEL,
419
+ elem_id="model-selector",
420
+ )
421
+
422
+ with gr.Row():
423
+ generate_button = gr.Button(
424
+ "✨ Create My Magical Story! ✨",
425
+ variant="primary",
426
+ )
427
+ clear_button = gr.Button(
428
+ "🧹 Start Over",
429
+ variant="secondary",
430
+ )
431
+
432
+ # Loading animation (initially hidden)
433
+ with gr.Row(visible=False) as loading_container:
434
+ gr.HTML("""
435
+ <div class="loading-animation">
436
+ 📚 <span style="color: #5d9df5;">Creating your magical story...</span> 📚
437
+ </div>
438
+ <div style="text-align: center; margin-top: 15px;">
439
+ <img src="https://i.imgur.com/JlfQMcz.gif" style="width: 150px;" alt="Magic Book">
440
+ </div>
441
+ """)
442
+
443
+ with gr.Blocks(elem_classes="gr-form", visible=False) as story_container:
444
+ gr.HTML("""
445
+ <div style="text-align: center; margin-bottom: 15px;">
446
+ <span class="step-header">Your Magical Story is Ready!</span>
447
+ </div>
448
+ """)
449
+
450
+ story_title = gr.Textbox(
451
+ label="Story Title",
452
+ interactive=False,
453
+ lines=1,
454
+ elem_id="story-title",
455
  )
456
 
457
+ story_text = gr.Textbox(
458
+ label="Generated Story",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
459
  interactive=False,
460
+ lines=15,
461
+ elem_id="story-text",
462
  )
463
 
464
+ with gr.Row():
465
+ process_chapters_button = gr.Button(
466
+ "🎨 Create Illustrated Chapters! 🎨",
467
+ variant="primary",
468
+ interactive=False,
469
+ elem_classes="chapters-button",
470
+ )
471
+
472
  with gr.Tab("Chapters & Image Prompts") as chapters_tab:
473
  # State to store chapter data
474
  chapters_state = gr.State(value=None)
475
 
476
+ # Chapter processing loading animation (initially hidden)
477
+ with gr.Row(visible=False) as chapter_loading_container:
478
+ gr.HTML("""
479
+ <div class="loading-animation">
480
+ 🎨 <span style="color: #ff9057;">Creating illustrated chapters...</span> 🎨
481
+ </div>
482
+ <div style="text-align: center; margin-top: 15px;">
483
+ <img src="https://i.imgur.com/JlfQMcz.gif" style="width: 150px;" alt="Magic Book">
484
+ </div>
485
+ """)
486
+
487
  @gr.render(inputs=[chapters_state])
488
  def render_chapters(chapters_data):
489
  if chapters_data is None:
490
+ gr.HTML("""
491
+ <div style="text-align: center; padding: 40px 20px; background-color: rgba(255, 255, 255, 0.8); border-radius: 15px; margin-top: 20px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);">
492
+ <img src="https://i.imgur.com/vnvL8Ey.png" style="width: 150px; margin-bottom: 20px;" alt="Empty Book">
493
+ <h2 style="color: #5d9df5; margin-bottom: 10px;">Your Illustrated Story Awaits!</h2>
494
+ <p style="font-size: 1.2em; color: #666;">First, create your story and then click the "Create Illustrated Chapters!" button to see your story come to life with pictures!</p>
495
+ </div>
496
+ """)
497
  return
 
498
 
499
  # Check if chapters_data is a string indicating an error
500
  if isinstance(chapters_data, str) and chapters_data.startswith(
501
  "Error:"
502
  ):
503
+ gr.HTML(f"""
504
+ <div style="text-align: center; padding: 20px; background-color: #ffeded; border-radius: 15px; border: 2px solid #ff5757; margin-top: 20px;">
505
+ <img src="https://i.imgur.com/Q1WUlHY.png" style="width: 80px; margin-bottom: 10px;" alt="Error">
506
+ <h3 style="color: #ff5757; margin-bottom: 10px;">Oops! Something went wrong</h3>
507
+ <p style="color: #333;">{chapters_data}</p>
508
+ </div>
509
+ """)
510
  return
511
 
512
+ # Display story title with magical styling
513
+ gr.HTML(f"""
514
+ <div style="text-align: center; margin: 20px 0;">
515
+ <img src="https://i.imgur.com/yVuULtS.png" style="width: 80px; height: 80px;" alt="Magic Book">
516
+ <h1 style="color: #5d9df5; margin: 10px 0; text-shadow: 2px 2px 4px rgba(0,0,0,0.1);">
517
+ {chapters_data.get("title", "Story Chapters")}
518
+ </h1>
519
+ </div>
520
+ """)
521
 
522
+ # Chapter navigation
523
+ gr.HTML("""
524
+ <div style="text-align: center; margin-bottom: 20px;">
525
+ <h3 style="color: #ff9057;">Your Adventure Chapters</h3>
526
+ <p>Click on each chapter to reveal its magical contents!</p>
527
+ </div>
528
+ """)
529
+
530
+ # Display each chapter with buttons in colorful cards
531
  for i, chapter in enumerate(chapters_data.get("chapters", []), 1):
532
  with gr.Accordion(
533
  label=f"Chapter {i}: {chapter.get('title', 'Untitled')}",
534
  open=False,
535
+ elem_classes="chapter-accordion",
536
  ):
537
+ with gr.Blocks(elem_classes="chapter-content-box"):
538
+ # Chapter content with magical styling
539
+ gr.HTML(f"""
540
+ <div style="padding: 10px; background-color: #f8fcff; border-radius: 10px; border-left: 5px solid #5d9df5;">
541
+ <div style="font-family: 'Comic Sans MS', cursive, sans-serif; font-size: 1.1em; line-height: 1.6; color: #333;">
542
+ {chapter.get("content", "").replace("\\n", "<br>")}
543
+ </div>
544
+ </div>
545
+ """)
546
+
547
+ gr.HTML(f"""
548
+ <div style="margin-top: 15px; padding: 10px; background-color: #fff9f5; border-radius: 10px; border-left: 5px solid #ff9057;">
549
+ <h4 style="color: #ff9057; margin-top: 0;">Image Description:</h4>
550
+ <p style="font-style: italic; color: #666;">{chapter.get("image_prompt", "")}</p>
551
+ </div>
552
+ """)
553
+
554
+ # Display image if available with nice styling
555
+ image_b64 = chapter.get("image_b64")
556
+
557
+ with gr.Row(equal_height=True):
558
+ with gr.Column(scale=1):
559
+ if image_b64:
560
+ gr.HTML(f"""
561
+ <div style="text-align: center;">
562
+ <div style="display: inline-block; padding: 10px; background-color: white; border-radius: 15px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);">
563
+ <img src="data:image/png;base64,{chapter["image_b64"]}" style="border-radius: 10px; max-width: 100%;" alt="Chapter Illustration"/>
564
+ </div>
565
+ </div>
566
+ """)
567
+ else:
568
+ gr.HTML("""
569
+ <div style="text-align: center; padding: 30px; background-color: #f0f7ff; border-radius: 15px;">
570
+ <div class="loading-animation" style="font-size: 30px;">🖌️</div>
571
+ <p style="color: #5d9df5; font-weight: bold; margin-top: 15px;">Creating your magical illustration...</p>
572
+ </div>
573
+ """)
574
+
575
+ with gr.Column(scale=1):
576
+ gr.HTML(f"""
577
+ <div style="text-align: center; margin-bottom: 15px;">
578
+ <h3 style="color: #5d9df5;">Listen to Chapter {i}</h3>
579
+ <img src="https://i.imgur.com/fvTLtL7.png" style="width: 60px; height: 60px;" alt="Audio">
580
+ </div>
581
+ """)
582
+
583
+ chapter_content_state = gr.State(
584
+ value=chapter.get("content", "")
585
+ )
586
+ audio_statuss = gr.Markdown("", visible=False)
587
+
588
+ read_aloud_btn = gr.Button(
589
+ f"🔊 Read Chapter {i} Aloud",
590
+ size="lg",
591
+ variant="secondary",
592
+ elem_classes="audio-button",
593
+ )
594
 
595
+ read_aloud_audio = gr.Audio(
596
+ label="Generated Audio",
597
+ interactive=False,
598
+ autoplay=True,
599
+ show_download_button=True,
600
+ show_share_button=False,
601
+ waveform_options=gr.WaveformOptions(
602
+ waveform_color="#ff9057",
603
+ waveform_progress_color="#ff6a3b",
604
+ skip_length=2,
605
+ show_recording_waveform=True,
606
+ ),
607
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
608
 
609
  read_aloud_btn.click(
610
  generate_audio_with_status,
611
  inputs=[chapter_content_state],
612
  outputs=[read_aloud_audio, audio_statuss],
613
  )
614
+
615
+ # Story melody section
616
+ gr.HTML("""
617
+ <div style="text-align: center; margin: 30px 0 15px 0;">
618
+ <img src="https://i.imgur.com/i4rYOxS.png" style="width: 80px; height: 80px;" alt="Musical Notes">
619
+ <h2 style="color: #8c69ff; margin: 10px 0;">Create a Magical Soundtrack for Your Story!</h2>
620
+ <p style="color: #666;">Click the button below to generate a special melody that matches the mood of your adventure!</p>
621
+ </div>
622
+ """)
623
+
624
+ with gr.Blocks(elem_classes="melody-box"):
625
+ with gr.Row():
626
+ with gr.Column(scale=1, min_width=200):
627
+ generate_melody_button = gr.Button(
628
+ "🎵 Create Story Soundtrack 🎵",
629
+ variant="primary",
630
+ elem_classes="melody-button",
631
+ )
632
+ melody_status = gr.Markdown("", visible=False)
633
+
634
+ with gr.Column(scale=2):
635
+ melody_output = gr.Audio(
636
+ label="Your Story's Magical Soundtrack",
637
+ interactive=False,
638
+ autoplay=True,
639
+ show_download_button=True,
640
+ show_share_button=False,
641
+ waveform_options=gr.WaveformOptions(
642
+ waveform_color="#9c5fff",
643
+ waveform_progress_color="#6a2fff",
644
+ skip_length=2,
645
+ show_recording_waveform=True,
646
+ ),
647
+ )
648
  # Add melody generation from story functionality
649
  generate_melody_button.click(
650
  generate_melody_from_story_with_status,
 
654
 
655
  error_text = gr.Markdown(visible=False)
656
 
657
+ # Connect UI components to actions with loading animations
658
  generate_button.click(
659
  process_story_generation,
660
  inputs=[
 
671
  outputs=[story_title, story_text, process_chapters_button],
672
  )
673
 
674
+ # Clear button - reset all fields and hide results
675
  clear_button.click(
676
  clear_fields,
677
  inputs=[],
678
  outputs=[subject, story_title, story_text, process_chapters_button],
679
  )
680
 
681
+ # Process chapters with loading animation
682
  process_chapters_button.click(
683
  handle_chapter_processing,
684
  inputs=[story_text, story_title],
685
  outputs=[chapters_state],
686
  )
687
 
688
+ # Launch the Gradio app with enhanced settings
689
  try:
690
+ logger.info("Starting Magic Story Creator app")
691
  demo.launch(mcp_server=True)
692
  except Exception as e:
693
  logger.error(f"Failed to start app: {e}", exc_info=True)
assets/images/bg.jpg ADDED

Git LFS Details

  • SHA256: 34d11a02043e4cb443035368a776e351c509ba9e28c43d3626d09006ece03520
  • Pointer size: 132 Bytes
  • Size of remote file: 1.07 MB
assets/images/wand.png ADDED

Git LFS Details

  • SHA256: 23f2fd6c36a5b9f918c075e594fc154ef5fad7fc5d608320d642e2a9fef5e672
  • Pointer size: 131 Bytes
  • Size of remote file: 622 kB
assets/images/wizard.png ADDED

Git LFS Details

  • SHA256: 3814e2e24645085c15327aec75001e75db81bb0e671dbd2fea4c6c888cfbb55e
  • Pointer size: 132 Bytes
  • Size of remote file: 1.14 MB
config/constants.py CHANGED
@@ -1,5 +1,7 @@
1
- STORY_TYPES = ["Historical", "Futuristic", "Fantasy"]
2
- TONE_TYPES = ["Funny", "Enthusiastic", "Mysterious"]
 
 
3
  MODEL_OPTIONS = [
4
  "Meta-Llama-3.1-8B-Instruct",
5
  "Llama-3.3-Swallow-70B-Instruct-v0.4",
@@ -8,7 +10,7 @@ MODEL_OPTIONS = [
8
  DEFAULT_MODEL = "Meta-Llama-3.1-8B-Instruct"
9
  KID_AGE_MIN = 3
10
  KID_AGE_MAX = 12
11
- DEFAULT_AGE = 6
12
  DEFAULT_LANGUAGE = "English"
13
  READING_TIME_MIN = 1
14
  READING_TIME_MAX = 7
 
1
+ STORY_TYPES = ["Historical", "Futuristic", "Fantasy", "Thriller", "Adventure"]
2
+ DEFAULT_STORY_TYPE = "Historical"
3
+ TONE_TYPES = ["Funny", "Enthusiastic", "Mysterious", "Serious", "Suspenseful"]
4
+ DEFAULT_TONE_TYPE = "Funny"
5
  MODEL_OPTIONS = [
6
  "Meta-Llama-3.1-8B-Instruct",
7
  "Llama-3.3-Swallow-70B-Instruct-v0.4",
 
10
  DEFAULT_MODEL = "Meta-Llama-3.1-8B-Instruct"
11
  KID_AGE_MIN = 3
12
  KID_AGE_MAX = 12
13
+ DEFAULT_KID_AGE = 6
14
  DEFAULT_LANGUAGE = "English"
15
  READING_TIME_MIN = 1
16
  READING_TIME_MAX = 7
controllers/app_controller.py CHANGED
@@ -6,6 +6,7 @@ from services.pdf_text_extractor import extract_text_from_pdf
6
  from services.streaming_chapter_processor import process_story_into_chapters_streaming
7
  from services.audio_generator import generate_audio, generate_melody_from_story
8
  import gradio as gr
 
9
 
10
  logger = logging.getLogger(__name__)
11
 
@@ -13,13 +14,13 @@ logger = logging.getLogger(__name__)
13
  def process_story_generation(
14
  story_type: str,
15
  tone: str,
16
- kid_age: Union[int, float],
17
- kid_language: str,
18
  kid_interests: str,
19
  subject: str,
20
- reading_time: int,
21
- pdf_file: Optional[Any],
22
- model_selector: str,
 
 
23
  ) -> Tuple[str, str, Any]:
24
  """Process the story generation request from the UI.
25
 
 
6
  from services.streaming_chapter_processor import process_story_into_chapters_streaming
7
  from services.audio_generator import generate_audio, generate_melody_from_story
8
  import gradio as gr
9
+ from config import constants
10
 
11
  logger = logging.getLogger(__name__)
12
 
 
14
  def process_story_generation(
15
  story_type: str,
16
  tone: str,
 
 
17
  kid_interests: str,
18
  subject: str,
19
+ kid_age: Union[int, float] = constants.DEFAULT_KID_AGE,
20
+ kid_language: str = constants.DEFAULT_LANGUAGE,
21
+ reading_time: int = constants.DEFAULT_READING_TIME,
22
+ pdf_file: Optional[Any] = None,
23
+ model_selector: str = constants.DEFAULT_MODEL,
24
  ) -> Tuple[str, str, Any]:
25
  """Process the story generation request from the UI.
26