Spaces:
Sleeping
Sleeping
File size: 23,120 Bytes
d770ff9 c323310 d770ff9 c323310 d770ff9 c323310 d770ff9 c323310 d770ff9 c323310 d770ff9 c323310 d770ff9 d82e593 69aaf62 d82e593 69aaf62 d82e593 208a2f2 69aaf62 208a2f2 69aaf62 208a2f2 69aaf62 208a2f2 69aaf62 208a2f2 69aaf62 208a2f2 69aaf62 208a2f2 69aaf62 208a2f2 69aaf62 208a2f2 69aaf62 208a2f2 69aaf62 208a2f2 69aaf62 208a2f2 69aaf62 208a2f2 69aaf62 208a2f2 69aaf62 208a2f2 69aaf62 208a2f2 69aaf62 208a2f2 69aaf62 208a2f2 69aaf62 208a2f2 69aaf62 208a2f2 69aaf62 208a2f2 69aaf62 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 |
"""
UI components for the Magic Story Creator application
"""
import gradio as gr
from config import constants
def create_header():
"""Create the main header section of the application"""
return gr.HTML(
'<div class="image-header-wrapper">'
'<h1 class="main-header">Magic Story Creator </h1>'
'<img src="/gradio_api/file=assets/images/wand.png" class="image-next-header" alt="Magic Wand">'
"</div>"
)
def create_instructions_accordion():
"""Create the instructions accordion"""
with gr.Accordion(
"How to Create Your Magical Story", elem_classes="gr-accordion", open=False
) as accordion:
gr.HTML("""
<div>
<div style="display: flex; align-items: center; margin-bottom: 15px;">
<div style="margin-right: 15px;">
<img src="/gradio_api/file=assets/images/wand.png" style="width: 80px; height: 80px;" alt="Magic Wand">
</div>
<div>
<h3 class="sub-header">Welcome to Magic Story Creator!</h3>
<p class="text">This magical tool helps you create personalized stories for children. Follow these simple steps:</p>
</div>
</div>
<div style="display: flex; flex-wrap: wrap; gap: 20px;">
<div class ="box">
<h4 class = "box-header">Step 1: Choose Your Adventure</h4>
<p>Select a story type and tone that your child will enjoy.</p>
<p><strong>Example:</strong> Fantasy with Enthusiastic tone</p>
</div>
<div class ="box">
<h4 class = "box-header">Step 2: Child's Details</h4>
<p>Tell us about the child who will be enjoying the story.</p>
<p><strong>Example:</strong> Age 6, English language, interests in dinosaurs and space</p>
</div>
<div class ="box">
<h4 class = "box-header">Step 3: Story Details</h4>
<p>Add a subject for your story and how long you'd like it to be.</p>
<p><strong>Example:</strong> "Quantum Computing" for 3 minutes of reading time</p>
</div>
<div class ="box">
<h4 class = "box-header">Step 4: Create Magic!</h4>
<p>Click "Create My Magical Story" and watch the magic happen! Once your story appears, click "Create Illustrated Chapters" to add pictures.</p>
</div>
</div>
</div>
""")
return accordion
def create_story_tab_header():
"""Create the header for the story tab"""
return gr.HTML("""
<div class="image-header-wrapper">
<img src="/gradio_api/file=assets/images/wizard.png" class="image-next-header" alt="Magic Wand">
<div class="sub-title" style="margin-right: 15px;">
Let's create a magical story just for you!
</div>
</div>
""")
def create_step_heading(number, title):
"""Create a step heading"""
return gr.HTML(f"""
<div style="text-align: center; margin-bottom: 15px;">
<span class="step-header">Step {number}: {title}</span>
</div>
""")
def create_story_options():
"""Create story type and tone selection options"""
with gr.Row():
# Story Type
with gr.Column(scale=1):
story_type = gr.Radio(
choices=constants.STORY_TYPES,
value=constants.DEFAULT_STORY_TYPE,
interactive=True,
label="Story Type",
elem_id="story-type-buttons",
)
# Tone
with gr.Column(scale=1):
tone = gr.Radio(
choices=constants.TONE_TYPES,
label="Story Tone",
value=constants.DEFAULT_TONE_TYPE,
interactive=True,
elem_id="story-tone-buttons",
)
return story_type, tone
def create_reader_details():
"""Create the reader details section"""
with gr.Row():
# Kid Age
with gr.Column(scale=1):
kid_age = gr.Slider(
minimum=constants.KID_AGE_MIN,
maximum=constants.KID_AGE_MAX,
value=constants.DEFAULT_KID_AGE,
step=1,
label="Kid Age",
elem_id="age-slider",
)
# Kid Language
with gr.Column(scale=1):
kid_language = gr.Textbox(
label="Kid Language",
value=constants.DEFAULT_LANGUAGE,
elem_id="language-input",
)
with gr.Row():
# Kid Interests with icon
with gr.Column():
kid_interests = gr.Textbox(
label="Kid Interests",
placeholder="Robots, Space, Dinosaurs...",
elem_id="interests-input",
)
return kid_age, kid_language, kid_interests
def create_story_details():
"""Create the story details section"""
with gr.Row():
# Subject
with gr.Column(scale=1):
subject = gr.Textbox(
placeholder="MCP servers, Quantum Computing...",
label="Subject (What is the story about?)",
elem_id="subject-input",
)
# Reading time
with gr.Column(scale=1):
reading_time = gr.Slider(
minimum=constants.READING_TIME_MIN,
maximum=constants.READING_TIME_MAX,
value=constants.DEFAULT_READING_TIME,
step=1,
label="Reading Time (minutes)",
elem_id="reading-time-slider",
)
with gr.Row():
# PDF Upload with icon
with gr.Column(scale=1):
pdf_upload = gr.File(
file_types=[".pdf"],
label="PDF Upload (Optional)",
elem_id="pdf-upload",
)
# AI Model with icon (hidden behind a collapsible for simplicity)
with gr.Column(scale=1):
with gr.Accordion("Advanced: Choose AI Model", open=False):
model_selector = gr.Dropdown(
constants.MODEL_OPTIONS,
label="AI Model",
value=constants.DEFAULT_MODEL,
elem_id="model-selector",
)
return subject, reading_time, pdf_upload, model_selector
def create_story_action_buttons():
"""Create the action buttons for story generation"""
with gr.Row():
generate_button = gr.Button(
"β¨ Create My Magical Story! β¨",
variant="primary",
)
clear_button = gr.Button(
"π§Ή Start Over",
variant="secondary",
)
return generate_button, clear_button
def create_loading_container():
"""Create the loading animation container"""
with gr.Row(visible=False) as container:
gr.HTML("""
<div class="loading-animation">
π <span style="color: #5d9df5;">Creating your magical story...</span> π
</div>
""")
return container
def story_ready_header():
"""Return the HTML for the story ready header"""
return gr.HTML("""
<div style="text-align: center; margin-bottom: 15px;">
<span class="step-header">Your Magical Story is Ready!</span>
</div>
""")
def create_story_output_container():
"""Create the container for displaying the generated story"""
story_title = gr.Textbox(
label="Story Title",
interactive=False,
lines=1,
elem_id="story-title",
)
story_text = gr.Textbox(
label="Generated Story",
interactive=False,
lines=15,
elem_id="story-text",
)
with gr.Row():
process_chapters_button = gr.Button(
"π¨ Create Illustrated Chapters! π¨",
variant="primary",
interactive=False,
elem_classes="chapters-button",
)
return story_title, story_text, process_chapters_button
def create_chapter_loading_container():
"""Create the loading animation container for chapters"""
with gr.Row(visible=False, elem_id="chapter-loading-container") as container:
gr.HTML("""
<div id="progress-info"">
<span id="progress-stage">...</span>
</div>
""")
return container
def create_empty_chapters_placeholder():
"""Create the placeholder for when no chapters are available"""
return gr.HTML("""
<div class="image-header-wrapper empty-placeholder" id="empty-chapters-placeholder">
<img src="/gradio_api/file=assets/images/empty_bk.png" style="width: 150px; margin-bottom: 20px;" alt="Empty Book">
<h1 class="sub-header" style="margin-left: 16px; padding-bottom: 36px;">
Your Illustrated Story Awaits!
</h1>
</div>
<p class="text empty-placeholder" style="font-size: 1.2em; text-align: center;">First, create your story and then click the "Create Illustrated Chapters!" button to see your story come to life with pictures!</p>
""")
def create_chapter_loading_placeholder():
return gr.HTML("""
<div style="text-align: center; padding: 30px; background-color: #f0f7ff; border-radius: 15px;">
<div class="loading-animation" style="font-size: 36px;">β¨πβ¨</div>
<h3 style="color: #5d9df5; margin-top: 20px;">Creating Your Magical Story Chapters...</h3>
<p style="color: #666; margin-top: 10px;">Our wizard is crafting beautiful chapters and illustrations for your story...</p>
<div class="small-loader" style="text-align: center; padding: 10px;">
<div class="spinner" style="display: inline-block; width: 20px; height: 20px;
border: 3px solid rgba(0, 0, 0, 0.1); border-radius: 50%;
border-top-color: #3498db; animation: spin 1s linear infinite;"></div>
<style>
@keyframes spin {
to { transform: rotate(360deg); }
}
</style>
</div>
</div>
""")
def create_chapter_error_display(error_message):
"""Create an error display for chapter generation issues"""
return gr.HTML(f"""
<div style="text-align: center; padding: 20px; background-color: #ffeded; border-radius: 15px; border: 2px solid #ff5757; margin-top: 20px;">
<h3 style="color: #ff5757; margin-bottom: 10px;">Oops! Something went wrong</h3>
<p style="color: #333;">{error_message}</p>
</div>
""")
def create_story_title_display(title):
"""Create a display for the story title"""
return gr.HTML(f"""
<div style="text-align: center; margin: 20px 0;">
<h1 class="step-header">
{title}
</h1>
</div>
""")
def create_chapter_navigation():
"""Create the chapter navigation display"""
return gr.HTML("""
<span style="display:flex; gap:8px; font-size: 1.2em;">
<p style="color: var( --accent-pink-light);margin: 0;">Your Adventure Chapters</p>
<p class="text"">Click on each chapter to reveal its magical contents!</p>
</span>
""")
def create_chapter_accordion(index, chapter):
"""Create an accordion for a story chapter"""
with gr.Accordion(
label=f"Chapter {index}: {chapter.get('title', 'Untitled')}",
open=False,
elem_classes="gr-accordion",
):
with gr.Blocks(elem_classes="chapter-content-box"):
# Chapter content with magical styling
gr.HTML(f"""
<div style="padding: 10px; background-color: #f8fcff; border-radius: 10px; border-left: 5px solid #5d9df5;">
<div style="font-family: 'Comic Sans MS', cursive, sans-serif; font-size: 1.1em; line-height: 1.6; color: #333;">
{chapter.get("content", "")}
</div>
</div>
""")
# Image prompt
gr.HTML(f"""
<div style="margin-top: 15px; padding: 10px; background-color: #fff9f5; border-radius: 10px; border-left: 5px solid #ff9057;">
<h4 style="color: #ff9057; margin-top: 0;">Image Description:</h4>
<p style="font-style: italic; color: #666;">{chapter.get("image_prompt", "")}</p>
</div>
""")
# Display image if available with nice styling
image_b64 = chapter.get("image_b64")
with gr.Row(equal_height=True):
with gr.Column(scale=1):
if image_b64:
gr.HTML(f"""
<div style="text-align: center;">
<div style="display: inline-block; padding: 10px; background-color: white; border-radius: 15px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);">
<img src="data:image/png;base64,{chapter["image_b64"]}" style="border-radius: 10px; max-width: 100%;" alt="Chapter Illustration"/>
</div>
</div>
""")
else:
gr.HTML("""
<div style="text-align: center; padding: 30px; background-color: #f0f7ff; border-radius: 15px;">
<div class="loading-animation" style="font-size: 30px;">ποΈ</div>
<p style="color: #5d9df5; font-weight: bold; margin-top: 15px;">{status}</p>
</div>
""")
with gr.Column(scale=1):
chapter_content_state = gr.State(value=chapter.get("content", ""))
audio_statuss = gr.Markdown("", visible=False, elem_classes="text")
read_aloud_btn = gr.Button(
f"π Read Chapter {index} Aloud",
size="lg",
variant="primary",
elem_classes="audio-button",
)
read_aloud_audio = gr.Audio(
label="Generated Audio",
interactive=False,
autoplay=True,
show_download_button=True,
show_share_button=False,
elem_classes="audio-output",
waveform_options=gr.WaveformOptions(
waveform_color="#ff9057",
waveform_progress_color="#ff6a3b",
skip_length=2,
show_controls=True,
),
)
return read_aloud_btn, read_aloud_audio, chapter_content_state, audio_statuss
def create_story_melody_section():
"""Create the section for generating a story melody"""
gr.HTML("""
<div class="image-header-wrapper" style="margin: 30px 0 15px 0;">
<img src="/gradio_api/file=assets/images/headphones.png" style="width: 132px; height: 132px; margin-bottom: 20px;" alt="Musical headphone">
<h1 class="sub-header" style="margin-left: 16px; padding-bottom: 36px;">
Create a Magical Soundtrack for Your Story!
</h1>
</div>
<p class="text" style="font-size: 1.2em; text-align: center;">Click the button below to generate a special melody that matches the mood of your adventure!</p>
""")
with gr.Blocks(elem_classes="melody-box"):
with gr.Row():
with gr.Column(scale=1, min_width=200):
generate_melody_button = gr.Button(
"π΅ Create Story Soundtrack π΅",
variant="primary",
elem_classes="melody-button",
)
melody_status = gr.Markdown("", visible=False)
with gr.Column(scale=2):
melody_output = gr.Audio(
label="Your Story's Magical Soundtrack",
interactive=False,
autoplay=True,
show_download_button=True,
show_share_button=False,
waveform_options=gr.WaveformOptions(
waveform_color="#9c5fff",
waveform_progress_color="#6a2fff",
skip_length=2,
show_controls=True,
),
)
return generate_melody_button, melody_status, melody_output
# TODO split this further later
def create_3d_model_viewer(story: str = "simple yellow duck"):
"""
Create a 3D model viewer component that displays a mesh from a base64 string.
For now, this uses a placeholder HTML with a message and a download link for the mesh.
In a real app, you would use a JS 3D viewer (e.g., Three.js) embedded in the HTML.
"""
gr.HTML("""
<div class="image-header-wrapper" style="margin: 30px 0 15px 0;">
<h1 class="sub-header" style="margin-left: 16px; padding-bottom: 36px;">
Create a Magical 3D Model from Your Story!
</h1>
</div>
<p class="text" style="font-size: 1.2em; text-align: center;">Click the button below to generate a 3D model from your story!</p>
""")
with gr.Blocks(elem_classes="melody-box"):
with gr.Row():
with gr.Column(scale=1, min_width=200):
generate_model_button = gr.Button(
"Create 3D Model π¨",
variant="primary",
elem_classes="melody-button",
)
with gr.Column(scale=2):
model_viewer = gr.Model3D(
label="3D Model Viewer", clear_color=[0.0, 0.0, 0.0, 0.0]
)
model_status = gr.Markdown("", visible=False)
# Add status message and model display
return (
generate_model_button,
model_viewer,
model_status,
)
def create_readme_display(section_titles=None):
"""
Create a component that displays the project's README file.
Optionally filter to show only specific sections by title.
Args:
section_titles (str or list, optional): If provided, only display these sections of the README.
Can be a single string (e.g., "Getting Started") or a list of strings
(e.g., ["Getting Started", "Features", "Installation"]).
"""
import re
from pathlib import Path
gr.set_static_paths(paths=[Path.cwd().absolute() / "/assets/images"])
# Handle single string for backward compatibility
if isinstance(section_titles, str):
section_titles = [section_titles]
elif section_titles is None:
section_titles = []
# Find the README file in the project root directory
current_dir = Path(__file__).parent
project_root = current_dir.parent
readme_paths = [
project_root / "README.md",
project_root / "Readme.md",
project_root / "readme.md",
]
readme_content = ""
for path in readme_paths:
if path.exists():
with open(path, "r", encoding="utf-8") as f:
readme_content = f.read()
break
if not readme_content:
readme_content = "README file not found in the project directory."
# If specific sections are requested, extract them
if section_titles and readme_content:
# Pattern to match headers (# Header, ## Header, etc.)
header_pattern = r"^(#+)\s+(.+)$"
# Split the content by lines
lines = readme_content.split("\n")
all_filtered_content = []
# Process each requested section
for section_title in section_titles:
filtered_content = []
in_target_section = False
current_level = 0
for line in lines:
# Check if line is a header
header_match = re.match(header_pattern, line, re.MULTILINE)
if header_match:
header_level = len(header_match.group(1)) # Number of # symbols
header_text = header_match.group(2).strip()
# If we found the target section
if header_text.lower() == section_title.lower():
in_target_section = True
current_level = header_level
filtered_content.append(line) # Include the section header
# If we're in the target section and find another header of same or higher level, exit section
elif in_target_section and header_level <= current_level:
in_target_section = False
# Include subsection headers if we're in the target section
elif in_target_section:
filtered_content.append(line)
# Include content if we're in the target section
elif in_target_section:
filtered_content.append(line)
# Add this section's content to our combined content
if filtered_content:
all_filtered_content.extend(filtered_content)
# Add a separator between sections unless it's the last section
if section_title != section_titles[-1]:
all_filtered_content.append("\n---\n")
else:
all_filtered_content.append(
f"Section '{section_title}' not found in README.\n"
)
# Update the content to only the filtered sections
if all_filtered_content:
readme_content = "\n".join(all_filtered_content)
else:
readme_content = "None of the requested sections were found in README."
# Create a header similar to other components
if not section_titles:
section_display = "Full Documentation"
elif len(section_titles) == 1:
section_display = f"{section_titles[0]} Section"
else:
section_display = "Sections: " + ", ".join(section_titles)
# Display the README content using Markdown
with gr.Blocks(elem_classes="readme-box"):
readme_display = gr.Markdown(
value=readme_content, elem_classes="readme-content"
)
return readme_display
|