Laramie2 commited on
Commit
0896d98
·
verified ·
1 Parent(s): 1c41e4f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +109 -60
app.py CHANGED
@@ -18,6 +18,7 @@ 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 ---
@@ -93,17 +94,56 @@ purple_theme = PurpleTheme()
93
  # ==========================================
94
  # --- ⚙️ Backend Logic & Functions ---
95
  # ==========================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  def get_debug_info():
97
- now = datetime.now().strftime("%H:%M:%S")
98
- files = os.listdir(PAPERS_DIR) if os.path.exists(PAPERS_DIR) else "Directory missing"
99
- output_detail = "Not generated"
100
- if os.path.exists(OUTPUT_DIR):
101
- all_output_items = []
102
- for root, dirs, files_in_out in os.walk(OUTPUT_DIR):
103
- for name in files_in_out:
104
- all_output_items.append(os.path.join(os.path.relpath(root, OUTPUT_DIR), name))
105
- output_detail = f"Found {len(all_output_items)} files: {all_output_items[:5]}..." if all_output_items else "Directory exists but is EMPTY"
106
- return f"[{now}] 📁 papers/ Content: {files}\n\n[{now}] 📂 mineru_outputs Status: {output_detail}"
 
 
 
 
 
 
 
107
 
108
  def save_pdf(file):
109
  if file is None: return "❌ Please select a PDF first", get_debug_info()
@@ -121,16 +161,41 @@ def save_pdf(file):
121
  return f"❌ Error: {str(e)}", get_debug_info()
122
 
123
  def clear_pdf():
124
- """Delete PDF from the backend directory when cleared in UI"""
125
  try:
126
  deleted_files = []
 
 
 
 
 
 
127
  for f in os.listdir(PAPERS_DIR):
128
  file_to_del = os.path.join(PAPERS_DIR, f)
129
  if os.path.isfile(file_to_del):
130
  os.remove(file_to_del)
131
  deleted_files.append(f)
132
- if deleted_files:
133
- return "🗑️ File deleted from workspace", get_debug_info()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  return "ℹ️ Workspace is already empty", get_debug_info()
135
  except Exception as e:
136
  return f"❌ Error deleting file: {str(e)}", get_debug_info()
@@ -268,7 +333,7 @@ body, .gradio-container {
268
  background-image: linear-gradient(rgba(168, 85, 247, .1) 1px, transparent 1px), linear-gradient(90deg, rgba(168, 85, 247, .1) 1px, transparent 1px) !important;
269
  }
270
 
271
- #col-container { margin: 0 auto; max-width: 100%; padding: 20px; }
272
 
273
  #main-title { text-align: center !important; padding: 1.5rem 0 0.5rem 0; }
274
  #main-title h1 {
@@ -285,20 +350,19 @@ body, .gradio-container {
285
  #subtitle p { margin: 0 auto; color: #666; font-size: 1.1rem; font-weight: 500; }
286
  .dark #subtitle p { color: #DAB2FF; }
287
 
288
- /* ======== 修改:模块卡片强化圆角和内外边距 ======== */
289
  .gradio-group {
290
  background: rgba(255, 255, 255, 0.9) !important;
291
  border: 2px solid #E9D5FF !important;
292
- border-radius: 24px !important; /* 更明显的圆角 */
293
  box-shadow: 0 4px 24px rgba(168, 85, 247, 0.08) !important;
294
  backdrop-filter: blur(10px);
295
  transition: all 0.3s ease;
296
- padding: 16px 16px 16px 16px !important; /* 边缘与内层内容的间距 */
297
  overflow: visible !important;
298
- margin-bottom: 10px !important; /* 模块之间的垂直间距 */
299
  display: flex !important;
300
  flex-direction: column !important;
301
- gap: 16px !important; /* 内部组件之间的自然间距 */
302
  }
303
  .gradio-group:hover {
304
  box-shadow: 0 8px 32px rgba(168, 85, 247, 0.12) !important;
@@ -309,19 +373,15 @@ body, .gradio-container {
309
  border-color: rgba(168, 85, 247, 0.3) !important;
310
  }
311
 
312
- /* 恢复 Gradio 内部组件的圆角(Gradio默认会将 group 内的元素边缘变直) */
313
  .gradio-group > div,
314
- .gradio-group > .form {
315
- border-radius: 12px !important;
316
- }
317
 
318
- /* ======== 优化:文件上传框居中与背景清理 ======== */
319
  #pdf-upload-box {
320
  border: 2px dashed rgba(192, 132, 252, 0.6) !important;
321
  border-radius: 16px !important;
322
  background-color: rgba(250, 245, 255, 0.5) !important;
323
  transition: all 0.3s ease !important;
324
- min-height: 220px !important; /* 增加高度提供居中空间 */
325
  position: relative !important;
326
  margin-top: 10px !important;
327
  }
@@ -332,16 +392,10 @@ body, .gradio-container {
332
  box-shadow: 0 4px 15px rgba(168, 85, 247, 0.15) !important;
333
  }
334
 
335
- /* 隐藏原生的中/英文上传提示、图标及背景 */
336
- #pdf-upload-box .upload-container {
337
- background: transparent !important;
338
- }
339
  #pdf-upload-box .upload-container > span,
340
- #pdf-upload-box .upload-container > svg {
341
- display: none !important;
342
- }
343
 
344
- /* 使用伪元素在中央注入自定义文字和图标 */
345
  #pdf-upload-box .upload-container::before {
346
  content: "📤\\A Click here to select a PDF\\A or Drag & Drop the file here";
347
  white-space: pre-wrap;
@@ -356,28 +410,25 @@ body, .gradio-container {
356
  width: 100%;
357
  height: 100%;
358
  position: absolute;
359
- top: 0;
360
- left: 0;
361
- pointer-events: none; /* 防止遮挡拖拽事件 */
362
  }
363
 
364
- /* ======== 修改:减小按钮尺寸,保持字体一致 ======== */
365
- /* 主要操作按钮 (Start Mineru & Generate All) */
366
  .primary-action-btn {
367
- border-radius: 25px !important; /* 调小 */
368
  background: linear-gradient(135deg, #9333EA, #7E22CE) !important;
369
  color: white !important; font-weight: 700 !important; border: none !important;
370
- height: 50px !important; /* 调小:60 -> 50 */
371
  width: 80% !important;
372
- margin-left: auto !important; /* 新增:自动左边距 */
373
- margin-right: auto !important; /* 新增:自动右边距 */
374
  margin-top: 10px !important;
375
  margin-bottom: 10px !important;
376
- display: block !important; /* 新增:确保块级元素 */
377
  transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275), box-shadow 0.3s ease !important;
378
  box-shadow: 0 4px 15px rgba(126, 34, 206, 0.3) !important;
379
  cursor: pointer !important;
380
- font-size: 1.15rem !important; /* 字体保持一致:略调小以配合新尺寸 */
381
  }
382
  .primary-action-btn:hover {
383
  transform: translateY(-5px) scale(1.02) !important;
@@ -386,16 +437,14 @@ body, .gradio-container {
386
  }
387
  .primary-action-btn:active { transform: translateY(2px) scale(0.98) !important; box-shadow: 0 2px 10px rgba(126, 34, 206, 0.2) !important; }
388
 
389
- /* 3个小按钮的样式 */
390
  .action-row { display: flex !important; justify-content: center !important; gap: 10px !important; margin-bottom: 10px !important; margin-top: 10px !important;}
391
  .action-btn {
392
- border-radius: 24px !important; /* 调小 */
393
  background: linear-gradient(135deg, #A855F7, #9333EA) !important;
394
  color: white !important; font-weight: 600 !important; border: none !important;
395
- height: 40px !important; /* 调小:55 -> 48 */
396
  width: 120px !important;
397
  flex: none !important;
398
- /*文字垂直居中*/
399
  display: flex !important;
400
  align-items: center !important;
401
  justify-content: center !important;
@@ -403,11 +452,10 @@ body, .gradio-container {
403
  padding: 0 !important;
404
  margin-top: 10px !important;
405
  margin-bottom: 10px !important;
406
-
407
  transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275), box-shadow 0.3s ease !important;
408
  box-shadow: 0 4px 15px rgba(147, 51, 234, 0.2) !important;
409
- cursor: pointer !important; display: flex !important; align-items: center !important; justify-content: center !important;
410
- font-size: 1.05rem !important; /* 字体保持一致:显式设置 */
411
  }
412
  .action-btn:hover {
413
  transform: translateY(-5px) scale(1.03) !important;
@@ -416,7 +464,6 @@ body, .gradio-container {
416
  }
417
  .action-btn:active { transform: translateY(2px) scale(0.98) !important; box-shadow: 0 2px 10px rgba(147, 51, 234, 0.2) !important; }
418
 
419
- /* Terminal Log Style */
420
  .log-box textarea { font-family: 'IBM Plex Mono', monospace !important; font-size: 13px !important; background-color: #1e1e1e !important; color: #DAB2FF !important; border: 1px solid #C084FC !important; border-radius: 8px !important; }
421
  .status-text textarea { background-color: transparent !important; border: none !important; box-shadow: none !important; font-weight: 600 !important; color: #6B21A8 !important; }
422
  .dark .status-text textarea { color: #C084FC !important; }
@@ -424,6 +471,10 @@ body, .gradio-container {
424
  ::-webkit-scrollbar-track { background: rgba(168, 85, 247, 0.05); border-radius: 4px; }
425
  ::-webkit-scrollbar-thumb { background: linear-gradient(135deg, #A855F7, #C084FC); border-radius: 4px; }
426
  ::-webkit-scrollbar-thumb:hover { background: linear-gradient(135deg, #9333EA, #A855F7); }
 
 
 
 
427
  """
428
 
429
  with gr.Blocks(theme=purple_theme, css=custom_css) as demo:
@@ -439,7 +490,7 @@ with gr.Blocks(theme=purple_theme, css=custom_css) as demo:
439
  with gr.Group(elem_classes="gradio-group"):
440
  gr.Markdown("### ⚙️ 1. Global API Configuration")
441
  with gr.Row():
442
- key_input = gr.Textbox(label="Gemini API Key", type="password", placeholder="sk-...", scale=1)
443
  api_base_url_input = gr.Textbox(label="Base URL (Optional)", placeholder="https://api.example.com", scale=1)
444
  key_btn = gr.Button("💾 Save API Configuration")
445
 
@@ -447,14 +498,12 @@ with gr.Blocks(theme=purple_theme, css=custom_css) as demo:
447
  with gr.Group(elem_classes="gradio-group"):
448
  gr.Markdown("### 📄 2. Document Parsing")
449
 
450
- # 简化了 Label,因为 CSS 已经接管了内部文字渲染
451
  pdf_input = gr.File(
452
  label="Upload Document",
453
  file_types=[".pdf"],
454
  elem_id="pdf-upload-box"
455
  )
456
 
457
- # 应用全新的 primary-action-btn 悬浮/圆角类
458
  parse_btn = gr.Button("🚀 Start Mineru & DAG Extraction", elem_classes="primary-action-btn")
459
 
460
  parse_status = gr.Textbox(
@@ -471,7 +520,6 @@ with gr.Blocks(theme=purple_theme, css=custom_css) as demo:
471
  gen_poster_btn = gr.Button("🖼️ Gen Poster", elem_classes="action-btn")
472
  gen_pr_btn = gr.Button("📰 Gen Article", elem_classes="action-btn")
473
 
474
- # 应用全新的 primary-action-btn 悬浮/圆角类
475
  gen_all_btn = gr.Button("✨ Generate All Assets (ALL)", elem_classes="primary-action-btn")
476
 
477
  # ================= RIGHT COLUMN: OUTPUTS & LOGS =================
@@ -496,15 +544,16 @@ with gr.Blocks(theme=purple_theme, css=custom_css) as demo:
496
 
497
  with gr.Tab("🔍 System Snapshot"):
498
  refresh_btn = gr.Button("🔄 Refresh Directory Tree")
499
- debug_view = gr.Textbox(
500
- label="Workspace Files", lines=13, interactive=False, value=get_debug_info(), elem_classes="log-box"
 
501
  )
502
 
503
  # ================= LOGIC BINDINGS =================
504
  key_btn.click(fn=save_api_settings, inputs=[key_input, api_base_url_input], outputs=[parse_status, debug_view])
505
 
506
  pdf_input.upload(fn=save_pdf, inputs=pdf_input, outputs=[parse_status, debug_view])
507
- # 增加清除逻辑点击 "x" 后彻底删除后端文件
508
  pdf_input.clear(fn=clear_pdf, outputs=[parse_status, debug_view])
509
 
510
  parse_btn.click(fn=run_mineru_parsing_and_dag_gen, outputs=[parse_status, debug_view, cmd_logs])
@@ -522,4 +571,4 @@ with gr.Blocks(theme=purple_theme, css=custom_css) as demo:
522
  refresh_btn.click(fn=get_debug_info, outputs=debug_view)
523
 
524
  if __name__ == "__main__":
525
- demo.launch()
 
18
  ZIP_OUTPUT_PATH = os.path.join(BASE_DIR, "mineru_results.zip")
19
 
20
  os.makedirs(PAPERS_DIR, exist_ok=True)
21
+ os.makedirs(OUTPUT_DIR, exist_ok=True)
22
 
23
  # ==========================================
24
  # --- 🎨 Custom Purple Theme Definition ---
 
94
  # ==========================================
95
  # --- ⚙️ Backend Logic & Functions ---
96
  # ==========================================
97
+
98
+ def get_tree_html(dir_path):
99
+ """递归生成可折叠的 HTML 目录树"""
100
+ if not os.path.exists(dir_path):
101
+ return "<div style='margin-left: 15px; color: #888;'><i>Directory missing</i></div>"
102
+
103
+ def build_html(current_path):
104
+ html = ""
105
+ try:
106
+ items = sorted(os.listdir(current_path))
107
+ except Exception:
108
+ return ""
109
+
110
+ if not items:
111
+ return ""
112
+
113
+ for item in items:
114
+ item_path = os.path.join(current_path, item)
115
+ if os.path.isdir(item_path):
116
+ # 文件夹节点 (可折叠)
117
+ html += f'<details style="margin-left: 15px; cursor: pointer; margin-top: 4px;"><summary style="outline: none; color: #C084FC;">📁 <b>{item}</b></summary>'
118
+ inner_html = build_html(item_path)
119
+ html += inner_html if inner_html else "<div style='margin-left: 20px; color: #888; font-size: 12px;'><i>Empty</i></div>"
120
+ html += '</details>'
121
+ else:
122
+ # 文件节点
123
+ html += f'<div style="margin-left: 18px; padding-left: 12px; border-left: 1px dotted #A855F7; margin-top: 4px; color: #DAB2FF;">📄 {item}</div>'
124
+ return html
125
+
126
+ content = build_html(dir_path)
127
+ return content if content else "<div style='margin-left: 15px; color: #888;'><i>Empty directory</i></div>"
128
+
129
  def get_debug_info():
130
+ """返回渲染好的目录树 HTML 容器"""
131
+ papers_tree = get_tree_html(PAPERS_DIR)
132
+ output_tree = get_tree_html(OUTPUT_DIR)
133
+
134
+ html = f"""
135
+ <div style="font-family: 'IBM Plex Mono', monospace; font-size: 13px; background-color: #1e1e1e; border: 1px solid #C084FC; border-radius: 8px; padding: 16px; max-height: 400px; overflow-y: auto;">
136
+ <details open style="margin-bottom: 12px; cursor: pointer;">
137
+ <summary style="outline: none; font-size: 15px; color: #A855F7;">📁 <b>papers/</b></summary>
138
+ {papers_tree}
139
+ </details>
140
+ <details open style="cursor: pointer;">
141
+ <summary style="outline: none; font-size: 15px; color: #A855F7;">📂 <b>mineru_outputs/</b></summary>
142
+ {output_tree}
143
+ </details>
144
+ </div>
145
+ """
146
+ return html
147
 
148
  def save_pdf(file):
149
  if file is None: return "❌ Please select a PDF first", get_debug_info()
 
161
  return f"❌ Error: {str(e)}", get_debug_info()
162
 
163
  def clear_pdf():
164
+ """Delete PDF and its corresponding mineru output directory"""
165
  try:
166
  deleted_files = []
167
+ deleted_dirs = []
168
+
169
+ # 1. 记录要被删除的 PDF 文件名(用于匹配输出目录)
170
+ pdf_names = [f for f in os.listdir(PAPERS_DIR) if f.endswith('.pdf')]
171
+
172
+ # 2. 删除 papers 目录下的 PDF
173
  for f in os.listdir(PAPERS_DIR):
174
  file_to_del = os.path.join(PAPERS_DIR, f)
175
  if os.path.isfile(file_to_del):
176
  os.remove(file_to_del)
177
  deleted_files.append(f)
178
+
179
+ # 3. 删除 mineru_outputs 下的同名文件夹
180
+ if os.path.exists(OUTPUT_DIR):
181
+ for pdf_name in pdf_names:
182
+ base_name = os.path.splitext(pdf_name)[0]
183
+ base_name_us = base_name.replace(" ", "_") # 兼容 Mineru 可能会把空格转为下划线
184
+
185
+ for item in os.listdir(OUTPUT_DIR):
186
+ # 匹配严格同名,或者下划线名称,或者带 .pdf 后缀的文件夹
187
+ if item in [base_name, base_name_us, pdf_name]:
188
+ dir_to_del = os.path.join(OUTPUT_DIR, item)
189
+ if os.path.isdir(dir_to_del):
190
+ shutil.rmtree(dir_to_del)
191
+ deleted_dirs.append(item)
192
+
193
+ # 4. 删除可能遗留的打包压缩文件
194
+ if os.path.exists(ZIP_OUTPUT_PATH):
195
+ os.remove(ZIP_OUTPUT_PATH)
196
+
197
+ if deleted_files or deleted_dirs:
198
+ return f"🗑️ Workspace cleared (Deleted: {len(deleted_files)} PDF, {len(deleted_dirs)} Output Folder)", get_debug_info()
199
  return "ℹ️ Workspace is already empty", get_debug_info()
200
  except Exception as e:
201
  return f"❌ Error deleting file: {str(e)}", get_debug_info()
 
333
  background-image: linear-gradient(rgba(168, 85, 247, .1) 1px, transparent 1px), linear-gradient(90deg, rgba(168, 85, 247, .1) 1px, transparent 1px) !important;
334
  }
335
 
336
+ #col-container { margin: 0 auto; max-width: 90%; padding: 20px; }
337
 
338
  #main-title { text-align: center !important; padding: 1.5rem 0 0.5rem 0; }
339
  #main-title h1 {
 
350
  #subtitle p { margin: 0 auto; color: #666; font-size: 1.1rem; font-weight: 500; }
351
  .dark #subtitle p { color: #DAB2FF; }
352
 
 
353
  .gradio-group {
354
  background: rgba(255, 255, 255, 0.9) !important;
355
  border: 2px solid #E9D5FF !important;
356
+ border-radius: 24px !important;
357
  box-shadow: 0 4px 24px rgba(168, 85, 247, 0.08) !important;
358
  backdrop-filter: blur(10px);
359
  transition: all 0.3s ease;
360
+ padding: 16px 16px 16px 16px !important;
361
  overflow: visible !important;
362
+ margin-bottom: 10px !important;
363
  display: flex !important;
364
  flex-direction: column !important;
365
+ gap: 16px !important;
366
  }
367
  .gradio-group:hover {
368
  box-shadow: 0 8px 32px rgba(168, 85, 247, 0.12) !important;
 
373
  border-color: rgba(168, 85, 247, 0.3) !important;
374
  }
375
 
 
376
  .gradio-group > div,
377
+ .gradio-group > .form { border-radius: 12px !important; }
 
 
378
 
 
379
  #pdf-upload-box {
380
  border: 2px dashed rgba(192, 132, 252, 0.6) !important;
381
  border-radius: 16px !important;
382
  background-color: rgba(250, 245, 255, 0.5) !important;
383
  transition: all 0.3s ease !important;
384
+ min-height: 220px !important;
385
  position: relative !important;
386
  margin-top: 10px !important;
387
  }
 
392
  box-shadow: 0 4px 15px rgba(168, 85, 247, 0.15) !important;
393
  }
394
 
395
+ #pdf-upload-box .upload-container { background: transparent !important; }
 
 
 
396
  #pdf-upload-box .upload-container > span,
397
+ #pdf-upload-box .upload-container > svg { display: none !important; }
 
 
398
 
 
399
  #pdf-upload-box .upload-container::before {
400
  content: "📤\\A Click here to select a PDF\\A or Drag & Drop the file here";
401
  white-space: pre-wrap;
 
410
  width: 100%;
411
  height: 100%;
412
  position: absolute;
413
+ top: 0; left: 0;
414
+ pointer-events: none;
 
415
  }
416
 
 
 
417
  .primary-action-btn {
418
+ border-radius: 25px !important;
419
  background: linear-gradient(135deg, #9333EA, #7E22CE) !important;
420
  color: white !important; font-weight: 700 !important; border: none !important;
421
+ height: 50px !important;
422
  width: 80% !important;
423
+ margin-left: auto !important;
424
+ margin-right: auto !important;
425
  margin-top: 10px !important;
426
  margin-bottom: 10px !important;
427
+ display: block !important;
428
  transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275), box-shadow 0.3s ease !important;
429
  box-shadow: 0 4px 15px rgba(126, 34, 206, 0.3) !important;
430
  cursor: pointer !important;
431
+ font-size: 1.15rem !important;
432
  }
433
  .primary-action-btn:hover {
434
  transform: translateY(-5px) scale(1.02) !important;
 
437
  }
438
  .primary-action-btn:active { transform: translateY(2px) scale(0.98) !important; box-shadow: 0 2px 10px rgba(126, 34, 206, 0.2) !important; }
439
 
 
440
  .action-row { display: flex !important; justify-content: center !important; gap: 10px !important; margin-bottom: 10px !important; margin-top: 10px !important;}
441
  .action-btn {
442
+ border-radius: 24px !important;
443
  background: linear-gradient(135deg, #A855F7, #9333EA) !important;
444
  color: white !important; font-weight: 600 !important; border: none !important;
445
+ height: 40px !important;
446
  width: 120px !important;
447
  flex: none !important;
 
448
  display: flex !important;
449
  align-items: center !important;
450
  justify-content: center !important;
 
452
  padding: 0 !important;
453
  margin-top: 10px !important;
454
  margin-bottom: 10px !important;
 
455
  transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275), box-shadow 0.3s ease !important;
456
  box-shadow: 0 4px 15px rgba(147, 51, 234, 0.2) !important;
457
+ cursor: pointer !important;
458
+ font-size: 1.05rem !important;
459
  }
460
  .action-btn:hover {
461
  transform: translateY(-5px) scale(1.03) !important;
 
464
  }
465
  .action-btn:active { transform: translateY(2px) scale(0.98) !important; box-shadow: 0 2px 10px rgba(147, 51, 234, 0.2) !important; }
466
 
 
467
  .log-box textarea { font-family: 'IBM Plex Mono', monospace !important; font-size: 13px !important; background-color: #1e1e1e !important; color: #DAB2FF !important; border: 1px solid #C084FC !important; border-radius: 8px !important; }
468
  .status-text textarea { background-color: transparent !important; border: none !important; box-shadow: none !important; font-weight: 600 !important; color: #6B21A8 !important; }
469
  .dark .status-text textarea { color: #C084FC !important; }
 
471
  ::-webkit-scrollbar-track { background: rgba(168, 85, 247, 0.05); border-radius: 4px; }
472
  ::-webkit-scrollbar-thumb { background: linear-gradient(135deg, #A855F7, #C084FC); border-radius: 4px; }
473
  ::-webkit-scrollbar-thumb:hover { background: linear-gradient(135deg, #9333EA, #A855F7); }
474
+
475
+ /* 使折叠树中的 details 和 summary 更加平滑 */
476
+ details > summary { transition: color 0.2s ease; }
477
+ details > summary:hover { color: #E9D5FF !important; }
478
  """
479
 
480
  with gr.Blocks(theme=purple_theme, css=custom_css) as demo:
 
490
  with gr.Group(elem_classes="gradio-group"):
491
  gr.Markdown("### ⚙️ 1. Global API Configuration")
492
  with gr.Row():
493
+ key_input = gr.Textbox(label="API Key", type="password", placeholder="sk-...", scale=1)
494
  api_base_url_input = gr.Textbox(label="Base URL (Optional)", placeholder="https://api.example.com", scale=1)
495
  key_btn = gr.Button("💾 Save API Configuration")
496
 
 
498
  with gr.Group(elem_classes="gradio-group"):
499
  gr.Markdown("### 📄 2. Document Parsing")
500
 
 
501
  pdf_input = gr.File(
502
  label="Upload Document",
503
  file_types=[".pdf"],
504
  elem_id="pdf-upload-box"
505
  )
506
 
 
507
  parse_btn = gr.Button("🚀 Start Mineru & DAG Extraction", elem_classes="primary-action-btn")
508
 
509
  parse_status = gr.Textbox(
 
520
  gen_poster_btn = gr.Button("🖼️ Gen Poster", elem_classes="action-btn")
521
  gen_pr_btn = gr.Button("📰 Gen Article", elem_classes="action-btn")
522
 
 
523
  gen_all_btn = gr.Button("✨ Generate All Assets (ALL)", elem_classes="primary-action-btn")
524
 
525
  # ================= RIGHT COLUMN: OUTPUTS & LOGS =================
 
544
 
545
  with gr.Tab("🔍 System Snapshot"):
546
  refresh_btn = gr.Button("🔄 Refresh Directory Tree")
547
+ # 这里改为 HTML 组件,以便渲染我们的 details/summary 折叠树
548
+ debug_view = gr.HTML(
549
+ value=get_debug_info()
550
  )
551
 
552
  # ================= LOGIC BINDINGS =================
553
  key_btn.click(fn=save_api_settings, inputs=[key_input, api_base_url_input], outputs=[parse_status, debug_view])
554
 
555
  pdf_input.upload(fn=save_pdf, inputs=pdf_input, outputs=[parse_status, debug_view])
556
+ # 彻底清除:不仅清除 PDF还清除 mineru output 中的对应文件
557
  pdf_input.clear(fn=clear_pdf, outputs=[parse_status, debug_view])
558
 
559
  parse_btn.click(fn=run_mineru_parsing_and_dag_gen, outputs=[parse_status, debug_view, cmd_logs])
 
571
  refresh_btn.click(fn=get_debug_info, outputs=debug_view)
572
 
573
  if __name__ == "__main__":
574
+ demo.launch(debug=True)