gradio遮罩修复1
Browse files
gradio-web/test/test_queue_session_limit_e2e.py
CHANGED
|
@@ -268,6 +268,7 @@ def test_entry_rejects_immediately_when_session_limit_is_full(monkeypatch):
|
|
| 268 |
assert overlay_snapshot["width"] and overlay_snapshot["width"] > 0
|
| 269 |
assert overlay_snapshot["height"] and overlay_snapshot["height"] >= 400
|
| 270 |
assert overlay_snapshot["background"] == "rgba(255, 255, 255, 0.92)"
|
|
|
|
| 271 |
rejection_snapshot = _read_session_wait_overlay_snapshot(pages[-1])
|
| 272 |
assert rejection_snapshot["content"] == config.UI_TEXT["progress"]["entry_rejected"]
|
| 273 |
assert _read_unified_overlay_text(pages[-1]) == config.UI_TEXT["progress"]["entry_rejected"]
|
|
@@ -372,6 +373,10 @@ def test_single_load_uses_native_episode_loading_copy(monkeypatch):
|
|
| 372 |
assert overlay_snapshot["width"] and overlay_snapshot["width"] > 0
|
| 373 |
assert overlay_snapshot["height"] and overlay_snapshot["height"] >= 400
|
| 374 |
assert overlay_snapshot["background"] == "rgba(255, 255, 255, 0.92)"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 375 |
|
| 376 |
page.wait_for_selector("#main_interface_root", state="visible", timeout=15000)
|
| 377 |
|
|
@@ -448,6 +453,7 @@ def test_execute_does_not_use_episode_loading_copy(monkeypatch):
|
|
| 448 |
body_text = page.evaluate("() => document.body.innerText")
|
| 449 |
assert "The episode is loading..." not in body_text
|
| 450 |
assert "too many people playing" not in body_text
|
|
|
|
| 451 |
|
| 452 |
browser.close()
|
| 453 |
finally:
|
|
|
|
| 268 |
assert overlay_snapshot["width"] and overlay_snapshot["width"] > 0
|
| 269 |
assert overlay_snapshot["height"] and overlay_snapshot["height"] >= 400
|
| 270 |
assert overlay_snapshot["background"] == "rgba(255, 255, 255, 0.92)"
|
| 271 |
+
assert pages[-1].evaluate("() => document.getElementById('robomme_episode_loading_copy') === null") is True
|
| 272 |
rejection_snapshot = _read_session_wait_overlay_snapshot(pages[-1])
|
| 273 |
assert rejection_snapshot["content"] == config.UI_TEXT["progress"]["entry_rejected"]
|
| 274 |
assert _read_unified_overlay_text(pages[-1]) == config.UI_TEXT["progress"]["entry_rejected"]
|
|
|
|
| 373 |
assert overlay_snapshot["width"] and overlay_snapshot["width"] > 0
|
| 374 |
assert overlay_snapshot["height"] and overlay_snapshot["height"] >= 400
|
| 375 |
assert overlay_snapshot["background"] == "rgba(255, 255, 255, 0.92)"
|
| 376 |
+
assert _read_progress_text(page) == config.UI_TEXT["progress"]["episode_loading"]
|
| 377 |
+
assert page.evaluate("() => document.getElementById('robomme_episode_loading_copy') === null") is True
|
| 378 |
+
wait_snapshot = _read_session_wait_overlay_snapshot(page)
|
| 379 |
+
assert wait_snapshot["content"] is None
|
| 380 |
|
| 381 |
page.wait_for_selector("#main_interface_root", state="visible", timeout=15000)
|
| 382 |
|
|
|
|
| 453 |
body_text = page.evaluate("() => document.body.innerText")
|
| 454 |
assert "The episode is loading..." not in body_text
|
| 455 |
assert "too many people playing" not in body_text
|
| 456 |
+
assert page.evaluate("() => document.getElementById('robomme_episode_loading_copy') === null") is True
|
| 457 |
|
| 458 |
browser.close()
|
| 459 |
finally:
|
gradio-web/test/test_ui_phase_machine_runtime_e2e.py
CHANGED
|
@@ -129,6 +129,72 @@ def _read_log_output_value(page) -> str | None:
|
|
| 129 |
)
|
| 130 |
|
| 131 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
def _read_elem_classes(page, elem_id: str) -> list[str] | None:
|
| 133 |
return page.evaluate(
|
| 134 |
"""(elemId) => {
|
|
@@ -1096,19 +1162,33 @@ def test_unified_loading_overlay_init_flow(monkeypatch):
|
|
| 1096 |
page.wait_for_function(
|
| 1097 |
"""() => {
|
| 1098 |
const node = document.querySelector('.progress-text');
|
| 1099 |
-
|
| 1100 |
-
|
| 1101 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1102 |
timeout=5000,
|
| 1103 |
)
|
| 1104 |
-
|
| 1105 |
-
|
| 1106 |
-
|
| 1107 |
-
|
| 1108 |
-
|
| 1109 |
-
|
| 1110 |
-
assert
|
| 1111 |
-
assert
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1112 |
assert legacy_copy not in page.content()
|
| 1113 |
assert page.locator("#loading_overlay_group").count() == 0
|
| 1114 |
|
|
@@ -1139,6 +1219,141 @@ def test_unified_loading_overlay_init_flow(monkeypatch):
|
|
| 1139 |
assert calls["init"] >= 1
|
| 1140 |
|
| 1141 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1142 |
def test_no_video_task_hides_manual_demo_button(monkeypatch):
|
| 1143 |
ui_layout = importlib.reload(importlib.import_module("ui_layout"))
|
| 1144 |
|
|
|
|
| 129 |
)
|
| 130 |
|
| 131 |
|
| 132 |
+
def _read_progress_markdown_snapshot(page) -> dict[str, bool | str | None]:
|
| 133 |
+
return page.evaluate(
|
| 134 |
+
"""() => {
|
| 135 |
+
const host = document.getElementById('native_progress_host');
|
| 136 |
+
const pending = host?.querySelector('.pending');
|
| 137 |
+
const markdown = host?.querySelector('[data-testid="markdown"]');
|
| 138 |
+
const prose = markdown ? markdown.querySelector('.prose, .md') || markdown : null;
|
| 139 |
+
if (!host) {
|
| 140 |
+
return {
|
| 141 |
+
pendingPresent: false,
|
| 142 |
+
pendingVisible: false,
|
| 143 |
+
markdownVisible: false,
|
| 144 |
+
text: null,
|
| 145 |
+
};
|
| 146 |
+
}
|
| 147 |
+
const pendingStyle = pending ? getComputedStyle(pending) : null;
|
| 148 |
+
const markdownStyle = markdown ? getComputedStyle(markdown) : null;
|
| 149 |
+
const text = prose ? ((prose.textContent || '').trim()) : '';
|
| 150 |
+
return {
|
| 151 |
+
pendingPresent: !!pending,
|
| 152 |
+
pendingVisible: !!pendingStyle && pendingStyle.display !== 'none' && pendingStyle.visibility !== 'hidden',
|
| 153 |
+
markdownVisible:
|
| 154 |
+
!!markdownStyle &&
|
| 155 |
+
markdownStyle.display !== 'none' &&
|
| 156 |
+
markdownStyle.visibility !== 'hidden',
|
| 157 |
+
text: text || null,
|
| 158 |
+
};
|
| 159 |
+
}"""
|
| 160 |
+
)
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
def _read_progress_text_snapshot(page) -> dict[str, float | bool | str | None]:
|
| 164 |
+
return page.evaluate(
|
| 165 |
+
"""() => {
|
| 166 |
+
const node = document.querySelector('.progress-text');
|
| 167 |
+
if (!node) {
|
| 168 |
+
return {
|
| 169 |
+
present: false,
|
| 170 |
+
visible: false,
|
| 171 |
+
text: null,
|
| 172 |
+
x: null,
|
| 173 |
+
y: null,
|
| 174 |
+
width: null,
|
| 175 |
+
height: null,
|
| 176 |
+
};
|
| 177 |
+
}
|
| 178 |
+
const style = getComputedStyle(node);
|
| 179 |
+
const rect = node.getBoundingClientRect();
|
| 180 |
+
return {
|
| 181 |
+
present: true,
|
| 182 |
+
visible:
|
| 183 |
+
style.display !== 'none' &&
|
| 184 |
+
style.visibility !== 'hidden' &&
|
| 185 |
+
Number.parseFloat(style.opacity || '1') > 0 &&
|
| 186 |
+
rect.width > 0 &&
|
| 187 |
+
rect.height > 0,
|
| 188 |
+
text: (node.textContent || '').trim() || null,
|
| 189 |
+
x: rect.x,
|
| 190 |
+
y: rect.y,
|
| 191 |
+
width: rect.width,
|
| 192 |
+
height: rect.height,
|
| 193 |
+
};
|
| 194 |
+
}"""
|
| 195 |
+
)
|
| 196 |
+
|
| 197 |
+
|
| 198 |
def _read_elem_classes(page, elem_id: str) -> list[str] | None:
|
| 199 |
return page.evaluate(
|
| 200 |
"""(elemId) => {
|
|
|
|
| 1162 |
page.wait_for_function(
|
| 1163 |
"""() => {
|
| 1164 |
const node = document.querySelector('.progress-text');
|
| 1165 |
+
if (!node) return false;
|
| 1166 |
+
const style = getComputedStyle(node);
|
| 1167 |
+
const rect = node.getBoundingClientRect();
|
| 1168 |
+
return (
|
| 1169 |
+
style.display !== 'none' &&
|
| 1170 |
+
style.visibility !== 'hidden' &&
|
| 1171 |
+
Number.parseFloat(style.opacity || '1') > 0 &&
|
| 1172 |
+
rect.width > 0 &&
|
| 1173 |
+
rect.height > 0 &&
|
| 1174 |
+
(node.textContent || '').includes('The episode is loading...')
|
| 1175 |
+
);
|
| 1176 |
+
}""",
|
| 1177 |
timeout=5000,
|
| 1178 |
)
|
| 1179 |
+
progress_snapshot = _read_progress_text_snapshot(page)
|
| 1180 |
+
markdown_snapshot = _read_progress_markdown_snapshot(page)
|
| 1181 |
+
|
| 1182 |
+
assert progress_snapshot["present"] is True
|
| 1183 |
+
assert progress_snapshot["visible"] is True
|
| 1184 |
+
assert progress_snapshot["text"] == canonical_copy
|
| 1185 |
+
assert progress_snapshot["x"] is not None and progress_snapshot["x"] < 500
|
| 1186 |
+
assert progress_snapshot["y"] is not None and progress_snapshot["y"] > 300
|
| 1187 |
+
assert markdown_snapshot["text"] is None
|
| 1188 |
+
assert markdown_snapshot["pendingVisible"] is False
|
| 1189 |
+
assert markdown_snapshot["markdownVisible"] is False
|
| 1190 |
+
assert page.locator("#robomme_episode_loading_copy").count() == 0
|
| 1191 |
+
assert superseded_copy not in str(progress_snapshot["text"] or "")
|
| 1192 |
assert legacy_copy not in page.content()
|
| 1193 |
assert page.locator("#loading_overlay_group").count() == 0
|
| 1194 |
|
|
|
|
| 1219 |
assert calls["init"] >= 1
|
| 1220 |
|
| 1221 |
|
| 1222 |
+
def test_episode_loading_copy_after_change_episode(monkeypatch):
|
| 1223 |
+
ui_layout = importlib.reload(importlib.import_module("ui_layout"))
|
| 1224 |
+
|
| 1225 |
+
fake_obs = np.zeros((24, 24, 3), dtype=np.uint8)
|
| 1226 |
+
fake_obs_img = Image.fromarray(fake_obs)
|
| 1227 |
+
calls = {"init": 0, "next": 0}
|
| 1228 |
+
|
| 1229 |
+
def _load_result(uid: str, episode_idx: int, log_text: str):
|
| 1230 |
+
return (
|
| 1231 |
+
uid,
|
| 1232 |
+
gr.update(visible=True), # main_interface
|
| 1233 |
+
gr.update(value=fake_obs_img.copy(), interactive=False), # img_display
|
| 1234 |
+
log_text, # log_output
|
| 1235 |
+
gr.update(choices=[("pick", 0)], value=None), # options_radio
|
| 1236 |
+
"goal", # goal_box
|
| 1237 |
+
"No need for coordinates", # coords_box
|
| 1238 |
+
gr.update(value=None, visible=False), # video_display
|
| 1239 |
+
gr.update(visible=False, interactive=False), # watch_demo_video_btn
|
| 1240 |
+
f"PickXtimes (Episode {episode_idx})", # task_info_box
|
| 1241 |
+
f"Completed: {episode_idx - 1}", # progress_info_box
|
| 1242 |
+
gr.update(interactive=True), # restart_episode_btn
|
| 1243 |
+
gr.update(interactive=True), # next_task_btn
|
| 1244 |
+
gr.update(interactive=True), # exec_btn
|
| 1245 |
+
gr.update(visible=False), # video_phase_group
|
| 1246 |
+
gr.update(visible=True), # action_phase_group
|
| 1247 |
+
gr.update(visible=True), # control_panel_group
|
| 1248 |
+
gr.update(value="hint"), # task_hint_display
|
| 1249 |
+
gr.update(interactive=True), # reference_action_btn
|
| 1250 |
+
)
|
| 1251 |
+
|
| 1252 |
+
def fake_init_app(_request=None):
|
| 1253 |
+
calls["init"] += 1
|
| 1254 |
+
return _load_result("uid-next-episode", 1, "ready-1")
|
| 1255 |
+
|
| 1256 |
+
def fake_load_next_task_wrapper(uid):
|
| 1257 |
+
calls["next"] += 1
|
| 1258 |
+
time.sleep(0.8)
|
| 1259 |
+
return _load_result(uid, 2, "ready-2")
|
| 1260 |
+
|
| 1261 |
+
monkeypatch.setattr(ui_layout, "init_app", fake_init_app)
|
| 1262 |
+
monkeypatch.setattr(ui_layout, "load_next_task_wrapper", fake_load_next_task_wrapper)
|
| 1263 |
+
|
| 1264 |
+
demo = ui_layout.create_ui_blocks()
|
| 1265 |
+
|
| 1266 |
+
port = _free_port()
|
| 1267 |
+
host = "127.0.0.1"
|
| 1268 |
+
root_url = f"http://{host}:{port}/"
|
| 1269 |
+
|
| 1270 |
+
app = FastAPI(title="episode-loading-copy-change-episode-test")
|
| 1271 |
+
app = gr.mount_gradio_app(app, demo, path="/")
|
| 1272 |
+
|
| 1273 |
+
config = uvicorn.Config(app, host=host, port=port, log_level="error")
|
| 1274 |
+
server = uvicorn.Server(config)
|
| 1275 |
+
thread = threading.Thread(target=server.run, daemon=True)
|
| 1276 |
+
thread.start()
|
| 1277 |
+
_wait_http_ready(root_url)
|
| 1278 |
+
|
| 1279 |
+
try:
|
| 1280 |
+
with sync_playwright() as p:
|
| 1281 |
+
browser = p.chromium.launch(headless=True)
|
| 1282 |
+
page = browser.new_page(viewport={"width": 1280, "height": 900})
|
| 1283 |
+
page.goto(root_url, wait_until="domcontentloaded")
|
| 1284 |
+
page.wait_for_selector("#main_interface_root", state="visible", timeout=15000)
|
| 1285 |
+
page.wait_for_function(
|
| 1286 |
+
"""() => {
|
| 1287 |
+
const root = document.getElementById('header_task');
|
| 1288 |
+
if (!root) return false;
|
| 1289 |
+
const input = root.querySelector('input');
|
| 1290 |
+
if (input && typeof input.value === 'string' && input.value.trim() === 'PickXtimes') {
|
| 1291 |
+
return true;
|
| 1292 |
+
}
|
| 1293 |
+
const selected = root.querySelector('.single-select');
|
| 1294 |
+
return !!selected && (selected.textContent || '').trim() === 'PickXtimes';
|
| 1295 |
+
}""",
|
| 1296 |
+
timeout=5000,
|
| 1297 |
+
)
|
| 1298 |
+
page.wait_for_function(
|
| 1299 |
+
"""() => {
|
| 1300 |
+
const host = document.getElementById('native_progress_host');
|
| 1301 |
+
const markdown = host?.querySelector('[data-testid="markdown"]');
|
| 1302 |
+
const prose = markdown ? markdown.querySelector('.prose, .md') || markdown : null;
|
| 1303 |
+
const text = prose ? ((prose.innerText || prose.textContent || '').trim()) : '';
|
| 1304 |
+
return text === '';
|
| 1305 |
+
}""",
|
| 1306 |
+
timeout=5000,
|
| 1307 |
+
)
|
| 1308 |
+
|
| 1309 |
+
page.locator("#next_task_btn button, button#next_task_btn").first.click()
|
| 1310 |
+
|
| 1311 |
+
page.wait_for_function(
|
| 1312 |
+
"""() => {
|
| 1313 |
+
const node = document.querySelector('.progress-text');
|
| 1314 |
+
if (!node) return false;
|
| 1315 |
+
const style = getComputedStyle(node);
|
| 1316 |
+
const rect = node.getBoundingClientRect();
|
| 1317 |
+
return (
|
| 1318 |
+
style.display !== 'none' &&
|
| 1319 |
+
style.visibility !== 'hidden' &&
|
| 1320 |
+
Number.parseFloat(style.opacity || '1') > 0 &&
|
| 1321 |
+
rect.width > 0 &&
|
| 1322 |
+
rect.height > 0 &&
|
| 1323 |
+
(node.textContent || '').trim() === 'The episode is loading...'
|
| 1324 |
+
);
|
| 1325 |
+
}""",
|
| 1326 |
+
timeout=5000,
|
| 1327 |
+
)
|
| 1328 |
+
|
| 1329 |
+
progress_snapshot = _read_progress_text_snapshot(page)
|
| 1330 |
+
markdown_snapshot = _read_progress_markdown_snapshot(page)
|
| 1331 |
+
assert progress_snapshot["present"] is True
|
| 1332 |
+
assert progress_snapshot["visible"] is True
|
| 1333 |
+
assert progress_snapshot["text"] == "The episode is loading..."
|
| 1334 |
+
assert markdown_snapshot["text"] is None
|
| 1335 |
+
assert markdown_snapshot["pendingVisible"] is False
|
| 1336 |
+
assert markdown_snapshot["markdownVisible"] is False
|
| 1337 |
+
assert page.locator("#robomme_episode_loading_copy").count() == 0
|
| 1338 |
+
|
| 1339 |
+
deadline = time.time() + 15.0
|
| 1340 |
+
while time.time() < deadline:
|
| 1341 |
+
if _read_log_output_value(page) == "ready-2":
|
| 1342 |
+
break
|
| 1343 |
+
time.sleep(0.1)
|
| 1344 |
+
else:
|
| 1345 |
+
raise AssertionError("next episode load did not complete")
|
| 1346 |
+
assert page.locator("#robomme_episode_loading_copy").count() == 0
|
| 1347 |
+
|
| 1348 |
+
browser.close()
|
| 1349 |
+
finally:
|
| 1350 |
+
server.should_exit = True
|
| 1351 |
+
thread.join(timeout=10)
|
| 1352 |
+
demo.close()
|
| 1353 |
+
|
| 1354 |
+
assert calls == {"init": 1, "next": 1}
|
| 1355 |
+
|
| 1356 |
+
|
| 1357 |
def test_no_video_task_hides_manual_demo_button(monkeypatch):
|
| 1358 |
ui_layout = importlib.reload(importlib.import_module("ui_layout"))
|
| 1359 |
|
gradio-web/ui_layout.py
CHANGED
|
@@ -367,17 +367,54 @@ PROGRESS_TEXT_REWRITE_JS = f"""
|
|
| 367 |
() => {{
|
| 368 |
const modeEpisodeLoad = {json.dumps(LOAD_STATUS_MODE_EPISODE_LOAD)};
|
| 369 |
const modeIdle = {json.dumps(LOAD_STATUS_MODE_IDLE)};
|
|
|
|
|
|
|
|
|
|
| 370 |
const episodeLoadingText = {json.dumps(UI_TEXT["progress"]["episode_loading"])};
|
| 371 |
const queueWaitText = {json.dumps(UI_TEXT["progress"]["queue_wait"])};
|
| 372 |
const elapsedOnlyPattern = /^\\d+(?:\\.\\d+)?s$/i;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 373 |
|
| 374 |
window.__robommeLoadStatusMode = window.__robommeLoadStatusMode || modeIdle;
|
| 375 |
const getMode = () => window.__robommeLoadStatusMode || modeIdle;
|
| 376 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 377 |
const ensureOverlayStyles = () => {{
|
| 378 |
const host = document.getElementById("native_progress_host");
|
| 379 |
if (!(host instanceof HTMLElement)) {{
|
| 380 |
-
return;
|
| 381 |
}}
|
| 382 |
host.style.setProperty("position", "fixed", "important");
|
| 383 |
host.style.setProperty("inset", "0", "important");
|
|
@@ -407,6 +444,12 @@ PROGRESS_TEXT_REWRITE_JS = f"""
|
|
| 407 |
markdown instanceof HTMLElement
|
| 408 |
? markdown.querySelector(".prose, .md") || markdown
|
| 409 |
: null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 410 |
if (markdown instanceof HTMLElement) {{
|
| 411 |
markdown.style.setProperty("position", "fixed", "important");
|
| 412 |
markdown.style.setProperty("inset", "0", "important");
|
|
@@ -427,72 +470,165 @@ PROGRESS_TEXT_REWRITE_JS = f"""
|
|
| 427 |
prose.style.setProperty("line-height", "1.5", "important");
|
| 428 |
prose.style.setProperty("white-space", "pre-line", "important");
|
| 429 |
}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 430 |
}};
|
| 431 |
|
| 432 |
-
const
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 437 |
|
| 438 |
-
const rewriteNode = (
|
|
|
|
| 439 |
if (!(node instanceof HTMLElement)) {{
|
| 440 |
return;
|
| 441 |
}}
|
| 442 |
|
| 443 |
-
const
|
| 444 |
-
const previousCustom = node.dataset.robommeProgressCustom || "";
|
| 445 |
-
const raw =
|
| 446 |
-
node.dataset.robommeProgressCustomized === "1" && displayed === previousCustom
|
| 447 |
-
? node.dataset.robommeProgressRaw || displayed
|
| 448 |
-
: displayed;
|
| 449 |
if (getMode() !== modeEpisodeLoad) {{
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
/^processing/i.test(raw) ||
|
| 453 |
-
/^queue:/i.test(raw))
|
| 454 |
-
) {{
|
| 455 |
node.textContent = "";
|
| 456 |
}}
|
| 457 |
-
if (
|
| 458 |
-
node.dataset.robommeProgressCustomized === "1" &&
|
| 459 |
-
displayed === previousCustom &&
|
| 460 |
-
node.dataset.robommeProgressRaw
|
| 461 |
-
) {{
|
| 462 |
-
node.textContent = node.dataset.robommeProgressRaw;
|
| 463 |
-
}}
|
| 464 |
-
delete node.dataset.robommeProgressCustomized;
|
| 465 |
-
delete node.dataset.robommeProgressRaw;
|
| 466 |
-
delete node.dataset.robommeProgressCustom;
|
| 467 |
return;
|
| 468 |
}}
|
| 469 |
|
| 470 |
-
|
| 471 |
-
|
| 472 |
-
|
| 473 |
-
|
| 474 |
-
const segments = splitSegments(raw);
|
| 475 |
-
const suffix = segments.length > 1 ? ` | ${{segments.slice(1).join(" | ")}}` : "";
|
| 476 |
-
custom = `${{episodeLoadingText}}${{suffix}}`;
|
| 477 |
-
}} else if (normalized.startsWith("queue:")) {{
|
| 478 |
-
custom = `${{queueWaitText}} | ${{raw}}`;
|
| 479 |
}}
|
| 480 |
|
| 481 |
-
if (
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 482 |
return;
|
| 483 |
}}
|
| 484 |
|
| 485 |
-
node
|
| 486 |
-
|
| 487 |
-
|
| 488 |
-
|
| 489 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 490 |
}}
|
| 491 |
}};
|
| 492 |
|
| 493 |
const rewriteAll = () => {{
|
| 494 |
-
ensureOverlayStyles();
|
| 495 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 496 |
}};
|
| 497 |
|
| 498 |
const scheduleRewrite = () => {{
|
|
@@ -799,7 +935,7 @@ def create_ui_blocks():
|
|
| 799 |
|
| 800 |
with gr.Column(visible=True, elem_id="main_interface_root") as main_interface:
|
| 801 |
native_progress_host = gr.Markdown(
|
| 802 |
-
value=
|
| 803 |
visible=True,
|
| 804 |
container=False,
|
| 805 |
elem_id="native_progress_host",
|
|
|
|
| 367 |
() => {{
|
| 368 |
const modeEpisodeLoad = {json.dumps(LOAD_STATUS_MODE_EPISODE_LOAD)};
|
| 369 |
const modeIdle = {json.dumps(LOAD_STATUS_MODE_IDLE)};
|
| 370 |
+
const overlayStateIdle = "idle";
|
| 371 |
+
const overlayStateEpisodeLoad = "episode-load";
|
| 372 |
+
const overlayStateQueue = "queue";
|
| 373 |
const episodeLoadingText = {json.dumps(UI_TEXT["progress"]["episode_loading"])};
|
| 374 |
const queueWaitText = {json.dumps(UI_TEXT["progress"]["queue_wait"])};
|
| 375 |
const elapsedOnlyPattern = /^\\d+(?:\\.\\d+)?s$/i;
|
| 376 |
+
const progressStyleKeys = [
|
| 377 |
+
"position",
|
| 378 |
+
"top",
|
| 379 |
+
"right",
|
| 380 |
+
"bottom",
|
| 381 |
+
"left",
|
| 382 |
+
"transform",
|
| 383 |
+
"width",
|
| 384 |
+
"max-width",
|
| 385 |
+
"display",
|
| 386 |
+
"margin",
|
| 387 |
+
"text-align",
|
| 388 |
+
"color",
|
| 389 |
+
"font-size",
|
| 390 |
+
"font-weight",
|
| 391 |
+
"line-height",
|
| 392 |
+
"white-space",
|
| 393 |
+
"z-index",
|
| 394 |
+
"order",
|
| 395 |
+
];
|
| 396 |
+
const wrapStyleKeys = ["flex-direction", "gap"];
|
| 397 |
+
const spinnerStyleKeys = ["order"];
|
| 398 |
|
| 399 |
window.__robommeLoadStatusMode = window.__robommeLoadStatusMode || modeIdle;
|
| 400 |
const getMode = () => window.__robommeLoadStatusMode || modeIdle;
|
| 401 |
|
| 402 |
+
const isEpisodeLoadRaw = (raw) =>
|
| 403 |
+
elapsedOnlyPattern.test(raw) || /^processing/i.test(raw);
|
| 404 |
+
|
| 405 |
+
const isQueueRaw = (raw) => /^queue:/i.test(raw);
|
| 406 |
+
|
| 407 |
+
const clearInlineStyles = (node, keys) => {{
|
| 408 |
+
if (!(node instanceof HTMLElement)) {{
|
| 409 |
+
return;
|
| 410 |
+
}}
|
| 411 |
+
keys.forEach((key) => node.style.removeProperty(key));
|
| 412 |
+
}};
|
| 413 |
+
|
| 414 |
const ensureOverlayStyles = () => {{
|
| 415 |
const host = document.getElementById("native_progress_host");
|
| 416 |
if (!(host instanceof HTMLElement)) {{
|
| 417 |
+
return null;
|
| 418 |
}}
|
| 419 |
host.style.setProperty("position", "fixed", "important");
|
| 420 |
host.style.setProperty("inset", "0", "important");
|
|
|
|
| 444 |
markdown instanceof HTMLElement
|
| 445 |
? markdown.querySelector(".prose, .md") || markdown
|
| 446 |
: null;
|
| 447 |
+
const pending =
|
| 448 |
+
markdown instanceof HTMLElement ? markdown.closest(".pending") : host.querySelector(".pending");
|
| 449 |
+
const spinner =
|
| 450 |
+
host.querySelector("svg") instanceof SVGElement
|
| 451 |
+
? host.querySelector("svg").closest("div")
|
| 452 |
+
: null;
|
| 453 |
if (markdown instanceof HTMLElement) {{
|
| 454 |
markdown.style.setProperty("position", "fixed", "important");
|
| 455 |
markdown.style.setProperty("inset", "0", "important");
|
|
|
|
| 470 |
prose.style.setProperty("line-height", "1.5", "important");
|
| 471 |
prose.style.setProperty("white-space", "pre-line", "important");
|
| 472 |
}}
|
| 473 |
+
return {{
|
| 474 |
+
wrap,
|
| 475 |
+
markdown,
|
| 476 |
+
prose,
|
| 477 |
+
pending,
|
| 478 |
+
spinner,
|
| 479 |
+
}};
|
| 480 |
}};
|
| 481 |
|
| 482 |
+
const resolveRawText = (node) => {{
|
| 483 |
+
const displayed = (node.innerText || node.textContent || "").trim();
|
| 484 |
+
return node.dataset.robommeProgressRaw || displayed;
|
| 485 |
+
}};
|
| 486 |
+
|
| 487 |
+
const setProgressNodeVisible = (node, visible) => {{
|
| 488 |
+
node.style.setProperty("visibility", visible ? "visible" : "hidden", "important");
|
| 489 |
+
node.style.setProperty("opacity", visible ? "1" : "0", "important");
|
| 490 |
+
}};
|
| 491 |
+
|
| 492 |
+
const resetProgressNode = (node) => {{
|
| 493 |
+
const raw = node.dataset.robommeProgressRaw || (node.innerText || node.textContent || "").trim();
|
| 494 |
+
if (raw) {{
|
| 495 |
+
node.textContent = raw;
|
| 496 |
+
}}
|
| 497 |
+
delete node.dataset.robommeProgressCustomized;
|
| 498 |
+
delete node.dataset.robommeProgressRaw;
|
| 499 |
+
delete node.dataset.robommeProgressCustom;
|
| 500 |
+
clearInlineStyles(node, progressStyleKeys);
|
| 501 |
+
setProgressNodeVisible(node, true);
|
| 502 |
+
}};
|
| 503 |
+
|
| 504 |
+
const applyEpisodeLoadLayout = (entry, overlayRefs) => {{
|
| 505 |
+
const node = entry.node;
|
| 506 |
+
const {{
|
| 507 |
+
wrap,
|
| 508 |
+
spinner,
|
| 509 |
+
}} = overlayRefs || {{}};
|
| 510 |
+
if (wrap instanceof HTMLElement) {{
|
| 511 |
+
wrap.style.setProperty("flex-direction", "column", "important");
|
| 512 |
+
wrap.style.setProperty("gap", "24px", "important");
|
| 513 |
+
}}
|
| 514 |
+
if (spinner instanceof HTMLElement) {{
|
| 515 |
+
spinner.style.setProperty("order", "1", "important");
|
| 516 |
+
}}
|
| 517 |
+
node.dataset.robommeProgressCustomized = "1";
|
| 518 |
+
node.dataset.robommeProgressCustom = episodeLoadingText;
|
| 519 |
+
node.textContent = episodeLoadingText;
|
| 520 |
+
setProgressNodeVisible(node, true);
|
| 521 |
+
node.style.setProperty("position", "static", "important");
|
| 522 |
+
node.style.setProperty("top", "auto", "important");
|
| 523 |
+
node.style.setProperty("right", "auto", "important");
|
| 524 |
+
node.style.setProperty("bottom", "auto", "important");
|
| 525 |
+
node.style.setProperty("left", "auto", "important");
|
| 526 |
+
node.style.setProperty("transform", "none", "important");
|
| 527 |
+
node.style.setProperty("width", "min(720px, calc(100vw - 48px))", "important");
|
| 528 |
+
node.style.setProperty("max-width", "calc(100vw - 48px)", "important");
|
| 529 |
+
node.style.setProperty("display", "block", "important");
|
| 530 |
+
node.style.setProperty("margin", "0", "important");
|
| 531 |
+
node.style.setProperty("text-align", "center", "important");
|
| 532 |
+
node.style.setProperty("color", "#0f172a", "important");
|
| 533 |
+
node.style.setProperty("font-size", "var(--text-lg)", "important");
|
| 534 |
+
node.style.setProperty("font-weight", "600", "important");
|
| 535 |
+
node.style.setProperty("line-height", "1.5", "important");
|
| 536 |
+
node.style.setProperty("white-space", "pre-line", "important");
|
| 537 |
+
node.style.setProperty("z-index", "20", "important");
|
| 538 |
+
node.style.setProperty("order", "2", "important");
|
| 539 |
+
}};
|
| 540 |
|
| 541 |
+
const rewriteNode = (entry, overlayState) => {{
|
| 542 |
+
const node = entry.node;
|
| 543 |
if (!(node instanceof HTMLElement)) {{
|
| 544 |
return;
|
| 545 |
}}
|
| 546 |
|
| 547 |
+
const raw = entry.raw;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 548 |
if (getMode() !== modeEpisodeLoad) {{
|
| 549 |
+
resetProgressNode(node);
|
| 550 |
+
if (isEpisodeLoadRaw(raw) || isQueueRaw(raw)) {{
|
|
|
|
|
|
|
|
|
|
| 551 |
node.textContent = "";
|
| 552 |
}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 553 |
return;
|
| 554 |
}}
|
| 555 |
|
| 556 |
+
node.dataset.robommeProgressRaw = raw;
|
| 557 |
+
if (overlayState === overlayStateEpisodeLoad && isEpisodeLoadRaw(raw)) {{
|
| 558 |
+
applyEpisodeLoadLayout(entry, window.__robommeOverlayRefs || null);
|
| 559 |
+
return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 560 |
}}
|
| 561 |
|
| 562 |
+
if (overlayState === overlayStateQueue && isQueueRaw(raw)) {{
|
| 563 |
+
const custom = `${{queueWaitText}} | ${{raw}}`;
|
| 564 |
+
node.dataset.robommeProgressCustomized = "1";
|
| 565 |
+
node.dataset.robommeProgressCustom = custom;
|
| 566 |
+
if ((node.innerText || node.textContent || "").trim() !== custom) {{
|
| 567 |
+
node.textContent = custom;
|
| 568 |
+
}}
|
| 569 |
+
setProgressNodeVisible(node, true);
|
| 570 |
return;
|
| 571 |
}}
|
| 572 |
|
| 573 |
+
resetProgressNode(node);
|
| 574 |
+
}};
|
| 575 |
+
|
| 576 |
+
const syncOverlayContent = (overlayRefs, overlayState) => {{
|
| 577 |
+
if (!overlayRefs) {{
|
| 578 |
+
return;
|
| 579 |
+
}}
|
| 580 |
+
const {{
|
| 581 |
+
wrap,
|
| 582 |
+
markdown,
|
| 583 |
+
prose,
|
| 584 |
+
pending,
|
| 585 |
+
spinner,
|
| 586 |
+
}} = overlayRefs;
|
| 587 |
+
const markdownText =
|
| 588 |
+
prose instanceof HTMLElement ? ((prose.innerText || prose.textContent || "").trim()) : "";
|
| 589 |
+
const showMarkdown = overlayState === overlayStateIdle && markdownText.length > 0;
|
| 590 |
+
|
| 591 |
+
if (wrap instanceof HTMLElement && overlayState !== overlayStateEpisodeLoad) {{
|
| 592 |
+
clearInlineStyles(wrap, wrapStyleKeys);
|
| 593 |
+
}}
|
| 594 |
+
if (spinner instanceof HTMLElement && overlayState !== overlayStateEpisodeLoad) {{
|
| 595 |
+
clearInlineStyles(spinner, spinnerStyleKeys);
|
| 596 |
+
}}
|
| 597 |
+
if (pending instanceof HTMLElement) {{
|
| 598 |
+
pending.style.setProperty("display", showMarkdown ? "block" : "none", "important");
|
| 599 |
+
pending.style.setProperty("visibility", showMarkdown ? "visible" : "hidden", "important");
|
| 600 |
+
}}
|
| 601 |
+
if (markdown instanceof HTMLElement) {{
|
| 602 |
+
markdown.style.setProperty("display", showMarkdown ? "flex" : "none", "important");
|
| 603 |
+
markdown.style.setProperty("visibility", showMarkdown ? "visible" : "hidden", "important");
|
| 604 |
}}
|
| 605 |
}};
|
| 606 |
|
| 607 |
const rewriteAll = () => {{
|
| 608 |
+
const overlayRefs = ensureOverlayStyles();
|
| 609 |
+
window.__robommeOverlayRefs = overlayRefs;
|
| 610 |
+
const progressEntries = Array.from(document.querySelectorAll(".progress-text"))
|
| 611 |
+
.filter((node) => node instanceof HTMLElement)
|
| 612 |
+
.map((node) => {{
|
| 613 |
+
const raw = resolveRawText(node);
|
| 614 |
+
return {{
|
| 615 |
+
node,
|
| 616 |
+
raw,
|
| 617 |
+
}};
|
| 618 |
+
}});
|
| 619 |
+
let overlayState = overlayStateIdle;
|
| 620 |
+
if (getMode() === modeEpisodeLoad) {{
|
| 621 |
+
const hasEpisodeLoad = progressEntries.some((entry) => isEpisodeLoadRaw(entry.raw));
|
| 622 |
+
const hasQueue = progressEntries.some((entry) => isQueueRaw(entry.raw));
|
| 623 |
+
if (hasEpisodeLoad) {{
|
| 624 |
+
overlayState = overlayStateEpisodeLoad;
|
| 625 |
+
}} else if (hasQueue) {{
|
| 626 |
+
overlayState = overlayStateQueue;
|
| 627 |
+
}}
|
| 628 |
+
}}
|
| 629 |
+
|
| 630 |
+
progressEntries.forEach((entry) => rewriteNode(entry, overlayState));
|
| 631 |
+
syncOverlayContent(overlayRefs, overlayState);
|
| 632 |
}};
|
| 633 |
|
| 634 |
const scheduleRewrite = () => {{
|
|
|
|
| 935 |
|
| 936 |
with gr.Column(visible=True, elem_id="main_interface_root") as main_interface:
|
| 937 |
native_progress_host = gr.Markdown(
|
| 938 |
+
value="",
|
| 939 |
visible=True,
|
| 940 |
container=False,
|
| 941 |
elem_id="native_progress_host",
|