Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 82 additions & 3 deletions static/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -10028,6 +10028,7 @@ function _anchorSceneNodeForRow(row, opts){
}
function _anchorSceneTransparentNodeForRow(row, opts){
const settled=!!(opts&&opts.settled);
const live=!!(opts&&opts.live);
if(!row) return null;
let node=null;
const meta={
Expand Down Expand Up @@ -10079,10 +10080,14 @@ function _anchorSceneTransparentNodeForRow(row, opts){
}
if(!node) return null;
node.setAttribute('data-anchor-scene-row','1');
node.setAttribute('data-anchor-settled-scene-row','1');
if(settled) node.setAttribute('data-anchor-settled-scene-row','1');
if(live) node.setAttribute('data-anchor-live-scene-row','1');
node.setAttribute('data-anchor-row-id',String(row.row_id||row.local_id||''));
node.setAttribute('data-anchor-row-role',String(row.role||'activity'));
node.setAttribute('data-anchor-source-event-type',String(row.source_event_type||''));
if(opts&&opts.streamId) node.setAttribute('data-anchor-stream-id',String(opts.streamId));
if(opts&&opts.sessionId) node.setAttribute('data-session-id',String(opts.sessionId));
if(live) node.setAttribute('data-live-stream-owned','1');
return node;
}
// Whitespace-insensitive compare so a scene prose row that IS the final answer
Expand Down Expand Up @@ -10259,6 +10264,9 @@ function _prepareLiveAnchorScrollRebuildGuard(scrollSnapshot){
}
function renderLiveAnchorActivityScene(streamId, scene, opts){
opts=opts||{};
if(typeof isTransparentStream==='function'&&isTransparentStream()){
return _renderLiveAnchorActivitySceneTransparent(streamId,scene,opts);
}
if(typeof isCompactWorklogMode==='function'&&!isCompactWorklogMode()) return false;
if(!S.session||!S.activeStreamId) return false;
if(opts.sessionId&&S.session.session_id!==opts.sessionId) return false;
Expand All @@ -10269,11 +10277,11 @@ function renderLiveAnchorActivityScene(streamId, scene, opts){
if(!turn){
turn=_createAssistantTurn();
turn.id='liveAssistantTurn';
if(S.session) turn.dataset.sessionId=S.session.session_id;
$('msgInner').appendChild(turn);
}
turn.setAttribute('data-anchor-scene-live-owner','1');
turn.setAttribute('data-anchor-stream-id',String(streamId||''));
// Re-stamp when reusing a turn restored or previously rendered in another mode.
if(S.session) turn.dataset.sessionId=S.session.session_id;
const blocks=_assistantTurnBlocks(turn);
if(!blocks) return false;
Expand Down Expand Up @@ -10319,8 +10327,79 @@ function renderLiveAnchorActivityScene(streamId, scene, opts){
if(!scrollRebuildGuard.readerAwayFromBottom&&typeof scrollIfPinned==='function') scrollIfPinned();
return true;
}
function _renderLiveAnchorActivitySceneTransparent(streamId, scene, opts){
opts=opts||{};
if(!S.session||!S.activeStreamId) return false;
if(opts.sessionId&&S.session.session_id!==opts.sessionId) return false;
if(streamId&&S.activeStreamId!==streamId) return false;
const rows=_anchorSceneRowsForRendering(scene,{settled:false});
if(!rows.length) return false;
$('emptyState').style.display='none';
let turn=$('liveAssistantTurn');
if(!turn){
turn=_createAssistantTurn();
turn.id='liveAssistantTurn';
$('msgInner').appendChild(turn);
}
turn.setAttribute('data-anchor-scene-live-owner','1');
turn.setAttribute('data-anchor-stream-id',String(streamId||''));
turn.setAttribute('data-live-assistant-turn','1');
if(S.session) turn.dataset.sessionId=S.session.session_id;
const blocks=_assistantTurnBlocks(turn);
if(!blocks) return false;
const scrollSnapshot=_captureMessageScrollSnapshot();
const scrollRebuildGuard=_prepareLiveAnchorScrollRebuildGuard(scrollSnapshot);
blocks.querySelectorAll('[data-anchor-scene-owner="1"],[data-anchor-scene-row="1"]').forEach(el=>el.remove());
// Clear every legacy live activity surface this renderer can replace. The
// anchor-scene rows are now the source of truth for visible live activity.
blocks.querySelectorAll(
'.live-worklog[data-live-worklog-shell="1"],'+
'.tool-worklog-group[data-live-tool-call-group="1"],'+
'.tool-call-group[data-live-tool-call-group="1"],'+
'.tool-card-row[data-live-tid],'+
'.agent-activity-thinking[data-live-thinking="1"],'+
'.transparent-event-row[data-live-tid],'+
'[data-live-stream-owned="1"],'+
'.interim-collapse-toggle'
).forEach(el=>el.remove());
// Match the compact path: keep legacy live segments as hidden anchors so
// stream-owned metadata survives while the anchor scene owns visible activity.
blocks.querySelectorAll('[data-live-assistant="1"]').forEach(el=>{
el.classList.add('assistant-segment-worklog-source');
el.setAttribute('aria-hidden','true');
el.hidden=true;
});
const liveFooter=blocks.querySelector('#liveRunStatus');
let wrote=false;
for(const row of rows){
const node=_anchorSceneTransparentNodeForRow(row,{
live:true,
settled:false,
streamId:streamId||S.activeStreamId||'',
sessionId:S.session&&S.session.session_id,
});
if(!node) continue;
if(liveFooter&&liveFooter.parentElement===blocks) blocks.insertBefore(node,liveFooter);
else blocks.appendChild(node);
wrote=true;
}
if(wrote) _syncTransparentEventControls(turn);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 _syncTransparentEventControls skipped after DOM clear when no rows are written

_syncTransparentEventControls is guarded by if(wrote), but the DOM-clearing sweeps above (removing [data-anchor-scene-row="1"], legacy live rows, [data-live-stream-owned="1"], etc.) run unconditionally once the rows-length guard is passed. If every row in a non-empty scene produces null from _anchorSceneTransparentNodeForRow — e.g. a scene containing only thinking rows when window._showThinking===false — the function clears all visible live content but leaves the turn's expand/collapse and copy-button controls wired to nodes that no longer exist. The compact path avoids this by not gating its equivalent sync call on whether content was written.

if(typeof _moveLiveRunStatusToTurnEnd==='function') _moveLiveRunStatusToTurnEnd();
_restoreMessageScrollSnapshotSameFrame(scrollSnapshot);
if(scrollRebuildGuard&&scrollRebuildGuard.release){
requestAnimationFrame(()=>{
scrollRebuildGuard.release();
if(_messageUserUnpinned) _restoreMessageScrollSnapshotSameFrame(scrollSnapshot);
});
}
if(!scrollRebuildGuard.readerAwayFromBottom&&typeof scrollIfPinned==='function') scrollIfPinned();
return wrote;
Comment thread
greptile-apps[bot] marked this conversation as resolved.
}
function _renderLiveAnchorActivitySceneForStream(streamId, sessionId, opts){
const scene=_projectLiveAnchorActivitySceneForStream(streamId,(opts&&opts.mode)||'compact_worklog');
const mode=(typeof isTransparentStream==='function'&&isTransparentStream())
? 'transparent_stream'
: ((opts&&opts.mode)||'compact_worklog');
const scene=_projectLiveAnchorActivitySceneForStream(streamId,mode);
if(!scene) return false;
return renderLiveAnchorActivityScene(streamId,scene,{...(opts||{}),sessionId});
}
Expand Down
Loading
Loading