/** * Session Panel — 세션 목록 UI 관리 */ class SessionPanel { constructor() { this.sessions = []; this.activeSessionId = null; this.onSessionSelect = null; // callback(sessionId) this.onSessionRemove = null; // callback(sessionId) this.listEl = document.getElementById('sessionList'); } /** * 세션 목록 업데이트 */ update(sessions) { this.sessions = sessions; this.render(); } /** * 활성 세션 설정 */ setActive(sessionId) { this.activeSessionId = sessionId; this.render(); } /** * 세션 목록 렌더링 */ render() { if (!this.listEl) return; if (this.sessions.length === 0) { this.listEl.innerHTML = `
세션이 없습니다
+ 버튼으로 추가하세요
`; return; } this.listEl.innerHTML = this.sessions.map(s => { const timeStr = s.lastModified ? this._relativeTime(s.lastModified) : ''; const projectBadge = s.project ? `${this._escapeHtml(s.project)}` : ''; const runningDot = s.isRunning ? '' : ''; const detail = s.isBridge ? `${s.stepCount || 0} steps${timeStr ? ' · ' + timeStr : ''}` : `${s.host}:${s.cdpPort} · ${this._statusText(s.status)}`; return `
${runningDot}${this._escapeHtml(s.name)}
${projectBadge}${detail}
`; }).join(''); // 이벤트 바인딩 this.listEl.querySelectorAll('.session-card').forEach(card => { card.addEventListener('click', (e) => { if (e.target.closest('.session-remove')) return; const id = card.dataset.sessionId; if (this.onSessionSelect) this.onSessionSelect(id); }); }); this.listEl.querySelectorAll('.session-remove').forEach(btn => { btn.addEventListener('click', (e) => { e.stopPropagation(); const id = btn.dataset.removeId; if (this.onSessionRemove) this.onSessionRemove(id); }); }); } _statusText(status) { const map = { connected: '연결됨', connecting: '연결 중...', disconnected: '연결 끊김', running: '실행 중', error: '오류', }; return map[status] || status; } _relativeTime(iso) { const diff = Date.now() - new Date(iso).getTime(); const mins = Math.floor(diff / 60000); if (mins < 1) return '방금'; if (mins < 60) return `${mins}분 전`; const hrs = Math.floor(mins / 60); if (hrs < 24) return `${hrs}시간 전`; const days = Math.floor(hrs / 24); return `${days}일 전`; } _projectColor(name) { const colors = { 'gravity_web': '#6366f1', 'Deriva': '#f59e0b', 'MMF': '#10b981', 'test1': '#8b5cf6', 'test2': '#ec4899', }; return colors[name] || '#64748b'; } _escapeHtml(str) { const div = document.createElement('div'); div.textContent = str; return div.innerHTML; } }