prithivMLmods commited on
Commit
808a0b9
Β·
verified Β·
1 Parent(s): 1fc71d0

update app

Browse files
Files changed (1) hide show
  1. app.py +106 -102
app.py CHANGED
@@ -1,4 +1,6 @@
1
  import os
 
 
2
  import hashlib
3
  import spaces
4
  import re
@@ -16,7 +18,63 @@ import fitz
16
  import html2text
17
  import markdown
18
  import tempfile
19
- from typing import Optional, Tuple, Dict, Any, List
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  pdf_suffixes = [".pdf"]
22
  image_suffixes = [".png", ".jpeg", ".jpg"]
@@ -59,9 +117,6 @@ logger.info(f"Model '{MODEL_ID_3}' loaded successfully.")
59
 
60
  @spaces.GPU
61
  def parse_page(image: Image.Image, model_name: str) -> str:
62
- """
63
- Parses a single document page image using the selected model.
64
- """
65
  if model_name == "Logics-Parsing":
66
  current_processor, current_model = processor_1, model_1
67
  elif model_name == "Gliese-OCR-7B-Post1.0":
@@ -71,21 +126,18 @@ def parse_page(image: Image.Image, model_name: str) -> str:
71
  else:
72
  raise ValueError(f"Unknown model choice: {model_name}")
73
 
74
- messages = [{"role": "user", "content": [{"type": "image", "image": image}, {"type": "text", "text": "Parse this document page into a clean, structured HTML representation. Preserve the logical structure with appropriate tags for content blocks such as paragraphs (<p>), headings (<h1>-<h6>), tables (<table>), figures (<figure>), formulas (<formula>), and others. Include category tags, and filter out irrelevant elements like headers and footers."}]}]
75
  prompt_full = current_processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
76
- inputs = current_processor(text=[prompt_full], images=[image], return_tensors="pt", padding=True).to(device)
77
 
78
  with torch.no_grad():
79
- generated_ids = current_model.generate(**inputs, max_new_tokens=2048, temperature=0.1, top_p=0.9, do_sample=True, repetition_penalty=1.05)
80
 
81
- generated_ids_trimmed = [out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)]
82
- output_text = current_processor.batch_decode(generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
83
  return output_text
84
 
85
  def convert_file_to_images(file_path: str, dpi: int = 200) -> List[Image.Image]:
86
- """
87
- Converts a PDF or image file into a list of PIL Images.
88
- """
89
  images = []
90
  file_ext = Path(file_path).suffix.lower()
91
 
@@ -104,7 +156,7 @@ def convert_file_to_images(file_path: str, dpi: int = 200) -> List[Image.Image]:
104
  page = pdf_document.load_page(page_num)
105
  pix = page.get_pixmap(matrix=mat)
106
  img_data = pix.tobytes("png")
107
- images.append(Image.open(BytesIO(img_data)))
108
  pdf_document.close()
109
  except Exception as e:
110
  logger.error(f"Failed to convert PDF using PyMuPDF: {e}")
@@ -112,13 +164,9 @@ def convert_file_to_images(file_path: str, dpi: int = 200) -> List[Image.Image]:
112
  return images
113
 
114
  def get_initial_state() -> Dict[str, Any]:
115
- """Returns the default initial state for the application."""
116
  return {"pages": [], "total_pages": 0, "current_page_index": 0, "page_results": []}
117
 
118
  def load_and_preview_file(file_path: Optional[str]) -> Tuple[Optional[Image.Image], str, Dict[str, Any]]:
119
- """
120
- Loads a file, converts all pages to images, and stores them in the state.
121
- """
122
  state = get_initial_state()
123
  if not file_path:
124
  return None, '<div class="page-info">No file loaded</div>', state
@@ -136,10 +184,7 @@ def load_and_preview_file(file_path: Optional[str]) -> Tuple[Optional[Image.Imag
136
  logger.error(f"Failed to load and preview file: {e}")
137
  return None, '<div class="page-info">Failed to load preview</div>', state
138
 
139
- async def process_all_pages(state: Dict[str, Any], model_choice: str):
140
- """
141
- Processes all pages stored in the state and updates the state with results.
142
- """
143
  if not state or not state["pages"]:
144
  error_msg = "<h3>Please upload a file first.</h3>"
145
  return error_msg, "", "", None, "Error: No file to process", state
@@ -149,14 +194,12 @@ async def process_all_pages(state: Dict[str, Any], model_choice: str):
149
 
150
  try:
151
  page_results = []
152
- for i, page_img in enumerate(state["pages"]):
153
- logger.info(f"Parsing page {i + 1}/{state['total_pages']}")
154
  html_result = parse_page(page_img, model_choice)
155
  page_results.append({'raw_html': html_result})
156
 
157
  state["page_results"] = page_results
158
 
159
- # Create a single markdown file for download with all content
160
  full_html_content = "\n\n".join([f'<!-- Page {i+1} -->\n{res["raw_html"]}' for i, res in enumerate(page_results)])
161
  full_markdown = html2text.html2text(full_html_content)
162
  with tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False, encoding='utf-8') as f:
@@ -166,7 +209,6 @@ async def process_all_pages(state: Dict[str, Any], model_choice: str):
166
  parsing_time = time.time() - start_time
167
  cost_time_str = f'Total processing time: {parsing_time:.2f}s'
168
 
169
- # Display the results for the current page
170
  current_page_results = get_page_outputs(state)
171
 
172
  return *current_page_results, md_path, cost_time_str, state
@@ -177,9 +219,6 @@ async def process_all_pages(state: Dict[str, Any], model_choice: str):
177
  return error_html, "", "", None, f"Error: {str(e)}", state
178
 
179
  def navigate_page(direction: str, state: Dict[str, Any]):
180
- """
181
- Navigates to the previous or next page and updates the UI accordingly.
182
- """
183
  if not state or not state["pages"]:
184
  return None, '<div class="page-info">No file loaded</div>', *get_page_outputs(state), state
185
 
@@ -203,126 +242,91 @@ def navigate_page(direction: str, state: Dict[str, Any]):
203
  return image_preview, page_info_html, *page_outputs, state
204
 
205
  def get_page_outputs(state: Dict[str, Any]) -> Tuple[str, str, str]:
206
- """Helper to get displayable outputs for the current page."""
207
  if not state or not state.get("page_results"):
208
  return "<h3>Process the document to see results.</h3>", "", ""
209
 
210
  index = state["current_page_index"]
 
 
 
211
  result = state["page_results"][index]
212
  raw_html = result['raw_html']
213
 
214
- mmd_source = html2text.html2text(raw_html)
215
- mmd_render = markdown.markdown(mmd_source, extensions=['fenced_code', 'tables'])
216
 
217
- return mmd_render, mmd_source, raw_html
218
 
219
  def clear_all():
220
- """Clears all UI components and resets the state."""
221
- return (
222
- None,
223
- None,
224
- "<h3>Results will be displayed here after processing.</h3>",
225
- "",
226
- "",
227
- None,
228
- "",
229
- '<div class="page-info">No file loaded</div>',
230
- get_initial_state()
231
- )
232
 
233
  @click.command()
234
  def main():
235
- """
236
- Sets up and launches the Gradio user interface for the Logics-Parsing app.
237
- """
238
  css = """
239
  .main-container { max-width: 1400px; margin: 0 auto; }
240
- .header-text { text-align: center; color: #2c3e50; margin-bottom: 20px; }
241
- .process-button { border: none !important; color: white !important; font-weight: bold !important; background-color: blue !important;}
242
- .process-button:hover { background-color: darkblue !important; transform: translateY(-2px) !important; box-shadow: 0 4px 8px rgba(0,0,0,0.2) !important; }
243
- .page-info { text-align: center; padding: 8px 16px; border-radius: 20px; font-weight: bold; margin: 10px 0; }
244
  """
245
- with gr.Blocks(theme="bethecloud/storj_theme", css=css, title="Logics-Parsing Demo") as demo:
246
  app_state = gr.State(value=get_initial_state())
247
 
248
  gr.HTML("""
249
  <div class="header-text">
250
  <h1>πŸ“„ Multimodal: VLM Parsing</h1>
251
- <p style="font-size: 1.1em; color: #6b7280;">An advanced Vision Language Model to parse documents and images into clean Markdown(.md)</p>
252
  <div style="display: flex; justify-content: center; gap: 20px; margin: 15px 0;">
253
- <a href="https://huggingface.co/collections/prithivMLmods/mm-vlm-parsing-68e33e52bfb9ae60b50602dc" target="_blank" style="text-decoration: none; color: #2563eb; font-weight: 500;">πŸ€— Model Info</a>
254
- <a href="https://github.com/PRITHIVSAKTHIUR/VLM-Parsing" target="_blank" style="text-decoration: none; color: #2563eb; font-weight: 500;">πŸ’» GitHub</a>
255
- <a href="https://huggingface.co/models?pipeline_tag=image-text-to-text&sort=trending" target="_blank" style="text-decoration: none; color: #2563eb; font-weight: 500;">πŸ“ Multimodal VLMs</a>
256
  </div>
257
  </div>
258
  """)
259
 
260
  with gr.Row(elem_classes=["main-container"]):
261
  with gr.Column(scale=1):
262
- model_choice = gr.Dropdown(choices=["Logics-Parsing", "Gliese-OCR-7B-Post1.0", "olmOCR-7B-0825"], label="Select Model⚑️", value="Logics-Parsing")
263
  file_input = gr.File(label="Upload PDF or Image", file_types=[".pdf", ".jpg", ".jpeg", ".png"], type="filepath")
264
- image_preview = gr.Image(label="Preview", type="pil", interactive=False, height=280)
 
 
 
 
265
 
266
  with gr.Row():
267
- prev_page_btn = gr.Button("β—€ Previous", size="md")
268
  page_info = gr.HTML('<div class="page-info">No file loaded</div>')
269
- next_page_btn = gr.Button("Next β–Ά", size="md")
270
-
271
  example_root = "examples"
272
  if os.path.exists(example_root) and os.path.isdir(example_root):
273
  example_files = [os.path.join(example_root, f) for f in os.listdir(example_root) if f.endswith(tuple(pdf_suffixes + image_suffixes))]
274
  if example_files:
275
- #with gr.Accordion("Open Examplesβš™οΈ", open=False):
276
- #with gr.row():
277
- gr.Examples(examples=example_files, inputs=file_input, examples_per_page=10)
278
 
279
- with gr.Accordion("Download DetailsπŸ•§", open=False):
280
  output_file = gr.File(label='Download Markdown Result', interactive=False)
281
- cost_time = gr.Text(label='Time Cost', interactive=False)
282
-
283
- process_btn = gr.Button("πŸš€ Process", variant="primary", elem_classes=["process-button"], size="lg")
284
- clear_btn = gr.Button("πŸ—‘οΈ Clear All", variant="secondary")
285
 
286
  with gr.Column(scale=2):
287
  with gr.Tabs():
288
- with gr.Tab("Markdown Rendering"):
289
- mmd_html = gr.TextArea(lines=27, label='Markdown Rendering', show_copy_button=True, interactive=True)
290
  with gr.Tab("Markdown Source"):
291
- mmd = gr.TextArea(lines=27, show_copy_button=True, label="Markdown Source", interactive=True)
292
  with gr.Tab("Generated HTML"):
293
- raw_html = gr.TextArea(lines=27, show_copy_button=True, label="Generated HTML")
294
-
295
- # --- Event Handlers ---
296
- file_input.change(
297
- fn=load_and_preview_file,
298
- inputs=file_input,
299
- outputs=[image_preview, page_info, app_state],
300
- show_progress="full")
301
 
302
- process_btn.click(
303
- fn=process_all_pages,
304
- inputs=[app_state, model_choice],
305
- outputs=[mmd_html, mmd, raw_html,
306
- output_file, cost_time, app_state],
307
- concurrency_limit=15,
308
- show_progress="full")
309
-
310
- prev_page_btn.click(
311
- fn=lambda s: navigate_page("prev", s),
312
- inputs=app_state, outputs=[image_preview,
313
- page_info, mmd_html, mmd, raw_html, app_state])
314
 
315
- next_page_btn.click(
316
- fn=lambda s: navigate_page("next", s),
317
- inputs=app_state, outputs=[image_preview,
318
- page_info, mmd_html, mmd, raw_html, app_state])
319
-
320
- clear_btn.click(
321
- fn=clear_all,
322
- outputs=[file_input, image_preview, mmd_html, mmd, raw_html,
323
- output_file, cost_time, page_info, app_state])
324
 
325
- demo.queue().launch(debug=True, share=True, mcp_server=True, ssr_mode=False, show_error=True)
326
 
327
  if __name__ == '__main__':
328
  if not os.path.exists("examples"):
 
1
  import os
2
+ import sys
3
+ from typing import Iterable, Optional, Tuple, Dict, Any, List
4
  import hashlib
5
  import spaces
6
  import re
 
18
  import html2text
19
  import markdown
20
  import tempfile
21
+
22
+ from gradio.themes import Soft
23
+ from gradio.themes.utils import colors, fonts, sizes
24
+
25
+ # --- Theme and CSS Definition ---
26
+
27
+ colors.steel_blue = colors.Color(
28
+ name="steel_blue",
29
+ c50="#EBF3F8", c100="#D3E5F0", c200="#A8CCE1", c300="#7DB3D2",
30
+ c400="#529AC3", c500="#4682B4", c600="#3E72A0", c700="#36638C",
31
+ c800="#2E5378", c900="#264364", c950="#1E3450",
32
+ )
33
+
34
+ class SteelBlueTheme(Soft):
35
+ def __init__(
36
+ self,
37
+ *,
38
+ primary_hue: colors.Color | str = colors.gray,
39
+ secondary_hue: colors.Color | str = colors.steel_blue,
40
+ neutral_hue: colors.Color | str = colors.slate,
41
+ text_size: sizes.Size | str = sizes.text_lg,
42
+ font: fonts.Font | str | Iterable[fonts.Font | str] = (
43
+ fonts.GoogleFont("Outfit"), "Arial", "sans-serif",
44
+ ),
45
+ font_mono: fonts.Font | str | Iterable[fonts.Font | str] = (
46
+ fonts.GoogleFont("IBM Plex Mono"), "ui-monospace", "monospace",
47
+ ),
48
+ ):
49
+ super().__init__(
50
+ primary_hue=primary_hue, secondary_hue=secondary_hue, neutral_hue=neutral_hue,
51
+ text_size=text_size, font=font, font_mono=font_mono,
52
+ )
53
+ super().set(
54
+ background_fill_primary="*primary_50",
55
+ background_fill_primary_dark="*primary_900",
56
+ body_background_fill="linear-gradient(135deg, *primary_200, *primary_100)",
57
+ body_background_fill_dark="linear-gradient(135deg, *primary_900, *primary_800)",
58
+ button_primary_text_color="white",
59
+ button_primary_text_color_hover="white",
60
+ button_primary_background_fill="linear-gradient(90deg, *secondary_500, *secondary_600)",
61
+ button_primary_background_fill_hover="linear-gradient(90deg, *secondary_600, *secondary_700)",
62
+ button_primary_background_fill_dark="linear-gradient(90deg, *secondary_600, *secondary_700)",
63
+ button_primary_background_fill_hover_dark="linear-gradient(90deg, *secondary_500, *secondary_600)",
64
+ slider_color="*secondary_500",
65
+ slider_color_dark="*secondary_600",
66
+ block_title_text_weight="600",
67
+ block_border_width="3px",
68
+ block_shadow="*shadow_drop_lg",
69
+ button_primary_shadow="*shadow_drop_lg",
70
+ button_large_padding="11px",
71
+ color_accent_soft="*primary_100",
72
+ block_label_background_fill="*primary_200",
73
+ )
74
+
75
+ steel_blue_theme = SteelBlueTheme()
76
+
77
+ # --- Model and App Logic ---
78
 
79
  pdf_suffixes = [".pdf"]
80
  image_suffixes = [".png", ".jpeg", ".jpg"]
 
117
 
118
  @spaces.GPU
119
  def parse_page(image: Image.Image, model_name: str) -> str:
 
 
 
120
  if model_name == "Logics-Parsing":
121
  current_processor, current_model = processor_1, model_1
122
  elif model_name == "Gliese-OCR-7B-Post1.0":
 
126
  else:
127
  raise ValueError(f"Unknown model choice: {model_name}")
128
 
129
+ messages = [{"role": "user", "content": [{"type": "image"}, {"type": "text", "text": "Parse this document page into a clean, structured HTML representation. Preserve the logical structure with appropriate tags for content blocks such as paragraphs (<p>), headings (<h1>-<h6>), tables (<table>), figures (<figure>), formulas (<formula>), and others. Include category tags, and filter out irrelevant elements like headers and footers."}]}]
130
  prompt_full = current_processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
131
+ inputs = current_processor(text=prompt_full, images=[image.convert("RGB")], return_tensors="pt").to(device)
132
 
133
  with torch.no_grad():
134
+ generated_ids = current_model.generate(**inputs, max_new_tokens=2048, do_sample=False)
135
 
136
+ generated_ids = generated_ids[:, inputs['input_ids'].shape[1]:]
137
+ output_text = current_processor.batch_decode(generated_ids, skip_special_tokens=True)[0]
138
  return output_text
139
 
140
  def convert_file_to_images(file_path: str, dpi: int = 200) -> List[Image.Image]:
 
 
 
141
  images = []
142
  file_ext = Path(file_path).suffix.lower()
143
 
 
156
  page = pdf_document.load_page(page_num)
157
  pix = page.get_pixmap(matrix=mat)
158
  img_data = pix.tobytes("png")
159
+ images.append(Image.open(BytesIO(img_data)).convert("RGB"))
160
  pdf_document.close()
161
  except Exception as e:
162
  logger.error(f"Failed to convert PDF using PyMuPDF: {e}")
 
164
  return images
165
 
166
  def get_initial_state() -> Dict[str, Any]:
 
167
  return {"pages": [], "total_pages": 0, "current_page_index": 0, "page_results": []}
168
 
169
  def load_and_preview_file(file_path: Optional[str]) -> Tuple[Optional[Image.Image], str, Dict[str, Any]]:
 
 
 
170
  state = get_initial_state()
171
  if not file_path:
172
  return None, '<div class="page-info">No file loaded</div>', state
 
184
  logger.error(f"Failed to load and preview file: {e}")
185
  return None, '<div class="page-info">Failed to load preview</div>', state
186
 
187
+ async def process_all_pages(state: Dict[str, Any], model_choice: str, progress=gr.Progress(track_tqdm=True)):
 
 
 
188
  if not state or not state["pages"]:
189
  error_msg = "<h3>Please upload a file first.</h3>"
190
  return error_msg, "", "", None, "Error: No file to process", state
 
194
 
195
  try:
196
  page_results = []
197
+ for i, page_img in progress.tqdm(enumerate(state["pages"]), desc="Processing Pages"):
 
198
  html_result = parse_page(page_img, model_choice)
199
  page_results.append({'raw_html': html_result})
200
 
201
  state["page_results"] = page_results
202
 
 
203
  full_html_content = "\n\n".join([f'<!-- Page {i+1} -->\n{res["raw_html"]}' for i, res in enumerate(page_results)])
204
  full_markdown = html2text.html2text(full_html_content)
205
  with tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False, encoding='utf-8') as f:
 
209
  parsing_time = time.time() - start_time
210
  cost_time_str = f'Total processing time: {parsing_time:.2f}s'
211
 
 
212
  current_page_results = get_page_outputs(state)
213
 
214
  return *current_page_results, md_path, cost_time_str, state
 
219
  return error_html, "", "", None, f"Error: {str(e)}", state
220
 
221
  def navigate_page(direction: str, state: Dict[str, Any]):
 
 
 
222
  if not state or not state["pages"]:
223
  return None, '<div class="page-info">No file loaded</div>', *get_page_outputs(state), state
224
 
 
242
  return image_preview, page_info_html, *page_outputs, state
243
 
244
  def get_page_outputs(state: Dict[str, Any]) -> Tuple[str, str, str]:
 
245
  if not state or not state.get("page_results"):
246
  return "<h3>Process the document to see results.</h3>", "", ""
247
 
248
  index = state["current_page_index"]
249
+ if index >= len(state["page_results"]):
250
+ return "<h3>Result not available for this page.</h3>", "", ""
251
+
252
  result = state["page_results"][index]
253
  raw_html = result['raw_html']
254
 
255
+ md_source = html2text.html2text(raw_html)
256
+ md_render = markdown.markdown(md_source, extensions=['fenced_code', 'tables'])
257
 
258
+ return md_render, md_source, raw_html
259
 
260
  def clear_all():
261
+ return None, None, "<h3>Results will be displayed here after processing.</h3>", "", "", None, "", '<div class="page-info">No file loaded</div>', get_initial_state()
 
 
 
 
 
 
 
 
 
 
 
262
 
263
  @click.command()
264
  def main():
 
 
 
265
  css = """
266
  .main-container { max-width: 1400px; margin: 0 auto; }
267
+ .header-text { text-align: center; margin-bottom: 20px; }
268
+ .page-info { text-align: center; padding: 8px 16px; font-weight: bold; margin: 10px 0; }
 
 
269
  """
270
+ with gr.Blocks(theme=steel_blue_theme, css=css, title="Logics-Parsing Demo") as demo:
271
  app_state = gr.State(value=get_initial_state())
272
 
273
  gr.HTML("""
274
  <div class="header-text">
275
  <h1>πŸ“„ Multimodal: VLM Parsing</h1>
276
+ <p style="font-size: 1.1em;">An advanced Vision Language Model to parse documents and images into clean Markdown (.md)</p>
277
  <div style="display: flex; justify-content: center; gap: 20px; margin: 15px 0;">
278
+ <a href="https://huggingface.co/collections/prithivMLmods/mm-vlm-parsing-68e33e52bfb9ae60b50602dc" target="_blank" style="text-decoration: none; font-weight: 500;">πŸ€— Model Info</a>
279
+ <a href="https://github.com/PRITHIVSAKTHIUR/VLM-Parsing" target="_blank" style="text-decoration: none; font-weight: 500;">πŸ’» GitHub</a>
280
+ <a href="https://huggingface.co/models?pipeline_tag=image-text-to-text&sort=trending" target="_blank" style="text-decoration: none; font-weight: 500;">πŸ“ Multimodal VLMs</a>
281
  </div>
282
  </div>
283
  """)
284
 
285
  with gr.Row(elem_classes=["main-container"]):
286
  with gr.Column(scale=1):
287
+ model_choice = gr.Dropdown(choices=["Logics-Parsing", "Gliese-OCR-7B-Post1.0", "olmOCR-7B-0825"], label="Select Model", value="Logics-Parsing")
288
  file_input = gr.File(label="Upload PDF or Image", file_types=[".pdf", ".jpg", ".jpeg", ".png"], type="filepath")
289
+
290
+ process_btn = gr.Button("Process Document", variant="primary", size="lg")
291
+ clear_btn = gr.Button("Clear All", variant="secondary")
292
+
293
+ image_preview = gr.Image(label="Preview", type="pil", interactive=False, height=320)
294
 
295
  with gr.Row():
296
+ prev_page_btn = gr.Button("β—€ Previous")
297
  page_info = gr.HTML('<div class="page-info">No file loaded</div>')
298
+ next_page_btn = gr.Button("Next β–Ά")
299
+
300
  example_root = "examples"
301
  if os.path.exists(example_root) and os.path.isdir(example_root):
302
  example_files = [os.path.join(example_root, f) for f in os.listdir(example_root) if f.endswith(tuple(pdf_suffixes + image_suffixes))]
303
  if example_files:
304
+ gr.Examples(examples=example_files, inputs=file_input, label="Examples")
 
 
305
 
306
+ with gr.Accordion("Download & Details", open=False):
307
  output_file = gr.File(label='Download Markdown Result', interactive=False)
308
+ cost_time = gr.Textbox(label='Time Cost', interactive=False)
 
 
 
309
 
310
  with gr.Column(scale=2):
311
  with gr.Tabs():
312
+ with gr.Tab("Rendered Markdown"):
313
+ md_render_output = gr.HTML(label='Markdown Rendering')
314
  with gr.Tab("Markdown Source"):
315
+ md_source_output = gr.Code(language="markdown", label="Markdown Source")
316
  with gr.Tab("Generated HTML"):
317
+ raw_html_output = gr.Markdown(language="html", label="Generated HTML")
318
+
319
+ file_input.change(fn=load_and_preview_file, inputs=file_input, outputs=[image_preview, page_info, app_state], show_progress="full")
 
 
 
 
 
320
 
321
+ process_btn.click(fn=process_all_pages, inputs=[app_state, model_choice], outputs=[md_render_output, md_source_output, raw_html_output, output_file, cost_time, app_state], show_progress="full")
322
+
323
+ prev_page_btn.click(fn=lambda s: navigate_page("prev", s), inputs=app_state, outputs=[image_preview, page_info, md_render_output, md_source_output, raw_html_output, app_state])
 
 
 
 
 
 
 
 
 
324
 
325
+ next_page_btn.click(fn=lambda s: navigate_page("next", s), inputs=app_state, outputs=[image_preview, page_info, md_render_output, md_source_output, raw_html_output, app_state])
326
+
327
+ clear_btn.click(fn=clear_all, outputs=[file_input, image_preview, md_render_output, md_source_output, raw_html_output, output_file, cost_time, page_info, app_state])
 
 
 
 
 
 
328
 
329
+ demo.queue().launch(debug=True, show_error=True)
330
 
331
  if __name__ == '__main__':
332
  if not os.path.exists("examples"):