mgbam commited on
Commit
8b1668a
Β·
verified Β·
1 Parent(s): d7896b6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +72 -65
app.py CHANGED
@@ -122,22 +122,28 @@ def add_scene_to_story_orchestrator(
122
 
123
  log_accumulator = [f"**πŸš€ Scene {current_story_obj.current_scene_number + 1} - {time.strftime('%H:%M:%S')}**"]
124
 
125
- # Initialize placeholders for the final return tuple values
 
 
 
 
 
 
126
  ret_story_state = current_story_obj
127
  ret_gallery = current_story_obj.get_all_scenes_for_gallery_display()
128
- ret_latest_image = None
129
- ret_latest_narrative_str = "## Processing...\nNarrative generation in progress."
130
- ret_status_bar_html = "<p class='processing_text status_text'>Processing...</p>"
131
- ret_log_str = "\n".join(log_accumulator)
132
 
133
- # Initial UI update by yielding a dictionary mapping component variables to their new configurations
134
  yield {
135
- output_status_bar: gr.HTML(value=f"<p class='processing_text status_text'>🌌 Weaving Scene {current_story_obj.current_scene_number + 1}...</p>"),
136
- output_latest_scene_image: gr.Image(value=create_placeholder_image("🎨 Conjuring visuals..."), visible=True),
137
- output_latest_scene_narrative: gr.Markdown(value=" Musing narrative...", visible=True),
138
  engage_button: gr.Button(interactive=False),
139
  surprise_button: gr.Button(interactive=False),
140
- output_interaction_log_markdown: gr.Markdown(value="\n".join(log_accumulator))
 
 
 
141
  }
142
 
143
  try:
@@ -156,13 +162,21 @@ def add_scene_to_story_orchestrator(
156
  text_response = None
157
  if text_model_info["type"] == "gemini": text_response = generate_text_gemini(user_p, model_id=text_model_info["id"], system_prompt=system_p, max_tokens=768 if narrative_length.startswith("Detailed") else 400)
158
  elif text_model_info["type"] == "hf_text": text_response = generate_text_hf(user_p, model_id=text_model_info["id"], system_prompt=system_p, max_tokens=768 if narrative_length.startswith("Detailed") else 400)
159
- if text_response and text_response.success: narrative_text_generated = basic_text_cleanup(text_response.text); log_accumulator.append(f" Narrative: Success.")
160
- elif text_response: narrative_text_generated = f"**Narrative Error ({text_model_key}):** {text_response.error}"; log_accumulator.append(f" Narrative: FAILED - {text_response.error}")
161
- else: log_accumulator.append(f" Narrative: FAILED - No response from {text_model_key}.")
162
- else: narrative_text_generated = "**Narrative Error:** Text model unavailable."; log_accumulator.append(f" Narrative: FAILED - Model '{text_model_key}' unavailable.")
 
 
 
 
 
 
 
 
163
 
164
- ret_latest_narrative_str = f"## Scene Idea: {scene_prompt_text}\n\n{narrative_text_generated}"
165
- yield { output_latest_scene_narrative: gr.Markdown(value=ret_latest_narrative_str),
166
  output_interaction_log_markdown: gr.Markdown(value="\n".join(log_accumulator)) }
167
 
168
  # --- 2. Generate Image ---
@@ -179,13 +193,22 @@ def add_scene_to_story_orchestrator(
179
  image_response = None
180
  if selected_image_provider_type == "stability_ai": image_response = generate_image_stabilityai(full_image_prompt, negative_prompt=negative_prompt_text or COMMON_NEGATIVE_PROMPTS, steps=40 if image_quality=="High Detail" else 25)
181
  elif selected_image_provider_type == "dalle": image_response = generate_image_dalle(full_image_prompt, quality="hd" if image_quality=="High Detail" else "standard")
182
- if image_response and image_response.success: image_generated_pil = image_response.image; log_accumulator.append(f" Image: Success.")
183
- elif image_response: image_generation_error_message = f"**Image Error ({image_response.provider}):** {image_response.error}"; log_accumulator.append(f" Image: FAILED - {image_response.error}")
184
- else: image_generation_error_message = f"**Image Error:** No response from {image_provider_key}."; log_accumulator.append(f" Image: FAILED - No response.")
185
- else: image_generation_error_message = "**Image Error:** Image provider unavailable."; log_accumulator.append(f" Image: FAILED - Provider '{image_provider_key}' unavailable.")
 
 
 
 
 
 
 
 
 
186
 
187
  ret_latest_image = image_generated_pil if image_generated_pil else create_placeholder_image("Image Gen Failed", color="#401010")
188
- yield { output_latest_scene_image: gr.Image(value=ret_latest_image),
189
  output_interaction_log_markdown: gr.Markdown(value="\n".join(log_accumulator)) }
190
 
191
  # --- 3. Add Scene to Story Object ---
@@ -208,45 +231,44 @@ def add_scene_to_story_orchestrator(
208
  # --- 4. Prepare Final Values for Return Tuple ---
209
  ret_gallery = current_story_obj.get_all_scenes_for_gallery_display()
210
  _ , latest_narr_for_display_final_str_temp = current_story_obj.get_latest_scene_details_for_display()
211
- ret_latest_narrative_str = latest_narr_for_display_final_str_temp
212
 
213
- ret_status_bar_html = f"<p class='error_text status_text'>Scene {current_story_obj.current_scene_number} added with errors.</p>" if final_scene_error else f"<p class='success_text status_text'>🌌 Scene {current_story_obj.current_scene_number} woven!</p>"
 
214
 
215
  progress(1.0, desc="Scene Complete!")
216
 
217
  except ValueError as ve:
218
  log_accumulator.append(f"\n**INPUT/CONFIG ERROR:** {ve}")
219
- ret_status_bar_html = f"<p class='error_text status_text'>❌ CONFIGURATION ERROR: {ve}</p>"
220
  ret_latest_narrative_str = f"## Error\n{ve}"
221
  except Exception as e:
222
  log_accumulator.append(f"\n**UNEXPECTED RUNTIME ERROR:** {type(e).__name__} - {e}\n{traceback.format_exc()}")
223
- ret_status_bar_html = f"<p class='error_text status_text'>❌ UNEXPECTED ERROR: {type(e).__name__}. Check logs.</p>"
224
  ret_latest_narrative_str = f"## Unexpected Error\n{type(e).__name__}: {e}\nSee log for details."
225
  finally:
226
  current_total_time = time.time() - start_time
227
  log_accumulator.append(f" Cycle ended at {time.strftime('%H:%M:%S')}. Total time: {current_total_time:.2f}s")
228
- ret_log_str = "\n".join(log_accumulator)
229
 
230
- # This final yield updates button states and the log just before the function returns.
231
- # It does NOT provide values for the main `outputs` list of the click handler.
232
  yield {
233
  engage_button: gr.Button(interactive=True),
234
  surprise_button: gr.Button(interactive=True),
235
- output_interaction_log_markdown: gr.Markdown(value=ret_log_str) # Final log update via yield
236
  }
237
 
238
- # This `return` statement's tuple structure and order must match the `outputs` list
239
- # of the `engage_button.click()` method.
240
  return (
241
  ret_story_state,
242
  ret_gallery,
243
  ret_latest_image,
244
- gr.Markdown(value=ret_latest_narrative_str),
245
- gr.HTML(value=ret_status_bar_html),
246
- gr.Markdown(value=ret_log_str) # Log is also part of final return
247
  )
248
 
249
-
250
  def clear_story_state_ui_wrapper():
251
  new_story = Story()
252
  placeholder_img = create_placeholder_image("Your StoryVerse is a blank canvas...", color="#1A1A2E", text_color="#A0A0C0")
@@ -267,25 +289,23 @@ def surprise_me_func():
267
  # --- Gradio UI Definition ---
268
  with gr.Blocks(theme=omega_theme, css=omega_css, title="✨ StoryVerse Omega ✨ - AI Story & World Weaver") as story_weaver_demo:
269
  # --- Define Python variables for UI components that need to be updated by `yield` or `return` ---
270
- # State
271
  story_state_output = gr.State(Story())
272
-
273
- # Input Column (some inputs are also outputs for clear/surprise)
274
  scene_prompt_input = gr.Textbox()
275
  image_style_input = gr.Dropdown()
276
  artist_style_input = gr.Textbox()
277
-
278
- # Output Column (and status bar from input column)
 
 
 
 
279
  output_latest_scene_image = gr.Image()
280
  output_latest_scene_narrative = gr.Markdown()
281
- output_gallery = gr.Gallery()
282
  output_status_bar = gr.HTML()
283
  output_interaction_log_markdown = gr.Markdown()
284
-
285
- # Buttons (interactivity updated by yield)
286
  engage_button = gr.Button()
287
  surprise_button = gr.Button()
288
- # clear_story_button is not directly updated by the main orchestrator's yield typically
289
 
290
  # --- Layout the UI ---
291
  gr.Markdown("<div align='center'><h1>✨ StoryVerse Omega ✨</h1>\n<h3>Craft Immersive Multimodal Worlds with AI</h3></div>")
@@ -306,16 +326,13 @@ with gr.Blocks(theme=omega_theme, css=omega_css, title="✨ StoryVerse Omega ✨
306
  with gr.Column(scale=7, min_width=450):
307
  gr.Markdown("### πŸ’‘ **Craft Your Scene**", elem_classes="input-section-header")
308
  with gr.Group():
309
- scene_prompt_input = gr.Textbox(lines=7, label="Scene Vision (Description, Dialogue, Action):", placeholder="e.g., Amidst swirling cosmic dust, Captain Eva pilots her damaged starfighter towards a colossal, ringed gas giant. Alarms blare. 'Just a little further,' she mutters, gripping the controls.")
310
-
311
  with gr.Row(elem_classes=["compact-row"]):
312
  with gr.Column(scale=2):
313
  image_style_input = gr.Dropdown(choices=["Default (Cinematic Realism)"] + sorted(list(STYLE_PRESETS.keys())), value="Default (Cinematic Realism)", label="Visual Style Preset")
314
  with gr.Column(scale=2):
315
- artist_style_input = gr.Textbox(label="Artistic Inspiration (Optional):", placeholder="e.g., Moebius, ZdzisΕ‚aw BeksiΕ„ski")
316
-
317
- negative_prompt_input = gr.Textbox(lines=2, label="Exclude from Image (Negative Prompt):", placeholder="Default exclusions applied. Add more if needed.", value=COMMON_NEGATIVE_PROMPTS)
318
-
319
  with gr.Accordion("βš™οΈ Advanced AI Configuration", open=False):
320
  with gr.Group():
321
  text_model_dropdown = gr.Dropdown(choices=list(TEXT_MODELS.keys()), value=UI_DEFAULT_TEXT_MODEL_KEY, label="Narrative AI Engine")
@@ -323,12 +340,10 @@ with gr.Blocks(theme=omega_theme, css=omega_css, title="✨ StoryVerse Omega ✨
323
  with gr.Row():
324
  narrative_length_dropdown = gr.Dropdown(["Short (1 paragraph)", "Medium (2-3 paragraphs)", "Detailed (4+ paragraphs)"], value="Medium (2-3 paragraphs)", label="Narrative Detail")
325
  image_quality_dropdown = gr.Dropdown(["Standard", "High Detail", "Sketch Concept"], value="Standard", label="Image Detail/Style")
326
-
327
  with gr.Row(elem_classes=["compact-row"], equal_height=True):
328
  engage_button = gr.Button("🌌 Weave This Scene!", variant="primary", scale=3, icon="✨")
329
  surprise_button = gr.Button("🎲 Surprise Me!", variant="secondary", scale=1, icon="🎁")
330
  clear_story_button = gr.Button("πŸ—‘οΈ New Story", variant="stop", scale=1, icon="♻️")
331
-
332
  output_status_bar = gr.HTML(value="<p class='processing_text status_text'>Ready to weave your first masterpiece!</p>")
333
 
334
  with gr.Column(scale=10, min_width=700):
@@ -337,10 +352,8 @@ with gr.Blocks(theme=omega_theme, css=omega_css, title="✨ StoryVerse Omega ✨
337
  with gr.TabItem("🌠 Latest Scene", id="latest_scene_tab"):
338
  output_latest_scene_image = gr.Image(label="Latest Scene Image", type="pil", interactive=False, show_download_button=True, height=512, show_label=False, elem_classes=["panel_image"])
339
  output_latest_scene_narrative = gr.Markdown()
340
-
341
  with gr.TabItem("πŸ“š Story Scroll", id="story_scroll_tab"):
342
  output_gallery = gr.Gallery(label="Story Scroll", show_label=False, columns=4, object_fit="cover", height=700, preview=True, allow_preview=True, elem_classes=["gallery_output"])
343
-
344
  with gr.TabItem("βš™οΈ Interaction Log", id="log_tab"):
345
  with gr.Accordion(label="Developer Interaction Log", open=False):
346
  output_interaction_log_markdown = gr.Markdown("Log will appear here...")
@@ -353,22 +366,17 @@ with gr.Blocks(theme=omega_theme, css=omega_css, title="✨ StoryVerse Omega ✨
353
  text_model_dropdown, image_provider_dropdown,
354
  narrative_length_dropdown, image_quality_dropdown
355
  ],
356
- outputs=[ # These components are updated by the FINAL return tuple of the orchestrator
357
- story_state_output,
358
- output_gallery,
359
- output_latest_scene_image,
360
- output_latest_scene_narrative,
361
- output_status_bar,
362
- output_interaction_log_markdown
363
  ]
364
  )
365
  clear_story_button.click(
366
  fn=clear_story_state_ui_wrapper,
367
  inputs=[],
368
  outputs=[
369
- story_state_output, output_gallery,
370
- output_latest_scene_image, output_latest_scene_narrative,
371
- output_status_bar, output_interaction_log_markdown,
372
  scene_prompt_input
373
  ]
374
  )
@@ -377,7 +385,6 @@ with gr.Blocks(theme=omega_theme, css=omega_css, title="✨ StoryVerse Omega ✨
377
  inputs=[],
378
  outputs=[scene_prompt_input, image_style_input, artist_style_input]
379
  )
380
-
381
  gr.Examples(
382
  examples=[
383
  ["A lone, weary traveler on a mechanical steed crosses a vast, crimson desert under twin suns. Dust devils dance in the distance.", "Sci-Fi Western", "Moebius", "greenery, water, modern city"],
 
122
 
123
  log_accumulator = [f"**πŸš€ Scene {current_story_obj.current_scene_number + 1} - {time.strftime('%H:%M:%S')}**"]
124
 
125
+ # Initialize values for the final return tuple
126
+ # These will be updated throughout the try block.
127
+ # Order must match engage_button.click outputs list:
128
+ # story_state_output, output_gallery, output_latest_scene_image,
129
+ # output_latest_scene_narrative, output_status_bar, output_interaction_log_markdown
130
+
131
+ # Set initial/default values for the elements that will be returned
132
  ret_story_state = current_story_obj
133
  ret_gallery = current_story_obj.get_all_scenes_for_gallery_display()
134
+ ret_latest_image = None # Will be PIL.Image or None
135
+ ret_latest_narrative_md = gr.Markdown(value="## Processing...\nNarrative being woven...")
136
+ ret_status_bar_html = gr.HTML(value="<p class='processing_text status_text'>Processing...</p>")
137
+ ret_log_md = gr.Markdown(value="\n".join(log_accumulator))
138
 
139
+ # Initial UI updates (disabling buttons, setting placeholders) are done via yield
140
  yield {
 
 
 
141
  engage_button: gr.Button(interactive=False),
142
  surprise_button: gr.Button(interactive=False),
143
+ output_status_bar: gr.HTML(value=f"<p class='processing_text status_text'>🌌 Weaving Scene {current_story_obj.current_scene_number + 1}...</p>"),
144
+ output_latest_scene_image: gr.Image(value=create_placeholder_image("🎨 Conjuring visuals...")),
145
+ output_latest_scene_narrative: gr.Markdown(value=" Musing narrative..."),
146
+ output_interaction_log_markdown: ret_log_md # Initial log
147
  }
148
 
149
  try:
 
162
  text_response = None
163
  if text_model_info["type"] == "gemini": text_response = generate_text_gemini(user_p, model_id=text_model_info["id"], system_prompt=system_p, max_tokens=768 if narrative_length.startswith("Detailed") else 400)
164
  elif text_model_info["type"] == "hf_text": text_response = generate_text_hf(user_p, model_id=text_model_info["id"], system_prompt=system_p, max_tokens=768 if narrative_length.startswith("Detailed") else 400)
165
+
166
+ if text_response and text_response.success:
167
+ narrative_text_generated = basic_text_cleanup(text_response.text)
168
+ log_accumulator.append(f" Narrative: Success. (Snippet: {narrative_text_generated[:50]}...)")
169
+ elif text_response:
170
+ narrative_text_generated = f"**Narrative Error ({text_model_key}):** {text_response.error}"
171
+ log_accumulator.append(f" Narrative: FAILED - {text_response.error}")
172
+ else:
173
+ log_accumulator.append(f" Narrative: FAILED - No response object from {text_model_key}.")
174
+ else:
175
+ narrative_text_generated = "**Narrative Error:** Selected text model not available or misconfigured."
176
+ log_accumulator.append(f" Narrative: FAILED - Model '{text_model_key}' not available.")
177
 
178
+ ret_latest_narrative_str_content = f"## Scene Idea: {scene_prompt_text}\n\n{narrative_text_generated}"
179
+ yield { output_latest_scene_narrative: gr.Markdown(value=ret_latest_narrative_str_content),
180
  output_interaction_log_markdown: gr.Markdown(value="\n".join(log_accumulator)) }
181
 
182
  # --- 2. Generate Image ---
 
193
  image_response = None
194
  if selected_image_provider_type == "stability_ai": image_response = generate_image_stabilityai(full_image_prompt, negative_prompt=negative_prompt_text or COMMON_NEGATIVE_PROMPTS, steps=40 if image_quality=="High Detail" else 25)
195
  elif selected_image_provider_type == "dalle": image_response = generate_image_dalle(full_image_prompt, quality="hd" if image_quality=="High Detail" else "standard")
196
+
197
+ if image_response and image_response.success:
198
+ image_generated_pil = image_response.image
199
+ log_accumulator.append(f" Image: Success from {image_response.provider}.")
200
+ elif image_response:
201
+ image_generation_error_message = f"**Image Error ({image_response.provider}):** {image_response.error}"
202
+ log_accumulator.append(f" Image: FAILED - {image_response.error}")
203
+ else:
204
+ image_generation_error_message = f"**Image Error:** No response object from {image_provider_key} service."
205
+ log_accumulator.append(f" Image: FAILED - No response object from {image_provider_key}.")
206
+ else:
207
+ image_generation_error_message = "**Image Error:** Selected image provider not available or misconfigured."
208
+ log_accumulator.append(f" Image: FAILED - Provider '{image_provider_key}' unavailable.")
209
 
210
  ret_latest_image = image_generated_pil if image_generated_pil else create_placeholder_image("Image Gen Failed", color="#401010")
211
+ yield { output_latest_scene_image: gr.Image(value=ret_latest_image), # Use gr.Image() for update
212
  output_interaction_log_markdown: gr.Markdown(value="\n".join(log_accumulator)) }
213
 
214
  # --- 3. Add Scene to Story Object ---
 
231
  # --- 4. Prepare Final Values for Return Tuple ---
232
  ret_gallery = current_story_obj.get_all_scenes_for_gallery_display()
233
  _ , latest_narr_for_display_final_str_temp = current_story_obj.get_latest_scene_details_for_display()
234
+ ret_latest_narrative_str = latest_narr_for_display_final_str_temp # This is a string
235
 
236
+ status_html_str_temp = f"<p class='error_text status_text'>Scene {current_story_obj.current_scene_number} added with errors.</p>" if final_scene_error else f"<p class='success_text status_text'>🌌 Scene {current_story_obj.current_scene_number} woven!</p>"
237
+ ret_status_bar_html = gr.HTML(value=status_html_str_temp) # Convert to HTML update
238
 
239
  progress(1.0, desc="Scene Complete!")
240
 
241
  except ValueError as ve:
242
  log_accumulator.append(f"\n**INPUT/CONFIG ERROR:** {ve}")
243
+ ret_status_bar_html = gr.HTML(value=f"<p class='error_text status_text'>❌ CONFIGURATION ERROR: {ve}</p>")
244
  ret_latest_narrative_str = f"## Error\n{ve}"
245
  except Exception as e:
246
  log_accumulator.append(f"\n**UNEXPECTED RUNTIME ERROR:** {type(e).__name__} - {e}\n{traceback.format_exc()}")
247
+ ret_status_bar_html = gr.HTML(value=f"<p class='error_text status_text'>❌ UNEXPECTED ERROR: {type(e).__name__}. Check logs.</p>")
248
  ret_latest_narrative_str = f"## Unexpected Error\n{type(e).__name__}: {e}\nSee log for details."
249
  finally:
250
  current_total_time = time.time() - start_time
251
  log_accumulator.append(f" Cycle ended at {time.strftime('%H:%M:%S')}. Total time: {current_total_time:.2f}s")
252
+ ret_log_str = "\n".join(log_accumulator) # Prepare final log string
253
 
254
+ # This final yield updates button states and the log markdown component.
255
+ # It does NOT update components that are part of the main `return` statement's responsibility.
256
  yield {
257
  engage_button: gr.Button(interactive=True),
258
  surprise_button: gr.Button(interactive=True),
259
+ output_interaction_log_markdown: gr.Markdown(value=ret_log_str) # Update log via yield
260
  }
261
 
262
+ # The final `return` provides values for the components listed in `engage_button.click(outputs=...)`
 
263
  return (
264
  ret_story_state,
265
  ret_gallery,
266
  ret_latest_image,
267
+ gr.Markdown(value=ret_latest_narrative_str), # Ensure it's a Gradio update object
268
+ ret_status_bar_html, # Already a Gradio update object
269
+ gr.Markdown(value=ret_log_str) # Ensure it's a Gradio update object for the log too
270
  )
271
 
 
272
  def clear_story_state_ui_wrapper():
273
  new_story = Story()
274
  placeholder_img = create_placeholder_image("Your StoryVerse is a blank canvas...", color="#1A1A2E", text_color="#A0A0C0")
 
289
  # --- Gradio UI Definition ---
290
  with gr.Blocks(theme=omega_theme, css=omega_css, title="✨ StoryVerse Omega ✨ - AI Story & World Weaver") as story_weaver_demo:
291
  # --- Define Python variables for UI components that need to be updated by `yield` or `return` ---
 
292
  story_state_output = gr.State(Story())
 
 
293
  scene_prompt_input = gr.Textbox()
294
  image_style_input = gr.Dropdown()
295
  artist_style_input = gr.Textbox()
296
+ # negative_prompt_input will be defined in layout
297
+ # text_model_dropdown will be defined in layout
298
+ # image_provider_dropdown will be defined in layout
299
+ # narrative_length_dropdown will be defined in layout
300
+ # image_quality_dropdown will be defined in layout
301
+ output_gallery = gr.Gallery()
302
  output_latest_scene_image = gr.Image()
303
  output_latest_scene_narrative = gr.Markdown()
 
304
  output_status_bar = gr.HTML()
305
  output_interaction_log_markdown = gr.Markdown()
 
 
306
  engage_button = gr.Button()
307
  surprise_button = gr.Button()
308
+ clear_story_button = gr.Button()
309
 
310
  # --- Layout the UI ---
311
  gr.Markdown("<div align='center'><h1>✨ StoryVerse Omega ✨</h1>\n<h3>Craft Immersive Multimodal Worlds with AI</h3></div>")
 
326
  with gr.Column(scale=7, min_width=450):
327
  gr.Markdown("### πŸ’‘ **Craft Your Scene**", elem_classes="input-section-header")
328
  with gr.Group():
329
+ scene_prompt_input = gr.Textbox(lines=7, label="Scene Vision (Description, Dialogue, Action):", placeholder="e.g., Amidst swirling cosmic dust...")
 
330
  with gr.Row(elem_classes=["compact-row"]):
331
  with gr.Column(scale=2):
332
  image_style_input = gr.Dropdown(choices=["Default (Cinematic Realism)"] + sorted(list(STYLE_PRESETS.keys())), value="Default (Cinematic Realism)", label="Visual Style Preset")
333
  with gr.Column(scale=2):
334
+ artist_style_input = gr.Textbox(label="Artistic Inspiration (Optional):", placeholder="e.g., Moebius...")
335
+ negative_prompt_input = gr.Textbox(lines=2, label="Exclude from Image (Negative Prompt):", value=COMMON_NEGATIVE_PROMPTS)
 
 
336
  with gr.Accordion("βš™οΈ Advanced AI Configuration", open=False):
337
  with gr.Group():
338
  text_model_dropdown = gr.Dropdown(choices=list(TEXT_MODELS.keys()), value=UI_DEFAULT_TEXT_MODEL_KEY, label="Narrative AI Engine")
 
340
  with gr.Row():
341
  narrative_length_dropdown = gr.Dropdown(["Short (1 paragraph)", "Medium (2-3 paragraphs)", "Detailed (4+ paragraphs)"], value="Medium (2-3 paragraphs)", label="Narrative Detail")
342
  image_quality_dropdown = gr.Dropdown(["Standard", "High Detail", "Sketch Concept"], value="Standard", label="Image Detail/Style")
 
343
  with gr.Row(elem_classes=["compact-row"], equal_height=True):
344
  engage_button = gr.Button("🌌 Weave This Scene!", variant="primary", scale=3, icon="✨")
345
  surprise_button = gr.Button("🎲 Surprise Me!", variant="secondary", scale=1, icon="🎁")
346
  clear_story_button = gr.Button("πŸ—‘οΈ New Story", variant="stop", scale=1, icon="♻️")
 
347
  output_status_bar = gr.HTML(value="<p class='processing_text status_text'>Ready to weave your first masterpiece!</p>")
348
 
349
  with gr.Column(scale=10, min_width=700):
 
352
  with gr.TabItem("🌠 Latest Scene", id="latest_scene_tab"):
353
  output_latest_scene_image = gr.Image(label="Latest Scene Image", type="pil", interactive=False, show_download_button=True, height=512, show_label=False, elem_classes=["panel_image"])
354
  output_latest_scene_narrative = gr.Markdown()
 
355
  with gr.TabItem("πŸ“š Story Scroll", id="story_scroll_tab"):
356
  output_gallery = gr.Gallery(label="Story Scroll", show_label=False, columns=4, object_fit="cover", height=700, preview=True, allow_preview=True, elem_classes=["gallery_output"])
 
357
  with gr.TabItem("βš™οΈ Interaction Log", id="log_tab"):
358
  with gr.Accordion(label="Developer Interaction Log", open=False):
359
  output_interaction_log_markdown = gr.Markdown("Log will appear here...")
 
366
  text_model_dropdown, image_provider_dropdown,
367
  narrative_length_dropdown, image_quality_dropdown
368
  ],
369
+ outputs=[
370
+ story_state_output, output_gallery, output_latest_scene_image,
371
+ output_latest_scene_narrative, output_status_bar, output_interaction_log_markdown
 
 
 
 
372
  ]
373
  )
374
  clear_story_button.click(
375
  fn=clear_story_state_ui_wrapper,
376
  inputs=[],
377
  outputs=[
378
+ story_state_output, output_gallery, output_latest_scene_image,
379
+ output_latest_scene_narrative, output_status_bar, output_interaction_log_markdown,
 
380
  scene_prompt_input
381
  ]
382
  )
 
385
  inputs=[],
386
  outputs=[scene_prompt_input, image_style_input, artist_style_input]
387
  )
 
388
  gr.Examples(
389
  examples=[
390
  ["A lone, weary traveler on a mechanical steed crosses a vast, crimson desert under twin suns. Dust devils dance in the distance.", "Sci-Fi Western", "Moebius", "greenery, water, modern city"],