Food Desert commited on
Commit
610c9e8
·
1 Parent(s): ddf8c33

Restore hover tooltips with robust row-index mapping

Browse files
Files changed (1) hide show
  1. app.py +157 -4
app.py CHANGED
@@ -72,6 +72,41 @@ def _choice_label_with_source_meta(tag: str, *, origin: str, preselected: bool)
72
  return _display_tag_text(tag)
73
 
74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  def _selection_source_rank(origin: str) -> int:
76
  o = _normalize_selection_origin(origin)
77
  if o == "structural":
@@ -562,10 +597,23 @@ def _build_display_audit_line(
562
  }
563
  for tag, rec in sorted(info_by_tag.items())
564
  ],
565
- }
566
  return "Display Tag Audit: " + json.dumps(payload, ensure_ascii=True)
567
 
568
 
 
 
 
 
 
 
 
 
 
 
 
 
 
569
  def _build_row_component_updates(
570
  row_defs: List[Dict[str, Any]],
571
  selected_tags: List[str],
@@ -713,9 +761,11 @@ def _build_ui_payload(
713
  continue
714
  selected_ui_seen.add(t)
715
  selected_ui.append(t)
 
716
  return [
717
  console_text,
718
  gr.update(visible=bool(row_defs)),
 
719
  prompt_text,
720
  selected_ui,
721
  False,
@@ -736,6 +786,7 @@ def _prepare_run_ui() -> List[Any]:
736
  return [
737
  "Running...",
738
  gr.skip(),
 
739
  "Running... usually completes in about 20 seconds.",
740
  [],
741
  False,
@@ -747,6 +798,18 @@ def _prepare_run_ui() -> List[Any]:
747
  ]
748
 
749
 
 
 
 
 
 
 
 
 
 
 
 
 
750
  def _rebuild_rows_from_selected(
751
  selected_tags_state: List[str],
752
  row_defs_state: List[Dict[str, Any]],
@@ -820,9 +883,11 @@ def _rebuild_rows_from_selected(
820
  selected_tags=selected_active,
821
  max_rows=display_max_rows_default,
822
  )
 
823
 
824
  return [
825
  gr.update(visible=bool(toggle_rows)),
 
826
  prompt_text,
827
  sorted(selected_active),
828
  False,
@@ -1252,10 +1317,67 @@ css = """
1252
  .suggested-prompt-card {
1253
  margin-top: 10px !important;
1254
  }
 
 
 
 
1255
  """
1256
 
1257
  client_js = """
1258
- () => {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1259
  """
1260
 
1261
 
@@ -1791,9 +1913,10 @@ with gr.Blocks(css=css, js=client_js) as app:
1791
  )
1792
  else:
1793
  mascot_img = gr.Markdown("`(mascot image unavailable)`")
1794
- submit_button = gr.Button("Run", variant="primary")
1795
  gr.Markdown("Typical runtime: up to ~20 seconds.", elem_classes=["run-hint"])
1796
-
 
1797
  selected_tags_state = gr.State([])
1798
  rows_dirty_state = gr.State(False)
1799
  row_defs_state = gr.State([])
@@ -1874,10 +1997,19 @@ with gr.Blocks(css=css, js=client_js) as app:
1874
  interactive=False,
1875
  placeholder="Progress logs will appear here."
1876
  )
 
 
 
 
 
 
 
 
1877
 
1878
  run_outputs = [
1879
  console,
1880
  toggle_instruction,
 
1881
  suggested_prompt,
1882
  selected_tags_state,
1883
  rows_dirty_state,
@@ -1888,7 +2020,21 @@ with gr.Blocks(css=css, js=client_js) as app:
1888
  *row_checkboxes,
1889
  ]
1890
 
 
 
 
 
 
 
 
 
1891
  submit_button.click(
 
 
 
 
 
 
1892
  _prepare_run_ui,
1893
  inputs=[],
1894
  outputs=run_outputs,
@@ -1901,6 +2047,12 @@ with gr.Blocks(css=css, js=client_js) as app:
1901
  )
1902
 
1903
  image_tags.submit(
 
 
 
 
 
 
1904
  _prepare_run_ui,
1905
  inputs=[],
1906
  outputs=run_outputs,
@@ -1934,6 +2086,7 @@ with gr.Blocks(css=css, js=client_js) as app:
1934
  inputs=[selected_tags_state, row_defs_state, row_values_state, display_top_groups, display_top_tags_per_group, display_rank_top_k],
1935
  outputs=[
1936
  toggle_instruction,
 
1937
  suggested_prompt,
1938
  selected_tags_state,
1939
  rows_dirty_state,
 
72
  return _display_tag_text(tag)
73
 
74
 
75
+ @lru_cache(maxsize=1)
76
+ def _load_tag_wiki_defs() -> Dict[str, str]:
77
+ p = Path("data/tag_wiki_defs.json")
78
+ if not p.exists():
79
+ return {}
80
+ try:
81
+ with p.open("r", encoding="utf-8") as f:
82
+ data = json.load(f)
83
+ out: Dict[str, str] = {}
84
+ if isinstance(data, dict):
85
+ for k, v in data.items():
86
+ tag = _norm_tag_for_lookup(str(k))
87
+ text = " ".join(str(v or "").split())
88
+ if tag and text:
89
+ out[tag] = text
90
+ return out
91
+ except Exception:
92
+ return {}
93
+
94
+
95
+ def _tooltip_text_for_tag(tag: str) -> str:
96
+ t = _norm_tag_for_lookup(tag)
97
+ parts: List[str] = []
98
+ try:
99
+ count = get_tag_counts().get(t)
100
+ except Exception:
101
+ count = None
102
+ if isinstance(count, int):
103
+ parts.append(f"Count: {count:,}")
104
+ d = _load_tag_wiki_defs().get(t, "")
105
+ if d:
106
+ parts.append(d)
107
+ return "\n".join(parts).strip()
108
+
109
+
110
  def _selection_source_rank(origin: str) -> int:
111
  o = _normalize_selection_origin(origin)
112
  if o == "structural":
 
597
  }
598
  for tag, rec in sorted(info_by_tag.items())
599
  ],
600
+ }
601
  return "Display Tag Audit: " + json.dumps(payload, ensure_ascii=True)
602
 
603
 
604
+ def _build_tooltip_payload(row_defs: List[Dict[str, Any]], max_rows: int) -> str:
605
+ row_defs_ui = (row_defs or [])[: max(0, int(max_rows))]
606
+ tips: Dict[str, str] = {}
607
+ rows: List[List[str]] = []
608
+ for row in row_defs_ui:
609
+ tags = _dedupe_norm_tags(row.get("tags", []) if isinstance(row, dict) else [])
610
+ rows.append(tags)
611
+ for t in tags:
612
+ if t not in tips:
613
+ tips[t] = _tooltip_text_for_tag(t)
614
+ return json.dumps({"rows": rows, "tips": tips}, ensure_ascii=True)
615
+
616
+
617
  def _build_row_component_updates(
618
  row_defs: List[Dict[str, Any]],
619
  selected_tags: List[str],
 
761
  continue
762
  selected_ui_seen.add(t)
763
  selected_ui.append(t)
764
+ tooltip_payload = _build_tooltip_payload(row_defs, display_max_rows_default)
765
  return [
766
  console_text,
767
  gr.update(visible=bool(row_defs)),
768
+ tooltip_payload,
769
  prompt_text,
770
  selected_ui,
771
  False,
 
786
  return [
787
  "Running...",
788
  gr.skip(),
789
+ "{}",
790
  "Running... usually completes in about 20 seconds.",
791
  [],
792
  False,
 
798
  ]
799
 
800
 
801
+ def _update_run_button_visibility(prompt_text: str, last_run_prompt: str):
802
+ curr = (prompt_text or "").strip()
803
+ last = (last_run_prompt or "").strip()
804
+ can_run = bool(curr) and curr != last
805
+ return gr.update(visible=can_run, interactive=can_run)
806
+
807
+
808
+ def _mark_run_triggered(prompt_text: str):
809
+ curr = (prompt_text or "").strip()
810
+ return gr.update(visible=False, interactive=False), curr
811
+
812
+
813
  def _rebuild_rows_from_selected(
814
  selected_tags_state: List[str],
815
  row_defs_state: List[Dict[str, Any]],
 
883
  selected_tags=selected_active,
884
  max_rows=display_max_rows_default,
885
  )
886
+ tooltip_payload = _build_tooltip_payload(toggle_rows, display_max_rows_default)
887
 
888
  return [
889
  gr.update(visible=bool(toggle_rows)),
890
+ tooltip_payload,
891
  prompt_text,
892
  sorted(selected_active),
893
  False,
 
1317
  .suggested-prompt-card {
1318
  margin-top: 10px !important;
1319
  }
1320
+
1321
+ .psq-hidden {
1322
+ display: none !important;
1323
+ }
1324
  """
1325
 
1326
  client_js = """
1327
+ () => {
1328
+ const readTooltipMap = () => {
1329
+ const el = document.querySelector("#psq-tooltip-map textarea, #psq-tooltip-map input");
1330
+ if (!el) return { rows: [], tips: {} };
1331
+ const raw = (el.value || "").trim();
1332
+ if (!raw) return { rows: [], tips: {} };
1333
+ try {
1334
+ const obj = JSON.parse(raw);
1335
+ if (!obj || typeof obj !== "object") return { rows: [], tips: {} };
1336
+ const rows = Array.isArray(obj.rows) ? obj.rows : [];
1337
+ const tips = (obj.tips && typeof obj.tips === "object") ? obj.tips : {};
1338
+ return { rows, tips };
1339
+ } catch (_) {
1340
+ return { rows: [], tips: {} };
1341
+ }
1342
+ };
1343
+
1344
+ const applyTooltips = () => {
1345
+ const payload = readTooltipMap();
1346
+ const rowTags = Array.isArray(payload.rows) ? payload.rows : [];
1347
+ const tipMap = (payload.tips && typeof payload.tips === "object") ? payload.tips : {};
1348
+ const rowEls = document.querySelectorAll(".lego-tags");
1349
+ rowEls.forEach((rowEl, rowIdx) => {
1350
+ const tags = Array.isArray(rowTags[rowIdx]) ? rowTags[rowIdx] : [];
1351
+ const labels = rowEl.querySelectorAll("label");
1352
+ labels.forEach((label, tagIdx) => {
1353
+ const span = label.querySelector("span");
1354
+ const tag = (tagIdx < tags.length) ? tags[tagIdx] : "";
1355
+ const tip = tag && Object.prototype.hasOwnProperty.call(tipMap, tag) ? (tipMap[tag] || "") : "";
1356
+ if (tip) {
1357
+ label.title = tip;
1358
+ if (span) span.title = tip;
1359
+ } else {
1360
+ label.removeAttribute("title");
1361
+ if (span) span.removeAttribute("title");
1362
+ }
1363
+ });
1364
+ });
1365
+ };
1366
+
1367
+ let scheduled = false;
1368
+ const scheduleApply = () => {
1369
+ if (scheduled) return;
1370
+ scheduled = true;
1371
+ requestAnimationFrame(() => {
1372
+ scheduled = false;
1373
+ applyTooltips();
1374
+ });
1375
+ };
1376
+
1377
+ scheduleApply();
1378
+ const observer = new MutationObserver(() => scheduleApply());
1379
+ observer.observe(document.body, { childList: true, subtree: true });
1380
+ }
1381
  """
1382
 
1383
 
 
1913
  )
1914
  else:
1915
  mascot_img = gr.Markdown("`(mascot image unavailable)`")
1916
+ submit_button = gr.Button("Run", variant="primary", visible=False, interactive=False)
1917
  gr.Markdown("Typical runtime: up to ~20 seconds.", elem_classes=["run-hint"])
1918
+
1919
+ last_run_prompt_state = gr.State("")
1920
  selected_tags_state = gr.State([])
1921
  rows_dirty_state = gr.State(False)
1922
  row_defs_state = gr.State([])
 
1997
  interactive=False,
1998
  placeholder="Progress logs will appear here."
1999
  )
2000
+ tooltip_map_payload = gr.Textbox(
2001
+ value="{}",
2002
+ visible=True,
2003
+ interactive=False,
2004
+ container=False,
2005
+ elem_id="psq-tooltip-map",
2006
+ elem_classes=["psq-hidden"],
2007
+ )
2008
 
2009
  run_outputs = [
2010
  console,
2011
  toggle_instruction,
2012
+ tooltip_map_payload,
2013
  suggested_prompt,
2014
  selected_tags_state,
2015
  rows_dirty_state,
 
2020
  *row_checkboxes,
2021
  ]
2022
 
2023
+ image_tags.change(
2024
+ _update_run_button_visibility,
2025
+ inputs=[image_tags, last_run_prompt_state],
2026
+ outputs=[submit_button],
2027
+ queue=False,
2028
+ show_progress="hidden",
2029
+ )
2030
+
2031
  submit_button.click(
2032
+ _mark_run_triggered,
2033
+ inputs=[image_tags],
2034
+ outputs=[submit_button, last_run_prompt_state],
2035
+ queue=False,
2036
+ show_progress="hidden",
2037
+ ).then(
2038
  _prepare_run_ui,
2039
  inputs=[],
2040
  outputs=run_outputs,
 
2047
  )
2048
 
2049
  image_tags.submit(
2050
+ _mark_run_triggered,
2051
+ inputs=[image_tags],
2052
+ outputs=[submit_button, last_run_prompt_state],
2053
+ queue=False,
2054
+ show_progress="hidden",
2055
+ ).then(
2056
  _prepare_run_ui,
2057
  inputs=[],
2058
  outputs=run_outputs,
 
2086
  inputs=[selected_tags_state, row_defs_state, row_values_state, display_top_groups, display_top_tags_per_group, display_rank_top_k],
2087
  outputs=[
2088
  toggle_instruction,
2089
+ tooltip_map_payload,
2090
  suggested_prompt,
2091
  selected_tags_state,
2092
  rows_dirty_state,