From 0fb1f35a34c0d60756a7c604ef152b3f58057eaa Mon Sep 17 00:00:00 2001 From: Michael Lam Date: Mon, 4 May 2026 16:57:08 -0700 Subject: [PATCH] fix: window long session message rendering --- docs/pr-media/734/message-window-top.png | Bin 0 -> 46119 bytes static/style.css | 2 + static/ui.js | 141 +++++++++++++++++------ tests/test_issue734_message_windowing.py | 45 ++++++++ 4 files changed, 153 insertions(+), 35 deletions(-) create mode 100644 docs/pr-media/734/message-window-top.png create mode 100644 tests/test_issue734_message_windowing.py diff --git a/docs/pr-media/734/message-window-top.png b/docs/pr-media/734/message-window-top.png new file mode 100644 index 0000000000000000000000000000000000000000..4b20d8410e5252ceb497851d569b825b92f22fbd GIT binary patch literal 46119 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 z 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 bLqupmBq_D2E&v3e2nZjg6eY{VjRO7`dZ #mainProfiles{display:flex;} /* ── Day-change separator ── */ .load-older-indicator{cursor:pointer;color:var(--accent-text,var(--blue));font-size:13px;padding:10px 0;text-align:center;user-select:none;} .load-older-indicator:hover{opacity:.75;} +.message-window-load-earlier{align-self:center;margin:4px auto 14px;padding:7px 14px;border:1px solid var(--border);border-radius:999px;background:var(--surface);box-shadow:var(--shadow-sm);} +.message-window-load-earlier:hover{background:var(--hover-bg);border-color:var(--accent);} .msg-date-sep { display: flex; align-items: center; gap: 10px; margin: 22px 0 10px; padding: 0 var(--msg-rail); diff --git a/static/ui.js b/static/ui.js index c45ab3da85..a0af57075f 100644 --- a/static/ui.js +++ b/static/ui.js @@ -90,6 +90,54 @@ function _renderUserFencedBlocks(text){ return s; } +const MESSAGE_RENDER_WINDOW_DEFAULT=50; +let _messageRenderWindowSid=null; +let _messageRenderWindowSize=MESSAGE_RENDER_WINDOW_DEFAULT; +function _resetMessageRenderWindow(sid){ + _messageRenderWindowSid=sid||null; + _messageRenderWindowSize=MESSAGE_RENDER_WINDOW_DEFAULT; +} +function _currentMessageRenderWindowSize(){ + return Math.max( + MESSAGE_RENDER_WINDOW_DEFAULT, + Number(_messageRenderWindowSize)||MESSAGE_RENDER_WINDOW_DEFAULT + ); +} +function _messageRenderableMessageCount(){ + let count=0; + for(const m of (S.messages||[])){ + if(!m||!m.role||m.role==='tool') continue; + if(_isContextCompactionMessage(m)||_isPreservedCompressionTaskListMessage(m)) continue; + const hasTc=Array.isArray(m.tool_calls)&&m.tool_calls.length>0; + const hasTu=Array.isArray(m.content)&&m.content.some(p=>p&&p.type==='tool_use'); + if(msgContent(m)||m.attachments?.length||(m.role==='assistant'&&(hasTc||hasTu||_messageHasReasoningPayload(m)))) count++; + } + return count; +} +function _messageHiddenBeforeCount(){ + return Math.max(0,_messageRenderableMessageCount()-_currentMessageRenderWindowSize()); +} +function _wireMessageWindowLoadEarlierButton(){ + const indicator=$('loadOlderIndicator'); + if(!indicator) return; + indicator.onclick=()=>{ + if(_messageHiddenBeforeCount()>0) _showEarlierRenderedMessages(); + else if(typeof _loadOlderMessages==='function') _loadOlderMessages(); + }; +} +function _showEarlierRenderedMessages(){ + const container=$('messages'); + const prevScrollH=container?container.scrollHeight:0; + const prevScrollTop=container?container.scrollTop:0; + _messageRenderWindowSize=_currentMessageRenderWindowSize()+MESSAGE_RENDER_WINDOW_DEFAULT; + renderMessages(); + if(container){ + const newScrollH=container.scrollHeight; + container.scrollTop=prevScrollTop+(newScrollH-prevScrollH); + } + _scrollPinned=false; +} + /* ── Image lightbox — click any .msg-media-img to enlarge ─────────────────── */ function _openImgLightbox(src, alt) { const lb = document.createElement('div'); @@ -3665,6 +3713,8 @@ function renderMessages(){ const inner=$('msgInner'); const sid=S.session?S.session.session_id:null; const msgCount=S.messages.length; + if(sid!==_messageRenderWindowSid) _resetMessageRenderWindow(sid); + const renderWindowSize=_currentMessageRenderWindowSize(); const hasTransientTranscriptUi=!!( (window._compressionUi&&(!window._compressionUi.sessionId||window._compressionUi.sessionId===sid)) || (window._handoffUi&&(!window._handoffUi.sessionId||window._handoffUi.sessionId===sid)) @@ -3680,9 +3730,10 @@ function renderMessages(){ // before those cards can be inserted. if(sid&&sid!==_sessionHtmlCacheSid&&!INFLIGHT[sid]&&!hasTransientTranscriptUi){ const cached=_sessionHtmlCache.get(sid); - if(cached&&cached.msgCount===msgCount){ + if(cached&&cached.msgCount===msgCount&&cached.renderWindowSize===renderWindowSize){ inner.innerHTML=cached.html; _sessionHtmlCacheSid=sid; + _wireMessageWindowLoadEarlierButton(); if(S.activeStreamId){scrollIfPinned();}else{scrollToBottom();} requestAnimationFrame(()=>{highlightCode();addCopyButtons();loadDiffInline();loadCsvInline();loadExcalidrawInline();loadPdfInline();loadHtmlInline();renderMermaidBlocks();renderKatexBlocks();}); requestAnimationFrame(()=>{highlightCode();addCopyButtons();initTreeViews();loadPdfInline();loadHtmlInline();renderMermaidBlocks();renderKatexBlocks();}); @@ -3716,15 +3767,6 @@ function renderMessages(){ }); $('emptyState').style.display=(vis.length||preservedCompressionTaskMessages.length)?'none':''; inner.innerHTML=''; - // Show "load older" indicator when older messages are available - if(typeof _messagesTruncated!=='undefined' && _messagesTruncated && S.messages.length>0){ - const indicator=document.createElement('div'); - indicator.id='loadOlderIndicator'; - indicator.className='load-older-indicator'; - indicator.textContent=typeof t==='function'?t('load_older_messages'):'↑ Scroll up or click to load older messages'; - indicator.onclick=()=>{if(typeof _loadOlderMessages==='function') _loadOlderMessages();}; - inner.appendChild(indicator); - } const compressionNode=compressionState?_compressionCardsNode(compressionState):null; const referenceMessage=S.messages.find(m=>_isContextCompactionMessage(m)); const referenceText=referenceMessage?msgContent(referenceMessage)||String(referenceMessage.content||''):''; @@ -3743,6 +3785,30 @@ function renderMessages(){ if(msgContent(m)||m.attachments?.length||(m.role==='assistant'&&(hasTc||hasTu||_messageHasReasoningPayload(m)))) visWithIdx.push({m,rawIdx}); rawIdx++; } + // Show a top affordance when earlier transcript content exists either in + // memory (DOM windowing) or on the server (paginated session fetch). + // Prefer expanding the local render window first so a fully loaded long + // session can reduce DOM nodes without losing in-memory transcript data. + const windowStart=Math.max(0, visWithIdx.length-renderWindowSize); + const hiddenBeforeCount=windowStart; + const renderVisWithIdx=visWithIdx.slice(windowStart); + const firstRenderedRawIdx=renderVisWithIdx.length?renderVisWithIdx[0].rawIdx:Infinity; + const hasServerOlder=!!(typeof _messagesTruncated!=='undefined' && _messagesTruncated && S.messages.length>0); + if(hiddenBeforeCount>0 || hasServerOlder){ + const indicator=document.createElement('button'); + indicator.type='button'; + indicator.id='loadOlderIndicator'; + indicator.className='load-older-indicator message-window-load-earlier'; + indicator.textContent=hiddenBeforeCount>0 + ? `Load earlier messages (${hiddenBeforeCount} hidden)` + : (typeof t==='function'?t('load_older_messages'):'Load earlier messages'); + indicator.onclick=()=>{ + if(hiddenBeforeCount>0) _showEarlierRenderedMessages(); + else if(typeof _loadOlderMessages==='function') _loadOlderMessages(); + }; + inner.appendChild(indicator); + _wireMessageWindowLoadEarlierButton(); + } let lastUserRawIdx=-1; for(let i=visWithIdx.length-1;i>=0;i--){ if(visWithIdx[i].m&&visWithIdx[i].m.role==='user'){ @@ -3751,7 +3817,7 @@ function renderMessages(){ } } const insertionAnchor=_compressionAnchorIndex( - visWithIdx, + renderVisWithIdx, compressionState ? compressionState.anchorMessageKey : sessionCompressionAnchorKey, compressionState ? (typeof compressionState.anchorVisibleIdx==='number' ? compressionState.anchorVisibleIdx : compressionState.anchorRawIdx) @@ -3762,8 +3828,10 @@ function renderMessages(){ const assistantSegments=new Map(); const assistantThinking=new Map(); const userRows=new Map(); - for(let vi=0;vi rawIdx){ + for(let i=0;i rawIdx){ anchorIdx=i; break; } @@ -3935,7 +4003,7 @@ function renderMessages(){ inner.appendChild(node); return; } - const anchorRawIdx=visWithIdx[anchorIdx].rawIdx; + const anchorRawIdx=renderVisWithIdx[anchorIdx].rawIdx; const anchorSeg=assistantSegments.get(anchorRawIdx); if(anchorSeg){ const turn=anchorSeg.closest('.assistant-turn'); @@ -3961,16 +4029,17 @@ function renderMessages(){ ? (()=>{const row=document.createElement('div');row.innerHTML=`
${_preservedCompressionTaskListCardsHtml(preservedCompressionTaskMessages)}
`;return row.firstElementChild;})() : null; const preservedOnlyAnchor=preservedCompressionRawIdxs.length - ? (()=>{let idx=null;for(let i=0;i{let idx=null;for(let i=0;iidx<=aIdx); anchorRow=fallbackIdx!==undefined?assistantSegments.get(fallbackIdx):assistantSegments.get(assistantIdxs[assistantIdxs.length-1]); } @@ -4088,6 +4158,7 @@ function renderMessages(){ const aIdx = parseInt(key); let anchorRow=assistantSegments.get(aIdx)||null; if(!anchorRow&&assistantIdxs.length){ + if(aIdxidx<=aIdx); anchorRow=fallbackIdx!==undefined?assistantSegments.get(fallbackIdx):assistantSegments.get(assistantIdxs[assistantIdxs.length-1]); } @@ -4126,25 +4197,26 @@ function renderMessages(){ } // Render per-turn duration and optional token usage on assistant messages. // Duration stays visible even when token usage is disabled, because it answers - // the basic "how long did that turn take?" UX question. + // the basic "how long did that turn take?" UX question. Only walk rendered + // assistant segments so hidden messages above the DOM window cannot skew the + // footer-to-message mapping. { - const asstRows=inner.querySelectorAll('.assistant-turn'); - let ai=0; // assistant-only index for DOM rows - for(let mi=0;mia-b); + for(const mi of renderedAssistantIdxs){ + const msg=S.messages[mi]||{}; + if(msg.role!=='assistant') continue; const hasTurnUsage=!!msg._turnUsage; const compactActivityForMessage=isSimplifiedToolCalling()&&( assistantThinking.has(mi)|| (S.toolCalls||[]).some(tc=>tc&&(tc.assistant_msg_idx!==undefined?tc.assistant_msg_idx:-1)===mi) ); const durationText=compactActivityForMessage?'':_formatTurnDuration(msg._turnDuration); - if(!hasTurnUsage&&!durationText){ai++;continue;} - if(ai>=asstRows.length) continue; - const row=asstRows[ai]; - const footerRows=row.querySelectorAll('.msg-foot'); + if(!hasTurnUsage&&!durationText) continue; + const seg=assistantSegments.get(mi); + const row=seg?seg.closest('.assistant-turn'):null; + const footerRows=row?row.querySelectorAll('.msg-foot'):[]; const targetFoot=footerRows.length?footerRows[footerRows.length-1]:null; - if(!targetFoot||targetFoot.querySelector('.msg-usage-inline,.msg-duration-inline')){ai++;continue;} + if(!targetFoot||targetFoot.querySelector('.msg-usage-inline,.msg-duration-inline')) continue; const fragments=[]; if(durationText){ const duration=document.createElement('span'); @@ -4167,7 +4239,6 @@ function renderMessages(){ targetFoot.classList.add('msg-foot-with-usage'); for(let i=fragments.length-1;i>=0;i--) targetFoot.insertBefore(fragments[i], targetFoot.firstChild); } - ai++; } } // Only force-scroll when not actively streaming — mid-stream re-renders @@ -4193,7 +4264,7 @@ function renderMessages(){ const _html=inner.innerHTML; // Only cache sessions with <300KB rendered HTML; evict oldest beyond 8 sessions. if(_html.length<300_000){ - _sessionHtmlCache.set(sid,{html:_html,msgCount}); + _sessionHtmlCache.set(sid,{html:_html,msgCount,renderWindowSize}); if(_sessionHtmlCache.size>8){_sessionHtmlCache.delete(_sessionHtmlCache.keys().next().value);} } } diff --git a/tests/test_issue734_message_windowing.py b/tests/test_issue734_message_windowing.py new file mode 100644 index 0000000000..f0b59ae0aa --- /dev/null +++ b/tests/test_issue734_message_windowing.py @@ -0,0 +1,45 @@ +from pathlib import Path + +REPO = Path(__file__).resolve().parents[1] +UI_JS = (REPO / "static" / "ui.js").read_text() +CSS = (REPO / "static" / "style.css").read_text() + + +def test_message_windowing_caps_initial_dom_to_recent_messages(): + assert "const MESSAGE_RENDER_WINDOW_DEFAULT=50" in UI_JS + assert "_messageRenderWindowSize=MESSAGE_RENDER_WINDOW_DEFAULT" in UI_JS + assert "const windowStart=Math.max(0, visWithIdx.length-renderWindowSize)" in UI_JS + assert "const renderVisWithIdx=visWithIdx.slice(windowStart)" in UI_JS + assert "for(let vi=0;vi0) _showEarlierRenderedMessages();" in UI_JS + assert "else if(typeof _loadOlderMessages==='function') _loadOlderMessages();" in UI_JS + + +def test_windowed_render_keeps_streaming_and_tool_activity_anchored_to_rendered_messages(): + assert "if(S.activeStreamId){\n scrollIfPinned();" in UI_JS + assert "const assistantIdxs=[...assistantSegments.keys()].sort((a,b)=>a-b);" in UI_JS + assert "if(aIdxa-b);" in UI_JS + assert "const seg=assistantSegments.get(mi);" in UI_JS + + +def test_window_state_participates_in_cache_and_cached_button_is_rewired(): + assert "cached.renderWindowSize===renderWindowSize" in UI_JS + assert "_sessionHtmlCache.set(sid,{html:_html,msgCount,renderWindowSize})" in UI_JS + assert "function _wireMessageWindowLoadEarlierButton()" in UI_JS + assert "_wireMessageWindowLoadEarlierButton();" in UI_JS + assert UI_JS.count("_wireMessageWindowLoadEarlierButton();") >= 2 + + +def test_load_earlier_affordance_has_button_styling_hook(): + assert "message-window-load-earlier" in UI_JS + assert ".message-window-load-earlier" in CSS + assert "border-radius:999px" in CSS