diff --git a/static/ui.js b/static/ui.js index 0d4abf6b..c8249f8b 100644 --- a/static/ui.js +++ b/static/ui.js @@ -265,10 +265,15 @@ function _statusCardHtml(card){ const MESSAGE_RENDER_WINDOW_DEFAULT=50; let _messageRenderWindowSid=null; let _messageRenderWindowSize=MESSAGE_RENDER_WINDOW_DEFAULT; +// Cached visWithIdx array — invalidated when S.messages.length changes. +let _visWithIdxCache=null; +let _visWithIdxCacheLen=0; function _resetMessageRenderWindow(sid){ _messageRenderWindowSid=sid||null; _messageRenderWindowSize=MESSAGE_RENDER_WINDOW_DEFAULT; _clearRenderCache(); + _visWithIdxCache=null; + _visWithIdxCacheLen=0; } // ── renderMd / _renderUserFencedBlocks cache ────────────────────────────── @@ -6120,15 +6125,29 @@ function renderMessages(options){ ? (()=>{const row=document.createElement('div');row.innerHTML=`
${_compressionReferenceCardHtml(referenceText,false)}${_preservedCompressionTaskListCardsHtml(preservedCompressionTaskMessages)}
`;return row.firstElementChild;})() : null; let preservedCompressionTaskCardsAttached=!!referenceNode; - const visWithIdx=[]; + // Cache visWithIdx so expanding the render window (Load earlier) doesn't + // re-scan S.messages from scratch. Invalidate only when the message array + // length changes — i.e. new messages arrived or session was truncated. + if(!_visWithIdxCache || _visWithIdxCacheLen !== S.messages.length){ + const rebuilt=[]; + let ri=0; + for(const m of S.messages){ + if(!m||!m.role||m.role==='tool'){ri++;continue;} + if(_isPreservedCompressionTaskListMessage(m)){ri++;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._statusCard||m.attachments?.length||(m.role==='assistant'&&(hasTc||hasTu||_messageHasReasoningPayload(m)))) rebuilt.push({m,rawIdx:ri}); + ri++; + } + _visWithIdxCache=rebuilt; + _visWithIdxCacheLen=S.messages.length; + } + const visWithIdx=_visWithIdxCache; const preservedCompressionRawIdxs=[]; let rawIdx=0; for(const m of S.messages){ if(!m||!m.role||m.role==='tool'){rawIdx++;continue;} if(_isPreservedCompressionTaskListMessage(m)){preservedCompressionRawIdxs.push(rawIdx);rawIdx++;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._statusCard||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