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) { if (session) {
chatPanel.showSession({ name: session.name, status: session.isRunning ? 'running' : 'connected' }); chatPanel.showSession({ name: session.name, status: session.isRunning ? 'running' : 'connected' });
} }
// trajectory에서 전체 대화 내용 가져오기 await refreshTrajectory(sessionId);
}
async function refreshTrajectory(sessionId, scrollToBottom = true) {
try { try {
const res = await fetch(`/api/bridge/trajectory/${sessionId}`); const res = await fetch(`/api/bridge/trajectory/${sessionId}`);
if (!res.ok) return; if (!res.ok) return;
@@ -152,12 +155,26 @@
if (data.trajectory?.steps) { if (data.trajectory?.steps) {
const messages = parseTrajectoryToMessages(data.trajectory.steps); const messages = parseTrajectoryToMessages(data.trajectory.steps);
chatPanel.updateChat(messages); chatPanel.updateChat(messages);
if (scrollToBottom) {
const el = document.getElementById('chatMessages');
if (el) setTimeout(() => el.scrollTop = el.scrollHeight, 50);
}
} }
} catch (e) { } catch (e) {
console.warn('[Bridge] trajectory 로드 실패:', 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 스타일 대화 흐름으로 변환 * Trajectory 스텝을 Antigravity 스타일 대화 흐름으로 변환
* *
@@ -301,7 +318,15 @@
break; break;
case 'bridge_event': 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; break;
case 'screenshot': case 'screenshot':