fix: preserve first-turn sidebar row during refresh
This commit is contained in:
committed by
nesquena-hermes
parent
5e01b00b8b
commit
20861b6721
BIN
docs/pr-media/1792/sidebar-first-turn-click-away-fixed.png
Normal file
BIN
docs/pr-media/1792/sidebar-first-turn-click-away-fixed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 140 KiB |
@@ -1450,6 +1450,53 @@ window.addEventListener('resize',()=>{
|
||||
// with stale data, causing sessions to vanish from the sidebar.
|
||||
let _renderSessionListGen = 0;
|
||||
|
||||
function _isOptimisticFirstTurnSessionRow(s){
|
||||
if(!s||!s.session_id||s.archived) return false;
|
||||
const messageCount=Number(s.message_count||0);
|
||||
if(messageCount<=0&&!s.pending_user_message) return false;
|
||||
return Boolean(
|
||||
s.is_streaming||
|
||||
s.active_stream_id||
|
||||
s.pending_user_message||
|
||||
s.pending_started_at||
|
||||
_isSessionLocallyStreaming(s)||
|
||||
_sessionStreamingById.get(s.session_id)===true
|
||||
);
|
||||
}
|
||||
|
||||
function _mergeOptimisticFirstTurnSessions(fetchedSessions){
|
||||
const merged=Array.isArray(fetchedSessions)?[...fetchedSessions]:[];
|
||||
const bySid=new Map();
|
||||
merged.forEach((s,idx)=>{if(s&&s.session_id) bySid.set(s.session_id,idx);});
|
||||
for(const local of Array.isArray(_allSessions)?_allSessions:[]){
|
||||
if(!_isOptimisticFirstTurnSessionRow(local)) continue;
|
||||
const sid=local.session_id;
|
||||
const idx=bySid.has(sid)?bySid.get(sid):-1;
|
||||
if(idx>=0){
|
||||
const fetched=merged[idx]||{};
|
||||
const localCount=Number(local.message_count||0);
|
||||
const fetchedCount=Number(fetched.message_count||0);
|
||||
const localTs=Number(local.last_message_at||local.updated_at||0);
|
||||
const fetchedTs=Number(fetched.last_message_at||fetched.updated_at||0);
|
||||
merged[idx]={
|
||||
...local,
|
||||
...fetched,
|
||||
message_count:Math.max(localCount,fetchedCount),
|
||||
last_message_at:Math.max(localTs,fetchedTs),
|
||||
updated_at:Math.max(Number(local.updated_at||0),Number(fetched.updated_at||0),localTs,fetchedTs),
|
||||
active_stream_id:fetched.active_stream_id||local.active_stream_id||null,
|
||||
pending_user_message:fetched.pending_user_message||local.pending_user_message||null,
|
||||
pending_started_at:fetched.pending_started_at||local.pending_started_at||null,
|
||||
is_streaming:Boolean(fetched.is_streaming||local.is_streaming||_isSessionLocallyStreaming(local)),
|
||||
};
|
||||
}else{
|
||||
merged.push({...local,is_streaming:true});
|
||||
bySid.set(sid,merged.length-1);
|
||||
}
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
||||
async function renderSessionList(){
|
||||
const _gen = ++_renderSessionListGen;
|
||||
try{
|
||||
@@ -1465,7 +1512,7 @@ async function renderSessionList(){
|
||||
// active profile so the "Show N from other profiles" toggle can render
|
||||
// without a second round-trip. Stashed on the module for renderSessionListFromCache.
|
||||
_otherProfileCount = sessData.other_profile_count || 0;
|
||||
_allSessions = sessData.sessions||[];
|
||||
_allSessions = _mergeOptimisticFirstTurnSessions(sessData.sessions||[]);
|
||||
_allProjects = projData.projects||[];
|
||||
// Capture server clock for clock-skew compensation (issue #1144).
|
||||
// server_time is epoch seconds from the server's time.time().
|
||||
|
||||
@@ -97,3 +97,20 @@ class TestSidebarFirstTurnVisibility:
|
||||
"The index-path empty-session filter must exempt pending first-turn sessions, "
|
||||
"matching the full-scan fallback."
|
||||
)
|
||||
|
||||
def test_session_refresh_preserves_optimistic_first_turn_rows_when_server_lags(self):
|
||||
src = read("static/sessions.js")
|
||||
assert "function _mergeOptimisticFirstTurnSessions" in src, (
|
||||
"renderSessionList() must merge locally optimistically inserted first-turn rows "
|
||||
"back into the fetched /api/sessions result. A session switch can re-fetch before "
|
||||
"the server has saved pending state, and replacing _allSessions would hide the "
|
||||
"new in-flight chat until the stream finishes."
|
||||
)
|
||||
render_start = src.index("async function renderSessionList")
|
||||
render_end = src.index("// ── Gateway session SSE", render_start)
|
||||
render_body = src[render_start:render_end]
|
||||
assign_idx = render_body.index("_allSessions =")
|
||||
assert "_mergeOptimisticFirstTurnSessions" in render_body[:assign_idx + 160], (
|
||||
"The fetched session list should be merged with optimistic rows at the assignment "
|
||||
"site, before completion transitions or renderSessionListFromCache() run."
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user