Pointf5ive commited on
Commit
df7d6cf
Β·
1 Parent(s): 309e8ca

Fix Smoke Signal hang: remove wizard.load, disable SSR

Browse files
Files changed (2) hide show
  1. app.py +72 -7
  2. smoke_signal_tab.py +10 -10
app.py CHANGED
@@ -71,16 +71,36 @@ CSS = """
71
 
72
  footer { display: none !important; }
73
 
74
- /* Keep native Gradio tab behavior, but force tab bar above rich dashboard overlays. */
75
  .gradio-container [role="tablist"] {
76
  position: sticky !important;
77
  top: 0 !important;
78
- z-index: 9999 !important;
79
  background: #fffaf0 !important;
 
 
 
 
80
  }
81
 
82
  .gradio-container [role="tab"] {
83
- pointer-events: auto !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  }
85
 
86
  #hidden-export, #hidden-status {
@@ -240,7 +260,52 @@ footer { display: none !important; }
240
  }
241
  """
242
 
243
- HEAD = ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
 
245
 
246
  REVISION_ACTIONS = {
@@ -794,7 +859,7 @@ def dashboard_html(path: Path, notice: str = "") -> str:
794
  <div class="nav-item">β–₯ TOTEM Analytics</div>
795
  <div class="nav-item">☷ Revision Queue</div>
796
  <div class="nav-item">⬑ Risk Clusters</div>
797
- <div class="nav-item">β—ˆ Codex Extractor (use top tabs)</div>
798
  <div class="nav-item">⇩ Export</div>
799
  <div class="sidebar-foot">Knowledge. Structure.<br>Story. Performance.</div>
800
  </aside>
@@ -1111,7 +1176,7 @@ def single_score(active_path, sequence, stanza_id, draft_pass, clarity, rhythm,
1111
 
1112
  # ── GRADIO INTERFACE ──────────────────────────────────────────────────────────
1113
 
1114
- with gr.Blocks(title="TOTEM Studio") as demo:
1115
  active_path = gr.State(str(DEFAULT_WORKBOOK))
1116
  log_state = gr.State(pd.DataFrame(columns=LOG_COLUMNS))
1117
 
@@ -1324,4 +1389,4 @@ with gr.Blocks(title="TOTEM Studio") as demo:
1324
 
1325
 
1326
  if __name__ == "__main__":
1327
- demo.launch(css=CSS + SS_CSS, head=HEAD)
 
71
 
72
  footer { display: none !important; }
73
 
 
74
  .gradio-container [role="tablist"] {
75
  position: sticky !important;
76
  top: 0 !important;
77
+ z-index: 60 !important;
78
  background: #fffaf0 !important;
79
+ border-bottom: 1px solid var(--studio-line);
80
+ padding: 8px 10px;
81
+ gap: 8px;
82
+ overflow: visible !important;
83
  }
84
 
85
  .gradio-container [role="tab"] {
86
+ opacity: 1 !important;
87
+ visibility: visible !important;
88
+ color: var(--studio-ink) !important;
89
+ background: #f4ecd6 !important;
90
+ border: 1px solid var(--studio-line) !important;
91
+ border-radius: 8px !important;
92
+ padding: 8px 14px !important;
93
+ font-weight: 700 !important;
94
+ }
95
+
96
+ .gradio-container [role="tab"][aria-selected="true"] {
97
+ background: linear-gradient(90deg, #f5c93c, #e5a721) !important;
98
+ color: white !important;
99
+ border-color: #d99e1b !important;
100
+ }
101
+
102
+ .nav-item[role="button"] {
103
+ cursor: pointer;
104
  }
105
 
106
  #hidden-export, #hidden-status {
 
260
  }
261
  """
262
 
263
+ HEAD = """
264
+ <script>
265
+ (() => {
266
+ const switchToCodexTab = () => {
267
+ const tabs = Array.from(document.querySelectorAll('[role="tab"]'));
268
+ if (!tabs.length) return false;
269
+ const target = tabs.find((tab) => /codex extractor/i.test((tab.textContent || "").trim()));
270
+ if (target) {
271
+ target.click();
272
+ return true;
273
+ }
274
+ if (tabs.length > 1) {
275
+ tabs[1].click();
276
+ return true;
277
+ }
278
+ return false;
279
+ };
280
+
281
+ const bindSidebarCodexLinks = () => {
282
+ document.querySelectorAll('.js-open-codex').forEach((el) => {
283
+ if (el.dataset.boundCodexNav === "1") return;
284
+ el.dataset.boundCodexNav = "1";
285
+ el.addEventListener('click', (e) => {
286
+ e.preventDefault();
287
+ switchToCodexTab();
288
+ });
289
+ el.addEventListener('keydown', (e) => {
290
+ if (e.key === 'Enter' || e.key === ' ') {
291
+ e.preventDefault();
292
+ switchToCodexTab();
293
+ }
294
+ });
295
+ });
296
+ };
297
+
298
+ const observer = new MutationObserver(() => bindSidebarCodexLinks());
299
+ observer.observe(document.documentElement, { childList: true, subtree: true });
300
+
301
+ if (document.readyState === 'loading') {
302
+ document.addEventListener('DOMContentLoaded', bindSidebarCodexLinks);
303
+ } else {
304
+ bindSidebarCodexLinks();
305
+ }
306
+ })();
307
+ </script>
308
+ """
309
 
310
 
311
  REVISION_ACTIONS = {
 
859
  <div class="nav-item">β–₯ TOTEM Analytics</div>
860
  <div class="nav-item">☷ Revision Queue</div>
861
  <div class="nav-item">⬑ Risk Clusters</div>
862
+ <div class="nav-item js-open-codex" role="button" tabindex="0" aria-label="Open Codex Extractor tab">β—ˆ Codex Extractor</div>
863
  <div class="nav-item">⇩ Export</div>
864
  <div class="sidebar-foot">Knowledge. Structure.<br>Story. Performance.</div>
865
  </aside>
 
1176
 
1177
  # ── GRADIO INTERFACE ──────────────────────────────────────────────────────────
1178
 
1179
+ with gr.Blocks(title="TOTEM Studio", css=CSS + SS_CSS, head=HEAD) as demo:
1180
  active_path = gr.State(str(DEFAULT_WORKBOOK))
1181
  log_state = gr.State(pd.DataFrame(columns=LOG_COLUMNS))
1182
 
 
1389
 
1390
 
1391
  if __name__ == "__main__":
1392
+ demo.launch()
smoke_signal_tab.py CHANGED
@@ -824,9 +824,7 @@ def run_ocr() -> tuple:
824
  "book_id": book_id, "filename": row["filename"],
825
  "page": page_num, "region_id": region_id,
826
  "region_class": "narration",
827
- "rights_class": row.get("rights_class", "unknown"),
828
  "crop_path": page_data.get("render_path",""),
829
- "page_image_path": page_data.get("render_path",""),
830
  "raw_ocr": raw_text,
831
  "confidence": conf, "confidence_class": conf_class,
832
  "status": "quarantine" if conf_class == "quarantine" else "pending",
@@ -969,10 +967,8 @@ def save_review_decision(idx: int, final_text: str, action: str, reviewer: str,
969
  f.write(json.dumps({
970
  **decision,
971
  "region_class": region_class,
972
- "rights_class": item.get("rights_class", "unknown"),
973
  "confidence": float(item.get("confidence", 0)),
974
  "conf_class": item.get("confidence_class",""),
975
- "page_image_path": item.get("page_image_path", item.get("crop_path", "")),
976
  }) + "\n")
977
 
978
  cal = load_calibration()
@@ -1143,7 +1139,6 @@ def smoke_signal_tab():
1143
  label="Source Manifest",
1144
  interactive=False,
1145
  wrap=True,
1146
- value=load_manifest_df(),
1147
  )
1148
  ingest_log = gr.Textbox(label="Log", lines=6, interactive=False, elem_classes=["ss-log"])
1149
 
@@ -1153,6 +1148,7 @@ def smoke_signal_tab():
1153
  outputs=[ingest_status, manifest_table, ingest_log],
1154
  )
1155
  gr.HTML('<div style="height:16px"></div>')
 
1156
 
1157
  # ── STEP 2: PROFILE ───────────────────────────────────────────────
1158
  with gr.TabItem("β‘‘ Profile", id="ss-profile"):
@@ -1202,7 +1198,6 @@ def smoke_signal_tab():
1202
 
1203
  review_status = gr.HTML(_review_status_html())
1204
  training_feedback = gr.HTML()
1205
- init_img, init_raw, init_info, _, _ = get_review_item(0)
1206
 
1207
  with gr.Row():
1208
  with gr.Column(scale=2):
@@ -1210,22 +1205,20 @@ def smoke_signal_tab():
1210
  label="Page Render",
1211
  type="filepath",
1212
  height=420,
1213
- value=init_img,
1214
  )
1215
- item_info = gr.HTML(value=init_info)
1216
 
1217
  with gr.Column(scale=2):
1218
  raw_text_box = gr.Textbox(
1219
  label="Raw OCR",
1220
  lines=6,
1221
  interactive=False,
1222
- value=init_raw,
1223
  )
1224
  final_text_box = gr.Textbox(
1225
  label="Final Text (edit to correct)",
1226
  lines=8,
1227
  interactive=True,
1228
- value=init_raw,
1229
  )
1230
  reviewer_name = gr.Textbox(label="Your name", placeholder="e.g. jamal", scale=1)
1231
  reason_code = gr.Dropdown(
@@ -1249,6 +1242,11 @@ def smoke_signal_tab():
1249
  review_outputs = [training_feedback, review_image, raw_text_box, item_info,
1250
  gr.State(), gr.State()]
1251
 
 
 
 
 
 
1252
  def next_item(idx):
1253
  new_idx = idx + 1
1254
  img, raw, info, done, total = get_review_item(new_idx)
@@ -1292,6 +1290,8 @@ def smoke_signal_tab():
1292
  next_btn.click(_next, inputs=[current_idx],
1293
  outputs=[current_idx, review_image, raw_text_box, final_text_box, item_info])
1294
 
 
 
1295
  # ── STEP 5: EXPORT ────────────────────────────────────────────────
1296
  with gr.TabItem("β‘€ Export", id="ss-export"):
1297
  gr.HTML("""<div class="ss-panel-header" style="padding:20px 0 0 0">
 
824
  "book_id": book_id, "filename": row["filename"],
825
  "page": page_num, "region_id": region_id,
826
  "region_class": "narration",
 
827
  "crop_path": page_data.get("render_path",""),
 
828
  "raw_ocr": raw_text,
829
  "confidence": conf, "confidence_class": conf_class,
830
  "status": "quarantine" if conf_class == "quarantine" else "pending",
 
967
  f.write(json.dumps({
968
  **decision,
969
  "region_class": region_class,
 
970
  "confidence": float(item.get("confidence", 0)),
971
  "conf_class": item.get("confidence_class",""),
 
972
  }) + "\n")
973
 
974
  cal = load_calibration()
 
1139
  label="Source Manifest",
1140
  interactive=False,
1141
  wrap=True,
 
1142
  )
1143
  ingest_log = gr.Textbox(label="Log", lines=6, interactive=False, elem_classes=["ss-log"])
1144
 
 
1148
  outputs=[ingest_status, manifest_table, ingest_log],
1149
  )
1150
  gr.HTML('<div style="height:16px"></div>')
1151
+ wizard.load(lambda: (load_manifest_df(),), outputs=[manifest_table])
1152
 
1153
  # ── STEP 2: PROFILE ───────────────────────────────────────────────
1154
  with gr.TabItem("β‘‘ Profile", id="ss-profile"):
 
1198
 
1199
  review_status = gr.HTML(_review_status_html())
1200
  training_feedback = gr.HTML()
 
1201
 
1202
  with gr.Row():
1203
  with gr.Column(scale=2):
 
1205
  label="Page Render",
1206
  type="filepath",
1207
  height=420,
1208
+ show_download_button=False,
1209
  )
1210
+ item_info = gr.HTML()
1211
 
1212
  with gr.Column(scale=2):
1213
  raw_text_box = gr.Textbox(
1214
  label="Raw OCR",
1215
  lines=6,
1216
  interactive=False,
 
1217
  )
1218
  final_text_box = gr.Textbox(
1219
  label="Final Text (edit to correct)",
1220
  lines=8,
1221
  interactive=True,
 
1222
  )
1223
  reviewer_name = gr.Textbox(label="Your name", placeholder="e.g. jamal", scale=1)
1224
  reason_code = gr.Dropdown(
 
1242
  review_outputs = [training_feedback, review_image, raw_text_box, item_info,
1243
  gr.State(), gr.State()]
1244
 
1245
+ def load_review():
1246
+ img, raw, info, done, total = get_review_item(0)
1247
+ status = _review_status_html()
1248
+ return status, img, raw, raw, info
1249
+
1250
  def next_item(idx):
1251
  new_idx = idx + 1
1252
  img, raw, info, done, total = get_review_item(new_idx)
 
1290
  next_btn.click(_next, inputs=[current_idx],
1291
  outputs=[current_idx, review_image, raw_text_box, final_text_box, item_info])
1292
 
1293
+ wizard.load(load_review, outputs=[review_status, review_image, raw_text_box, final_text_box, item_info])
1294
+
1295
  # ── STEP 5: EXPORT ────────────────────────────────────────────────
1296
  with gr.TabItem("β‘€ Export", id="ss-export"):
1297
  gr.HTML("""<div class="ss-panel-header" style="padding:20px 0 0 0">