feat(cdp): Phase 2 - real-time UI mirroring via screencast + remote input

This commit is contained in:
2026-03-07 20:41:53 +09:00
parent 9234be33db
commit 4b855c9e57
6 changed files with 526 additions and 1 deletions

View File

@@ -6,9 +6,11 @@
// ─── 컴포넌트 초기화 ──────────────────────────────────
const sessionPanel = new SessionPanel();
const chatPanel = new ChatPanel();
const mirrorPanel = new MirrorPanel();
let ws = null;
let reconnectTimer = null;
let currentView = 'chat'; // 'chat' | 'mirror'
const WS_URL = `ws://${location.host}/ws`;
// ─── DOM 레퍼런스 ─────────────────────────────────────
@@ -31,6 +33,12 @@
const screenshotImage = document.getElementById('screenshotImage');
const closeScreenshot = document.getElementById('closeScreenshot');
const tabChat = document.getElementById('tabChat');
const tabMirror = document.getElementById('tabMirror');
const chatMessagesWrap = document.getElementById('chatMessages');
const chatInputArea = document.querySelector('.chat-input-area');
const mirrorHint = document.getElementById('mirrorHint');
// ─── WebSocket 연결 ───────────────────────────────────
function connectWebSocket() {
ws = new WebSocket(WS_URL);
@@ -105,6 +113,22 @@
screenshotOverlay.style.display = 'flex';
break;
case 'screencast_frame':
if (msg.sessionId === sessionPanel.activeSessionId) {
mirrorPanel.updateFrame(msg.data, msg.metadata);
if (mirrorHint) mirrorHint.style.display = 'none';
}
break;
case 'screencast_started':
if (!msg.success) {
showToast('Screencast 시작 실패', 'error');
}
break;
case 'screencast_stopped':
break;
case 'error':
showToast(msg.message, 'error');
break;
@@ -140,6 +164,42 @@
});
};
// ─── 미러 패널 이벤트 ─────────────────────────────
mirrorPanel.onStartScreencast = (sessionId) => {
sendWs({ type: 'start_screencast', sessionId });
};
mirrorPanel.onStopScreencast = (sessionId) => {
sendWs({ type: 'stop_screencast', sessionId });
};
mirrorPanel.onInputEvent = (sessionId, event) => {
sendWs({ type: 'input_event', sessionId, event });
};
// ─── 탭 전환 (Chat ↔ Mirror) ────────────────────────
function switchView(view) {
currentView = view;
// 탭 UI
tabChat.classList.toggle('active', view === 'chat');
tabMirror.classList.toggle('active', view === 'mirror');
// 채팅 영역 표시/숨기기
chatMessagesWrap.style.display = view === 'chat' ? '' : 'none';
if (chatInputArea) chatInputArea.style.display = view === 'chat' ? '' : 'none';
if (view === 'mirror') {
if (mirrorHint) mirrorHint.style.display = '';
mirrorPanel.start(sessionPanel.activeSessionId);
} else {
mirrorPanel.stop();
}
}
tabChat?.addEventListener('click', () => switchView('chat'));
tabMirror?.addEventListener('click', () => switchView('mirror'));
// ─── 모달 ─────────────────────────────────────────────
function showModal() {
addSessionModal.style.display = 'flex';