R14-TQ2-PQ1-R3-R2 Live Progress First-Paint + Monitor Scroll Reliability

#65
Files changed (1) hide show
  1. app.py +23 -7
app.py CHANGED
@@ -1039,6 +1039,15 @@ def _render_task_status_once(lang_code: str) -> None:
1039
  if not task_id:
1040
  return
1041
  status = str(task.get("status") or "")
 
 
 
 
 
 
 
 
 
1042
  if status in ACTIVE_TASK_STATUSES:
1043
  try:
1044
  result = generation_task_status(task_id)
@@ -1055,8 +1064,6 @@ def _render_task_status_once(lang_code: str) -> None:
1055
  status = str(task.get("status") or "")
1056
 
1057
  st.session_state["sp_last_ui_task_status"] = status
1058
- _render_monitor_box(task, lang_code)
1059
- _render_checkpoint_preview(task, lang_code)
1060
 
1061
  if status == "resumable":
1062
  resume_capable, _ = _local_resume_capability(task)
@@ -1127,7 +1134,7 @@ def _inject_generation_action_scroll(mode: str) -> None:
1127
 
1128
 
1129
  def _scroll_to_generation_action_once() -> None:
1130
- """One-time post-launch/resume scroll to the live manuscript, never per checkpoint."""
1131
  if not bool(st.session_state.get("sp_scroll_to_generation_action")):
1132
  return
1133
  st.session_state["sp_scroll_to_generation_action"] = False
@@ -1140,14 +1147,20 @@ def _scroll_to_generation_action_once() -> None:
1140
  const parentDocument = window.parent.document;
1141
  const fallbackId = {json.dumps(fallback_id)};
1142
  let done = false;
1143
- const locate = () => {{
1144
  if (done) return;
1145
- const target = parentDocument.getElementById('sp-live-manuscript-shell') || parentDocument.getElementById(fallbackId);
 
 
 
1146
  if (!target) return;
1147
  done = true;
1148
  target.scrollIntoView({{behavior:'auto', block:'center'}});
1149
  }};
1150
- [0, 40, 100, 220, 450, 800].forEach((delay) => window.parent.setTimeout(locate, delay));
 
 
 
1151
  }})();
1152
  </script>
1153
  """,
@@ -1280,7 +1293,10 @@ if st.session_state.get("sp_payment_required"):
1280
  _task_for_monitor = st.session_state.get("sp_generation_task") or {}
1281
  if str(_task_for_monitor.get("task_id") or "").strip():
1282
  st.markdown("<div id='sp-task-monitor-anchor'></div>", unsafe_allow_html=True)
1283
- _render_task_status_once(lang_code)
 
 
1284
  _scroll_to_generation_action_once()
 
1285
 
1286
 
 
1039
  if not task_id:
1040
  return
1041
  status = str(task.get("status") or "")
1042
+
1043
+ # First-paint invariant: render useful, localized progress and manuscript
1044
+ # placeholders before any browser bridge call that may yield once via
1045
+ # st.stop(). The browser watcher mutates these stable DOM targets in place
1046
+ # after the bridge handshake, so no duplicate monitor/preview is needed.
1047
+ st.session_state["sp_last_ui_task_status"] = status
1048
+ _render_monitor_box(task, lang_code)
1049
+ _render_checkpoint_preview(task, lang_code)
1050
+
1051
  if status in ACTIVE_TASK_STATUSES:
1052
  try:
1053
  result = generation_task_status(task_id)
 
1064
  status = str(task.get("status") or "")
1065
 
1066
  st.session_state["sp_last_ui_task_status"] = status
 
 
1067
 
1068
  if status == "resumable":
1069
  resume_capable, _ = _local_resume_capability(task)
 
1134
 
1135
 
1136
  def _scroll_to_generation_action_once() -> None:
1137
+ """One-time post-launch/resume scroll to the monitor/live manuscript."""
1138
  if not bool(st.session_state.get("sp_scroll_to_generation_action")):
1139
  return
1140
  st.session_state["sp_scroll_to_generation_action"] = False
 
1147
  const parentDocument = window.parent.document;
1148
  const fallbackId = {json.dumps(fallback_id)};
1149
  let done = false;
1150
+ const locate = (allowFallback = false) => {{
1151
  if (done) return;
1152
+ const target =
1153
+ parentDocument.getElementById('sp-task-monitor-anchor') ||
1154
+ parentDocument.getElementById('sp-live-manuscript-shell') ||
1155
+ (allowFallback ? parentDocument.getElementById(fallbackId) : null);
1156
  if (!target) return;
1157
  done = true;
1158
  target.scrollIntoView({{behavior:'auto', block:'center'}});
1159
  }};
1160
+ [0, 60, 140, 300, 650, 1000, 1600].forEach((delay) =>
1161
+ window.parent.setTimeout(() => locate(false), delay)
1162
+ );
1163
+ window.parent.setTimeout(() => locate(true), 2200);
1164
  }})();
1165
  </script>
1166
  """,
 
1293
  _task_for_monitor = st.session_state.get("sp_generation_task") or {}
1294
  if str(_task_for_monitor.get("task_id") or "").strip():
1295
  st.markdown("<div id='sp-task-monitor-anchor'></div>", unsafe_allow_html=True)
1296
+ # Install the one-time navigation script before task-status bridge calls.
1297
+ # If the first bridge handshake yields via st.stop(), the browser still
1298
+ # receives the anchor and can position the user at visible task progress.
1299
  _scroll_to_generation_action_once()
1300
+ _render_task_status_once(lang_code)
1301
 
1302