Pointf5ive commited on
Commit
c6376b5
·
1 Parent(s): 3b34fc0

Remove duplicate OCR progress bars and redesign Smoke Signal hero banner

Browse files
Files changed (1) hide show
  1. smoke_signal_tab.py +124 -83
smoke_signal_tab.py CHANGED
@@ -117,74 +117,157 @@ SS_CSS = """
117
  --ss-muted: #7d8590;
118
  }
119
 
120
- /* Wizard step bar */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  .ss-wizard {
122
  display: flex;
123
  align-items: center;
124
  gap: 0;
125
- padding: 20px 28px 0;
126
- background: var(--ss-bg);
127
- border-bottom: 1px solid var(--ss-border);
128
  overflow-x: auto;
 
129
  }
130
 
131
  .ss-step {
132
- display: flex;
 
133
  align-items: center;
134
  gap: 10px;
135
- padding: 14px 20px;
136
- cursor: pointer;
137
- border-bottom: 3px solid transparent;
138
  transition: all 0.2s;
139
  white-space: nowrap;
140
  font-family: 'Source Code Pro', 'Courier New', monospace;
141
- font-size: 12px;
142
- font-weight: 600;
143
- color: var(--ss-muted);
144
  letter-spacing: 1px;
145
  text-transform: uppercase;
146
  }
147
 
148
  .ss-step.active {
149
- color: var(--ss-signal);
150
- border-bottom-color: var(--ss-signal);
151
- }
152
-
153
- .ss-step.complete {
154
- color: var(--ss-green);
155
- border-bottom-color: var(--ss-green);
156
  }
157
 
158
- .ss-step.locked {
159
- color: var(--ss-border);
160
- cursor: not-allowed;
 
 
 
 
 
 
 
161
  }
162
 
163
  .ss-num {
164
  width: 26px;
165
  height: 26px;
166
- border-radius: 50%;
167
  display: flex;
168
  align-items: center;
169
  justify-content: center;
170
  font-size: 11px;
171
  font-weight: 900;
172
- background: var(--ss-surface);
173
  border: 2px solid currentColor;
174
  flex-shrink: 0;
175
  }
176
 
177
- .ss-step.complete .ss-num {
178
- background: var(--ss-green);
179
- color: var(--ss-bg);
180
- border-color: var(--ss-green);
181
- }
182
-
183
  .ss-connector {
184
- width: 40px;
185
  height: 2px;
186
- background: var(--ss-border);
187
  flex-shrink: 0;
 
 
 
 
 
 
188
  }
189
 
190
  /* Main panel */
@@ -1053,7 +1136,7 @@ def _run_tesseract_batch(images):
1053
  return outputs
1054
 
1055
 
1056
- def run_ocr(progress=gr.Progress(track_tqdm=False)) -> tuple:
1057
  """Run Surya OCR on all profiled PDFs."""
1058
  df = load_manifest_df()
1059
  debug = f"[DEBUG] SS_ROOT={SS_ROOT}\nMANIFEST_CSV={MANIFEST_CSV}\nCSV exists={MANIFEST_CSV.exists()}\n"
@@ -1081,9 +1164,6 @@ def run_ocr(progress=gr.Progress(track_tqdm=False)) -> tuple:
1081
  log = []
1082
  queue_rows = []
1083
 
1084
- progress(0, desc="Preparing OCR run...")
1085
-
1086
- progress(0, desc="Loading OCR engine...")
1087
  surya, surya_error, reused = _get_surya_runtime_with_timeout(SS_SURYA_LOAD_TIMEOUT_SEC)
1088
  if surya:
1089
  if reused:
@@ -1099,26 +1179,6 @@ def run_ocr(progress=gr.Progress(track_tqdm=False)) -> tuple:
1099
  )
1100
  )
1101
 
1102
- total_pages_planned = 0
1103
- total_ocr_targets_planned = 0
1104
- for _, row in eligible.iterrows():
1105
- profile_path = PROFILES_DIR / f"{row['book_id']}_page_profile.json"
1106
- if not profile_path.exists():
1107
- continue
1108
- try:
1109
- with open(profile_path, encoding="utf-8") as f:
1110
- profile = json.load(f)
1111
- pages = profile.get("pages", [])
1112
- total_pages_planned += len(pages)
1113
- total_ocr_targets_planned += sum(1 for p in pages if p.get("route") != "embedded_text")
1114
- except Exception:
1115
- continue
1116
- total_pages_planned = max(total_pages_planned, 1)
1117
- total_ocr_targets_planned = max(total_ocr_targets_planned, 1)
1118
- rendered_pages = 0
1119
- ocr_processed_pages = 0
1120
- total_work_units = max(total_pages_planned + total_ocr_targets_planned, 1)
1121
-
1122
  for _, row in eligible.iterrows():
1123
  book_id = row["book_id"]
1124
  profile_path = PROFILES_DIR / f"{book_id}_page_profile.json"
@@ -1149,11 +1209,6 @@ def run_ocr(progress=gr.Progress(track_tqdm=False)) -> tuple:
1149
  for page_data in profile.get("pages", []):
1150
  page_num = page_data["page_number"]
1151
  route = page_data["route"]
1152
- rendered_pages += 1
1153
- progress(
1154
- (rendered_pages, total_work_units),
1155
- desc=f"Rendering {book_id} p{page_num} @ {render_dpi}dpi",
1156
- )
1157
 
1158
  if route == "embedded_text":
1159
  continue
@@ -1260,21 +1315,11 @@ def run_ocr(progress=gr.Progress(track_tqdm=False)) -> tuple:
1260
  "confidence": conf,
1261
  "method": "surya",
1262
  }
1263
- ocr_processed_pages += 1
1264
- progress(
1265
- (total_pages_planned + ocr_processed_pages, total_work_units),
1266
- desc=f"OCR {book_id} p{item['page_num']}",
1267
- )
1268
  else:
1269
  fallback_preds = _run_tesseract_batch(batch_images)
1270
  timeout_count = sum(1 for pred in fallback_preds if "batch-timeout" in str(pred.get("method", "")))
1271
  for item, pred in zip(batch_items_with_images, fallback_preds):
1272
  ocr_lookup[item["page_num"]] = pred
1273
- ocr_processed_pages += 1
1274
- progress(
1275
- (total_pages_planned + ocr_processed_pages, total_work_units),
1276
- desc=f"Tesseract {book_id} p{item['page_num']}",
1277
- )
1278
  if timeout_count:
1279
  log.append(
1280
  log_line(
@@ -1290,11 +1335,6 @@ def run_ocr(progress=gr.Progress(track_tqdm=False)) -> tuple:
1290
  timeout_count = sum(1 for pred in fallback_preds if "batch-timeout" in str(pred.get("method", "")))
1291
  for item, pred in zip(batch_items_with_images, fallback_preds):
1292
  ocr_lookup[item["page_num"]] = pred
1293
- ocr_processed_pages += 1
1294
- progress(
1295
- (total_pages_planned + ocr_processed_pages, total_work_units),
1296
- desc=f"Tesseract retry {book_id} p{item['page_num']}",
1297
- )
1298
  if timeout_count:
1299
  log.append(
1300
  log_line(
@@ -1421,7 +1461,6 @@ def run_ocr(progress=gr.Progress(track_tqdm=False)) -> tuple:
1421
  qdf = pd.concat([existing, qdf], ignore_index=True).drop_duplicates(subset=["region_id"])
1422
  qdf.to_csv(QUEUE_CSV, index=False)
1423
 
1424
- progress((total_work_units, total_work_units), desc="OCR complete")
1425
  save_manifest_df(df)
1426
  return _ocr_status_html(), "\n".join(log)
1427
 
@@ -1679,16 +1718,18 @@ def _wizard_header_html(active_step: int = 1) -> str:
1679
  step_html.append('<div class="ss-connector"></div>')
1680
 
1681
  return """
1682
- <div style="background:linear-gradient(135deg,#0d1117,#161b22);padding:24px 28px 0;border-bottom:1px solid #30363d">
1683
- <div style="display:flex;align-items:center;gap:16px;margin-bottom:20px">
1684
- <div style="font-size:38px;line-height:1"></div>
1685
  <div>
1686
- <div style="font-family:'Playfair Display',Georgia,serif;font-size:26px;font-weight:900;color:#e6edf3;line-height:1">Smoke Signal</div>
1687
- <div style="font-family:'Source Code Pro',monospace;font-size:10px;color:#7d8590;letter-spacing:3px;text-transform:uppercase;margin-top:4px">Picture-Book OCR · Extraction Pipeline · v1</div>
1688
  </div>
1689
  </div>
1690
- <div class="ss-wizard">
1691
- """ + "".join(step_html) + """
 
 
1692
  </div>
1693
  </div>
1694
  """
 
117
  --ss-muted: #7d8590;
118
  }
119
 
120
+ /* Hero banner + wizard */
121
+ .ss-hero {
122
+ position: relative;
123
+ overflow: hidden;
124
+ border-radius: 16px;
125
+ border: 1px solid #2a3240;
126
+ margin: 12px 0 16px;
127
+ background:
128
+ radial-gradient(1000px 420px at 8% 10%, rgba(86, 151, 255, 0.22), transparent 55%),
129
+ radial-gradient(820px 380px at 88% 0%, rgba(240, 136, 62, 0.18), transparent 60%),
130
+ linear-gradient(132deg, #040816 0%, #0a1329 45%, #030916 100%);
131
+ box-shadow: 0 24px 70px rgba(3, 9, 25, 0.42);
132
+ }
133
+
134
+ .ss-hero::before {
135
+ content: "";
136
+ position: absolute;
137
+ inset: 0;
138
+ background-image:
139
+ radial-gradient(circle at 14% 26%, rgba(255, 255, 255, 0.28) 0 1px, transparent 2px),
140
+ radial-gradient(circle at 58% 16%, rgba(255, 185, 120, 0.20) 0 1px, transparent 2px),
141
+ radial-gradient(circle at 84% 30%, rgba(110, 193, 255, 0.24) 0 1px, transparent 2px),
142
+ radial-gradient(circle at 75% 72%, rgba(255, 255, 255, 0.12) 0 1px, transparent 2px);
143
+ background-size: 220px 220px, 280px 280px, 300px 300px, 260px 260px;
144
+ opacity: 0.8;
145
+ pointer-events: none;
146
+ }
147
+
148
+ .ss-hero-top {
149
+ position: relative;
150
+ display: flex;
151
+ align-items: center;
152
+ gap: 16px;
153
+ padding: 24px 28px 8px;
154
+ z-index: 1;
155
+ }
156
+
157
+ .ss-hero-emblem {
158
+ width: 54px;
159
+ height: 54px;
160
+ border-radius: 12px;
161
+ border: 1px solid rgba(125, 133, 144, 0.35);
162
+ background: linear-gradient(140deg, rgba(94, 160, 255, 0.25), rgba(240, 136, 62, 0.22));
163
+ display: flex;
164
+ align-items: center;
165
+ justify-content: center;
166
+ font-size: 28px;
167
+ box-shadow: 0 8px 28px rgba(88, 166, 255, 0.25);
168
+ }
169
+
170
+ .ss-hero-title {
171
+ margin: 0;
172
+ line-height: 1;
173
+ font-family: 'Playfair Display', Georgia, serif;
174
+ font-size: 48px;
175
+ font-weight: 900;
176
+ letter-spacing: 0.4px;
177
+ background: linear-gradient(90deg, #c892ff 0%, #f39d53 45%, #58a6ff 95%);
178
+ -webkit-background-clip: text;
179
+ background-clip: text;
180
+ color: transparent;
181
+ text-shadow:
182
+ 0 0 16px rgba(195, 146, 255, 0.30),
183
+ 0 0 24px rgba(88, 166, 255, 0.16);
184
+ }
185
+
186
+ .ss-hero-sub {
187
+ margin: 8px 0 0;
188
+ font-family: 'Source Code Pro', monospace;
189
+ font-size: 11px;
190
+ color: #ffb46f;
191
+ letter-spacing: 3px;
192
+ text-transform: uppercase;
193
+ }
194
+
195
+ .ss-hero-step-wrap {
196
+ position: relative;
197
+ z-index: 1;
198
+ margin: 12px 20px 20px;
199
+ border-radius: 14px;
200
+ border: 1px solid rgba(125, 133, 144, 0.26);
201
+ background: linear-gradient(180deg, rgba(11, 18, 35, 0.88), rgba(8, 14, 30, 0.88));
202
+ padding: 12px 10px 10px;
203
+ }
204
+
205
  .ss-wizard {
206
  display: flex;
207
  align-items: center;
208
  gap: 0;
 
 
 
209
  overflow-x: auto;
210
+ padding: 2px 4px;
211
  }
212
 
213
  .ss-step {
214
+ position: relative;
215
+ display: inline-flex;
216
  align-items: center;
217
  gap: 10px;
218
+ padding: 12px 14px 18px;
219
+ cursor: default;
 
220
  transition: all 0.2s;
221
  white-space: nowrap;
222
  font-family: 'Source Code Pro', 'Courier New', monospace;
223
+ font-size: 11px;
224
+ font-weight: 700;
225
+ color: #7f89a0;
226
  letter-spacing: 1px;
227
  text-transform: uppercase;
228
  }
229
 
230
  .ss-step.active {
231
+ color: #ff8e56;
 
 
 
 
 
 
232
  }
233
 
234
+ .ss-step.active::after {
235
+ content: "";
236
+ position: absolute;
237
+ left: 6px;
238
+ right: 6px;
239
+ bottom: 2px;
240
+ height: 3px;
241
+ border-radius: 99px;
242
+ background: linear-gradient(90deg, #ff6c4a, #ffb357);
243
+ box-shadow: 0 0 14px rgba(240, 136, 62, 0.42);
244
  }
245
 
246
  .ss-num {
247
  width: 26px;
248
  height: 26px;
249
+ border-radius: 999px;
250
  display: flex;
251
  align-items: center;
252
  justify-content: center;
253
  font-size: 11px;
254
  font-weight: 900;
255
+ background: rgba(11, 18, 35, 0.74);
256
  border: 2px solid currentColor;
257
  flex-shrink: 0;
258
  }
259
 
 
 
 
 
 
 
260
  .ss-connector {
261
+ width: 44px;
262
  height: 2px;
263
+ background: linear-gradient(90deg, rgba(104, 132, 180, 0.4), rgba(104, 132, 180, 0.15));
264
  flex-shrink: 0;
265
+ border-radius: 999px;
266
+ }
267
+
268
+ @media (max-width: 900px) {
269
+ .ss-hero-title { font-size: 34px; }
270
+ .ss-hero-sub { letter-spacing: 2px; }
271
  }
272
 
273
  /* Main panel */
 
1136
  return outputs
1137
 
1138
 
1139
+ def run_ocr() -> tuple:
1140
  """Run Surya OCR on all profiled PDFs."""
1141
  df = load_manifest_df()
1142
  debug = f"[DEBUG] SS_ROOT={SS_ROOT}\nMANIFEST_CSV={MANIFEST_CSV}\nCSV exists={MANIFEST_CSV.exists()}\n"
 
1164
  log = []
1165
  queue_rows = []
1166
 
 
 
 
1167
  surya, surya_error, reused = _get_surya_runtime_with_timeout(SS_SURYA_LOAD_TIMEOUT_SEC)
1168
  if surya:
1169
  if reused:
 
1179
  )
1180
  )
1181
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1182
  for _, row in eligible.iterrows():
1183
  book_id = row["book_id"]
1184
  profile_path = PROFILES_DIR / f"{book_id}_page_profile.json"
 
1209
  for page_data in profile.get("pages", []):
1210
  page_num = page_data["page_number"]
1211
  route = page_data["route"]
 
 
 
 
 
1212
 
1213
  if route == "embedded_text":
1214
  continue
 
1315
  "confidence": conf,
1316
  "method": "surya",
1317
  }
 
 
 
 
 
1318
  else:
1319
  fallback_preds = _run_tesseract_batch(batch_images)
1320
  timeout_count = sum(1 for pred in fallback_preds if "batch-timeout" in str(pred.get("method", "")))
1321
  for item, pred in zip(batch_items_with_images, fallback_preds):
1322
  ocr_lookup[item["page_num"]] = pred
 
 
 
 
 
1323
  if timeout_count:
1324
  log.append(
1325
  log_line(
 
1335
  timeout_count = sum(1 for pred in fallback_preds if "batch-timeout" in str(pred.get("method", "")))
1336
  for item, pred in zip(batch_items_with_images, fallback_preds):
1337
  ocr_lookup[item["page_num"]] = pred
 
 
 
 
 
1338
  if timeout_count:
1339
  log.append(
1340
  log_line(
 
1461
  qdf = pd.concat([existing, qdf], ignore_index=True).drop_duplicates(subset=["region_id"])
1462
  qdf.to_csv(QUEUE_CSV, index=False)
1463
 
 
1464
  save_manifest_df(df)
1465
  return _ocr_status_html(), "\n".join(log)
1466
 
 
1718
  step_html.append('<div class="ss-connector"></div>')
1719
 
1720
  return """
1721
+ <div class="ss-hero">
1722
+ <div class="ss-hero-top">
1723
+ <div class="ss-hero-emblem"></div>
1724
  <div>
1725
+ <p class="ss-hero-title">Smoke Signal</p>
1726
+ <p class="ss-hero-sub">Picture-Book OCR · Extraction Pipeline · v1</p>
1727
  </div>
1728
  </div>
1729
+ <div class="ss-hero-step-wrap">
1730
+ <div class="ss-wizard">
1731
+ """ + "".join(step_html) + """
1732
+ </div>
1733
  </div>
1734
  </div>
1735
  """