Laramie2 commited on
Commit
93e2311
Β·
verified Β·
1 Parent(s): 37808b1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +224 -95
app.py CHANGED
@@ -6,16 +6,93 @@ import subprocess
6
  import sys
7
  from datetime import datetime
8
  from concurrent.futures import ThreadPoolExecutor, as_completed
 
 
 
9
 
10
  # Initialize environment paths
11
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
12
  PAPERS_DIR = os.path.join(BASE_DIR, "papers")
13
  CONFIG_PATH = os.path.join(BASE_DIR, "config.yaml")
14
  OUTPUT_DIR = os.path.join(BASE_DIR, "mineru_outputs")
15
- ZIP_OUTPUT_PATH = os.path.join(BASE_DIR, "mineru_results.zip") # Zip path
16
 
17
  os.makedirs(PAPERS_DIR, exist_ok=True)
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  def get_debug_info():
20
  """Read server file system status"""
21
  now = datetime.now().strftime("%H:%M:%S")
@@ -29,7 +106,7 @@ def get_debug_info():
29
  for name in files_in_out:
30
  all_output_items.append(os.path.join(os.path.relpath(root, OUTPUT_DIR), name))
31
  output_detail = f"Found {len(all_output_items)} files: {all_output_items[:5]}..." if all_output_items else "Directory exists but is EMPTY"
32
-
33
  return f"[{now}] πŸ“ papers/ Content: {files}\n\n[{now}] πŸ“‚ mineru_outputs Status: {output_detail}"
34
 
35
  def save_pdf(file):
@@ -68,7 +145,6 @@ def save_api_settings(api_key, api_base_url=None):
68
  except Exception as e:
69
  return f"❌ Error: {str(e)}", get_debug_info()
70
 
71
-
72
  def run_mineru_parsing_and_dag_gen():
73
  """Execute PDF parsing and DAG generation (supports real-time streaming)"""
74
  if not os.path.exists(PAPERS_DIR) or not any(f.endswith('.pdf') for f in os.listdir(PAPERS_DIR)):
@@ -89,7 +165,6 @@ def run_mineru_parsing_and_dag_gen():
89
  full_log += "--- Mineru Executing ---\n"
90
  yield "⏳ Executing Mineru parsing...", get_debug_info(), full_log
91
 
92
- # 1. Use Popen for real-time streaming
93
  process_mineru = subprocess.Popen(
94
  command_mineru,
95
  env=env,
@@ -99,11 +174,9 @@ def run_mineru_parsing_and_dag_gen():
99
  bufsize=1
100
  )
101
 
102
- # 2. Read output line by line and yield to Gradio
103
  for line in iter(process_mineru.stdout.readline, ''):
104
  full_log += line
105
  yield "⏳ Executing Mineru parsing...", get_debug_info(), full_log
106
-
107
  process_mineru.stdout.close()
108
  returncode_mineru = process_mineru.wait()
109
 
@@ -129,7 +202,6 @@ def run_mineru_parsing_and_dag_gen():
129
  for line in iter(process_dag.stdout.readline, ''):
130
  full_log += line
131
  yield "⏳ Executing DAG generation...", get_debug_info(), full_log
132
-
133
  process_dag.stdout.close()
134
  returncode_dag = process_dag.wait()
135
 
@@ -145,10 +217,7 @@ def run_mineru_parsing_and_dag_gen():
145
  yield "❌ Execution Exception", get_debug_info(), error_log
146
 
147
  def run_final_generation(task_type="all"):
148
- """
149
- Execute generation scripts and zip results
150
- task_type supports: 'ppt', 'poster', 'pr', 'all'
151
- """
152
  if not os.path.exists(OUTPUT_DIR):
153
  return "❌ Please run the parsing step first", get_debug_info(), "No output folder found.", None
154
 
@@ -169,12 +238,7 @@ def run_final_generation(task_type="all"):
169
 
170
  def execute_script(script):
171
  command = [sys.executable, script]
172
- result = subprocess.run(
173
- command,
174
- capture_output=True,
175
- text=True,
176
- timeout=600
177
- )
178
  return script, result
179
 
180
  try:
@@ -206,7 +270,7 @@ def run_final_generation(task_type="all"):
206
  # Zip the mineru_outputs folder
207
  zip_base_name = ZIP_OUTPUT_PATH.replace(".zip", "")
208
  shutil.make_archive(zip_base_name, 'zip', OUTPUT_DIR)
209
-
210
  success_msg = f"βœ… {task_type.upper()} generated and zipped successfully"
211
  return success_msg, get_debug_info(), full_log, ZIP_OUTPUT_PATH
212
 
@@ -215,19 +279,38 @@ def run_final_generation(task_type="all"):
215
  return "❌ Global exception during final generation", get_debug_info(), error_log, None
216
 
217
  # ==========================================
218
- # --- πŸš€ UI Configuration (Left-Right Layout) ---
219
  # ==========================================
220
-
221
  custom_css = """
222
- /* Prominent Title Animation from Reference */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  #main-title {
224
  text-align: center !important;
225
- padding: 1rem 0 0.5rem 0;
226
  }
 
227
  #main-title h1 {
228
  font-size: 2.6em !important;
229
  font-weight: 800 !important;
230
- background: linear-gradient(135deg, #4f46e5 0%, #818cf8 50%, #4338ca 100%);
231
  background-size: 200% 200%;
232
  -webkit-background-clip: text;
233
  -webkit-text-fill-color: transparent;
@@ -235,14 +318,17 @@ custom_css = """
235
  animation: gradient-shift 4s ease infinite;
236
  letter-spacing: -0.02em;
237
  }
 
238
  @keyframes gradient-shift {
239
  0%, 100% { background-position: 0% 50%; }
240
  50% { background-position: 100% 50%; }
241
  }
 
242
  #subtitle {
243
  text-align: center !important;
244
- margin-bottom: 1.5rem;
245
  }
 
246
  #subtitle p {
247
  margin: 0 auto;
248
  color: #666;
@@ -250,98 +336,141 @@ custom_css = """
250
  font-weight: 500;
251
  }
252
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  /* Terminal Log Style */
254
  .log-box textarea {
255
- font-family: 'Courier New', Consolas, monospace !important;
256
  font-size: 13px !important;
257
  background-color: #1e1e1e !important;
258
- color: #4AF626 !important;
 
 
259
  }
 
260
  /* Borderless Status Text */
261
  .status-text textarea {
262
  background-color: transparent !important;
263
  border: none !important;
264
  box-shadow: none !important;
265
- font-weight: bold;
 
266
  }
 
 
 
 
 
 
 
 
 
 
267
  """
268
 
269
- with gr.Blocks(theme=gr.themes.Soft(primary_hue="indigo"), css=custom_css) as demo:
270
- gr.Markdown("# **PaperX / Mineru Parsing Platform**", elem_id="main-title")
271
- gr.Markdown("One-click parsing of academic PDFs, DAG structuring, and multi-modal asset generation.", elem_id="subtitle")
272
-
273
- with gr.Row():
274
- # ================= LEFT COLUMN: SETTINGS & ACTIONS =================
275
- with gr.Column(scale=1):
276
-
277
- # 1. API Configuration
278
- with gr.Accordion("βš™οΈ 1. Global API Configuration", open=False):
279
- with gr.Row():
280
- key_input = gr.Textbox(label="API Key", type="password", placeholder="sk-...", scale=1)
281
- api_base_url_input = gr.Textbox(label="Base URL (Optional)", placeholder="https://api.example.com", scale=1)
282
- key_btn = gr.Button("πŸ’Ύ Save API Configuration")
283
-
284
- # 2. Document Parsing
285
- with gr.Group():
286
- gr.Markdown("### πŸ“„ 2. Document Parsing")
287
- pdf_input = gr.File(label="Drag and drop or click to upload PDF", file_types=[".pdf"])
288
-
289
- parse_btn = gr.Button("πŸš€ Start Mineru & DAG Extraction", variant="primary", size="lg")
290
-
291
- parse_status = gr.Textbox(
292
- show_label=False,
293
- placeholder="Waiting for document upload...",
294
- lines=1,
295
- interactive=False,
296
- elem_classes="status-text"
297
- )
298
-
299
- # 3. Asset Generation
300
- with gr.Group():
301
- gr.Markdown("### 🎯 3. Asset Generation")
302
- gr.Markdown("Generate final formats based on DAG structure:")
303
 
304
- with gr.Row():
305
- gen_ppt_btn = gr.Button("πŸ“Š Generate PPT")
306
- gen_poster_btn = gr.Button("πŸ–ΌοΈ Generate Poster")
307
- gen_pr_btn = gr.Button("πŸ“° Generate PR Article")
 
 
308
 
309
- gen_all_btn = gr.Button("✨ Generate All Assets (ALL)", variant="primary")
 
 
 
 
 
 
 
 
 
 
 
 
 
310
 
 
 
 
 
 
 
 
 
 
 
 
311
 
312
- # ================= RIGHT COLUMN: OUTPUTS & LOGS =================
313
- with gr.Column(scale=1):
314
-
315
- # 4. Results & Downloads
316
- with gr.Group():
317
- gr.Markdown("### πŸ“¦ Generation Results & Download")
318
- gen_status = gr.Textbox(
319
- show_label=False,
320
- placeholder="No generation task currently...",
321
- lines=2,
322
- interactive=False,
323
- elem_classes="status-text"
324
- )
325
- download_file = gr.File(label="πŸ“₯ Get Final Zip Archive", interactive=False, visible=False)
326
-
327
- # 5. Debugging & Terminal
328
- gr.Markdown("### πŸ› οΈ Developer Monitoring (Debug Only)")
329
- with gr.Tabs():
330
- with gr.Tab("πŸ“œ Terminal Stream"):
331
- cmd_logs = gr.Textbox(
332
- label="Stdout / Stderr",
333
- placeholder="Waiting for task to start...",
334
- lines=18,
335
  interactive=False,
336
- elem_classes="log-box"
337
  )
338
-
339
- with gr.Tab("πŸ” System Snapshot"):
340
- refresh_btn = gr.Button("πŸ”„ Refresh Directory Tree")
341
- debug_view = gr.Textbox(label="Workspace Files", lines=17, interactive=False, value=get_debug_info())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
 
343
  # ================= LOGIC BINDINGS =================
344
-
345
  key_btn.click(fn=save_api_settings, inputs=[key_input, api_base_url_input], outputs=[parse_status, debug_view])
346
 
347
  pdf_input.upload(fn=save_pdf, inputs=pdf_input, outputs=[parse_status, debug_view])
 
6
  import sys
7
  from datetime import datetime
8
  from concurrent.futures import ThreadPoolExecutor, as_completed
9
+ from typing import Iterable
10
+ from gradio.themes import Soft
11
+ from gradio.themes.utils import colors, fonts, sizes
12
 
13
  # Initialize environment paths
14
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
15
  PAPERS_DIR = os.path.join(BASE_DIR, "papers")
16
  CONFIG_PATH = os.path.join(BASE_DIR, "config.yaml")
17
  OUTPUT_DIR = os.path.join(BASE_DIR, "mineru_outputs")
18
+ ZIP_OUTPUT_PATH = os.path.join(BASE_DIR, "mineru_results.zip")
19
 
20
  os.makedirs(PAPERS_DIR, exist_ok=True)
21
 
22
+ # ==========================================
23
+ # --- 🎨 Custom Purple Theme Definition ---
24
+ # ==========================================
25
+ colors.purple = colors.Color(
26
+ name="purple",
27
+ c50="#FAF5FF",
28
+ c100="#F3E8FF",
29
+ c200="#E9D5FF",
30
+ c300="#DAB2FF",
31
+ c400="#C084FC",
32
+ c500="#A855F7",
33
+ c600="#9333EA",
34
+ c700="#7E22CE",
35
+ c800="#6B21A8",
36
+ c900="#581C87",
37
+ c950="#3B0764",
38
+ )
39
+
40
+ class PurpleTheme(Soft):
41
+ def __init__(
42
+ self,
43
+ *,
44
+ primary_hue: colors.Color | str = colors.gray,
45
+ secondary_hue: colors.Color | str = colors.purple,
46
+ neutral_hue: colors.Color | str = colors.slate,
47
+ text_size: sizes.Size | str = sizes.text_lg,
48
+ font: fonts.Font | str | Iterable[fonts.Font | str] = (
49
+ fonts.GoogleFont("Outfit"), "Arial", "sans-serif",
50
+ ),
51
+ font_mono: fonts.Font | str | Iterable[fonts.Font | str] = (
52
+ fonts.GoogleFont("IBM Plex Mono"), "ui-monospace", "monospace",
53
+ ),
54
+ ):
55
+ super().__init__(
56
+ primary_hue=primary_hue,
57
+ secondary_hue=secondary_hue,
58
+ neutral_hue=neutral_hue,
59
+ text_size=text_size,
60
+ font=font,
61
+ font_mono=font_mono,
62
+ )
63
+ super().set(
64
+ background_fill_primary="*primary_50",
65
+ background_fill_primary_dark="*primary_900",
66
+ body_background_fill="linear-gradient(135deg, *primary_200, *primary_100)",
67
+ body_background_fill_dark="linear-gradient(135deg, *primary_900, *primary_800)",
68
+ button_primary_text_color="white",
69
+ button_primary_text_color_hover="white",
70
+ button_primary_background_fill="linear-gradient(90deg, *secondary_500, *secondary_600)",
71
+ button_primary_background_fill_hover="linear-gradient(90deg, *secondary_600, *secondary_700)",
72
+ button_primary_background_fill_dark="linear-gradient(90deg, *secondary_600, *secondary_700)",
73
+ button_primary_background_fill_hover_dark="linear-gradient(90deg, *secondary_500, *secondary_600)",
74
+ button_secondary_text_color="black",
75
+ button_secondary_text_color_hover="white",
76
+ button_secondary_background_fill="linear-gradient(90deg, *primary_300, *primary_300)",
77
+ button_secondary_background_fill_hover="linear-gradient(90deg, *primary_400, *primary_400)",
78
+ button_secondary_background_fill_dark="linear-gradient(90deg, *primary_500, *primary_600)",
79
+ button_secondary_background_fill_hover_dark="linear-gradient(90deg, *primary_500, *primary_500)",
80
+ slider_color="*secondary_500",
81
+ slider_color_dark="*secondary_600",
82
+ block_title_text_weight="600",
83
+ block_border_width="3px",
84
+ block_shadow="*shadow_drop_lg",
85
+ button_primary_shadow="*shadow_drop_lg",
86
+ button_large_padding="11px",
87
+ color_accent_soft="*primary_100",
88
+ block_label_background_fill="*primary_200",
89
+ )
90
+
91
+ purple_theme = PurpleTheme()
92
+
93
+ # ==========================================
94
+ # --- βš™οΈ Backend Logic & Functions ---
95
+ # ==========================================
96
  def get_debug_info():
97
  """Read server file system status"""
98
  now = datetime.now().strftime("%H:%M:%S")
 
106
  for name in files_in_out:
107
  all_output_items.append(os.path.join(os.path.relpath(root, OUTPUT_DIR), name))
108
  output_detail = f"Found {len(all_output_items)} files: {all_output_items[:5]}..." if all_output_items else "Directory exists but is EMPTY"
109
+
110
  return f"[{now}] πŸ“ papers/ Content: {files}\n\n[{now}] πŸ“‚ mineru_outputs Status: {output_detail}"
111
 
112
  def save_pdf(file):
 
145
  except Exception as e:
146
  return f"❌ Error: {str(e)}", get_debug_info()
147
 
 
148
  def run_mineru_parsing_and_dag_gen():
149
  """Execute PDF parsing and DAG generation (supports real-time streaming)"""
150
  if not os.path.exists(PAPERS_DIR) or not any(f.endswith('.pdf') for f in os.listdir(PAPERS_DIR)):
 
165
  full_log += "--- Mineru Executing ---\n"
166
  yield "⏳ Executing Mineru parsing...", get_debug_info(), full_log
167
 
 
168
  process_mineru = subprocess.Popen(
169
  command_mineru,
170
  env=env,
 
174
  bufsize=1
175
  )
176
 
 
177
  for line in iter(process_mineru.stdout.readline, ''):
178
  full_log += line
179
  yield "⏳ Executing Mineru parsing...", get_debug_info(), full_log
 
180
  process_mineru.stdout.close()
181
  returncode_mineru = process_mineru.wait()
182
 
 
202
  for line in iter(process_dag.stdout.readline, ''):
203
  full_log += line
204
  yield "⏳ Executing DAG generation...", get_debug_info(), full_log
 
205
  process_dag.stdout.close()
206
  returncode_dag = process_dag.wait()
207
 
 
217
  yield "❌ Execution Exception", get_debug_info(), error_log
218
 
219
  def run_final_generation(task_type="all"):
220
+ """Execute generation scripts and zip results"""
 
 
 
221
  if not os.path.exists(OUTPUT_DIR):
222
  return "❌ Please run the parsing step first", get_debug_info(), "No output folder found.", None
223
 
 
238
 
239
  def execute_script(script):
240
  command = [sys.executable, script]
241
+ result = subprocess.run(command, capture_output=True, text=True, timeout=600)
 
 
 
 
 
242
  return script, result
243
 
244
  try:
 
270
  # Zip the mineru_outputs folder
271
  zip_base_name = ZIP_OUTPUT_PATH.replace(".zip", "")
272
  shutil.make_archive(zip_base_name, 'zip', OUTPUT_DIR)
273
+
274
  success_msg = f"βœ… {task_type.upper()} generated and zipped successfully"
275
  return success_msg, get_debug_info(), full_log, ZIP_OUTPUT_PATH
276
 
 
279
  return "❌ Global exception during final generation", get_debug_info(), error_log, None
280
 
281
  # ==========================================
282
+ # --- πŸš€ UI Configuration & Advanced CSS ---
283
  # ==========================================
 
284
  custom_css = """
285
+ @import url('https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap');
286
+
287
+ body, .gradio-container {
288
+ background-color: #FAF5FF !important;
289
+ background-image: linear-gradient(#E9D5FF 1px, transparent 1px), linear-gradient(90deg, #E9D5FF 1px, transparent 1px) !important;
290
+ background-size: 40px 40px !important;
291
+ font-family: 'Outfit', sans-serif !important;
292
+ }
293
+
294
+ .dark body, .dark .gradio-container {
295
+ background-color: #1a1a1a !important;
296
+ background-image: linear-gradient(rgba(168, 85, 247, .1) 1px, transparent 1px), linear-gradient(90deg, rgba(168, 85, 247, .1) 1px, transparent 1px) !important;
297
+ background-size: 40px 40px !important;
298
+ }
299
+
300
+ #col-container {
301
+ margin: 0 auto;
302
+ max-width: 1200px;
303
+ }
304
+
305
  #main-title {
306
  text-align: center !important;
307
+ padding: 1.5rem 0 0.5rem 0;
308
  }
309
+
310
  #main-title h1 {
311
  font-size: 2.6em !important;
312
  font-weight: 800 !important;
313
+ background: linear-gradient(135deg, #A855F7 0%, #C084FC 50%, #9333EA 100%);
314
  background-size: 200% 200%;
315
  -webkit-background-clip: text;
316
  -webkit-text-fill-color: transparent;
 
318
  animation: gradient-shift 4s ease infinite;
319
  letter-spacing: -0.02em;
320
  }
321
+
322
  @keyframes gradient-shift {
323
  0%, 100% { background-position: 0% 50%; }
324
  50% { background-position: 100% 50%; }
325
  }
326
+
327
  #subtitle {
328
  text-align: center !important;
329
+ margin-bottom: 2rem;
330
  }
331
+
332
  #subtitle p {
333
  margin: 0 auto;
334
  color: #666;
 
336
  font-weight: 500;
337
  }
338
 
339
+ .dark #subtitle p { color: #DAB2FF; }
340
+
341
+ /* Glassmorphism Cards */
342
+ .gradio-group {
343
+ background: rgba(255, 255, 255, 0.9) !important;
344
+ border: 2px solid #E9D5FF !important;
345
+ border-radius: 12px !important;
346
+ box-shadow: 0 4px 24px rgba(168, 85, 247, 0.08) !important;
347
+ backdrop-filter: blur(10px);
348
+ transition: all 0.3s ease;
349
+ padding: 15px !important;
350
+ }
351
+
352
+ .gradio-group:hover {
353
+ box-shadow: 0 8px 32px rgba(168, 85, 247, 0.12) !important;
354
+ border-color: #C084FC !important;
355
+ }
356
+
357
+ .dark .gradio-group {
358
+ background: rgba(30, 30, 30, 0.9) !important;
359
+ border-color: rgba(168, 85, 247, 0.3) !important;
360
+ }
361
+
362
  /* Terminal Log Style */
363
  .log-box textarea {
364
+ font-family: 'IBM Plex Mono', Consolas, monospace !important;
365
  font-size: 13px !important;
366
  background-color: #1e1e1e !important;
367
+ color: #DAB2FF !important;
368
+ border: 1px solid #C084FC !important;
369
+ border-radius: 8px !important;
370
  }
371
+
372
  /* Borderless Status Text */
373
  .status-text textarea {
374
  background-color: transparent !important;
375
  border: none !important;
376
  box-shadow: none !important;
377
+ font-weight: 600 !important;
378
+ color: #6B21A8 !important;
379
  }
380
+
381
+ .dark .status-text textarea {
382
+ color: #C084FC !important;
383
+ }
384
+
385
+ /* Custom Scrollbars */
386
+ ::-webkit-scrollbar { width: 8px; height: 8px; }
387
+ ::-webkit-scrollbar-track { background: rgba(168, 85, 247, 0.05); border-radius: 4px; }
388
+ ::-webkit-scrollbar-thumb { background: linear-gradient(135deg, #A855F7, #C084FC); border-radius: 4px; }
389
+ ::-webkit-scrollbar-thumb:hover { background: linear-gradient(135deg, #9333EA, #A855F7); }
390
  """
391
 
392
+ with gr.Blocks(theme=purple_theme, css=custom_css) as demo:
393
+ with gr.Column(elem_id="col-container"):
394
+ gr.Markdown("# **PaperX / Mineru Parsing Platform**", elem_id="main-title")
395
+ gr.Markdown("One-click parsing of academic PDFs, DAG structuring, and multi-modal asset generation.", elem_id="subtitle")
396
+
397
+ with gr.Row():
398
+ # ================= LEFT COLUMN: SETTINGS & ACTIONS =================
399
+ with gr.Column(scale=1):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
 
401
+ # 1. API Configuration
402
+ with gr.Accordion("βš™οΈ 1. Global API Configuration", open=False, elem_classes="gradio-group"):
403
+ with gr.Row():
404
+ key_input = gr.Textbox(label="API Key", type="password", placeholder="sk-...", scale=1)
405
+ api_base_url_input = gr.Textbox(label="Base URL (Optional)", placeholder="https://api.example.com", scale=1)
406
+ key_btn = gr.Button("πŸ’Ύ Save API Configuration")
407
 
408
+ # 2. Document Parsing
409
+ with gr.Group(elem_classes="gradio-group"):
410
+ gr.Markdown("### πŸ“„ 2. Document Parsing")
411
+ pdf_input = gr.File(label="Drag and drop or click to upload PDF", file_types=[".pdf"])
412
+
413
+ parse_btn = gr.Button("πŸš€ Start Mineru & DAG Extraction", variant="primary", size="lg")
414
+
415
+ parse_status = gr.Textbox(
416
+ show_label=False,
417
+ placeholder="Waiting for document upload...",
418
+ lines=1,
419
+ interactive=False,
420
+ elem_classes="status-text"
421
+ )
422
 
423
+ # 3. Asset Generation
424
+ with gr.Group(elem_classes="gradio-group"):
425
+ gr.Markdown("### 🎯 3. Asset Generation")
426
+ gr.Markdown("Generate final formats based on DAG structure:")
427
+
428
+ with gr.Row():
429
+ gen_ppt_btn = gr.Button("πŸ“Š Generate PPT")
430
+ gen_poster_btn = gr.Button("πŸ–ΌοΈ Generate Poster")
431
+ gen_pr_btn = gr.Button("πŸ“° Generate PR Article")
432
+
433
+ gen_all_btn = gr.Button("✨ Generate All Assets (ALL)", variant="primary")
434
 
435
+ # ================= RIGHT COLUMN: OUTPUTS & LOGS =================
436
+ with gr.Column(scale=1):
437
+
438
+ # 4. Results & Downloads
439
+ with gr.Group(elem_classes="gradio-group"):
440
+ gr.Markdown("### πŸ“¦ Generation Results & Download")
441
+ gen_status = gr.Textbox(
442
+ show_label=False,
443
+ placeholder="No generation task currently...",
444
+ lines=2,
 
 
 
 
 
 
 
 
 
 
 
 
 
445
  interactive=False,
446
+ elem_classes="status-text"
447
  )
448
+ download_file = gr.File(label="πŸ“₯ Get Final Zip Archive", interactive=False, visible=False)
449
+
450
+ # 5. Debugging & Terminal
451
+ with gr.Group(elem_classes="gradio-group"):
452
+ gr.Markdown("### πŸ› οΈ Developer Monitoring (Debug Only)")
453
+ with gr.Tabs():
454
+ with gr.Tab("πŸ“œ Terminal Stream"):
455
+ cmd_logs = gr.Textbox(
456
+ label="Stdout / Stderr",
457
+ placeholder="Waiting for task to start...",
458
+ lines=14,
459
+ interactive=False,
460
+ elem_classes="log-box"
461
+ )
462
+
463
+ with gr.Tab("πŸ” System Snapshot"):
464
+ refresh_btn = gr.Button("πŸ”„ Refresh Directory Tree")
465
+ debug_view = gr.Textbox(
466
+ label="Workspace Files",
467
+ lines=13,
468
+ interactive=False,
469
+ value=get_debug_info(),
470
+ elem_classes="log-box"
471
+ )
472
 
473
  # ================= LOGIC BINDINGS =================
 
474
  key_btn.click(fn=save_api_settings, inputs=[key_input, api_base_url_input], outputs=[parse_status, debug_view])
475
 
476
  pdf_input.upload(fn=save_pdf, inputs=pdf_input, outputs=[parse_status, debug_view])