feat(realtime): live trajectory refresh on step_changed, auto-scroll to bottom, debounced 2s

This commit is contained in:
2026-03-08 00:48:55 +09:00
parent 1d0eae769a
commit 45efef442a

View File

@@ -144,7 +144,10 @@
if (session) {
chatPanel.showSession({ name: session.name, status: session.isRunning ? 'running' : 'connected' });
}
// trajectory에서 전체 대화 내용 가져오기
await refreshTrajectory(sessionId);
}
async function refreshTrajectory(sessionId, scrollToBottom = true) {
try {
const res = await fetch(`/api/bridge/trajectory/${sessionId}`);
if (!res.ok) return;
@@ -152,12 +155,26 @@
if (data.trajectory?.steps) {
const messages = parseTrajectoryToMessages(data.trajectory.steps);
chatPanel.updateChat(messages);
if (scrollToBottom) {
const el = document.getElementById('chatMessages');
if (el) setTimeout(() => el.scrollTop = el.scrollHeight, 50);
}
}
} catch (e) {
console.warn('[Bridge] trajectory 로드 실패:', e);
}
}
// 실시간 갱신 디바운스
let refreshTimer = null;
function scheduleRefresh() {
if (refreshTimer) return;
refreshTimer = setTimeout(() => {
refreshTimer = null;
if (activeBridgeSession) refreshTrajectory(activeBridgeSession, false);
}, 2000); // 2초 디바운스
}
/**
* Trajectory 스텝을 Antigravity 스타일 대화 흐름으로 변환
*
@@ -301,7 +318,15 @@
break;
case 'bridge_event':
handleBridgeEvent(msg);
// Bridge WS 이벤트 처리
if (msg.step_changed || msg.type === 'step_changed') {
scheduleRefresh();
loadBridgeSessions(); // 세션 목록도 갱신 (stepCount 등)
} else if (msg.type === 'session_changed' || msg.type === 'new_conversation') {
loadBridgeSessions();
} else if (msg.type === 'state_changed') {
scheduleRefresh();
}
break;
case 'screenshot':