mertunsall commited on
Commit
3322b77
·
1 Parent(s): ddc5859
Files changed (1) hide show
  1. app.py +187 -65
app.py CHANGED
@@ -1,10 +1,11 @@
 
1
  from functools import lru_cache
2
  import re
3
  import traceback
4
  from typing import Optional
5
 
6
  import gradio as gr
7
- from huggingface_hub import HfApi, hf_hub_url
8
  from huggingface_hub.utils import HfHubHTTPError
9
 
10
 
@@ -21,6 +22,7 @@ IMAGE_EXTENSIONS = (
21
  )
22
  INIT_SCREENSHOT_NAMES = {"intial_screenshot", "initial_screenshot"}
23
  STEP_FILENAME_PATTERN = re.compile(r"^step_(\d+)(?:\.[^.]+)?$", re.IGNORECASE)
 
24
 
25
 
26
  api = HfApi()
@@ -59,11 +61,72 @@ def _build_path(*parts) -> str:
59
  return "/".join(part for part in parts if part)
60
 
61
 
62
- def _get_image_urls(repo_id: str, directory: str) -> list[str]:
63
- """Return URLs for image files directly within the given directory."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  if not directory:
65
  return []
66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  prefix = f"{directory}/"
68
  files = [path for path in _list_repo_files(repo_id) if path.startswith(prefix)]
69
 
@@ -75,25 +138,46 @@ def _get_image_urls(repo_id: str, directory: str) -> list[str]:
75
  ]
76
 
77
  sorted_files = sorted(image_files, key=_image_sort_key)
78
-
79
- return [
80
  hf_hub_url(repo_id=repo_id, filename=path, repo_type="dataset")
81
  for path in sorted_files
82
  ]
83
 
 
 
 
84
 
85
- def _image_sort_key(path: str):
86
- filename = path.rsplit("/", 1)[-1]
87
- lower_name = filename.lower()
 
 
 
 
88
 
89
- if any(lower_name.startswith(name) for name in INIT_SCREENSHOT_NAMES):
90
- return (0, 0)
91
 
92
- match = STEP_FILENAME_PATTERN.match(lower_name)
93
- if match:
94
- return (1, int(match.group(1)))
95
 
96
- return (2, lower_name)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
 
99
  def _dropdown_update(
@@ -126,6 +210,7 @@ def refresh_repo(repo_id: str):
126
  gr.update(choices=[], value=None, interactive=False),
127
  gr.update(choices=[], value=None, interactive=False),
128
  gr.update(value=[]),
 
129
  gr.update(value=f"❌ Unable to load repo `{repo_id}`: {error}"),
130
  )
131
  except Exception as error: # pragma: no cover - network and auth edge cases
@@ -137,6 +222,7 @@ def refresh_repo(repo_id: str):
137
  gr.update(choices=[], value=None, interactive=False),
138
  gr.update(choices=[], value=None, interactive=False),
139
  gr.update(value=[]),
 
140
  gr.update(value=f"❌ Unexpected error loading `{repo_id}`: {error}"),
141
  )
142
 
@@ -169,13 +255,12 @@ def refresh_repo(repo_id: str):
169
  )
170
  fourth_value = fourth_dirs[0] if fourth_dirs else None
171
 
172
- image_urls = (
173
- _get_image_urls(
174
- repo_id, _build_path(top_value, second_value, third_value, fourth_value)
175
- )
176
  if fourth_value
177
- else []
178
  )
 
179
 
180
  first_dropdown_update = _dropdown_update(
181
  choices=top_dirs,
@@ -209,7 +294,8 @@ def refresh_repo(repo_id: str):
209
  empty_info="No fourth-level folders under the selection",
210
  )
211
 
212
- gallery_update = gr.update(value=image_urls)
 
213
 
214
  return (
215
  first_dropdown_update,
@@ -217,6 +303,7 @@ def refresh_repo(repo_id: str):
217
  third_dropdown_update,
218
  fourth_dropdown_update,
219
  gallery_update,
 
220
  gr.update(value="\n".join(status_lines)),
221
  )
222
 
@@ -246,7 +333,10 @@ def update_second_dropdown(repo_id: str, top_level_dir: str):
246
  filled_info="Choose a fourth-level folder",
247
  empty_info="Select a higher-level folder first",
248
  )
249
- return empty_second, empty_third, empty_fourth, gr.update(value=[])
 
 
 
250
 
251
  second_dirs = _get_subdirectories(repo_id, top_level_dir)
252
  second_value = second_dirs[0] if second_dirs else None
@@ -265,14 +355,12 @@ def update_second_dropdown(repo_id: str, top_level_dir: str):
265
  )
266
  fourth_value = fourth_dirs[0] if fourth_dirs else None
267
 
268
- image_urls = (
269
- _get_image_urls(
270
- repo_id,
271
- _build_path(top_level_dir, second_value, third_value, fourth_value),
272
- )
273
  if fourth_value
274
- else []
275
  )
 
276
 
277
  return (
278
  _dropdown_update(
@@ -296,7 +384,8 @@ def update_second_dropdown(repo_id: str, top_level_dir: str):
296
  filled_info="Choose a fourth-level folder",
297
  empty_info="No fourth-level folders under the selection",
298
  ),
299
- gr.update(value=image_urls),
 
300
  )
301
  except Exception as error:
302
  print(f"[update_second_dropdown] Error for {repo_id}/{top_level_dir}: {error}", flush=True)
@@ -322,7 +411,13 @@ def update_second_dropdown(repo_id: str, top_level_dir: str):
322
  filled_info="Choose a fourth-level folder",
323
  empty_info="Unable to load subdirectories",
324
  )
325
- return empty_second, empty_third, empty_fourth, gr.update(value=[])
 
 
 
 
 
 
326
 
327
 
328
  def update_third_dropdown(repo_id: str, top_level_dir: str, second_level_dir: str):
@@ -343,7 +438,10 @@ def update_third_dropdown(repo_id: str, top_level_dir: str, second_level_dir: st
343
  filled_info="Choose a fourth-level folder",
344
  empty_info="Select higher-level folders first",
345
  )
346
- return empty_third, empty_fourth, gr.update(value=[])
 
 
 
347
 
348
  third_dirs = _get_subdirectories(
349
  repo_id, _build_path(top_level_dir, second_level_dir)
@@ -359,14 +457,12 @@ def update_third_dropdown(repo_id: str, top_level_dir: str, second_level_dir: st
359
  )
360
  fourth_value = fourth_dirs[0] if fourth_dirs else None
361
 
362
- image_urls = (
363
- _get_image_urls(
364
- repo_id,
365
- _build_path(top_level_dir, second_level_dir, third_value, fourth_value),
366
- )
367
  if fourth_value
368
- else []
369
  )
 
370
 
371
  return (
372
  _dropdown_update(
@@ -383,7 +479,8 @@ def update_third_dropdown(repo_id: str, top_level_dir: str, second_level_dir: st
383
  filled_info="Choose a fourth-level folder",
384
  empty_info="No fourth-level folders under the selection",
385
  ),
386
- gr.update(value=image_urls),
 
387
  )
388
  except Exception as error:
389
  print(
@@ -405,7 +502,12 @@ def update_third_dropdown(repo_id: str, top_level_dir: str, second_level_dir: st
405
  filled_info="Choose a fourth-level folder",
406
  empty_info="Unable to load subdirectories",
407
  )
408
- return empty_third, empty_fourth, gr.update(value=[])
 
 
 
 
 
409
 
410
 
411
  def update_fourth_dropdown(
@@ -424,7 +526,10 @@ def update_fourth_dropdown(
424
  filled_info="Choose a fourth-level folder",
425
  empty_info="Select higher-level folders first",
426
  )
427
- return empty_fourth, gr.update(value=[])
 
 
 
428
 
429
  fourth_dirs = _get_subdirectories(
430
  repo_id,
@@ -432,19 +537,17 @@ def update_fourth_dropdown(
432
  )
433
  fourth_value = fourth_dirs[0] if fourth_dirs else None
434
 
435
- image_urls = (
436
- _get_image_urls(
437
- repo_id,
438
- _build_path(
439
- top_level_dir,
440
- second_level_dir,
441
- third_level_dir,
442
- fourth_value,
443
- ),
444
  )
445
  if fourth_value
446
- else []
447
  )
 
448
 
449
  return (
450
  _dropdown_update(
@@ -454,7 +557,8 @@ def update_fourth_dropdown(
454
  filled_info="Choose a fourth-level folder",
455
  empty_info="No fourth-level folders under the selection",
456
  ),
457
- gr.update(value=image_urls),
 
458
  )
459
  except Exception as error:
460
  print(
@@ -470,7 +574,11 @@ def update_fourth_dropdown(
470
  filled_info="Choose a fourth-level folder",
471
  empty_info="Unable to load subdirectories",
472
  )
473
- return empty_fourth, gr.update(value=[])
 
 
 
 
474
 
475
 
476
  def update_gallery(
@@ -483,18 +591,19 @@ def update_gallery(
483
  """Update the image gallery when the fourth-level selection changes."""
484
  try:
485
  if not all([top_level_dir, second_level_dir, third_level_dir, fourth_level_dir]):
486
- return gr.update(value=[])
 
 
 
487
 
488
- image_urls = _get_image_urls(
489
- repo_id,
490
- _build_path(
491
- top_level_dir,
492
- second_level_dir,
493
- third_level_dir,
494
- fourth_level_dir,
495
- ),
496
  )
497
- return gr.update(value=image_urls)
 
498
  except Exception as error:
499
  print(
500
  "[update_gallery] Error for "
@@ -502,7 +611,7 @@ def update_gallery(
502
  flush=True,
503
  )
504
  print(traceback.format_exc(), flush=True)
505
- return gr.update(value=[])
506
 
507
 
508
  with gr.Blocks(title="HF Dataset Explorer") as demo:
@@ -527,6 +636,7 @@ Provide a dataset repository ID (e.g. `org/dataset`) to list its top-level folde
527
  third_level_dropdown = gr.Dropdown(label="Third-level folders", interactive=False)
528
  fourth_level_dropdown = gr.Dropdown(label="Fourth-level folders", interactive=False)
529
  image_gallery = gr.Gallery(label="Images", columns=4)
 
530
 
531
  reload_button.click(
532
  refresh_repo,
@@ -537,6 +647,7 @@ Provide a dataset repository ID (e.g. `org/dataset`) to list its top-level folde
537
  third_level_dropdown,
538
  fourth_level_dropdown,
539
  image_gallery,
 
540
  status_display,
541
  ],
542
  )
@@ -549,13 +660,19 @@ Provide a dataset repository ID (e.g. `org/dataset`) to list its top-level folde
549
  third_level_dropdown,
550
  fourth_level_dropdown,
551
  image_gallery,
 
552
  ],
553
  )
554
 
555
  second_level_dropdown.change(
556
  update_third_dropdown,
557
  inputs=[repo_id_input, folder_dropdown, second_level_dropdown],
558
- outputs=[third_level_dropdown, fourth_level_dropdown, image_gallery],
 
 
 
 
 
559
  )
560
 
561
  third_level_dropdown.change(
@@ -566,7 +683,11 @@ Provide a dataset repository ID (e.g. `org/dataset`) to list its top-level folde
566
  second_level_dropdown,
567
  third_level_dropdown,
568
  ],
569
- outputs=[fourth_level_dropdown, image_gallery],
 
 
 
 
570
  )
571
 
572
  fourth_level_dropdown.change(
@@ -578,7 +699,7 @@ Provide a dataset repository ID (e.g. `org/dataset`) to list its top-level folde
578
  third_level_dropdown,
579
  fourth_level_dropdown,
580
  ],
581
- outputs=[image_gallery],
582
  )
583
 
584
  demo.load(
@@ -590,6 +711,7 @@ Provide a dataset repository ID (e.g. `org/dataset`) to list its top-level folde
590
  third_level_dropdown,
591
  fourth_level_dropdown,
592
  image_gallery,
 
593
  status_display,
594
  ],
595
  )
 
1
+ import json
2
  from functools import lru_cache
3
  import re
4
  import traceback
5
  from typing import Optional
6
 
7
  import gradio as gr
8
+ from huggingface_hub import HfApi, hf_hub_download, hf_hub_url
9
  from huggingface_hub.utils import HfHubHTTPError
10
 
11
 
 
22
  )
23
  INIT_SCREENSHOT_NAMES = {"intial_screenshot", "initial_screenshot"}
24
  STEP_FILENAME_PATTERN = re.compile(r"^step_(\d+)(?:\.[^.]+)?$", re.IGNORECASE)
25
+ TRAJECTORY_FILENAME = "traj.jsonl"
26
 
27
 
28
  api = HfApi()
 
61
  return "/".join(part for part in parts if part)
62
 
63
 
64
+ def _image_sort_key(path: str):
65
+ filename = path.rsplit("/", 1)[-1]
66
+ lower_name = filename.lower()
67
+
68
+ if any(lower_name.startswith(name) for name in INIT_SCREENSHOT_NAMES):
69
+ return (0, 0)
70
+
71
+ match = STEP_FILENAME_PATTERN.match(lower_name)
72
+ if match:
73
+ return (1, int(match.group(1)))
74
+
75
+ return (2, lower_name)
76
+
77
+
78
+ def _load_traj_entries(repo_id: str, directory: str) -> list:
79
+ """Load trajectory annotations from traj.jsonl within the given directory."""
80
  if not directory:
81
  return []
82
 
83
+ traj_path = _build_path(directory, TRAJECTORY_FILENAME)
84
+ repo_files = _list_repo_files(repo_id)
85
+
86
+ if traj_path not in repo_files:
87
+ return []
88
+
89
+ local_path = hf_hub_download(
90
+ repo_id=repo_id,
91
+ filename=traj_path,
92
+ repo_type="dataset",
93
+ )
94
+
95
+ entries: list = []
96
+ with open(local_path, "r", encoding="utf-8") as file:
97
+ for raw_line in file:
98
+ stripped = raw_line.strip()
99
+ if not stripped:
100
+ continue
101
+
102
+ parsed = json.loads(stripped)
103
+ if isinstance(parsed, list):
104
+ entries.extend(parsed)
105
+ else:
106
+ entries.append(parsed)
107
+
108
+ return entries
109
+
110
+
111
+ def _format_annotation(index: int, annotation) -> str:
112
+ prefix = f"Step {index + 1}"
113
+ if isinstance(annotation, str):
114
+ content = annotation.strip()
115
+ else:
116
+ try:
117
+ content = json.dumps(annotation, ensure_ascii=False)
118
+ except TypeError:
119
+ content = str(annotation)
120
+ return f"{prefix}: {content}" if content else prefix
121
+
122
+
123
+ def _prepare_gallery_items(
124
+ repo_id: str, directory: Optional[str]
125
+ ) -> tuple[list[tuple[str, str]], list[str]]:
126
+ """Prepare gallery items and status messages for a directory."""
127
+ if not directory:
128
+ return [], ["Select a fourth-level folder to view screenshots."]
129
+
130
  prefix = f"{directory}/"
131
  files = [path for path in _list_repo_files(repo_id) if path.startswith(prefix)]
132
 
 
138
  ]
139
 
140
  sorted_files = sorted(image_files, key=_image_sort_key)
141
+ image_urls = [
 
142
  hf_hub_url(repo_id=repo_id, filename=path, repo_type="dataset")
143
  for path in sorted_files
144
  ]
145
 
146
+ status_lines: list[str] = [
147
+ f"🖼️ Images: {len(image_urls)}",
148
+ ]
149
 
150
+ annotations: list = []
151
+ try:
152
+ annotations = _load_traj_entries(repo_id, directory)
153
+ except json.JSONDecodeError as error:
154
+ status_lines.append(f"⚠️ Failed to parse `{TRAJECTORY_FILENAME}`: {error}")
155
+ except Exception as error: # pragma: no cover - unexpected IO errors
156
+ status_lines.append(f"⚠️ Error loading `{TRAJECTORY_FILENAME}`: {error}")
157
 
158
+ status_lines.append(f"📝 Annotations: {len(annotations)}")
 
159
 
160
+ if not image_urls:
161
+ status_lines.append("⚠️ No images found in this folder.")
 
162
 
163
+ if image_urls and not annotations:
164
+ status_lines.append(
165
+ f"⚠️ `{TRAJECTORY_FILENAME}` missing or empty; no annotations to display."
166
+ )
167
+
168
+ if annotations and len(annotations) != len(image_urls):
169
+ status_lines.append(
170
+ "⚠️ Mismatch between images and annotations; displaying available pairs."
171
+ )
172
+
173
+ gallery_items: list[tuple[str, str]] = []
174
+ for idx, url in enumerate(image_urls):
175
+ caption = "No annotation available"
176
+ if idx < len(annotations):
177
+ caption = _format_annotation(idx, annotations[idx])
178
+ gallery_items.append((url, caption))
179
+
180
+ return gallery_items, status_lines
181
 
182
 
183
  def _dropdown_update(
 
210
  gr.update(choices=[], value=None, interactive=False),
211
  gr.update(choices=[], value=None, interactive=False),
212
  gr.update(value=[]),
213
+ gr.update(value=""),
214
  gr.update(value=f"❌ Unable to load repo `{repo_id}`: {error}"),
215
  )
216
  except Exception as error: # pragma: no cover - network and auth edge cases
 
222
  gr.update(choices=[], value=None, interactive=False),
223
  gr.update(choices=[], value=None, interactive=False),
224
  gr.update(value=[]),
225
+ gr.update(value=""),
226
  gr.update(value=f"❌ Unexpected error loading `{repo_id}`: {error}"),
227
  )
228
 
 
255
  )
256
  fourth_value = fourth_dirs[0] if fourth_dirs else None
257
 
258
+ target_directory = (
259
+ _build_path(top_value, second_value, third_value, fourth_value)
 
 
260
  if fourth_value
261
+ else None
262
  )
263
+ gallery_items, gallery_status = _prepare_gallery_items(repo_id, target_directory)
264
 
265
  first_dropdown_update = _dropdown_update(
266
  choices=top_dirs,
 
294
  empty_info="No fourth-level folders under the selection",
295
  )
296
 
297
+ gallery_update = gr.update(value=gallery_items)
298
+ annotation_update = gr.update(value="\n".join(gallery_status))
299
 
300
  return (
301
  first_dropdown_update,
 
303
  third_dropdown_update,
304
  fourth_dropdown_update,
305
  gallery_update,
306
+ annotation_update,
307
  gr.update(value="\n".join(status_lines)),
308
  )
309
 
 
333
  filled_info="Choose a fourth-level folder",
334
  empty_info="Select a higher-level folder first",
335
  )
336
+ annotation_update = gr.update(
337
+ value="Select a top-level folder to load screenshots and annotations."
338
+ )
339
+ return empty_second, empty_third, empty_fourth, gr.update(value=[]), annotation_update
340
 
341
  second_dirs = _get_subdirectories(repo_id, top_level_dir)
342
  second_value = second_dirs[0] if second_dirs else None
 
355
  )
356
  fourth_value = fourth_dirs[0] if fourth_dirs else None
357
 
358
+ target_directory = (
359
+ _build_path(top_level_dir, second_value, third_value, fourth_value)
 
 
 
360
  if fourth_value
361
+ else None
362
  )
363
+ gallery_items, gallery_status = _prepare_gallery_items(repo_id, target_directory)
364
 
365
  return (
366
  _dropdown_update(
 
384
  filled_info="Choose a fourth-level folder",
385
  empty_info="No fourth-level folders under the selection",
386
  ),
387
+ gr.update(value=gallery_items),
388
+ gr.update(value="\n".join(gallery_status)),
389
  )
390
  except Exception as error:
391
  print(f"[update_second_dropdown] Error for {repo_id}/{top_level_dir}: {error}", flush=True)
 
411
  filled_info="Choose a fourth-level folder",
412
  empty_info="Unable to load subdirectories",
413
  )
414
+ return (
415
+ empty_second,
416
+ empty_third,
417
+ empty_fourth,
418
+ gr.update(value=[]),
419
+ gr.update(value="Unable to load screenshots or annotations."),
420
+ )
421
 
422
 
423
  def update_third_dropdown(repo_id: str, top_level_dir: str, second_level_dir: str):
 
438
  filled_info="Choose a fourth-level folder",
439
  empty_info="Select higher-level folders first",
440
  )
441
+ annotation_update = gr.update(
442
+ value="Select higher-level folders to load screenshots and annotations."
443
+ )
444
+ return empty_third, empty_fourth, gr.update(value=[]), annotation_update
445
 
446
  third_dirs = _get_subdirectories(
447
  repo_id, _build_path(top_level_dir, second_level_dir)
 
457
  )
458
  fourth_value = fourth_dirs[0] if fourth_dirs else None
459
 
460
+ target_directory = (
461
+ _build_path(top_level_dir, second_level_dir, third_value, fourth_value)
 
 
 
462
  if fourth_value
463
+ else None
464
  )
465
+ gallery_items, gallery_status = _prepare_gallery_items(repo_id, target_directory)
466
 
467
  return (
468
  _dropdown_update(
 
479
  filled_info="Choose a fourth-level folder",
480
  empty_info="No fourth-level folders under the selection",
481
  ),
482
+ gr.update(value=gallery_items),
483
+ gr.update(value="\n".join(gallery_status)),
484
  )
485
  except Exception as error:
486
  print(
 
502
  filled_info="Choose a fourth-level folder",
503
  empty_info="Unable to load subdirectories",
504
  )
505
+ return (
506
+ empty_third,
507
+ empty_fourth,
508
+ gr.update(value=[]),
509
+ gr.update(value="Unable to load screenshots or annotations."),
510
+ )
511
 
512
 
513
  def update_fourth_dropdown(
 
526
  filled_info="Choose a fourth-level folder",
527
  empty_info="Select higher-level folders first",
528
  )
529
+ annotation_update = gr.update(
530
+ value="Select higher-level folders to load screenshots and annotations."
531
+ )
532
+ return empty_fourth, gr.update(value=[]), annotation_update
533
 
534
  fourth_dirs = _get_subdirectories(
535
  repo_id,
 
537
  )
538
  fourth_value = fourth_dirs[0] if fourth_dirs else None
539
 
540
+ target_directory = (
541
+ _build_path(
542
+ top_level_dir,
543
+ second_level_dir,
544
+ third_level_dir,
545
+ fourth_value,
 
 
 
546
  )
547
  if fourth_value
548
+ else None
549
  )
550
+ gallery_items, gallery_status = _prepare_gallery_items(repo_id, target_directory)
551
 
552
  return (
553
  _dropdown_update(
 
557
  filled_info="Choose a fourth-level folder",
558
  empty_info="No fourth-level folders under the selection",
559
  ),
560
+ gr.update(value=gallery_items),
561
+ gr.update(value="\n".join(gallery_status)),
562
  )
563
  except Exception as error:
564
  print(
 
574
  filled_info="Choose a fourth-level folder",
575
  empty_info="Unable to load subdirectories",
576
  )
577
+ return (
578
+ empty_fourth,
579
+ gr.update(value=[]),
580
+ gr.update(value="Unable to load screenshots or annotations."),
581
+ )
582
 
583
 
584
  def update_gallery(
 
591
  """Update the image gallery when the fourth-level selection changes."""
592
  try:
593
  if not all([top_level_dir, second_level_dir, third_level_dir, fourth_level_dir]):
594
+ annotation_update = gr.update(
595
+ value="Select all folder levels to load screenshots and annotations."
596
+ )
597
+ return gr.update(value=[]), annotation_update
598
 
599
+ directory = _build_path(
600
+ top_level_dir,
601
+ second_level_dir,
602
+ third_level_dir,
603
+ fourth_level_dir,
 
 
 
604
  )
605
+ gallery_items, gallery_status = _prepare_gallery_items(repo_id, directory)
606
+ return gr.update(value=gallery_items), gr.update(value="\n".join(gallery_status))
607
  except Exception as error:
608
  print(
609
  "[update_gallery] Error for "
 
611
  flush=True,
612
  )
613
  print(traceback.format_exc(), flush=True)
614
+ return gr.update(value=[]), gr.update(value="Unable to load screenshots or annotations.")
615
 
616
 
617
  with gr.Blocks(title="HF Dataset Explorer") as demo:
 
636
  third_level_dropdown = gr.Dropdown(label="Third-level folders", interactive=False)
637
  fourth_level_dropdown = gr.Dropdown(label="Fourth-level folders", interactive=False)
638
  image_gallery = gr.Gallery(label="Images", columns=4)
639
+ annotation_status = gr.Markdown()
640
 
641
  reload_button.click(
642
  refresh_repo,
 
647
  third_level_dropdown,
648
  fourth_level_dropdown,
649
  image_gallery,
650
+ annotation_status,
651
  status_display,
652
  ],
653
  )
 
660
  third_level_dropdown,
661
  fourth_level_dropdown,
662
  image_gallery,
663
+ annotation_status,
664
  ],
665
  )
666
 
667
  second_level_dropdown.change(
668
  update_third_dropdown,
669
  inputs=[repo_id_input, folder_dropdown, second_level_dropdown],
670
+ outputs=[
671
+ third_level_dropdown,
672
+ fourth_level_dropdown,
673
+ image_gallery,
674
+ annotation_status,
675
+ ],
676
  )
677
 
678
  third_level_dropdown.change(
 
683
  second_level_dropdown,
684
  third_level_dropdown,
685
  ],
686
+ outputs=[
687
+ fourth_level_dropdown,
688
+ image_gallery,
689
+ annotation_status,
690
+ ],
691
  )
692
 
693
  fourth_level_dropdown.change(
 
699
  third_level_dropdown,
700
  fourth_level_dropdown,
701
  ],
702
+ outputs=[image_gallery, annotation_status],
703
  )
704
 
705
  demo.load(
 
711
  third_level_dropdown,
712
  fourth_level_dropdown,
713
  image_gallery,
714
+ annotation_status,
715
  status_display,
716
  ],
717
  )